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_flag
pop rdi / ret
-> Pop the next value into registerrdi
0
-> This is what’s popped intordi
exit
-> Return toexit(rdi)
akaexit(0)
Level 2
return_flag()
-> Returns the flag inrax
mov rdi, rax / ret
-> Moves the flag pointer intordi
puts
-> Return toputs(rdi)
orputs(flag)
pop rdi / ret
-> Pop the next value intordi
0
-> This is what’s popped intordi
exit
-> 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 inrax
mov 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 intorsi
fgets()
-> 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 toputs
puts
-> Callputs(rdi)
, orputs(writable_memory)
pop rdi / ret
-> Move the next value intordi
, the first parameter toexit()
0
-> This is what goes intordi
exit()
->exit(rdi)
akaexit(0)
Comments
Join the conversation on this Mastodon post (replies will appear below)!
Loading comments...