17.10.22-NUAACTF总结

队友把我们的WP放出来了…wp 。趁着现在对题还有点印象,我把没做的题再看一看,结合未来的WriteUp总结一下我们第一次比较正式的比赛。

REV-pychon

令人智熄的比赛时刻

下来之后想进行反编译,结果 http://tool.lu/pyc/ 居然无法连接!有点后悔电脑中没装uncompyle2了,不过本着不可能只有一个反编译网站的决心,果然找到了第二个反编译网站: http://tools.bugscaner.com/decompyle/ 。于是乎兴冲冲地提交上去,显示如下错误:

1
2
抱歉,反编译失败
Error DeBug Logging Unknown magic number 3350 in {}

于是就明白这是magic number的问题了,但是!我一直以为这是Python2.x的编译文件,于是乎怎么改都不成功啊。直到最后5分钟才想起来Python是有3.x+的,非常智熄,时间也不够了,要是时间够的话(也超不过第三…..23333)。

分析

到网上看了一下.pyc的结构,前四个字节是magic number,代表此pyc的版本信息,不同版本的Magic都在Python/import.c 内定义。

既然知道了正确的思路,那么本地生成一个.pyc,看看 它的文件头并替代这个有问题的文件,就是我们即将要做的事情了。

参考资料:PYC文件格式分析

生成.pyc的方法

Python内部提供了生成.pyc的函数,我们用一下就好了。

写一个脚本:

1
2
import py_compile
py_compile.compile(r'%PATH%\name.py')

在Python 3.6中运行,得到一个包含.pyc的文件夹。

将这个.pyc文件用UltraEdit打开,并于题目中提供的.pyc对比magic number。

错误的做法

于是我修改了前四位,如图所示:

被我改成了

