Description
What could go wrong if we let Word documents run programs? Download weird.docm and find out.
Setup
Download the challenge file weird.docm from the picoGym challenge page.
wget <url>/weird.docmSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Identify the file type
ObservationThe extension is .docm rather than .docx, so this is a macro-enabled Word document. Run the file command to confirm the container format before picking an extraction tool.Run the file command on weird.docm to confirm it is a Microsoft Word 2007+ document, meaning a ZIP-based OOXML container rather than a legacy .doc. The 'm' suffix in .docm signals that the file can contain VBA or script macros that execute when the document is opened. This is the entry point for the challenge.bashfile weird.docmWhat didn't work first
Tried: Rename the file to .zip and unzip it expecting to find VBA source files directly.
Like .docx, a .docm is a ZIP-based OOXML container, and unzipping shows the usual directory structure. But the macro source is not readable XML: it sits inside word/vbaProject.bin, which is itself an OLE2 binary stream. Digging through that by hand is impractical, so olevba does the work, opening the ZIP, finding vbaProject.bin, decompressing the VBA bytecode, and printing readable source.
Tried: Run strings weird.docm and grep for picoCTF to find the flag without any other tool.
The flag is base64-encoded inside the macro, so strings never shows the picoCTF prefix. You may catch fragments of VBA bytecode but no readable source, because the OLE2 stream keeps VBA in compressed binary form. olevba decompresses and decompiles it into something you can read.
Learn more
.docm is Microsoft's macro-enabled Word format. Like .docx, it is a ZIP-based OOXML container (ECMA-376). Unzipping it reveals the standard directory structure, but the macros are not stored as readable XML. Instead, they live inside
word/vbaProject.bin, which is an OLE2 (Object Linking and Embedding) binary stream embedded within the ZIP. That stream holds compiled VBA bytecode and source.Macros in Office documents can execute arbitrary code - read files, spawn processes, download payloads. This is why modern Office installations block macros from internet-downloaded files by default and why .docm files are treated as high-risk by email gateways.
Step 2Extract the macro code with olevba
ObservationUnzipping the container only gets you word/vbaProject.bin, an OLE2 stream holding compressed VBA bytecode rather than readable XML. olevba from oletools opens the ZIP, parses that stream, and prints the macro source.Use olevba from the python-oletools suite to dump all macro source code embedded in the document. olevba parses the OLE2 container, decompresses the VBA source stored inside vbaProject.bin, and prints each macro module to stdout. Look through the output for anything that looks encoded - a long base64 string is the clue.bashpip install oletools # install if not already presentbasholevba weird.docmIn the output you will see a
Sub runpython()that calls a Python one-liner. The Python command prints the stringcGljb0NURnttNGNyMHNfcl9kNG5nM3IwdXN9. That is a base64-encoded flag.What didn't work first
Tried: Use oleid instead of olevba to read the macro source.
oleid ships with oletools too, but it reports metadata and risk indicators rather than source. It will confirm macros are present and never print the runpython subroutine or the encoded string inside it. olevba is the tool that dumps readable VBA.
Tried: Run oledump.py on the file and try to extract the macro stream by index number without knowing which stream holds the VBA.
oledump.py lists every OLE stream by index and marks the VBA-bearing one with an M. Pick the wrong index and you extract binary content or document text instead of source. olevba picks the right stream and decompresses it for you, which is faster for straightforward macro extraction.
Learn more
olevba is part of the oletools suite by Philippe Lagadec. It decompresses VBA source from the
vbaProject.binOLE stream and prints each Sub, Function, and property, along with a risk summary flagging suspicious keywords likeShell,CreateObject, orWScript.Run.The macro in this challenge uses a
Shellcall to invoke Python and print an encoded string - a pattern seen in real macro malware that uses scripting engines to execute payloads without triggering keyword-based AV signatures tuned to VBA itself.Alternative - LibreOffice macro editor: Open the file in LibreOffice Writer, then go to Tools > Macros > Edit Macros. Navigate to weird.docm > Project > Document Objects > ThisDocument to read the same macro source interactively.
Step 3Decode the base64 string to reveal the flag
ObservationThe olevba output holds a long alphanumeric run with no picoCTF prefix anywhere in it. That is the shape of base64, so pipe it through base64 -d for the plaintext flag.Pipe the base64 string into base64 -d (or base64 --decode) to recover the plaintext flag. The decoded output is the flag in the standard picoCTF format.bashecho "cGljb0NURnttNGNyMHNfcl9kNG5nM3IwdXN9" | base64 -dExpected output
picoCTF{m4cr0s_r_d4ng3r0us}What didn't work first
Tried: Use base64 -D (capital D) instead of base64 -d to decode the string.
On Linux the decode flag is lowercase -d; capital -D is not valid for GNU coreutils base64 and just errors. BSD base64 on macOS does accept -D, so a script that works on a Mac breaks on a Linux CTF box. Use lowercase -d for portability.
Tried: Pipe the base64 string through CyberChef 'From Base64' using the URL-safe alphabet.
The encoded string uses standard base64, with plus and slash, not the URL-safe variant that swaps them for dash and underscore. CyberChef defaults to the standard alphabet and decodes fine, but selecting URL-safe mode by hand corrupts every character that maps to plus or slash.
Learn more
Base64 encodes binary data (or ASCII text) using 64 printable characters (A-Z, a-z, 0-9, +, /). Each 3 bytes of input become 4 base64 characters, so the output is about 33% longer than the input.
base64 -dreverses this exactly.Hiding a flag or payload as base64 inside a macro is a simple obfuscation layer. It defeats naive string searches for
picoCTFin the raw file bytes, since the encoded form looks like random alphanumeric noise. Analysts always check for base64 when strings look like long runs of A-Z/a-z/0-9 characters.
Interactive tools
- File Magic IdentifierIdentify file types from magic numbers. Paste hex bytes or drop a file to detect PNG, JPEG, ZIP, PDF, ELF, PCAP, SQLite, and dozens of other formats.
Flag
Reveal flag
picoCTF{m4cr0s_r_d4ng3r0us}
weird.docm is a macro-enabled Word document, a ZIP-based OOXML container whose VBA lives in the OLE2 stream word/vbaProject.bin. olevba extracts a Sub runpython() macro that prints a base64 string; decoding it with base64 -d reveals the flag.