Description
Escape the matrix.
Setup
Download the binary from the challenge page.
wget <challenge_url>/matrix # download the binarySolution
Walk me through it- Step 1Identify the binary and load into GhidraRun
fileon the binary to confirm it is an ELF executable, then load it into Ghidra for static analysis. Create a new project, import the binary, and let Ghidra auto-analyze it.bashfile matrixbashchmod +x matrixbash./matrixLearn more
Ghidra is an open-source reverse engineering framework developed by the NSA. It decompiles native binaries back into pseudo-C source code, making it far easier to understand program logic than reading raw assembly. After auto-analysis, use the Symbol Tree panel to navigate to exported or well-named functions.
Running the binary first helps understand its expected input format - does it prompt for a string? Take command-line arguments? Exit immediately? The behavior at runtime narrows down which functions are worth examining in Ghidra.
- Step 2Locate the validation function in GhidraIn the Ghidra decompiler, find the main function and trace calls to any function that compares user input against expected values. Look for loops iterating over a two-dimensional array - this is the matrix validation.
Learn more
Matrix-based validation is a common CTF reverse engineering pattern. The binary stores a 2D array of expected character values in the data segment (.rodata or .data). The validation function iterates over the matrix row by row, comparing each byte of user input against the corresponding cell. If all comparisons pass, the flag is accepted.
In Ghidra's decompiler output, look for double-nested loops with array indexing like
expected[row][col]. The array dimensions tell you the expected input length. XOR operations against a constant key are also common - Ghidra will show them as^ 0xNN.Use Ghidra's data type manager to redefine raw arrays as
char[N][M]to get readable string representations in the listing view, which makes the expected values immediately visible. - Step 3Extract the matrix values and reconstruct the flagOnce the matrix is identified, dump its contents from the binary. Read out the bytes row by row, applying any transformation (XOR key, offset) the validation function uses, to reconstruct the flag string.bash
# Extract raw bytes from the binary at the matrix addressbashobjdump -s -j .rodata matrixbash# Or use Python to parse the binary directlypythonpython3 -c "import struct; data=open('matrix','rb').read(); print(data[0xADDR:0xADDR+N])"Learn more
objdump -s dumps the raw hex contents of each section. The
.rodatasection holds read-only constants - string literals, lookup tables, and validation matrices. Cross-reference the address Ghidra shows for the matrix against the file offset in objdump output.If the binary applies a transformation before comparing (e.g., XOR with 0x42), you must apply the inverse transformation to the stored matrix values to get the original flag characters. Since XOR is its own inverse, XORing the stored bytes with the same key recovers the plaintext.
Write a short Python script to automate this: read the matrix bytes from the binary at the known offset, apply the inverse transform, and print the resulting ASCII string as the flag.
Flag
picoCTF{...}
The flag is stored as a matrix of bytes in .rodata - read each cell, apply the inverse of any XOR transform used in the validation function, and concatenate to get the flag string.