ROP Petting Zoo is a challenge designed to teach the principles of return-oriented programming. It’s mostly written in Javascript, with a backend powered by a Ruby web server, along with a tool I wrote called Mandrake. Source code is shared between the three parts of the challenge, and is available here.
Mandrake is a debugger / tracer I wrote that executes a binary and traces all code run between two points. It will show registers, memory, all the good stuff. ROP Petting Zoo is kind of a wrapper around that.
Basically, you have a list of potential ROP gadgets and libc calls. You build
a stack from all the ROP gadgets, hit Execute!, and the harness will return to
the first address on the stack.
Everything is running forreal in a container, so you get to see what would actually happen if this is a real exploit!
The challenges are very guided / on-rails, with tutorials that show the exact steps you will need to take, but here are the solutions I wrote.
It’s helpful to remember that when a function is called, the arguments are,
in order, passed in the registers rdi, rsi, rdx, and rcx.
Level 1
print_flag()-> Immediately return toprint_flagpop rdi / ret-> Pop the next value into registerrdi0-> This is what’s popped intordiexit-> Return toexit(rdi)akaexit(0)
Level 2
return_flag()-> Returns the flag inraxmov rdi, rax / ret-> Moves the flag pointer intordiputs-> Return toputs(rdi)orputs(flag)pop rdi / ret-> Pop the next value intordi0-> This is what’s popped intordiexit-> Return toexit(rdi)akaexit(0)
Level 3
This part unfortunately ran a lot slower than I’d intended, but hopefully it’s educational enough:
write_flag_to_file()-> Writes the flag to a file, returns the name inraxmov rdi, rax / ret-> Moves the filename tordi, the first parameter tofopen()get_letter_r-> Returns a pointer to the string"r"mov rsi, rax / ret-> Moves the string"r"torsi, the second parameter tofopen()fopen()-> Return tofopen(rdi, rsi), which isfopen(filename, "r")mov rdx, rax / ret-> Move the file handle intordx, the third parameter tofgets()get_writable_memory()-> Get a pointer to some writable memorymov rdi, rax / ret-> Move the pointer to writable memory tordi, the first parameter tofgets()pop rsi / ret-> Move the next value intorsi, the second parameter tofgets()0xff-> This is what’s moved intorsifgets()-> Callsfgets(rdi, rsi, rdx), orfgets(writable_memory, 0xff, file_handle)get_writable_memory()-> Gets a handle to the writable memory againmov rdi, rax / ret-> Move the writable memory handle intordi, the first argument toputsputs-> Callputs(rdi), orputs(writable_memory)pop rdi / ret-> Move the next value intordi, the first parameter toexit()0-> This is what goes intordiexit()->exit(rdi)akaexit(0)

Comments
Join the conversation on this Mastodon post (replies will appear below)!
Loading comments...