0. 前言
成信大举办的一年一度的极客大挑战。去年没打,今年参加有幸获得校外赛道第一。
先说结论,难度上感觉pwn水的一批(要我出题难度绝对比这高),同类型水题多,一个ret2libc连考三题(虽然有一题写的是ret2csu,但因为还是有调用过打印函数,所以还是ret2libc)。虽然如此,但pwn水也是相对而言的,平心而论,pwn这个难度拿来考新生我认为是合格的。其它方向有些题出的多少有些放飞自我了。像是RE的自定义字节码,还有Android逆向第二题。以及misc的取证题。这些题出的多少是有些放飞自我。
吐槽了这么多,说说这个比赛好的地方。一是比赛赛题覆盖面广(除了pwn),可以让新生了解各方向不同的题型,体验不同的感觉。二是比赛时间安排合理。和ISCC一样采用月赛周上题制,难度循序渐进(maybe)。
总的来说打的还是很开心的,也希望极客大挑战能办的更好。
1. RE
呜呜呜,队里没有RE爷,只能自己去啃。(被迫学会了IDA动调、NET逆向、WASM和差点学会了frida hook)
1-1 level1
查壳:
64位ELF文件,无壳。
拖入IDA看:
主逻辑很简单,对输入的进行加密,然后和加密数据进行比较来判断输入的是否是正确的flag。
加密的逻辑也不难理解,逐字符与定值和下一个字符做异或(最后一个字符异或的是已经加密过的第一个字符)。解密的时候注意逆序来就行。
解密脚本如下:
1 | data = [0xA1, 0xBA, 0x6E, 0x46, 0x80, 0xF4, 0xD9, 0xAA, 0xB4, 0x36, 0x5A, 0xCC, 0x8C, 0x1E, |
解得flag:
1 | SYC{bIIngxvJqVDOUqbuiHDpadwfdRePetteyBP} |
1-2 level2
查壳结果和level1一样。
IDA看反编译:
加密逻辑也很简单,先异或0x25,再RC4。
由于两个过程都是对称的,所以如果输入的为加密数据,则最后处理得到的s是flag。
运用IDA远程动态调试:
在这两处下断点:
先用53个a填充s:
运行到第一个断点处,修改s的内容为加密数据:
再运行到第二个断点处:
查看s的内容得到flag:
1-3 baby_android
简简单单Android逆向(
先拖进JEB,进行Java层分析:
发现对输入框中的内容调用了enc函数加密。
enc函数是定义在Native层的,故需要对so文件进行逆向分析。
提取出so文件(我这里提的是x86_64的),拖入IDA进行分析:
找到函数,进行分析,确定关键加密代码为下面两部分:
加密逻辑为先异或再base64加密,故解密为先base64解密再异或,这里用CyberChef一把梭:
1-4 babyre
查壳:
PE32,无壳。
拖入IDA:
关键的三个函数,对应三个加密。
在这之前会先逆序:
题目说了常见加密,那直接找字符串就行:
根据特征与积累,从下到上(即按三个加密函数调用的顺序)分别是sha256、md5和base64。
分别解出三个字符串:
将结果拼接,逆序,再套上SYC{}即是flag。
1-5 Reverse1
查壳:
ELF64,无壳。
直接拖进IDA逆向分析,关键加密部分代码如下:
可以看出先异或了个0x77,再通过sub_401D25函数判断是加55异或0x21还是加77异或0x4A。
加密数据存放在v17。
由于我看不懂sub_401D25函数的逻辑,所以写解密脚本的时候直接搬过来了。
解密脚本如下:
1 | v13 = [59, 68, 74, 19, 118, 219, 83, 43, 47, 45, 104, 63, 27, 222, 63, 23, 72, 53, 114, 63, 219, 204, 117, 222, 17, 63, 26, 38, 104, 63, 27, 47, 5, 24, 233, 233, 172, 29] |
解出flag:
1 | SYC{W3Lcome_t0_gEEk_3Nj0y_the_tour!!!} |
1-6 EzXor
查壳,ELF64,无壳。
IDA逆向分析,关键代码如下:
简简单单异或,解密脚本如下:
1 | v9 = [] |
解出flag:
1 | SYC{1t~i$~a-ea5sy_de8vg-ri9ht} |
1-7 sdzz
解数独,找到数据,取反得到9*9数独:
1 | data = [0xFA ,0xFF ,0xFF ,0xF9 ,0xFE ,0xFF ,0xFF ,0xFF ,0xFF, |
得到的数独为(0代表需要填的数):
1 | [5, 0, 0, 6, 1, 0, 0, 0, 0] |
将结果拼接套上SYC{}得到flag:
1 | SYC{5616324186698236843523241973358} |
1-8 放松心情
查壳:
PE64,无壳,golang写的。
当初做的时候静态没分析出来,动态调试做的。
用IDA进行本地动态调试,在下图所示处下断点:
当输入的为单一字符时,查看此时的v31(处理输入后),发现值全为0:
因此推测是每个与后一个(最后一个和第一个)进行了某些操作。
替换输入的前四个为SYC{,得到结果如下:
对比加密数据的前三个发现一致,但第四个不一致,因此可以基本确定前面的猜想成立。
将S和Y,Y和C,C和{分别异或发现得到的结果和前三个加密数据高四位和第四位互换,因此推测加密流程是逐个与后一个循环异或,并进行了高四位和第四位互换。由于这两个过程都是对称的,因此先后关系不影响。
根据调试得到的结果推测出的加密算法,编写解密脚本如下(其中data是将加密数据高低四位互换完的结果):
1 | data = [0xa, 0x1a, 0x38, 0xc, 0x12, 0x9, 0xf, 0xc, 0x2, 0x8, 0x3a, 0x2b, 0x1b, 0x30, 0x2c, 0xa, 0x1a, 0xf, 0x3, 0x19, 0x13, 0x17, 0xf, 0x77] |
解得flag:
1 | SYC{welcome_to_syclover} |
1-9 CrazyThursday
疯狂星期四V我50
查壳,PE64,无壳。
拖入IDA逆向分析。关键加密函数是由一个函数指针指向的:
这段代码进行了混淆处理,静态分析看不出来干了什么。因此采用动态调试。
在call这个加密函数处下断点:
测试填充的数据是SYC{wwwwwwwwwwwwwwwwwwwwwwww},到断点处,可以查看这个加密代码:
由于还是比较难看出来具体干了什么(菜),所以直接对加密后的填充数据进行分析。
步过,查看加密后的填充数据:
将这个数据逐个异或w即可得到xor组(说实话,这个有猜的成分,但是猜对了)。
解密脚本如下:
1 | data = [0xe7, 0x5f, 0xc6, 0xe9, 0xee, 0xc0, 0xe2, 0x39, 0x7f, 0xb1, 0xdb, 0x6c, 0x0, 0xcb, 0x2, 0xee, 0xff, 0xd9, 0xbc, 0xe1, 0x53, 0xe8, 0xae, 0xea] |
解得flag:
1 | SYC{wwww@RC_RC_RC_RC@wwww:)H} |
1-10 WASM
逆向题中臭名昭著的WASM。
用wabt将wasm.wasm还原成c文件,再编译生成o文件,放入IDA逆向分析。
(懒得截图了)
有一说一,加密部分的真的难看。我是参考去年的WASM才写出来的,加密逻辑也很简单,不断和0x4d异或,再加上i(i从0开始)。
解密脚本如下:
1 | data = [ 0x1E, 0x15, 0x10, 0x39, |
解得flag:
1 | SYC{Y0u_S0lv3d_WASM_Probl3m} |
1-11 For_Girl…s?
查壳:
.NET逆向,放入ILSpy进行逆向分析,反编译为C#。
找到主逻辑部分:
有4个结局,我们要达成的是HE。
查看加密逻辑:
加密逻辑不难,也很好逆,但是没逆出来。
找了半天原因,终于发现,原来,GirlsHeart会随秒数变化……
可能性很多,只能一个个去试,发现第一秒的值能解出flag。
解密脚本如下:
1 | GirlsHeart = [1536903838, 521453914, 3046417386, 1214738056] |
解得flag:
1 | SYC{y%U_@2e_@11_m7_wLnGs} |
2. PWN
水中水之大水特水。很难想象一个精神状态正常的出题人为什么会做出不出堆题这种选择。出pwn题不出堆题,就像四大名著不看红楼梦,说明这个人的文学造诣和自我修养不足,他理解不了这种内在的阳春白雪的高雅艺术,他只能看到外表的辞藻堆砌,参不透其中的深奥的精神内核,他整个人的层次就卡在这里了,只能度过一个相对失败的人生。
2-1 nc
签到有什么好写的?
2-2 pwn1_1
给了个后门,直接ret2text,没什么好说的。
exp如下:
1 | from pwn import * |
2-3 pwn2_1
依然给了个后门,只不过加了个strlen限制长度,用\x00截断绕过就行。
exp如下:
1 | from pwn import * |
2-4 pwn2_2
简简单单的ret2shellocde,没加什么限制,设置好上下文后用shellcraft生成即可。
exp如下:
1 | from pwn import * |
2-5 pwn2_3
这题一开始给的附件有问题(保护全开根本没法做啊)。
后面更新了附件就很简单了。
漏洞函数如下:
给了栈上的一个地址,然后允许往栈上输入一个内存页的数据,并且有溢出。之后给了一处任意地址写。
由于没开NX保护,因此堆栈具有RWX权限。而且给了我们栈上的地址,因此栈是可控的。思路就是往栈上写shellcode再返回到这个shellcode。这里返回有两个思路:1是无溢出写shellcode,根据给的栈地址计算返回地址所在位置,利用任意地址写修改返回地址为shellcode;2是写完shellcode后填充字符溢出到canary,之后程序触发保护机制会调用___stack_chk_fail函数,又由于程序没有开全RELRO和PIE,允许我们hijack got,因此可以利用任意地址写修改__stack_chk_fail@got为shellcode首地址。
这里提供第一个方法的exp:
1 | from pwn import * |
2-6 pwn3_1
保护如下:
主函数如下:
看到左边函数列表一堆库函数就知道是静态链接的程序。
所以先找system函数或者exec族函数,如果能找到,那么构建rop链直接返回到这些函数就行。但是可惜没有。
但是能找到read函数和mprotect函数:
因此,其中一种rop链构造思路就是先调用read往bss写入shellcode,再调用mprotect改bss权限为RWX,最后返回到shellcode所在位置执行shellcode。这里需要注意由于溢出长度有限,在构造时需要综合考虑。
exp如下:
1 | from pwn import * |
2-7 pwn3_2
ret2plt的水题,没什么好说的,phone_number填入/bin/sh就行。
exp如下:
1 | from pwn import * |
2-8 pwn3_3
ret2libc的模板题。
exp如下:
1 | from pwn import * |
2-9 pwn4_1
ret2libc模板题 x2…
exp如下:
1 | from pwn import * |
2-10 pwn4_2
ret2libc模板题 x3…
exp如下:
1 | from pwn import * |
2-11 pwn4_3
一眼格式化字符串漏洞,唯一的难点是绕过check函数:
绕过方法有很多,我这里是用hijack got改exit@got为alarm@plt地址,这样即使触发了禁词也不会退出。
之后就是用格式化字符串漏洞泄露libc地址,然后改写printf@got为system函数地址,再输入/bin/sh就相当于执行了system(“/bin/sh”)。
exp如下:
1 | from pwn import * |
2-12 pwn5_1
还是格式化字符串漏洞,思路和深育杯2021那题类似。
exp如下:
1 | #41(0x1100) 43(canary) 45(__libc_start_main+243) |
3. CRYPTO
3-1 w_or_m?
题目:
1 | 0_cmdo1elfe_2_}WtoC!{0mr!C__7!YtepoS34 |
看题目猜测是M型栅栏加密。但是我找不到现成工具,也不会写脚本,所以就手搓了…
1 | 0 _ |
得出flag:
1 | SYC{We1c0me_t0_t3e_mo2ld_of_Cr7p4o!!!} |
3-2 definitely ez RSA
task:
1 | from Crypto.Util.number import * |
e=6,经典小指数攻击,直接套脚本就行:
1 | import gmpy2 |
解得flag:
1 | SYC{0ops_y0u_f1Nd_m3!} |
3-3 just lcg
task:
1 | import signal |
简单的线性同余。
因为
b ≡ a * k + c (mod n)
所以
c ≡ b - a * k (mod n)
用pwntools写交互脚本:
1 | from pwn import * |
4. MISC
4-1 PuPp1T的秘密
png隐写+zip套娃。
简单说下思路,首先用foremost分离可得到zip,
第一层爆破,
第二层还是爆破,
第三层掩码,
第四层flag.txt零宽字符隐写,得到信息try usually word,猜测密码为password,
第五层明文。
然后bin->hex->str,得到flag:
1 | SYC{You_are_a_good_misc_misc_miscer!} |