逆向压轴的brainfuck比较有意思.
Misc方向:
MiniForensicsⅠ:
打开虚拟机,桌面上有一个全是坐标的txt和一个流量包,
在此电脑 > 文档中找到一个隐藏文件夹nihao
,
其中有一个ai.rar
和一个pwd.txt
,提示密钥是7位纯数字,爆破得到密钥1846287
,
解压后发现给了一个没用的图片,并提示压缩包还有东西,
发现有一个ssl.log
被标记为了type03(服务块)而非02(文件块),改为02,
重新计算CRC32得到40f42e68
,小端序682ef440
覆盖,发现已经可以识别出ssl.log,
用这个作为SSLKEYLOGFILE
解密流量,导出对象,得到两个upload,
第一个是D盘的密钥521433-074470-317097-543499-149259-301488-189849-252032,
第二个是一个zip压缩包,用密钥对D盘进行解密,
发现D盘有一个透明贴图空白文字的文件夹,里面有一个c.txt
,也是一串坐标,转为图像后发现是fakeflag,
但是b是ac的平均值,求得a的坐标序列,转为图片得到flag:miniLCTF{forens1c5_s0ooooo_1nt4resting}.
MiniForensicsⅡ:
对upload2进行明文爆破(因为是zipcrypto store
,且有一个png文件),
偏移量为0的位置已知89504E470D0A1A0A0000000D49484452
,得到密钥组45797e52 f747cc4c 800bd117,
解密得到一个base64:aHR0cHM6Ly9naXRodWIuY29tL3Jvb3QtYWRtaW4tdXNlci93aGF0X2RvX3lvdV93YW5uYV9maW5kLmdpdA==,
解密得到https://github.com/root-admin-user/what_do_you_wanna_find.git,
访问后发现有几个文件,有一个fakeflag,还有一个模拟哈希碰撞的python脚本,
在上传历史中找到上传脚本那次的记录,给了一个提示Here’s something that might be useful.
,
在脚本中注意到给出了一个sha1哈希值89045a3653af483b6bb390e27c10db16873a60d1,
并且有这样一行:print(f”The hash value of historical commits is: {target_hash} calculated by the simulation of calculating hash machine”) ,
说明这个哈希是一个commit的对应哈希值,访问https://github.com/root-admin-user/what_do_you_wanna_find/commit/89045a3653af483b6bb390e27c10db16873a60d1,
发现存在一个secret.py的隐藏文件,
下载运行这个py文件得到flag:miniLCTF{c0ngr4tul4ti0n5_70u’v3_g0t_th3_s3cr3ts}.
Reverse方向:
0.s1gn1n:
查找main没找到,依据字符串搜索,发现main有花,去花,查看main:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
查看check:
1 | int __cdecl check(_BYTE *a1) |
发现了三个函数,首先用gentree生成了v2:
1 | _DWORD *__cdecl gen(_BYTE *a1) |
这是一个生成完全二叉树的函数,会把传入的字符串转为二叉树结构,再看iter函数:
1 | int __cdecl iter(int a1, int a2, _DWORD *a3) |
这是一个递归函数,最后会改变输入字符串的顺序,之后有一个base64加密,然后是一个异或以及密钥异或,最后将自身所有字符值加起来,减去28,减去自身长度,要求该值为0,即为正确,编写同构脚本:
1 | from collections import deque |
由于key的长度为60位,所以对应的明文应该在43~45左右,假设异或后的值为全0数组,进行解密:
1 | import base64 |
得到flag:miniLCTF{esrevER_gnir33nignE_Is_K1nd_0F_@rt}.
d1ffer3nce:
脱壳,发现是go语言写的,查看main:
1 | // main.main |
发现有一个关键加密函数sub1145141919,此外密文的十六进制是729daebea2e3845b310f01f1b3e703c24c810a9ca0ed2c4d9252a214882d7721,查看加密函数:
1 | // main.sub_1145141919 |
发现是一个魔改XXTEA,轮数为6 + 2025 / n,delta为0x4D696E69,编写同构加解密脚本:
1 | def bytes_to_dwords_little_endian(byte_array): |
得到flag:miniLCTF{W3lc0m3~MiN1Lc7F_2O25}.
x96re:
查看main:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
发现了一个值为2025minilctf!!!!的密钥,以及32字节的密文,主要函数就是init、whathappened和encode_fun,查看init:
1 | unsigned int init() |
发现地址爆红,动态调试,发现是一个SMC,解出来的函数是:
1 | int __usercall addr_@<eax>(int a1@<ebp>) |
发现是把输入值的前30个异或了0x4C,查看whathappened:
1 | void __cdecl whathappened() |
发现就是执行了上面的SMC函数,对输入值进行了简单异或,查看加密函数,发现是一个SM4加密,将密文提取解密得到3ac159d665b4ccfb25c0927c1a23edb3,flag:miniLCTF{3ac159d665b4ccfb25c0927c1a23edb3}.
rbf:
观察java层:
1 | package com.doctor3.rbf; |
发现flag中间的12字节会被带入so层检验,查看so层:
1 | __int64 __fastcall Java_com_doctor3_rbf_MainActivity_Check(__int64 a1, __int64 a2, __int64 a3) |
发现了一个执行brainfuck代码的虚拟机,其中输出部分将字节转为了数值,查找init_array段发现了一个解密函数:
1 | __int64 sub_23820() |
存在一个32030字节的密文,密钥为myrust,发现加密方式是RC4,解密后得到了三万个字符的brainfuck,运行发现要输入12个字节,并返回了0,恰好对应12字节空缺的flag,说明这就是这个程序的核心逻辑,编写代码读取brainfuck运行后的内存:
1 | def brainfuck_interpreter_with_memory_dump(code, input_string): |
发现内存第13~24字节为密文,28字节为校验值,且加密貌似是一个简单的单字节加密,用以下脚本得到对照表:
1 | def brainfuck_interpreter_with_memory_dump(code, input_string): |
发现加密只有将每个字节的值加上0x9F,接下来要寻找密文,运行过程中监测输出的“0”是从哪来的:
1 | def brainfuck_interpreter_with_memory_dump(code, input_string): |
发现是在最后一部分从0加上去的,提取最后的几百个指令,进行分析:
1 | <[-] |
注意到这里正是最后输出0或1的关键逻辑,某个位置减去某值之后判断是否为0(即这个位置是否等于这个值),如果不等于就跳转到0(错误),否则跳转到1(正确),由于字符串的长度是12,合理猜测这里要和12进行比较的是经过某种校验后“正确的”字符个数,监测”[->-<]“这个结构体,在其运行时打印执行流:
1 | 位置3586进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 0, 0, 37, 253, 0, 0] |
1 | 位置3586进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 0, 0, 40, 253, 0, 0] |
发现当右侧两者比较相等时会在左侧的对应内存索引上+1,而右边的数字是定值,这说明该数组即为密文:[253,199,248,147,158,102,192,169,255,243,220,229],每个字节在加上了0x9F
后经过了某种加密得到了该数组,且该加密几乎是全局相关的,无法单字节爆破,经过仔细观察后发现貌似是某种矩阵,用python验证:
1 | def trybf(code, input_string): |
得到矩阵系数:
1 | [[3, 3, 2, 0, 1, 0, 1, 3, 3, 1, 0, 3], |
接下来恢复加密逻辑:
1 | matr = [[3, 3, 2, 0, 1, 0, 1, 3, 3, 1, 0, 3], |
z3求解即可:
1 | from z3 import * |
找到解: [5, 0, 21, 24, 23, 22, 4, 10, 15, 15, 14, 0]
对应的字符串: favyxwekppoa
flag即为miniLCTF{favyxwekppoa}.