We're back in ret2win territory, but this time with no useful gadgets.
How will we populate critical registers without them?
Click below to download the binary:
This challenge is very similar to "callme", with the exception of the useful gadgets.
Simply call the
ret2win() function in the accompanying library with same arguments that you used to beat the "callme" challenge (
ret2win(0xdeadbeef, 0xcafebabe, 0xd00df00d) for the ARM & MIPS binaries,
ret2win(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d) for the x86_64 binary.
Populating the elusive 3rd register using ROP can prove more difficult than you might expect, especially in smaller binaries with fewer gadgets. This can become particularly irksome since many useful GLIBC functions require three arguments.
Start by using ropper to search for sensible gadgets, if there's no
pop rdx for example, perhaps there's a
mov rdx, rbp that you could chain with a
If you're all out of ideas go ahead and read the last paragraph.
Fortunately some very smart people have come up with a solution to your problem and as is customary in infosec given it a collection of pretentious names, including "Universal ROP", "μROP", "return-to-csu" or just "ret2csu".
You can learn all you need to on the subject from this
BlackHat Asia paper.
Note that more recent versions of gcc may use different registers from the example in
__libc_csu_init(), including the version that compiled this challenge.