前言
这次比赛出了三道web,两道逆向,一道逆向基本解出,算是近期来做的最不错的一次了。但是细细分析下来还有很多值得深究的地方。
web
简单的计算机1,2
这题说起来感觉就是黑名单没做好,实际上1,2可以用一个payload。考的是python代码注入,由于1,2均没有过滤exec,所以考虑用open加上read函数的组合读取文件内容,并通过exec将文件内容输入到session内,并带出,代码给的是1的,但是黑名单就写2 fuzz出来的结果好了。
关键代码如下(1的代码,2也差不多,用这个思路不影响):
1 | calc_result = str((eval(input_question + "=" + str(input)))) |
随便试了下过滤了,read 函数,eval 函数,or 关键词。
发现and,exec没有过滤。exec中可以执行等式赋值,而session是flask的全局变量。考虑使用正确结果 and exec来读取文件,黑名单可以用字符拼接来过。
最后payload如下:
1 | xxx and exec("""session["question"]=(op"""+"""en("/fl"""+"""ag").re"""+"""ad())""") |
提交payload,最后解密cookie即可。
phpunt
这题其实是个网鼎杯加上4月月赛的套娃。
主要是用php反序列化中大写S的时候可以使用16进制替代字符的特性来绕过黑名单。
index.php
1 | ······ |
pop链非常明显,Hacker_A触发_destruct中,stristr触发Hacker_B的_toString,而$tmp最后触发Hacker_C的invoke方法。
class.php
1 |
|
1 | //functions.pp |
exp如下
1 |
|
$padding 是username的输入,而$s1 作为password的输入,将原来的password覆盖为hacker对象。
跳转后
逆向
easymaze
白给的迷宫题,就进去之后把100大小的数组拷贝出来0代表路,O代表墙,hujk代表左,上,下,右即可。
从 左上角出发
0OOOO0000#
000OO0OOOO
OO0OO0000O
O00OOOOO0O
O0OOOO000O
O00OO00OOO
OO0OO0OOOO
OO0000OOOO
OOOOOOOOOO
OOOOOOOOOO
最后结果是 jkkjjhjjkjjkkkuukukkuuhhhuukkkk
MD5一下就是flag
pyc修复题
这题主要是改了下load constant变量那里的值,然后直接看opcode 写出原本代码来处理的。
这里主要参考了这几篇东西。
https://wiki.x10sec.org/misc/other/pyc/#pycdc
http://unpyc.sourceforge.net/Opcodes.html
https://github.com/python/cpython/blob/fc7df0e664198cb05cafd972f190a18ca422989c/Include/opcode.h#L69
首先简单地介绍下pyc的结构,
- 最开始4个字节是一个Maigc int, 标识此pyc的版本信息。
- 之后的4个字节代表了创建时间。
- 8个字节之后都是序列化的 PyCodeObject
使用了pycdas工具对python字节码进行反汇编,还原python opcode。
没有处理过的pyc文件第2条opcode有点问题
修复字节0020h的4,5字节全部改为0即可
依次还原py代码
1 | import base64 |
很显然,flag是一段被混淆的base64,只需要将前32位中的偶数位的a全部删除就是原本的base64
magia
ida f5 核心逻辑如下:
有三个数组,来验证输入。
归纳一下,输入是个32位的字符串,输入要满足三个条件:
- input[i]^input[31-i]==a1[i]
- input[i]&input[31-i]==a2[i]
- input[i]&0xF==a3[i]
然后最后结果又要是
Nep{xxx}这样的形式
然后无脑地写了个爆破脚本
1 | import string |
最后还要进sub_403000()才能出结果。然而比赛结束了,,,就这样吧。
总结
从解题情况来看,主要的收获还是在学到了下解pyc题的皮毛上,web的几个题都是旧题目的套娃,没啥意思,有意思的4个题都想歪到哪里去都不知道了。有空复现下easyflask和filecheck吧。