Reversing random stealer
TL;DR
Reversing a randomly-picked MalShare sample that turned out to be an ambitious .NET stealer:
- Credential and Discord token theft, encrypted strings and an embedded payload
- Static analysis with Binary Ninja: AMSI bypass, resource loading and payload decryption
- From a blind, random download all the way to full behaviour, step by step
Intro
It had been a while since I last took the time to reverse a real malware sample from scratch.
To avoid getting rusty (and because it’s genuinely fun), I gave myself a simple challenge: grab a completely random malware sample and see how far I could go analyzing it.
Off to MalShare, random click, download a binary without knowing what I was getting into.
Spoiler: it wasn’t a crappy crack, it was a fairly ambitious stealer, with password theft, Discord tokens, encryption, and the usual toolkit.
If you want a more involved stealer, I later went deep on a StealC infostealer using Heaven's Gate.
Fingerprints
First data points:
1 | File: malware |
At the time this article is written, these identifiers are already known (VT, etc).
However, the IoCs (especially contacted IPs) do not fully match what I found 👀
First contact
Let’s open the sample with the best tool (the one and only :binja_love: ). And then:
.text → 200 KB (Executable code)
.data → 50 KB (data, ok)
.rsrc → 539 KB WTF?!
539 KB of resources… In a 1.5 MB console executable… yeah, right.
For reference, .rsrc is supposed to contain legit stuff: icons, Windows dialogs, UI strings, etc. Normally just a few KB, rarely over 100 KB.
Here, we have half a megabyte of data. And looking more closely: no structured icons, no dialogs, no multilingual strings… Just a big binary blob with very high entropy (7.8/8.0).
Almost certainly encrypted.
Strings & Ciphers
First step: look at the strings, we quickly stumble onto:
Jackpot !
1 | "XOR cipher reversed" |
Not subtle at all…
We also notice many anti-debug related strings, plus classic malware stuff (AMSI patch, sandbox detection, etc). The logging is very verbose, they probably forgot to remove debug messages…
With the xrefs, we can identify the method that “decrypts” something:
This function is fairly large, with many memcpy/malloc, lots of loops, strongly suggesting a “decryption” routine.
Looking at where it’s called, we see another heavy function that (surprise) fetches data from .rsrc before calling decryption…
Cleaning up all the AMSI, ETW, etc. patching calls, it looks like:
1 | ... |
Clearly, it scans resources for a specific one, checks sizes, decrypts it, and executes the output, classic.
Decrypt
Now we need to extract and decrypt the payload to understand the malware behavior.
According to the embedded debug strings, the algorithm is in 3 stages:
- Transposition cipher reversed
- Substitution cipher reversed
- XOR cipher reversed
Payload extraction
First, we extract the payload.
The dropper pulls resources of type 0xa.
Using debug strings and size checks, we can infer the header structure:
1 | "Header parsed - headerSize: %d, blockSize: %d, keySize: %d, encryptedSize: %d" |
There’s also a check:
1 | 004062fd if (_Size u<= 0x31) |
From this, we deduce a header like:
- headerSize : between 8 and 15 bytes
- blockSize : probably 16
- keySize : probably 32
The rest looks like key + encrypted data.
Looking inside .rsrc, we quickly find a blob matching the format (right after standard resource structs):
We also see buffers similar to:
1 | struct DataBuffer |
Then the 3 decryption steps start.
Transposition
This one is simple: data is processed in 16-byte blocks and each block is reversed.
1 | Input: [00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F] [10 11 12 ...] |
Easy
Substitution
This part is trickier.
It uses a dynamic substitution table built per-byte using the key.
- Compute
base = (i + key[i % keySize]) % 256 - Build table:
table[j] = (base + j) % 256 - Find inverse:
find j such that table[j] == encrypted_byte - Replace byte with
j
It’s more obfuscation than encryption.
Thanks to debug strings, it was much easier to understand :D
As simple as a XOR
Final step: classic XOR loop using the 32‑byte key.
Let’s decrypt
Running all 3 steps on the extracted payload gives:
1 | def reverse_transposition(data, blockSize): |
We indeed get another binary!
1 | ghozt@maze:~/research/malware$ file output.exe |
.NET stealer
With ilspy we extract the C# code:
1 | ghozt@maze:~/research/malware$ ilspycmd output.exe -o ./decompiled_dotnet/ |
We quickly notice the strings are obfuscated.
Still, we can analyze the general behavior: credential theft, Discord token extraction, decrypting Chrome’s password store via DPAPI…
As for the strings, everything is AES‑encrypted with a lightly obfuscated key:
1 | public static class Strings |
There are 190 encrypted strings.
All use AES‑CBC, but with a twist: ciphertext is reversed before decryption.
AES keys themselves are XOR’d then Base64‑encoded.
Reproducing the algorithm gives:
1 | AES Key: OFhBENNkSFZaYyg2w6yQi5Mrqn7ypiPXDrim64w/FM0= |
And we can decrypt everything!
Targets and data theft
Browsers :
1 | Login Data → Credentials Chrome/Edge/Brave |
Messaging :
1 | discord\Local Storage\leveldb\ → Discord Tokens |
Crypto Wallets :
1 | wallet.dat → Bitcoin Core, Litecoin |
VPN :
1 | NordVPN\user.config |
Gaming & FTP :
1 | Steam\loginusers.vdf |
And the big one:
1 | net.tcp://185.172.128.70:3808 |
This is the C2 where all stolen data is sent using WCF (Windows Communication Foundation).
System recon
The malware also collects lots of system info:
1 | SELECT * FROM Win32_Processor -- CPU |
And detects security products:
1 | ROOT\SecurityCenter\AntivirusProduct |
Overview
With all strings decrypted, we can reconstruct the complete workflow:
Phase 1 : Recon
- System info (CPU, GPU, RAM, disks, OS)
- Security product detection
- VM/sandbox checks (QEMU, RDP)
- Running processes
- Installed software
- GeoIP via https://api.ip.sb/ip
Phase 2 : Data theft
- Browsers: credentials, cookies, autofill, credit cards
- Discord: tokens via LevelDB
- Telegram: tdata folder
- Wallets: crypto wallet files, extensions
- VPN: configs for NordVPN/ProtonVPN/OpenVPN
- FTP: FileZilla
- Gaming: Steam login data
Phase 3 : Exfiltration
- Compress stolen data
- Connect to C2: net.tcp://185.172.128.70:3808
- Send via WCF binary protocol
Persistance
One encrypted Base64 string decodes to another .NET executable, which is written into the Startup folder:
1 | using System; |
This is likely a dormant backdoor allowing later reattachment.
Conclusion
This sample appears to be a variant of Reline, a fork of RedLine Stealer that surfaced after the October 2024 takedown.
It features:
- Custom packer with simple ciphers (transposition + substitution + XOR)
- WCF exfiltration instead of SOAP/HTTP
- Debug strings left in
- Obfuscated .NET payload
IoCs Summary
1 | Dropper: |