skip to content
Pocket Change

Malware Analysis: Stealc

/ 5 min read

Stealc

Introduction

I have been spending some time on olderish malwares learning different techniques etc.. This time I wanted to actually take a look at a recent sample and trendy one to put some skills in action and see where I am at.

Analysis

sha256: 7d09e2f4a9ee25024a1498f833448d095787e6faf8908ed9ed3fe536032c0de6

Its a 3 stage malware that mainly drops in AutoIt3.exe which is a legitimate executable for automating the windows GUI. Its used by malware to run its malicious script.

image

The exectuable drops AutoIt3.exe and Vintage.a3x, we can decompile the Autolt v3 script using Autoit-ripper autoit-ripper Vintage.a3x output_folder.

image

Its a huge shellcode with lots of obfuscation.

Full Flow Dynamic Analysis

The chain runs: QuickFetch.exeAutoIt3.exe #1 (decodes/runs the Vintage.a3x shellcode) → AutoIt3.exe #2 → AutoIt3.exe #3 (hollowed, gathers info and beacons to C2). Given the amount of obfuscation, its easier to understand the multiple stage behaviour dynamically

image

The executable hits CreateProcessA passing Vintage.a3x (the obfuscated shellcode) as first payload.

image

We can confirm that on return, we see the full command

image

Attaching the process created by the main process QuickFetch.exe to follow the second stage.

image

The second stage following similar process by creating a 2nd AutoIt3.exe process.

image

Attaching the third and final process, PID 964, which will turn out to be the one that receives the payload via process hollowing, covered next.

Process Injection: Process Hollowing for 3rd payload (Attack.mitre: T1055.012)

Process hollowing is commonly performed by creating a process in a suspended state then unmapping/hollowing its memory, which can then be replaced with malicious code

The 2nd payload’s AutoIt3.exe process is spawned via CreateProcessW in a suspended state, then:

image

Calls NtWriteVirtualMemory undocumented WIN API

NtWriteVirtualMemory(
    _In_ HANDLE ProcessHandle,
    _In_opt_ PVOID BaseAddress,
    _In_reads_bytes_(NumberOfBytesToWrite) PVOID Buffer,
    _In_ SIZE_T NumberOfBytesToWrite,
    _Out_opt_ PSIZE_T NumberOfBytesWritten
    );

Following the base Address at 1E91F5DB7D0 we see executable.

The stall/mystery

During analysis at some point after a couple of days I wasn’t able to reach the final HttpSendRequestW call at the third process. Tried different environments and clean ones too, but wasn’t able to reach the same behaviour I had before.

image

Until manual stepping lead to this interesting string date 06/08/2026, it appears it will stop the campaign and reaching C2 instance after this date, so manually adjusting the computer time got me the full execution back.

Final Load

image image
L"nJDK\\jdk-21.0.1\\bin;C:\\Tools\\apktool;C:\\Users\\Hassan\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Program Files\\BinDiff\\bin;C:\\Tools\\Beta-cbb1d5c32d02b4e07128a197c8b8fb6ea597916a;C:\\Tools\\DidierStevensSuite-8190354314d6f42c9ddc477a795029dc446176c5;C:\\Program Files\\dotnet\\;C:\\Program Files (x86)\\dotnet\\;C:\\Program Files\\Git\\cmd;C:\\Program Files\\nodejs\\;C:\\Program Files\\Microsoft VS Code\\bin;C:\\Users\\Hassan\\AppData\\Local\\Microsoft\\WindowsApps;;C:\\Tools\\Cmder;C:\\Users\\H"

Info gathering involving gathering some of the reverse-engineering-tool detection likely used to fingerprint the machine it’s installed on. The same function also runs through a checklist of known VM/sandbox artifacts — virtualbox, vmtools, VMwareService, VMwareTray, VBoxService, VBoxTray, qemu-ga, and prl_cc — alongside the PATH string, before the results get written into a report structure together with the build ID (james1).

Reaching C2

After adjusting the clock past the kill-date, execution finally continued past the GetComputerNameW and PATH-gathering steps I covered above. A few instructions later, I finally hit the call I’d been chasing for days:

image

wininet.HttpSendRequestW — the actual outbound request. The headers confirm what’s being sent:

igd7OJA5OvYiyOQaWLZBZdxYieRIKTTHClnLykBlJQRPREwcqq71xr5f3AxZulVhHVhaHNjFebVAYprywUH+4PAW80lhtqJlO2lEjKrhzyPYA/aR XmnrIq2gOs3k3v1Mg2/

The destination being: http://87.120.104.81/d19ca32cb5a444ac8b87[.]php

Crosschecking my analysis with JoeSandbox anaylsis of the same sample (same C2 URL, same process names and PIDs), which is a good sanity check. I didn’t decode the base64 body for this post — that’s a reasonable next step if I revisit this sample, since it would confirm exactly what fields are being sent on first check-in. For now, this confirms the malware completed its info-gathering and successfully phoned home once the anti-analysis date check was out of the way.

Attempting RC4 Key Extraction

With the C2 endpoint confirmed, the next step was pulling the RC4 key(s) StealC uses to encrypt its C2 traffic and config strings, ideally without having to manually locate the key-scheduling routine by hand.

I dumped the unpacked payload directly out of process memory during the hollowing step described earlier (the buffer written via NtWriteVirtualMemory, ~0xD4000 bytes, sitting cleanly as a valid PE with intact MZ/PE headers), then ran it against RussianPanda95’s public StealC config extractor:

The script returned a result immediately:

[+] Detected RC4 key: Ml9YqYQp+CVwWsQMxtg4NGqr04Y= [+] Detected RC4 traffic key: WRH9StRDAE8Qdr2SBo { “metadata”: { “build_id”: “james1”, “rc4_key”: “Ml9YqYQp+CVwWsQMxtg4NGqr04Y=”, “rc4_traffic”: “WRH9StRDAE8Qdr2SBo”, “c2”: null }, “decrypted_strings”: [ “P\v4”,… ] }

The garbage output made sense once I looked at how the script actually works. It finds StealC’s config by searching the binary for the string "string too long", then assumes the RC4 key sits a fixed distance away from it. It wasn’t the case for me.

I didn’t go find the real key by hand for this post — that would mean either locating the RC4 key-scheduling loop directly in the disassembly, or catching the key live in a debugger at the moment it’s actually used.

Conclusion

This sample turned out to be a good exercise in patience as much as technique — the multi-stage AutoIt3 relaunch chain and process hollowing were straightforward enough to trace once I knew what to look for, but the kill-date check cost me several days of “why isn’t this working” before I found the actual cause. That’s probably the main takeaway: when dynamic analysis stops producing results partway through a chain that worked before, it’s worth checking for time-based or environment-based gates before assuming the breakpoints or environment are wrong.

The RC4 extraction attempt was a good reminder that public tooling built against one sample doesn’t always generalize cleanly to every build of a malware family.

IOCs

Sample

  • SHA256: 7d09e2f4a9ee25024a1498f833448d095787e6faf8908ed9ed3fe536032c0de6
  • Original filename: QuickFetch.exe

C2

  • http://87.120.104.81/d19ca32cb5a444ac8b87[.]php

Anti-analysis

  • Kill-date check: execution halts before C2 communication if system date is past 06/08/2026

MITRE ATT&CK

  • T1055.012 — Process Injection: Process Hollowing
  • T1140 — Deobfuscate/Decode Files or Information (string decryption)
  • T1497.001 — Virtualization/Sandbox Evasion
  • T1027 — Obfuscated Files or Information (AutoIt shellcode wrapper)