Return-Address Control: Door Controller
Door Controller
Section titled “Door Controller”The door controller unlocks the staff entrance. Somebody types in the day’s entry code, the controller checks it, and the door opens. It also keeps the staff roster, which it never prints.
Your goal is to get the roster printed.
Get the files
Section titled “Get the files”Download door-controller-v1.0.0.tar into
~/underdunn-course/downloads and check it against this digest:
cd4f892dc330c1b6402a4d81033f19e421f63ded2ce3d05f909b4a514efdb11b door-controller-v1.0.0.tarcd ~/underdunn-course/downloadssha256sum door-controller-v1.0.0.tarmkdir -p ~/underdunn-course/labs/door-controllertar -xf door-controller-v1.0.0.tar -C ~/underdunn-course/labs/door-controllercd ~/underdunn-course/labs/door-controller/door-controller-v1.0.0sha256sum -c CHECKSUMS.sha256sh build.shThe build prints two warnings and nothing else. Read TASK.md and door.c, then talk to the
controller a few times before you measure anything.
Hint 1: the code is not the way in
Before you try to go around the check, establish that you can’t go through it. Follow the value it compares against backwards to wherever it gets written, and notice which function fills it and when that happens.
There’s a difference between “I couldn’t guess it” and “no input I can prepare will ever match it,” and only the second one is something the program tells you.
Hint 2: what the compiler already told you
The build warned you twice about the same function. That function reads into a buffer without knowing how large the buffer is.
Separately, run nm over the binary and read the names. One of them is a routine that does exactly
what you want and is called from nowhere.
Hint 3: the distance is not the buffer size
The declaration tells you how many bytes the array holds. It doesn’t tell you where the array sits in the frame, and here those are different numbers, because there’s another local in this function and it isn’t below the buffer.
Three instructions in the prologue carry what you need: the one that reserves the frame, the one that
initialises the other local, and the lea that produces the address handed to the reading function.
The saved frame pointer is at rbp+0 and the saved return address at rbp+8.
Hint 4: the two numbers and where to read them
objdump -d -Mintel ./door | sed -n '/<challenge>:/,/^$/p'nm -n ./door | grep ' open_door$'Take the buffer’s offset from rbp out of the lea. The return slot is at rbp+8, so the distance
is the difference between those, and the length that covers all eight of its bytes is eight more than
that.
Write the input from a script that prints its own byte count, so a length mistake is visible before you run anything.
Hint 5: it faults and you want to change the padding
Don’t. Read the slot instead. Break on the ret in the challenge function and look at the eight
bytes at the stack pointer, because that’s exactly what ret is about to consume:
(gdb) break *<the ret in challenge>(gdb) run < win.bin(gdb) x/gx $rspIf those bytes are not the address you meant to put there, your distance is wrong. If they are the address and it still faults, your distance is fine and the address is wrong. Those are different problems and the padding fixes neither of them.
The whole route
The check compares against a global filled at run time from a source your input never touches, so no
prepared input satisfies it. gets has no idea how large the buffer is. open_door is compiled in,
has a symbol, and is at a fixed address because the build isn’t position-independent.
objdump -d -Mintel ./door | sed -n '/<challenge>:/,/^$/p' # buffer offset from rbp, out of the leanm -n ./door | grep ' open_door$' # the addressfrom pathlib import Pathfrom struct import pack
payload = b"A" * <the distance you derived> + pack("<Q", <the address>)Path("win.bin").write_bytes(payload)print(f"wrote {len(payload)} bytes")./door < win.binThe flag
Section titled “The flag”Paste exactly what the controller printed, with no surrounding whitespace:
If something looks wrong
The build reports something other than its two warnings. Restore the supplied source and
run sh build.sh unmodified.
Addresses don’t match the lesson’s. They shouldn’t. This isn’t the lesson’s program and none of its numbers carry over. Its own symbols are fixed across runs because the build isn’t position-independent.
It crashes and you want to know where. Run it under GDB with the same input rather than reasoning from the shell’s message. A segmentation fault reported by the shell doesn’t tell you which value was wrong.