软件系统安全赛-华北-初赛re
软件系统安全赛-华北-初赛re
周六打的一个比赛,个人感觉re题好套娃呀 。也可能是因为本人是菜鸡🥲
话不多说,下面是菜鸡的题解,记录一下🤓
re1
拿到附件后是一个mp4和一个Loader ELF文件。先对那个ELF文件查壳
无壳,64位,直接ida进行逆向分析。

这个main函数的逻辑是先检查当前目录是否存在video.mp4,如果不存在就打印提示退出,如果存在就把内置的一个base64编码的pyc数据 解码出来,写入文件stager.pyc,然后给他加执行权限,再调用其他函数去运行这个Python 字节码文件。
直接进行base64解码

然后去010里面新建一个16进制文件,把这些16进制复制进去(使用快捷键Ctrl+Shift+V),再把文件名改成了1.pyc并保存。

然后去在线网站上将pyc文件转成py文件
1 | from PIL import Image |
它的作用不是“正常生成视频”,而是把一个文件 payload 编码成黑白视频 video.mp4。
这段代码是读取文件payload的全部二进制内容,把每个字节转成8位的二进制字符串,对每个字节做一次固定异或0xAA。
然后把异或的比特流按照1 bit = 1个黑白块,编码进视频帧,输出成 video.mp4。
解码:先读取 video.mp4,然后逐帧提取图像,把每个 8×8 块采样成 1 bit。
黑=1,白=0,拼回 bitstream,每 8 bit 还原成 1 byte。每个 byte 再异或 0xAA,最后输出原始 payload。
解密脚本如下:
1 | import imageio |
得到一个elf文件,接着进行逆向分析。
main函数

看到off_4020,发现有很多的16进制字符串

我猜测可能是md5,找个在线网站试试

发现这个是d,dart的字符d。
那这个逻辑就是flag的每一位都是md5,写个爆破更快些。
脚本如下:
1 | import hashlib |
re2
拿到题目附件后,先放到die里面去查壳

发现加了upx壳,放到010里面看看有没有魔改,发现把UPX都换成了CTF,把这些都改回来。

改后保存一下,发现用upx -d 脱不出来,想着去手脱,但是发现有反调试,一用x64dbg打开就会闪退。然后就去用工具XVolkolak.v0.22-Win32去脱壳,发现可以脱掉。

这个脱掉之后就是一个challenge_1.unp.exe文件,直接去ida打开,进行分析。
通过搜索字符串 error: Invalid password! 可以定位关键的逻辑。

大概分析一下,这个是校验口令是不是Str2 “NGeQwv8eCRpINEcO”,如果是的话,就会把一段内置的base64数据进行解码,然后写到一个临时文件(PE文件)里面。但是前面那个 1A30函数 和 1550()函数分别做了初始化和反脱壳的检测吧,如果检测通过才会提示用户输入字符串然后比较,sub_401660()是一个base64解密。
这里我选择了直接进行base64解密,然后写入到exe文件。

然后把16进制在010里面新建一个16进制的文本,复制进去,保存后改成1.exe。

继续使用die去查信息,无壳,64位

直接ida启动。继续进行分析
通过搜索字符串可以确定关键的代码处。也就是程序的开始吧,函数sub_401550

这个大概意思是从标准输入读入一行到Buffer,然后计算长度,检查长度是否合法,接着就去调用nullsub_3(Buffer, n240),这里面才是真正的key校验逻辑,然后根据结果通过qword_40B030去回传状态,S就是成功,F就是输入非法,Error就是校验失败。
那需要接着看nullsub_3

双击后看到程序跳到一大段一大段的数据,很明显,这个可能是smc那种的。
又发现函数里有rc4加密


这个是标准的rc4加密,通过交叉引用可以看到哪里调用了

这个可以说明,这就是smc,程序用标准的rc4去解密一个.mydata段,把unk_4070C0 的前 32 字节当成rc4的key。
再对这个函数进行交叉引用可以看到

说明同样的也是对.hello段进行了rc4的自解密。

我们可以查到起始和终止的地址,直接写idapython脚本去解密
1 | addr=0x408000 |
也可以直接在call 这里下断点,直接动调。

输入个111,然后找到hello段,去进行U结构,P重新定义,就能看到加密逻辑了。
会先进到_BOOL8 __fastcall sub_404EF0(char *a1)函数里面,这就与一开始分析的联系上了,a1就是用户输入的字符串,返回值是0或1(输入正确)。

逻辑是从 a1 里读取字符串,一旦碰到 \0,说明字符串结束,就去做 padding。然后是PKCS#7 填充。
这个函数校验只处理一个16字节块,接着调用加密函数loc_404CB0。
下面是loc_404CB0函数

这个函数大概是CBC 模式加密。
这个比较重要的地方在于它 能说明.mydata 里那 3 块密文不是随便放的,而是:用同一个 AES-256 key,用同一个 IV,按 CBC 链式加密出来的整段数据。
接着就是继续去跟进函数去分析。这个就是一个AES-CBC模式的加密。
sub_404B60:AES-256 单块加密

byte_4071E0:这个函数就是AES 的S盒
sub_404940:AES-256 的密钥扩展
sub_404070:这个是ShiftRows,aes加密里的行变换
sub_404190:这个是MixColumns,aes加密里的列混淆
但这个有魔改的地方,在于
1.自定义 S盒
2,自定义的 RCON。AES-256 标准 RCON 是 [0x01,0x02,0x04,…],但是程序里用的是:
RCON = [0x9C, 0x10, 0x13, 0x15, 0x19, 0x01, 0x31, 0x51, 0x91, 0x0A, 0x27]
3.在 sub_404A5E 里先把明文块和最后一轮 round key 做 XOR
4.自定义的列混淆
5.不是标准 AES-256 密钥扩展,而是按照 sub_404940 实现,每 8 个字(32 字节)循环一次,第 8 的倍数做 rotate + SBOX + RCON XOR,第 4 个做 S盒。
key从 0x408001 开始,到 0x408020 结束
key的下面就是iv和3组密文

