Case study · Sample Analysis
Darkhotel JPEG Steganography Sample Analysis
You are the analyst assigned a suspected APT steganography sample, under a fully static, zero-execution constraint. Work through this article and you will get a precise identification of the stego format, a full behavior-chain reconstruction of the roughly 990KB parasitic payload, and an IOC list that includes the archive password and the injected DLL.
JPEG · 1,321,015 bytes· Stego payload ≈ 990KB (Base64-encoded)· Parasitic host: WinRAR 32-bit· Fully static · zero execution· Landi N2.5
Session replays & reports are original records in Simplified Chinese · Built with AVL Code + the Landi model
Overview
- 01
Break the type deadlock — the error value 0xd8ff exposes the JPEG magic
You hand AVL Code an MD5-named sample; PE parsing and imphash fail in a row, but the head/tail readings steer the direction back to "goods hidden in an image".
- 02
Locate the boundary — a JPEG + CRLF + two-segment Base64 stego format
You see FF D9 at offset 703, a 24-character first line decoding into 18 bytes, and the payload ending in "=="; after one failed decode it self-heals from the line structure and recovers the PE payload.
- 03
Triple identity verification — the 7-Zip hypothesis flips to WinRAR
You get the PE32 fingerprint, compile time and entropy; a sharp.exe compression command initially points to 7-Zip, until the PDB path finally pins the identity as WinRAR 32-bit.
- 04
Behavior and IOCs — detection and response usable the same day
You get the behavior chain of persistence, WOW64 injection, theft packaging and obfuscated PowerShell execution, plus an IOC table with the archive password and injected DLL, and a concluding report.
Task for AVL Code
Analyze the sample 08943bb237926dd1376d799a4afe797d for me Prerequisites: The sample to analyze is already in the workspace (for this case, an MD5-named sample file in an air-gapped analysis environment); no analysis tools need to be installed, and the whole run is read-only with the sample never executed
Reproduction materials at the end of this article Try it yourself ↓
Typing the Sample
The sample is MD5-named and gives away nothing — the analysis starts with the most basic profiling and typing:
Analyze the sample 08943bb237926dd1376d799a4afe797d for meCollects the sample hashes and basics (1,321,015 bytes)
→ PE parsing errors out and the entropy 5.349 suggests mixed content → reads the file head: FF D8 FF + "JFIF" — it is a JPEG
→ Large stretches of 0x41 padding data at the file tail
→ sec.strings finds a Base64-shaped PE header at offset 735 (it decodes to MZ)
→ Summary: a Base64-encoded Windows executable is attached to the image tailExtracting the Stego Payload
The 18-byte "key/identifier" on the first line and the Base64 shredded by line breaks are the two turning points of this step:
Analyze the sample 08943bb237926dd1376d799a4afe797d for meSearches for the JPEG end marker and MZ → hit: Base64 data immediately after FF D9, all the way to the file tail
→ Writes a script to extract and decode: blocked for a while by Python/escaping → rewrites it as a script file and runs it
→ The decoded result does not start with MZ → investigation finds the Base64 shredded by line breaks: the first line’s 24 characters decode into an 18-byte "key/identifier", and only from the second line does MZ begin
→ Drops the first line, strips whitespace, decode succeeds: a roughly 990KB PE payload recoveredPE Profiling
The identity does not settle on the first try: one compression-command string led the first hypothesis astray:
Analyze the sample 08943bb237926dd1376d799a4afe797d for mesec.pe_parse builds the profile: PE32 (x86) · entropy 6.44, unpacked · imphash 1a94fa4f…
→ Strings show sharp.exe and a compression command line → looks like 7-Zip
→ Searching 7-Zip-related PDB paths misses, but hits RAR.pdb instead → identity flips: it is the WinRAR 32-bit archiver
→ Hash profiling (MD5 / SHA256)Behavior and IOCs
Parasitic inside the payload is a complete information-theft pipeline, with every piece of evidence drawn from strings and PE structure:
Analyze the sample 08943bb237926dd1376d799a4afe797d for meUses a Python script to search key strings (grep does not work on binaries)
→ Persistence: Run key + AppInit_Dlls · Injection: CreateRemoteThread / WriteProcessMemory, injecting the DLL dpnom.dll
→ Theft targets: mail identity databases, recent-documents list, etc.
→ Packaging and exfiltration: sharp.exe encrypted archive (password PleaseTakeOut!@#) · Script execution: drops winword.ps1 and invokes PowerShell with case obfuscation
→ Self-deletion, privilege escalation and information-gathering APIs all present
→ Compiles the IOC table and the concluding reportTry it yourself
Download the session and import it into your workspace to replay the full process, or hand your sample straight to AVL Code:
› Analyze the sample <your sample filename> for me - Limitations:
- ① This session ran in an air-gapped environment; only the sample hash was provided and nothing was executed;
- ② Tool calls render as empty in the session export, so the action sequences follow the assistant’s narration, the compact summaries and the final report;
- ③ The YARA/SIGMA/network detection rules and standardized response workflow recorded in the existing entry do not appear in this session export, so this article does not present them; ④ The behavior-chain items (persistence/injection/theft/script execution) in the session come mainly from the final-report section, with no second independent source beyond the existing deliverables summary; ⑤ The "key/identifier" nature of the first line’s 18 Base64 bytes is a conjecture from the session; ⑥ The Darkhotel attribution comes from the existing entry for this case and no attribution process appears in the session.
Next question: The payload itself is a WinRAR parasitized by a theft module, but network-side IOCs are missing — next step: chase the payload source of the injected DLL dpnom.dll and the dropped behavior of winword.ps1, and extend the detection rules to the host side.
