第一次遇到鸿蒙软件逆向,简单学了学之后成功做出来了,好耶
签到题:
欢迎!:
打开容器发现是“文本替换器”,只能执行指定的指令:sed's/{.\*}/ text /g' path
,并给出了一个示例文本文档,题目说明flag在环境变量内,首先在右侧路径的位置输入环境变量:/proc/self/environ
,返回以下内容:
1 | RET2SHELL\_34\_656\_PORT=tcp://10.233.17.192:3000RET2SHELL\_34\_656\_SERVICE\_PORT=3000KUBERNETES\_SERVICE\_PORT=443KUBERNETES\_PORT=tcp://10.233.0.1:443RET2SHELL\_34\_388\_PORT=tcp://10.233.2.153:3000RET2SHELL\_34\_388\_SERVICE\_PORT=3000RET2SHELL\_34\_488\_SERVICE\_PORT=3000RET2SHELL\_34\_488\_PORT=tcp://10.233.29.73:3000HOSTNAME=ret2shell-34-76BUN\_INSTALL\_BIN=/usr/local/binSHLVL=1RET2SHELL\_34\_76\_PORT\_3000\_TCP\_ADDR=10.233.56.123HOME=/home/ctfRET2SHELL\_34\_78\_PORT\_3000\_TCP\_ADDR=10.233.37.202RET2SHELL\_34\_322\_PORT\_3000\_TCP\_ADDR=10.233.46.29RET2SHELL\_34\_76\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_76\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_78\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_513\_PORT\_3000\_TCP\_ADDR=10.233.38.34RET2SHELL\_34\_261\_PORT\_3000\_TCP\_ADDR=10.233.32.69RET2SHELL\_34\_370\_PORT\_3000\_TCP\_ADDR=10.233.50.48RET2SHELL\_34\_322\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_78\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_335\_PORT\_3000\_TCP\_ADDR=10.233.36.17RET2SHELL\_34\_322\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_261\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_513\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_642\_PORT\_3000\_TCP\_ADDR=10.233.7.77RET2SHELL\_34\_261\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_513\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_370\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_76\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_652\_PORT\_3000\_TCP\_ADDR=10.233.4.122RET2SHELL\_34\_335\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_370\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_78\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_335\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_654\_PORT\_3000\_TCP\_ADDR=10.233.10.93RET2SHELL\_34\_642\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_642\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_322\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_652\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_76\_PORT\_3000\_TCP=tcp://10.233.56.123:3000RET2SHELL\_34\_652\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_76\_SERVICE\_HOST=10.233.56.123RET2SHELL\_34\_656\_PORT\_3000\_TCP\_ADDR=10.233.17.192KUBERNETES\_PORT\_443\_TCP\_ADDR=10.233.0.1RET2SHELL\_34\_261\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_654\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_78\_PORT\_3000\_TCP=tcp://10.233.37.202:3000RET2SHELL\_34\_513\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bun-node-fallback-binRET2SHELL\_34\_370\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_654\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_388\_PORT\_3000\_TCP\_ADDR=10.233.2.153RET2SHELL\_34\_322\_PORT\_3000\_TCP=tcp://10.233.46.29:3000RET2SHELL\_34\_78\_SERVICE\_HOST=10.233.37.202RET2SHELL\_34\_656\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_488\_PORT\_3000\_TCP\_ADDR=10.233.29.73RET2SHELL\_34\_322\_SERVICE\_HOST=10.233.46.29RET2SHELL\_34\_335\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000KUBERNETES\_PORT\_443\_TCP\_PORT=443RET2SHELL\_34\_261\_PORT\_3000\_TCP=tcp://10.233.32.69:3000KUBERNETES\_PORT\_443\_TCP\_PROTO=tcpRET2SHELL\_34\_656\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_513\_PORT\_3000\_TCP=tcp://10.233.38.34:3000RET2SHELL\_34\_642\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_652\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_513\_SERVICE\_HOST=10.233.38.34RET2SHELL\_34\_388\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_370\_PORT\_3000\_TCP=tcp://10.233.50.48:3000RET2SHELL\_34\_261\_SERVICE\_HOST=10.233.32.69RET2SHELL\_34\_335\_PORT\_3000\_TCP=tcp://10.233.36.17:3000RET2SHELL\_34\_388\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_488\_PORT\_3000\_TCP\_PORT=3000RET2SHELL\_34\_370\_SERVICE\_HOST=10.233.50.48RET2SHELL\_34\_76\_SERVICE\_PORT=3000RET2SHELL\_34\_488\_PORT\_3000\_TCP\_PROTO=tcpRET2SHELL\_34\_654\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_76\_PORT=tcp://10.233.56.123:3000RET2SHELL\_34\_335\_SERVICE\_HOST=10.233.36.17RET2SHELL\_34\_642\_PORT\_3000\_TCP=tcp://10.233.7.77:3000RET2SHELL\_34\_642\_SERVICE\_HOST=10.233.7.77RET2SHELL\_34\_652\_PORT\_3000\_TCP=tcp://10.233.4.122:3000RET2SHELL\_34\_78\_PORT=tcp://10.233.37.202:3000RET2SHELL\_34\_78\_SERVICE\_PORT=3000RET2SHELL\_34\_656\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_652\_SERVICE\_HOST=10.233.4.122RET2SHELL\_34\_322\_PORT=tcp://10.233.46.29:3000RET2SHELL\_34\_322\_SERVICE\_PORT=3000RET2SHELL\_34\_654\_PORT\_3000\_TCP=tcp://10.233.10.93:3000RET2SHELL\_34\_388\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_654\_SERVICE\_HOST=10.233.10.93KUBERNETES\_SERVICE\_PORT\_HTTPS=443KUBERNETES\_PORT\_443\_TCP=tcp://10.233.0.1:443RET2SHELL\_34\_261\_PORT=tcp://10.233.32.69:3000RET2SHELL\_34\_488\_SERVICE\_PORT\_SIGNIN\_TEST\_FILEREPLACER=3000RET2SHELL\_34\_513\_SERVICE\_PORT=3000RET2SHELL\_34\_261\_SERVICE\_PORT=3000RET2SHELL\_34\_656\_PORT\_3000\_TCP=tcp://10.233.17.192:3000RET2SHELL\_34\_513\_PORT=tcp://10.233.38.34:3000RET2SHELL\_34\_370\_SERVICE\_PORT=3000RET2SHELL\_34\_656\_SERVICE\_HOST=10.233.17.192RET2SHELL\_34\_370\_PORT=tcp://10.233.50.48:3000RET2SHELL\_34\_335\_SERVICE\_PORT=3000RET2SHELL\_34\_335\_PORT=tcp://10.233.36.17:3000RET2SHELL\_34\_388\_PORT\_3000\_TCP=tcp://10.233.2.153:3000KUBERNETES\_SERVICE\_HOST=10.233.0.1PWD=/home/ctfRET2SHELL\_34\_488\_PORT\_3000\_TCP=tcp://10.233.29.73:3000RET2SHELL\_34\_642\_PORT=tcp://10.233.7.77:3000RET2SHELL\_34\_388\_SERVICE\_HOST=10.233.2.153RET2SHELL\_34\_642\_SERVICE\_PORT=3000RET2SHELL\_34\_488\_SERVICE\_HOST=10.233.29.73RET2SHELL\_34\_652\_SERVICE\_PORT=3000RET2SHELL\_34\_652\_PORT=tcp://10.233.4.122:3000FLAG=VNCTFRET2SHELL\_34\_654\_PORT=tcp://10.233.10.93:3000RET2SHELL\_34\_654\_SERVICE\_PORT=3000NODE\_ENV=productionBUN\_RUNTIME\_TRANSPILER\_CACHE\_PATH=0 |
注意到了其中的FLAG=VNCTF
,后面则是其他的内容,则已经定位到flag了,接下来要绕过sed的替换就很简单了,直接输入\0
绕过替换,得到FLAG=VNCTF{W3lc0M3_4nd_HAvE_@-g0OD_tlmE_qfX08kJju3Sr@39@G3V7Pe}.
Crypto方向:
Easymath:
题目给出了一个多项式,是x减去三个质数的数的乘积,则二次项、一次项、常数项系数分别对应-(n0+n1+n2)
、(n0n1+n1n2+n0n2)
、n0n1n2
,写出脚本求解:
1 | from sympy import symbols, solve |
得到解和N:
1 | The roots (values of m, n, p) are: |
然后flag是用e=2加密的,用中国剩余定理求解:
1 | from functools import reduce |
输出如下:
1 | b"9\xb3\x11\x03\*o\xd4v\xbcU@\x14R\xd4\xa5\xf1'nX\xe0V\x0e\x98\x92\x05\x19\x0750\x19\xdc\x8d\xb9\xee\xc9\x90\x00\xe9\xa4\xab:\xc6qh5`\x83G\x80@\n\xf6`\x0b|#\xd9\x90,\xb1\x07\xa5\xcba\x81\xb1\x19f\xcb\xd2\xb8\xc6" |
则flag为VNCTF{90dcfb2dfb21a21e0c8715cbf3643f4a47d3e2e4b3f7b7975954e6d9701d9648}.
Misc方向:
VN_Lang:
Ida打开,动态调试,搜索字符串,找到flag:VNCTF{CtJBEEFcPvlcJISEfe18ncSWLf4JB8BwlmJD0T0NM2hQQ}.
Reverse方向:
kotlindroid:
代码含有混淆jadx没法正确反编译,用jeb反编译,仔细检查后发现了一个关键的activity:SearchAcitivityKt
:
1 | public final class SearchActivityKt { |
观察得到很明显的AES-GCM加密,密钥是(new byte[]{0x76, 99, 101, 0x7E, 0x7C, 0x72, 110, 100}[v1] ^ 23)
和(new byte[]{0x7B, 0x71, 109, 99, 97, 0x7A, 0x7C, 105}[v] ^ 8)
的拼合(atrikeyssyekirta),iv是new byte[]{49, 49, 52, 53, 49, 52}
(114514),密文base64是“MTE0NTE0HMuJKLOW1BqCAi2MxpHYjGjpPq82XXQ/jgx5WYrZ2MV53a9xjQVbRaVdRiXFrSn6EcQPzA==”,动态调试发现密文的前6字节是iv,后16字节是GCM模式特有的验证tag,至此把内容全部输入cyberchef解密发现并不正确,接着动态调试,找到一个关键的加密函数SearchActivityKt.sec.1
:
1 | final class SearchActivityKt.sec.1 extends SuspendLambda implements Function2 { |
注意到其中有AAD的加入,动态调试到JNI那一行找到AAD:string@24461:“mysecretadd”,带入解密,成功解出,得到flag:VNCTF{Y0U_@re_th3_Ma5t3r_0f_C0mp0s3}.
Hook Fish:
观察MainActivity:
1 | public class MainActivity extends AppCompatActivity { |
可以看到一个加密和比较的过程,还有一个dex在比较时联网下载,先用frida把dex在动态调试时dump出来:
1 | import frida |
1 | Java.perform(function() { |
1 | def main(target_process): |
成功获得dex文件后反编译查看:
1 | package fish; |
是对a-z以及0-9的重映射,编写解密脚本:
1 | decode_map = { |
得到中间密文:0qksrtuw0x74r2n3s2x3ooi4ps54r173k2os12r32pmqnu73r1h432n301twnq43prruo2h5,再接着看MainActivity中的加密,发现分为几步:①每个字节的ASCII+68并转为十六进制 ②调用静态方法code让相邻两个字符的值交换 ③根据每个字符是否在a-f之间来应用不同的映射(两种映射的值域交集为空集),据此写出解密脚本:
1 | def reverse_transform(cipher_char, index): |
输出flag:VNCTF{u_re4l1y_kn0w_H0Ok_my_f1Sh!1l}.
1 | Encrypted: 0qksrtuw0x74r2n3s2x3ooi4ps54r173k2os12r32pmqnu73r1h432n301twnq43prruo2h5 |
Fuko’s starfish:
反编译查看是一个小游戏,分为猜数字、贪吃蛇和第三步的输入密钥,然后整个程序不知道塞了多少反调试反正一调就直接闪退,再观察附带的starfish.dll,发现第三步游戏在dll里面,前两个游戏没啥关键信息就不分析了,仔细观察所给的dll,发现好几个函数有花(特征是call $+5后一大段然后一个jz一个retn),把jz改jmp,其他没用的全抹掉,观察到几个关键函数:
1 | void __noreturn sub_1800025F0() |
至此已经找到了flag的密文:[3d 01 1c 19 0b a0 90 81 5f 67 27 31 a8 9a a4 74 97 36 21 67 ab 2e b4 a0 94 18 d3 7d 93 e6 46 e7],对于那两个标橙色的函数详细分析:
1 | v145 = a2; |
可以注意到这是一个包含了AES加密的函数(由于没有初始化向量猜测是ECB模式),经过分析发现带入的两个参数里面第一个参数是要加密的内容,第二个参数是加密结果的缓冲区,那么密钥在哪?这时发现函数调用了恰好16个全局变量,找到这16个全局变量,发现是11 45 14 19 19 81 11 45 14 19 19 81 ?? ?? ?? ??,由于这个题目根本就不想要我调试(不然塞这么多反调试干嘛),密钥一定不是这个数值(毕竟还有四个字节未知),查看这些全局变量的交叉引用,发现了一个函数:
1 | __int64 __fastcall ThreadProc(LPVOID lpThreadParameter) |
这就很奇怪了,16个字节居然是全随机,不动态调试根本无法获取其值,但结合密文是固定的,并且下面return了一个诡异的数:6i64,不难猜测这里可能藏了一个花,在汇编界面打开一看还真是,抹掉花之后获得真正的函数:
1 | srand(0x1BF52u); |
可以注意到下面又生成了16次随机值,但这次给了固定的srand,这意味着每个随机值其实都是固定的,那么不言而喻这应该就是密钥了,编写C语言程序取得随机值:
1 |
|
输出:
1 | Random value 1: 13545, Processed: 13598 |
又因为全局变量限定了是byte,再转成字节并拼合:
1 | values = [ |
得到结果:Hex representation: 1ef2eafc7f2662a1a62c931f86fc6fc5
将十六进制数组异或0x17得到09e5fdeb683175b6b13b840891eb78d2,在cyberchef选择AES-ECB/NoPadding解密得到flag:VNCTF{W0w_u_g0t_Fuk0’s_st4rf1sh}.
抽奖转盘:
发现是鸿蒙应用,先改成.zip,取出其中的.abc字节码,扔进jadx-dev进行反编译,大量寻找后得到以下几个关键信息:
1 | //来自index类 |
则该应用在解题部分(加密部分)的逻辑已经清晰:输入的文本会先带入libhello.so,在其中进行加密后输出,输入值的每一位的ASCII值加上1,之后与0x7异或,并与输出的再次加密的flag密文进行比较,接下来反编译libhello.so:
1 | __int64 __fastcall MyCry(__int64 a1, __int64 a2) |
至此全部加密已经解析完毕,开始解密:
flag密文异或0x7之后-1得到:aLJ5aJqO/ApBpA/C9S8gUIsa1MSDBtijKDeqYwsziTYs,共有44位,并且恰好在base64字符集的范围内,解码后得到中间密文的十六进制表达:\x68\xb2\x79\x68\x9a\x8e\xfc\x0a\x41\xa4\x0f\xc2\xf5\x2f\x20\x50\x8b\x1a\xd4\xc4\x83\x06\xd8\xa3\x28\x37\xaa\x63\x0b\x33\x89\x36\x2c,然后用RC4解密,密钥为Take_it_easy,再异或40,得到\x59\x51\x46\x57\x49\x7e\x4d\x58\x76\x77\x62\x27\x71\x68\x62\x4c\x64\x76\x77\x62\x67\x64\x71\x66\x68\x62\x35\x33\x35\x38\x24\x80\x03,再减去3,得到flag:VNCTF{JUst_$ne_Iast_dance_2025!}.