写出解密脚本
1 | SBOX = [ |

re3
拿到附件后是两个,1个是pcap流量,还有1个是client文件,先去DIE里面去查。

这个是python语言写的,用了PyInstaller工具打包,那就是把这个用Pyinstxtractor工具变成pyc文件,然后pyc文件再去转py文件。
我是直接用的在线网站去做了,https://pyinstxtractor-web.netlify.app/
这是转pyc的,结果如下

把client.pyc接着去转成py文件,我用的在线网站
转出的结果如下所示

这只是第一部分,大概是先做了一个反调试检测,然后把一大段Base85 编码内容存放在 _1667 中,再通过 _obf_exec(...) 去动态执行第二部分。在第二部分会导入crypt_core,得到一段密钥,对本地的文件进行处理后,通过TCP socket去发往远端。
接着对这些内容进行base85解码
1 | import base64 |
运行出来的第二部分如下所示
1 | _j0 = lambda: (30 ^ 126) + (520 % 26) |
从这个第2部分能得到一些信息、
KEY_B64 = _oe(“C7MAupdc5tRBM!52kv4WmpHle6Pz5wdF*y=E$zQv!xZ”, 83, 214, 17) KEY = CustomBase64.decode(KEY_B64)A4N5t?5nObY+L
它先用_oe(...) 解出一个字符串,然后再用自定义 Base64 表去还原成 bytes,才能得到真正的key。
但是发送的时候只用了前16字节,
FILES_TO_SEND = [
_oe(“I-p}FvS)q0emD”, 83, 214, 17),
oe(“B(-BJ<B6O”, 83, 214, 17),
_oe(“C$MxRtZ99{emD”, 83, 214, 17)
]
这部分是实际上要发送的文件名,但被_oe加密了。
SERVER_PORT = 9999,知道端口是9999。
真正的加密逻辑在crypt_core.encode_data(…)。
关于_oe,可能是在线网站没有分析好,那就看它的字节码,字节码是比py文件更准确的。分析字节码大概能看出来
它本质上是三步:
1..对输入字符串做 base64.b85decode
2.对解出来的字节按循环密钥 (_k1, _k2, _rn) 做异或
3.对结果里的字母和数字再做逆向轮转
字母:
-rn mod 26数字:
-rn mod 10
换成等价的伪代码就是下面的
1 | def _oe(d, k1, k2, rn): |
知道这个那CustomBase64.CUSTOM_ALPHABET 解出来是:
QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890!@
自定义 Base64其实就是把标准 Base64 换了一套字符表。
知道这些就能解出来key的值了,先解出来的KEY_B64
eUYme4MkN1KSC1bWJZJ2w3FUJCiEXT13D2u1KmiNtfhXKZYE
然后去用网站换表解

注意哈,这里key是取前16字节的,那就是passvkcDKWLAA45o
也跟着就能解出来
_oe(“C#-fVpm;c-emD”, 83, 214, 17) → “ciphertext”
_oe(“I-p}FvS)q0emD”, 83, 214, 17) → “readme.txt”
oe(“B(-BJ<B6O”, 83, 214, 17) → “flag.txt”
_oe(“C$MxRtZ99{emD”, 83, 214, 17) → “config.txt”
然后知道密钥了,但不知道加密逻辑,就继续去逆向分析这个crypt_core.so文件。
先搜字符串

通过交叉引用后定位在了 sub_60B0。

看 sub_60B0 的结构
开头先做 16 - n % 16,然后填充整块 0x10/0x05/…,这就是 PKCS#7
然后key 按 4 个 u32 读入
明文块也按 4 个 u32 读入
轮函数的核心形式是:
X[i+4] = X[i] ^ T(X[i+1] ^ X[i+2] ^ X[i+3] ^ rk[i])
结尾是最后 4 个字反序输出
这些都证明了这个加密是SM4 类型的。
轮函数里能看到 rol 2 / 10 / 18 / 24,密钥扩展里能看到 rol 13 / 23,这些都正好对应了
L(x) = x ^ rol2 ^ rol10 ^ rol18 ^ rol24,L’(x) = x ^ rol13 ^ rol23。
但是在这里读不出来S盒和其他常量,就交叉引用到了函数sub_38B4里面,这里面是模块初始化函数。


0x3fc4 开始把 AC10..AD00 拷到 DBA0..DC90,这就是S盒,256字节
0x409d 把 AD10 拷到 DB80,这就是FK,16字节
0x40ab 开始把 AD20.. 拷到 DB00..,这就是CK,128字节
与标准的SM4加密进行对比,发现这个S盒不是标准的,魔改了。
FK也不同
标准 FK:A3B1BAC6 56AA3350 677D9197 B27022DC
样本 FK:A4861F3B 2D33F783 8EBAAD58 733FDC71
CK也不一样。
在 0x63ab:
1 | add rbp, 4 |
0x60 / 4 = 24,说明只生成 24 个轮密钥。
块加密时从 0x6970 进入,轮密钥指针也是每次加 4,整体也是 24 轮状态推进。

不是标准 32 轮。
所以写密钥扩展的时候是先把 128-bit key 切成四个 32-bit word,对每轮生成 round key,用自定义的 tp 函数 + FK/CK,注意 24 轮而不是 32 轮。
这个加密逻辑有了,接着就去看那个流量。

拿到flag.txt的内容,这就是密文,对它进行解密
写出解密脚本
1 | BLOCK_SIZE = 16 |




