I recently discovered how much malware is distributed through YouTube videos advertising video game hacks, and analyzing these samples has quickly become a hobby of mine. Searching “free Roblox hacks” in Youtube returns a treasure trove of skiddy malware which I use to get some fun RE practice in. The sample I analyze in this writeup seems to be a SalatStealer variant and is delivered by a dropper disguised as a well known ‘executor’ called Xeno.
Overview
This malware appears to be a SalatStealer style Go based stealer and RAT distributed through malicious Roblox executor downloads. This sample uses a multi stage infection chain, beginning with a padded dropper that elevates privileges through UAC, adds Microsoft Defender exclusions and then deploys a second stage Go payload. the payload targets credentials and cookies stored in chromium browsers including Brave, Edge, Chrome, Yandex, and Firefox. It also steals tokens and user information from other applications like Discord and Steam.
This sample also contains RAT functionality including remote command execution, file operations, keylogging, desktop streaming, webcam and microphone access, and a self removal feature. It performs SYSTEM impersonation and uses DPAPI to decrypt protected data.
The malware uses websocket based command and control infrastructure with dynamically resolved endpoints, DNS over HTTPS requests through Cloudflare and TON based endpoint resolution, suggesting the malware can retrieve or rotate C2 infrastructure dynamically.
Stage 1: The Dropper
| Name | Xeno.exe |
| Size | 773,120 KB |
| SHA256 | 9D7B41D89F1DD6AD8D6CD2BA4888B85BC3CD6A688920F2F62F187E7C9FAF0286 |
I ran into some roadblocks during my usual initial analysis steps. Since the file is over 700 MB I was unable to use VirusTotal because of their 500 MB limit and when I put it into Detect it Easy, it would stop responding after a certain point and not finish the analysis. Since the file was so large I decided to analyze it in a hex editor before going into a disassembler. I opened it in HxD and found that it was padded with null bytes after offset 0x24F70 all the way to 0x2F2FFFF0 . I carved out the null bytes and saved the new executable and now it was only 148 KB, time to throw it into a disassembler.

I opened the executable in Binary Ninja, found main and immediately saw some suspicious system calls. Immediately after starting it checks its process token for admin privileges with CheckTokenMembership. If it doesn’t have admin it launches a new instance of the program with ShellExecuteExW and the runas parameter. This prompts the user with UAC if accepted the non escalated program will exit, if denied the non escalated program will continue.

After the admin check there is a custom sleep loop which is a detonation timer and an anti analysis trick. The function takes an int value for time in MS as an arg and the loop will sleep for a max 24 hours before rechecking to see if it has waited long enough to be either equal or greater than the target time. Once it detects the target time has been reached it will return and continue execution.

After the sleep timer has been passed the program starts to prep for the next stage. The string “C:\Users” get loaded into a register and passed into a function that uses it to run powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -Command "Add-MpPreference -ExclusionPath 'C:\Users'" which excludes all the user profiles from windows defender, disabling it inside all the user directories.
After adding the exclusion, it downloads an executable named krelas.exe from a webserver, saves it as mtkadk.exe and executes it. This is the payload for the second stage.

