didier::stevens

dfir malware document triage
Shared modelPDF OLE / Office / RTF / emlXOR / encoding Cobalt Strike / PEFlag gotchas
WHAT IS THIS → A pack of standalone single-file Python triage tools (github.com/DidierStevens/DidierStevensSuite), the de-facto standard for pulling payloads out of documents, archives and malware. Preinstalled on REMnux / SIFT. They all share one usage model — learn it once (§01), apply everywhere.
01Install & the Shared Usage Model
Get it & run it
git clone https://github.com/DidierStevens/DidierStevensSuite
python3 oledump.py -h     # every tool self-documents
python3 oledump.py -m     # -m/--man = full embedded manual
pip install olefile        # only hard dep (oledump); rest are dep-free
# On REMnux/SIFT they're on PATH already: oledump.py file.doc
List → select → render
# Shared by oledump / zipdump / base64dump / pdf-parser / emldump
TOOL.py file            # bare run = NUMBERED listing of items
TOOL.py -s 3 -d file    # -s selects item 3, -d = raw dump (pipe/redirect)
TOOL.py -s 3 -x file    # -x hexdump   -a asciidump   -S strings
# base64dump extras: -s a = all items, -s L = largest
The cut mini-language (-c)
# Carve a byte range from the selected item: [from]:[to]
-c 0x10:0x110       # hex or decimal positions
-c 0:0x100l         # 'l' on 2nd term = LENGTH → first 256 bytes
-c :-0x100          # negative = from EOF (all but last 256)
-c "['MZ']:0x100l"   # ['ascii'] / [hex] = search anchor
-c "['ABC']2:"       # trailing N = Nth occurrence
Pipe stages together
# -d to stdout, '-' reads stdin → chain a whole unpack in one line
oledump.py -s 3 -v doc.doc | base64dump.py -n 40
base64dump.py -s L -d f | xorsearch -i - This
pdf-parser.py -o 8 -f -d - f.pdf | file-magic.py -
02PDF — pdfid + pdf-parser
pdfid.py — triage (count risk keywords)
pdfid.py -n file.pdf     # -n = only non-zero keywords (start here)
# Nonzero /JS /JavaScript /OpenAction /AA → auto-run code
#        /Launch → run external cmd   /EmbeddedFile → payload
#        /URI → exfil                 /ObjStm → hidden objects
pdfid.py -e file.pdf     # -e extra: entropy, name-obfuscation, dates
pdfid.py -f file.pdf     # -f FORCE parse (renamed/carved, no %PDF header)
pdfid.py -d file.pdf     # -d = DISARM: write neutered copy (NOT dump!)
pdf-parser.py — extraction
pdf-parser.py -a file.pdf              # --stats: type/keyword histogram
pdf-parser.py -s JavaScript file.pdf   # --search objects → get object id
pdf-parser.py -o 5 -f -w -d js.txt file.pdf
# -o object 5, -f=FILTER (FlateDecode…), -w raw, -d=DUMP → js.txt
pdf-parser.py --searchstream flag --unfiltered file.pdf
# grep INSIDE decompressed streams — flag buried in Flate
pdf-parser.py -t /EmbeddedFile file.pdf   # then -o <id> -f -d out.bin
JS EXTRACTION RECIPE → pdfid.py -n f.pdf (see what's dangerous) → pdf-parser.py -s JavaScript f.pdf (get object id) → pdf-parser.py -o <id> -f -w -d js.txt f.pdf (decompress + dump). Objects inside a compressed /ObjStm are invisible until you decompress the ObjStm first (-o <id> -f).
03OLE / Office / RTF / Email
oledump.py — OLE2 (.doc/.xls/.ppt/.msg) + VBA
oledump.py sample.doc            # list streams: M=macro, m=attrs-only, O=OLE obj
oledump.py -s 3 -v sample.doc   # -v/--vbadecompress → readable macro (NOT -d)
oledump.py --vbadecompresscorrupt -s 3 sample.doc
#   ↑ recover VBA when compression headers are tampered
oledump.py -s 3 -S sample.doc   # -S strings of a stream (fast IOC/URL)
oledump.py -p plugin_http_heuristics sample.doc
#   plugins: plugin_biff (XLS), plugin_vba_summary, plugin_dridex
rtfdump.py — RTF → embedded OLE
rtfdump.py sample.rtf         # tree of items; high hex ratio = embedded object
rtfdump.py -O sample.rtf      # -O overview of just the OLE objects
rtfdump.py -O -s 1 -H -d sample.rtf > obj1.bin
# -s select, -H hex-decode the RTF stream, -d dump → feed oledump
rtfdump.py -F O sample.rtf    # -F find OLE heuristically (obfuscated objdata)
emldump.py — MIME email (.eml)
emldump.py sample.eml            # list MIME parts (index, type, size)
emldump.py -s 4 -d sample.eml > attach.bin
#   -s by index OR by mime-type; -d auto-decodes b64/quoted-printable
emldump.py -I sample.eml         # -I show headers (routing/sender)
emldump.py -y rules.yar sample.eml # -y YARA-scan the parts
zipdump.py — ZIP / OOXML / JAR / APK
zipdump.py sample.zip            # list entries (index, size, CRC, encryption)
zipdump.py -s 2 -d sample.zip > entry.bin
zipdump.py -e sample.zip         # -e extended: MD5/magic per entry
zipdump.py -p infected -s 1 -d malware.zip
#   -p single password (default 'infected');  -P = password FILE (dict)
MALDOC CHAIN → rtfdump.py -O -s 1 -H -d doc.rtf > obj.bin → oledump.py obj.bin → oledump.py -s N -v obj.bin (decompress macro) → | base64dump.py -n 40 | xorsearch -i - http to unwrap the stager.
04XOR / Encoding / Byte Tooling
base64dump.py — find & decode blobs
base64dump.py file.txt          # list candidate streams (default -e b64)
base64dump.py -e all -u file.txt # -e all encodings (hex,b32,b85,url…); -u unique
base64dump.py -n 20 file.txt     # -n = min decoded length (kill noise)
base64dump.py -s L -d file.txt > out.bin  # -s L = largest, -d dump
XORSearch — brute XOR/ROL/ROT/ADD
xorsearch -i file.bin http    # brute all keys for a string; -i case-insensitive
xorsearch -h file.bin 4D5A     # -h = search for a HEX string (MZ)
xorsearch -p file.bin          # -p = search for embedded PE across all keys
xorsearch -s file.bin flag     # -s = SAVE decoded file for the matching key
xorsearch -w file.bin 'http?://*'  # -w wildcards (? one, * any)
xor-kpa.py — known-plaintext key recovery
xor-kpa.py plaintext.txt cipher.bin  # recover repeating XOR key from a crib
xor-kpa.py -n dos malware.vir   # -n dos = 'This program cannot be run in DOS mode'
xor-kpa.py -d plaintext.txt cipher.bin  # -d = also decode with recovered key
translate.py — per-byte transform
translate.py enc.bin -o dec.bin 'byte ^ 0x4D'
#   vars: byte, position; helpers rol()/ror(); '-' = stdin/stdout
translate.py in.bin -o out.bin 'byte ^ [0x11,0x22,0x33][position % 3]'
#   ↑ multi-byte repeating key via position index
translate.py in.bin -o out.bin 'rol(byte, 3)'
byte-stats.py — entropy / histogram
byte-stats.py file.bin           # entropy + null/printable/high byte counts
byte-stats.py -b 1024 file.bin  # -b bucket → per-region entropy + Position
#   locate an embedded encrypted/packed blob inside a larger file
re-search.py + helpers
re-search.py -n all -u sample.bin  # builtin IOC regexes: email,url,ipv4,md5,sha256
re-search.py '[A-Z]{2}[0-9]{2}\{[^}]+\}' dump.bin  # hunt CF26{...}
numbers-to-string.py dump.txt    # char-code array → ASCII (chr() deobfusc.)
format-bytes.py -f '<IIH' header.bin  # struct-parse typed header fields
05Cobalt Strike / PE / Authenticode
1768.py — CS beacon config
1768.py beacon.bin               # parse PE, auto XOR (0x69/0x2e), print config
1768.py -r shellcode.bin       # -r raw: don't parse as PE (shellcode/dumps)
1768.py -x dump.bin            # -x brute all single-byte XOR keys
1768.py -c *.bin               # -c CSV row per file (bulk C2/watermark triage)
1768.py -S -J beacon.bin       # -S sanity-check + -J JSON output
cs-decrypt-metadata / cs-parse-traffic
# Decrypt beacon checkin metadata with the team-server RSA key
cs-decrypt-metadata.py -f privkey.txt -V metadata.bin
#   -V shows recovered raw/AES/HMAC keys
# Then decrypt captured traffic (tool is cs-parse-TRAFFIC.py)
cs-parse-traffic.py -k <hmac>:<aes> capture.pcap
cs-parse-traffic.py -f dns -k H:A capture.pcap  # -f http|dns; -e extract
pecheck.py — PE triage
pecheck.py sample.exe            # headers, sections+entropy, overlay, PEiD
pecheck.py -l P dump.bin        # -l P = locate/carve MZ-PE headers in memory
pecheck.py -s -e 7.2 /samples   # -s scan folder, -e min entropy → flag packed
pecheck.py -g 0x1234,0x100 -x sample.exe  # -g offset,len then -x dump (overlay)
disitool.py — Authenticode
disitool.py extract signed.exe sig.der   # pull the signature blob
disitool.py delete signed.exe unsigned.exe # strip signature
disitool.py inject --paddata src.exe data.bin out.exe
#   hide data in the signature block (overlay stego)
06Flag Gotchas & Workflow
Same letter, different meaning
FlagIn this toolvs. in that tool
-dpdfid: DISARM (neuter copy)pdf-parser / oledump / *dump: DUMP bytes
-fpdfid: FORCE parsepdf-parser: apply FILTER (decompress)
-voledump: --vbadecompress (macro)NOT -d — that gives raw compressed bytes
-hxorsearch: search a HEX string(elsewhere -h is usually help)
-pxorsearch: find embedded PEzipdump: single password
TRIAGE CHECKLIST →  ① file + byte-stats.py — what am I holding, is it packed?  ② PDF → pdfid.py -n then pdf-parser.py  ③ Office/OLE → oledump.py (+ -v macros); RTF → rtfdump.py -O  ④ Archive/eml → zipdump.py / emldump.py  ⑤ Encoded blob → base64dump.py → xorsearch/xor-kpa → translate.py  ⑥ PE/beacon → pecheck.py, 1768.py  ⑦ When unsure of a flag: -h or -m, don't guess.