然后反编译之,出现如下结果:

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
抱歉,反编译失败
Error DeBug Logging --- This code section failed: ---
4 0 LOAD_NAME '__name__'
2 <0>
4 <0>
6 COMPARE_OP '=='
8 <0>
10 <188> 0 ''
5 12 LOAD_CONST 81 81
14 <0>
16 ROT_TWO
18 LOAD_CONST 52 52
20 <0>
22 DUP_TOP
24 LOAD_CONST 53 53
26 <0>
28 <6>
30 LOAD_CONST 88 88
32 <0>
34 <8>
36 LOAD_CONST 60 60
38 <0>
40 UNARY_POSITIVE
42 LOAD_CONST 60 60
44 <0>
46 UNARY_NEGATIVE
48 LOAD_CONST 88 88
50 <0>
52 UNARY_NOT
54 LOAD_CONST 112 112
56 <0>
58 <14>
60 BUILD_LIST_16 16
62 <0>
64 POP_TOP
6 66 LOAD_CONST 1 1
68 <0>
70 BINARY_MATRIX_MULTIPLY
72 LOAD_CONST 3 3
74 <0>
76 <18>
78 LOAD_CONST 5 5
80 <0>
82 BINARY_MULTIPLY
84 LOAD_CONST 7 7
86 <0>
88 BINARY_MODULO
90 LOAD_CONST 9 9
92 <0>
94 BINARY_SUBTRACT
96 LOAD_CONST 9 9
98 <0>
100 BINARY_MODULO
102 LOAD_CONST 7 7
104 <0>
106 BINARY_MULTIPLY
108 LOAD_CONST 5 5
110 <0>
112 <18>
114 BUILD_LIST_16 16
116 <0>
118 ROT_TWO
8 120 LOAD_CONST ''
122 <0>
124 ROT_THREE
9 126 SETUP_LOOP 177 'to 177'
128 <0>
130 DUP_TOP
132 LOAD_NAME 'str0'
134 <0>
136 ROT_TWO
138 CALL_FUNCTION_2 2
140 <0>
142 FOR_ITER 176 'to 176'
144 <0>
146 ROT_TWO
148 STORE_NAME 'i'
150 <0>
152 <6>
10 154 LOAD_NAME 'ans'
156 <0>
158 <7>
160 LOAD_NAME 'i'
162 <0>
164 <6>
166 BINARY_XOR
168 POP_TOP
170 INPLACE_ADD
172 ROT_THREE
174 JUMP_BACK 142 'to 142'
176 <0>
11 178 LOAD_CONST 'nuaactf{%s}'
180 <0>
182 ROT_THREE
184 BINARY_MODULO
186 <8>
188 LOAD_CONST ''
190 <0>
Parse error at or near `<0>' instruction at offset 2

可以看到,虽然出现了错误,但是也反编译出了一部分汇编代码,也是我在比赛中最后三分钟看到的内容(摔)。看到他LOAD_CONST,我的第一反应是ASCII编码,于是拿以前的脚本试一下,结果得到了奇怪的Q45X<<Xp,想到可能是对其进行某些计算得到flag,(最经典的是str[i]^i吧),第9段中有个call func2令人蛋疼,不过好像是个生成空字符串的操作,我们不要管他。看第166句,有XOR操作,就想经典操作是否可行,结果显然是失败的(不撞南墙不回头)…于是这次比赛就在分析它的过程中结束了…

正确的做法

渊神的指导下,我终于发现了盲点=。=mangic number只有后两位改动了,原来是010A,而实际上后两个byte一定是0D0A,只需要改变这个就可以反编译了..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# uncompyle6 version 2.13.2
# Python bytecode 3.5 (3350)
# Decompiled from: Python 2.7.13 (default, Jan 19 2017, 14:48:08)
# [GCC 6.3.0 20170118]
# Embedded file name: rev0.py
# Compiled at: 2017-04-29 23:22:18
# Size of source mod 2**32: 266 bytes
if __name__ == '__main__':
str0 = [
81, 91, 52, 76, 53, 72, 88, 57, 60, 85, 60, 56, 88, 64, 112, 74]
str1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4]
ans = ''
for i, j in zip(str0, str1):
ans += chr(i ^ j)
flag = 'nuaactf{%s}' % ans
# okay decompiling pychon - 副本.pyc

感觉有点蛋疼…我对magic number的理解还是不够深刻,有时间整理一下。

参考资料:不同版本python 编译出的pyc 不相容

PWN-hello_pwn

趁着上午陈浩学长刚讲完这道题,把思路再整理一下…

把hello_pwn拖到IDA中,打开main函数,F5,我们可以看到如下的代码:

1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
alarm(0x3Cu);
setbuf(stdout, 0LL);
puts("~~ welcome to nuaa ctf ~~");
puts("lets get helloworld for bof");
read(0, &unk_601068, 0x10uLL);
if ( dword_60106C == 'nuaa' )
sub_400686(0LL, &unk_601068);
return 0LL;
}

read()函数读取了一个数组(起点为601068,长度为10),然后从位置60106C处与字符串”nuaa”比较,相等则调用sub_400686()函数。那是个什么函数呢?

1
2
3
4
5
__int64 sub_400686()
{
system("cat flag.txt");
return 0LL;
}

好吧,很明显,只需要成功调用这个函数我们就可以得到flag。怎样构造输入字符,使其能调用呢?

这时我们发现,比较的位置正好在601068之后,如图所示:

我们可不可以输入这样一个字符串,从而满足题意呢?

这时候我们还需要注意到,计算机处理字节序的时候采取的是小端字节序,即从右向左(也可以是从下向上),而输入的时候是采取大端字节序的方式。因此我们应该这样输入:

参考资料:理解字节序

于是构造这样一个字符串并输入,就能get到flag了。

Linux的nc命令

netcat,nc ip 8080可以通过客户端模式连接到对方的8080端口,发送一些文本,它会被发送到服务器端。

未完待续…

坚持原创技术分享,您的支持将鼓励我继续创作!