Stage 2: Go RAT
Running the dropped file, krelas.exe through VirusTotal showed many detections with SalatStealer as the common threat label. Running it through Detect it Easy showed that it was UPX packed and compiled in a Go compiler. Being written in Go makes my job much easier because Go stores a lot of runtime metadata. Even a stripped executables dependencies, function names and address paths can be recovered using tools like GoReSym.
| Name | krelas.exe / mtkadk.exe |
| SHA-256 | 045f8ce9642990a68c7009489cbd8d52181cbe87b0574035782e63f22ebeb173 |
| VirusTotal Link | https://www.virustotal.com/gui/file/045f8ce9642990a68c7009489cbd8d52181cbe87b0574035782e63f22ebeb173/detection |
I UPX unpacked it and ran it through GoReSym, yielding unobfuscated function names, dependencies and relative virtual addresses I could use in Binary Ninja to find the exact functions I was looking for. Also interestingly enough the metadata referenced a Go module named “salat”, so this could be SalatStealer but I have not confirmed it. I’ll list some notable function names that were recovered below.
| startKeylogger | screenStream |
| startShell | getWebcams |
| getMics | proxySocks |
| Suicide | Elevate |
| Steal | getSteams |
| getDiscord | getChromeAutofills |
| getGeckoLogins | getSystemToken |
| impersonateSystem | getEp(get endpoint) |
| tonResolve | DPAPI |
After using Binja for a little bit I decided to switch to IDA free and to my surprise it was able to recover all the function names itself when I loaded in the executable, giving me a much more readable disassembly.
Acquiring Privilege’s
Shortly after execution begins, the malware checks if it has admin privileges, if not it uses ShellExecute to launch itself again with runas which triggers a UAC prompt. If the user elevates it both processes continue. It also creates a global mutex.
Once it has admin privileges, it obtains SYSTEM privileges by enabling SeDebugPrivilege. It duplicates the system token and calls ImpersonateLoggedOnUser, which causes the current thread to execute in the SYSTEM security context. Instead of creating a new SYSTEM process, this sample performs thread level impersonation. The impersonation routine is referenced in the DPAPI function which is used to get the Chromium master key and decrypt browser data.
Stealing Steam Accounts
The Steam install path is found by opening the Steam registry key, SOFTWARE\\Valve\\Steam and getting the value for SteamPath. If the Steam registry is not found it will fall back to the default steam path c:/program files (x86)/steam/config/. Once the Steam path is found it reads Steam VDF files "config.vdf" and "loginusers.vdf". It parses and traverses the VDF files looking for "InstallConfigStore", "Software", "Valve", "Steam", "users", and "AccountName". Then opens local.vdf and finds "MachineUserConfigStore", and "ConnectCache" which contain encrypted account and session linkage information. Once it has found everything it was looking for in those files it gets the CRC32 hash of the Steam username and uses it as the entropy string/key to decrypt your cached Steam session information using DPAPI. It returns the resulting text to its caller, the Steal function.
Now it has the information it needs to steal your Steam session. It formats it and writes it to a file called SteamTokens.txt and add its to a zip archive for exfil.
Webcam, Microphone, and Display Capture
ffmpeg.exe is used to enumerate webcams and microphones with ffmpeg.exe -hide_banner -list_devices true -f dshow -i dummy then parses the output with regex.
To capture and stream from a webcam it uses DirectShow and ffmpeg, ffmpeg.exe -f dshow video=<device name> mjpeg. It reads the stdout stream for JPEG markers bytes and sends complete JPEG frames through go channels for remote streaming.
Capturing from the mic uses a similar process and is started using ffmpeg.exe -hide_banner -loglevel error -nostats -f dshow -i audio=<mic name> -af aformat=sample_fmts=s32:channel_layouts=mono:sample_rates=32000.
Keylogging
This sample compiles a function main.keyPressCallback into a windows callback using syscall.compileCallback. Then it calls SetWindowsHookExA and hooks into WH_KEYBOARD_LL which monitors low level keyboard input events. It then sends the thread into a Windows message loop to keep it alive using GetMessageW so keyboard events can be delivered to the main.keyPressCallback callback.
The key press callback function translates the key codes into readable characters. It accounts for Shift, Caps Lock, keyboard layout, tracks all special keys and the active foreground window.
Command and Control Architecture
This sample uses an obfuscated command and control architecture that resolves it’s endpoints at runtime rather than storing plaintext server addresses. Endpoint entries are selected from internal tables, decoded through a custom routine, and then used to establish outbound connections. During initialization the malware creates HTTPS/TLS clients with certificate validation disabled, allowing communication with self signed or attacker controlled infrastructure.
Communication is performed through multiple transport mechanisms, including HTTPS, websocket style sessions, raw TCP connections, and integration with the TON blockchain. TON records are resolved at runtime and passed through a custom decoding routine that extracts hidden configuration data from TON address strings. This allows operators to update C2 infrastructure without modifying the malware binary. The use of endpoint obfuscation, disabled TLS verification, decentralized TON based communications, and multiple transport methods provides operational flexibility while making detection and infrastructure disruption more difficult.
