Description
Debugger0_c stores 0x2262c96b on the stack. Examine the bytes exactly as they appear in memory and wrap them (in order) inside picoCTF{...}.
Setup
Launch the binary under gdb and switch to layout asm to see where the MOV that stores 0x2262c96b executes.
Set a breakpoint right afterward (main+25) so the stack contents can be inspected before anything is overwritten.
wget https://artifacts.picoctf.net/c/531/debugger0_cchmod +x debugger0_cgdb --args ./debugger0_cSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Break after the storeObservationI noticed the challenge says 0x2262c96b is stored on the stack by a MOV instruction, which suggested I needed to halt execution immediately after that store so the bytes are in place and not yet overwritten by subsequent instructions.Place b *(main+25) so execution halts immediately after the constant is written to the stack. Run the program to hit that breakpoint.bashb *(main+25)bashrunWhat didn't work first
Tried: Breaking at main instead of main+25 to inspect the stack slot early.
At main+0 the stack frame has not been set up yet and the MOV that writes 0x2262c96b has not executed, so rbp-4 holds whatever garbage was already there. The breakpoint must be placed after main+25, the instruction immediately following the store, so the four bytes are actually in place when you examine them.
Tried: Using 'break main' and then stepping through with 'next' until reaching the store instruction.
Single-stepping through a function with source-level 'next' can skip over individual assembly instructions because GDB maps multiple instructions to a single source line. If the store happens inside a line that 'next' completes in one step, you may land past it and see a later value. Using 'b *(main+25)' targets the exact assembly offset and guarantees you stop at the right instruction boundary.
Learn more
This challenge introduces memory inspection: reading raw bytes from a specific address rather than just reading a register value. After the MOV instruction writes
0x2262c96bto[rbp-0x4], the four bytes are stored in RAM at that stack address. Breaking immediately after the store lets you inspect those bytes before anything overwrites them.The key concept is that the CPU stores multi-byte integers in little-endian byte order on x86. Little-endian means the least significant byte is stored at the lowest memory address. So the 32-bit value
0x2262c96bis stored in memory as the byte sequence6b c9 62 22(lowest byte first). When the challenge asks you to report the bytes "in order," it means in the order they appear in memory - which is the reversed byte order compared to how you read the constant in the assembly listing.Understanding endianness is critical in networking (network byte order is big-endian), binary exploitation (shellcode must account for byte order when encoding addresses), and file format parsing (file formats vary by endianness - JPEG is big-endian, most x86 executables are little-endian). Getting endianness wrong is one of the most common sources of bugs in low-level code.
Step 2
Dump four bytes from the stackObservationI noticed the flag description asks for the bytes 'in order' as they appear in memory, which suggested using GDB's x/4xb command to read each byte individually from the [rbp-0x4] stack slot and reveal the little-endian byte sequence rather than reading the constant as written in the assembly listing.Use x/4xb $rbp-4 to view the byte order as stored in memory. The address $rbp-4 matches the [rbp-0x4] stack slot where the assembly stored the constant; the little-endian byte order means the bytes appear reversed compared to reading 0x2262c96b left-to-right. Concatenate those bytes, preserving order, to form the hexadecimal inside the flag.bashx/4xb $rbp-4Expected output
0x...: 0x6b 0xc9 0x62 0x22
What didn't work first
Tried: Reading the flag bytes directly from the constant 0x2262c96b as printed in the assembly listing, giving '2262c96b'.
The assembly listing shows the value in logical (big-endian) notation, but x86 stores it in little-endian order so the bytes appear reversed in memory. The correct sequence to concatenate is the one x/4xb prints - '6b c9 62 22' - not the left-to-right reading of the immediate. Submitting '2262c96b' is the most common wrong answer for this challenge.
Tried: Using x/1xw $rbp-4 to print the whole 32-bit word at once instead of examining individual bytes.
GDB's word-sized examine command (x/1xw) prints the value as a 32-bit integer and re-applies the little-endian-to-host conversion, showing '0x2262c96b' - the same number you already saw in the assembly. This hides the byte-level reversal rather than revealing it. You need x/4xb to see each byte individually in the order it sits in memory.
Learn more
The GDB examine command (
x) reads memory at an arbitrary address and displays it in a chosen format. The syntaxx/4xb $rbp-4means: examine 4 units, formatted as hex, each unit being one byte, starting at the address$rbp - 4. GDB prints each byte individually, separated by spaces, which makes it easy to read the byte sequence in memory order.To build the flag, concatenate the four bytes exactly as GDB prints them. Because x86 is little-endian, the byte GDB prints first is the least-significant byte of the original 32-bit value - so the output will appear "reversed" compared to
0x2262c96b. The challenge is testing whether you understand this reversal and report the bytes in memory order rather than the logical order.The
xcommand is invaluable in real debugging and exploitation work. When inspecting a buffer overflow, you might usex/32xb $rspto view 32 bytes of stack data. When verifying a shellcode injection,x/10i $ripdisassembles ten instructions starting at the instruction pointer. Learning the format specifiers (x=hex, d=decimal, s=string, i=instruction, b=byte, h=halfword, w=word, g=giant/8-byte) is worth the small investment.
Flag
Reveal flag
picoCTF{0x6b......22}
Your exact byte sequence may differ; always copy the four bytes exactly as x/4xb prints them.