Before all
I had posted a note of pwn for beginners(link here), and recently I’ve learned more about ROP, so here is this post~
ROP
What is ROP?
ROP(Return Oriented Program) is an attack method extended from BOF(Buffer Overflow), in ROP, the key point is to cover return address and lead it to Program Fragments you want(can be repeated many times if you have many usable fragments).
Basically, a fragment end with ret
is mostly used.
x86_64 ROP
In x86_64, size of an address is 8 bytes(p64 in pwntool), so registers used frequently are rax, rdi, rsi, rdx ……
To connect the ROP Chain, it’s important to make sure every register called in the function, and find the corresponding fregment in the program. Is easy to find fregment with tools such as ROPgadget
and pwntool
.
ROPgadget:
1 | ROPgadget --binary=binary_file | grep "mov dword .* ret" | less |
In this command, I want to find a gadget start with mov_dword and end with ret.
but is much more easier to find a register fragment with pwntool.
pwntool
1 | from pwn import * |
In x86_64 system, if a variable want to be executive(or other action), it needs an syscall.
1 | rop.syscall |
syscall table
syscall table has each register need for every call and the meaning for each register.
p.s. execve is excution call.
Connect fragment like pop rdi; ret
with a value can modify the value of rdi
and move to mext fregment(possibly a function or syscall)
An example for an excution syscall:
1 | #setting environment |
This ROP chain would make the value of rax become 0x3b
(execve on syscall table), and rdi value become the address of shellcode(like string: /bin/sh\x00
)\x00
is a symbol to stop reading.
Also, vmmap in geb-peda is often used to find writable(or something else) datas.
x86 ROP
Same logic as x86_64
syscall table
Some differents between x86 and x86_64:
- In x86 system, each address and register are mostly 4 bytes
- int80 in x86 has the same function with syscall in x86_64
Example:
execve in x86:1
2
3
4#setting environment
context.arch='i386'
#payload
flat(pop_eax_ret, 0xb, pop_edx_ebx_ret, 0, data, pop_ecx_ret, 0, int80)
Challenges
Pwnasm from LoTuX CTF
Click link
It’s a simple ROP
Solution
Start with pwntool:
1 | from pwn import * |
Extracted datas:
1 | Arch: amd64-64-little |
It’s a x86_64 system(amd64), so check out the syscall table ,register gadgets needed to trigger syscall
are rax
, rdi
, rsi
, rdx
.
Also, from the reverse result by Ghidra
there are some weird datas on the stack(our shell('/bin/sh'
at0x00402000
) and first part of flag), so it’s possible to construct an ROP Chain and excuse the shell
1 | rop.rax |
Extracted datas:
1 | >>> rop.rax |
Finally, my exploit code:
1 | from pwn import * |
echo server
file
source code
A x86_64 file
ROP Chain:pop rdi; ret
->change rdi location to global variable
->trigger get function
->return to runCMD function to bypass getString() filtering function
Exploit:
1 | from pwn import * |
rop1-sean_Pwn-2
Stack Migration
Stack Migration is a technique to cover register base into a modified stack location, and the first 8 bytes(if it’s a x86_64 system)/first 4 bytes(if it’s a x86 system) need to be a location for new register(garbage)
Solution
First write the ROP Chain into global variable, and trgger it in func1
, remember to leave.
1 | from pwn import * |
ropfu from picoCTF
link
An i386
(x86) system, key points are on above.
Use fragments like mov dword [edx] eax; ret
to write shell on eax into location edx.
p.s.the shell input need to be converted into hex and reversed
exploit
1 | from pwn import * |