你驻足于春色中,于那独一无二的春色之中.
让你在前端感受逆向题的酸爽。题目的最大难度在于考验人的耐心吧。
题目就不用多说了,很容易发现是Javascript代码审计。
整个文件的变量名/函数名可以看作是混淆了的,分析一下整个文件的结构
——
|- _0x180a,关键字的替换数组
|- 匿名函数,对数组元素进行重排
|- _0xa180,取出对应索引的数组元素
|- check,主要的分析函数
|- test,主要的运行函数
这道题结合浏览器进行动态调试,可以节省很多脑力。
首先是重排,这里不需要去深究到底逻辑原理,让引擎代替你去把数组重排好即可。结合程序员计算器和CyberChef分析更加方便。
这样我们可以直接进入check函数进行分析。
——
|- _0x2e2f8d,又一次进行数组混淆,得到一个新数组
|- _0x50559f,获取flag的前四位,即‘hctf’
|- _0x5cea12,由‘hctf’生成一个基数
|- 这里有一个debug的事件,个人认为是阻止使用F12调试用的,所以可以直接删去
|- 匿名函数,对_0x2e2f8d这个数组再进行排列
|- _0x43c8d1,根据输入获取数组中相应值的函数
|- _0x1c3854,将输入的ascii码转化为16进制,再加上‘0x’
以上部分可以看成是准备部分,这一部分的难点在于多次处理了数组,在动态调试时,很多函数如果多次执行就会产生与原答案不同的数组结构,因此,每次执行都需要重新初始化。
——
|- _0x76e1e8,以下划线分割输入,从后面分析可以得知flag一共有5段
|- _0x34f55b,这一段给出了第一个逆向的条件,结合下一句if条件。
单独来分析,其实最初我看掉了一个括号,结果弄混了符号优先级,导致觉得这个条件没有意义。
这个条件是说,第一段的最后两个字符的16进制和‘{’的16进制异或后,对第一段的长度求余应该等于5。
这里可以先进行如下猜测
第一段,已经有‘hctf{’了,这里正好去最后两位,先猜测第一段一共只有7位,这个猜测是后验的,先不细说。
——
|- b2c
理解这个函数极为重要,通过随机输入进行测试,输出结果有些眼熟,像是base64但不对,比对后确定是base32编码,知道这个就不用再去多解读它了。同时,这里也有一个debug需要删除
——
|- e,第二个逆向条件
这一句是说,第三段做base32编码,取等号前的部分,再进行16进制和0x53a3f32异或等于0x4b7c0a73
计算 0x4b7c0a73^0x53a3f32=0x4E463541
4E463541 => NF5A 16进制转字符
NF5A => iz base32解码
因此,flag暂时如下hctf{x_x_iz_x_x}
——
|- f,第三个逆向条件
这一句是说,第四段和第三段一样编码后,和0x4b7c0a73异或等于0x4315332
计算 0x4315332^0x4b7c0a73=0x4F4D5941
4F4D5941 => OMYA
OMYA => s0
flag hctf{x_x_iz_s0_x}
——
|- n,f*e*第一段的长度(先不管)
|- h,将输入字符串的每一个字符ascii码进行计算(*第二段长度)
后连接起来显示(字符到ascii码转换)
|- j,将第二段以‘3’分割,又后面可以确定是分成了两部分
|- 第四个逆向条件
首先是,分割的两部份长度相等,第一部分和第二部分16进制异或等于0x1613,这个条件只能后验,也先不管。
——
|- k,输入的ascii码*第二段的长度
|- l,第一部分逐字符ascii码*第二段长度等于0x2f9b5072
首先,0x2f9b5072 == 798707826
798 707 826
正好分成三个,已知h是对应ascii码*常数,
所以假设第一部分有三个字符,那么就是变成了求解常数
也就是798 707 826的最大公约数
求解得常数为7
字符 114 101 118 => rev
所以,第二段一共有7个字符,前四个字符为rev3,带入上面的后验条件0x1613
0x726576^0x1613=0x727365
727365 => rse
flag hctf{?_rev3rse_iz_s0_?}
——
|- m,第五个逆向条件,第五段的前四位和第一段的长度有关
题目的hint提示,每一段都有意义,因此我们这里做个爆破,假设第一段的长度在6-30之间,我们可以算出n,在用n去算第五段前四位。
n = f*e*(6-30)
第五段前四位 = n % 0x2f9b5072 + 0x48a05362
代码:
1 | import binascii |
结果:
qK┒
h4r
_;
Vn
L钔V
sr姘
j[瘶
aDx€
X-Ah
O
P
u?
l傡
ck祕
ZT~b
Q=GJ
w羆
n?
e掤t
\{籠
Sd凞
JMM,
p裦
g?n
^ⅧV
U嬃>
可以看到大多数字符都没有意义,除了h4r让人遐想联翩,可惜还是不全,但是结合已经分析出的flag,猜测应该是h4rd。
flag hctf{??_rev3rse_iz_s0_h4rd?}
——
|- _0x5a6d56,将输入重复指定次数组合
|- 第六个逆向条件和第七个逆向条件
由以上条件可以推出以下flag
hctf{??_rev3ser_iz_s0_h4rd2?3??3333}
先假设2和3之间没有数字了,这时7-8位还未知但是7-8位相同,这时的方程
而且在这里,由于直接把0x去掉,所以x的16进制一定全为数字
字符拼接{hex(x)hex(x)} = ascii(x)*13*5
爆破代码:
1 | import binascii |
验证前面的后验条件可以确定如下flag
hctf{??_rev3ser_iz_s0_h4rd23ee3333}
只剩下最前面的两位,为了方便,利用题目提供的sha256结果,我就不回溯条件在判断,直接进行碰撞。
1 | import hashlib |
output:
hctf{j5_rev3rse_iz_s0_h4rd23ee3333}