题目:就是这里 → http://ctf.idf.cn/game/web/28
点击链接,弹出一个输入框。要求输入flag。
查看源代码,发现一个script脚本。
然后复制到站长工具js混淆加密压缩那里解密。
http://tool.chinaz.com/js.aspx
得到解密后的代码:
var a = prompt("\u8f93\u5165\u4f60\u7684\x66\x6c\x61\x67\u5427\uff0c\u5c11\u5e74\uff01", "");
var b = "\x66\x33\x33\x37\x33\x65\x33\x36\x63\x36\x37\x37\x37\x35\x30\x37\x37\x39\x66\x35\x64\x30\x34\x66\x66\x37\x38\x38\x35\x62\x33\x65";
var c = /.+_.+_.+/gi;
var d = 0x0;
var e = a.substr(0x8, 0x5);
if ($.md5(e) == b.replace(/7/ig, ++d).replace(/8/ig, d * 0x2)) {
var f = a.substr(0x0 / d, 0x7);
if (f.substr(0x5, 0x2) == "\x6a\x73" && $.md5(f.substr(0x0 / d, d + 0x3)) == "\x64\x30\x31\x35\x34\x64\x35\x30\x34\x38\x62\x35\x61\x35\x65\x62\x31\x30\x65\x66\x31\x36\x34\x36\x34\x30\x30\x37\x31\x39\x66\x31") {
r = a.substr(0xd);
if (r.charCodeAt(d) - 0x19 == r.charCodeAt(++d) - 0x19 && r.charCodeAt(--d) - 0x19 == r.charCodeAt(--d)) {
var g = String.fromCharCode(0x4f);
g = g.toLowerCase() + g.toLowerCase();
if (r.substr((++d) * 0x3, 0x6) == g.concat("\x65\x61\x73\x79") && c.test(a)) {
d = String(0x1) + String(a.length)
}
}
}
};
if (a.substr(0x4, 0x1) != String.fromCharCode(d) || a.substr(0x4, 0x1) == "\x7a") {
alert("\u989d\uff0c\u518d\u53bb\u60f3\u60f3\u3002\u3002")
} else {
alert("\u606d\u559c\u606d\u559c\uff01")
}
将代码中的unicode码和ascii码全部转换为字符,将16进制数转换为10进制数。
转换方法:直接在浏览器控制台输入即可转换。
转换后的代码如下:
var a = prompt("输入你的flag吧,少年!", "");
var b = "f3373e36c677750779f5d04ff7885b3e";
var c = /.+_.+_.+/gi;
var d = 0;
var e = a.substr(8, 5);
if ($.md5(e) == b.replace(/7/ig, ++d).replace(/8/ig, d * 2)) {
var f = a.substr(0 / d, 7);
if (f.substr(5, 2) == "js" && $.md5(f.substr(0 / d, d + 3)) == "d0154d5048b5a5eb10ef1646400719f1") {
r = a.substr(13);
if (r.charCodeAt(d) - 25 == r.charCodeAt(++d) - 25 && r.charCodeAt(--d) - 25 == r.charCodeAt(--d)) {
var g = String.fromCharCode(79);
g = g.toLowerCase() + g.toLowerCase();
if (r.substr((++d) * 3, 6) == g.concat("easy") && c.test(a)) {
d = String(1) + String(a.length)
}
}
}
};
if (a.substr(4, 1) != String.fromCharCode(d) || a.substr(4, 1) == "z") {
alert("额,再去想想。。")
} else {
alert("恭喜恭喜!")
}
好,那么现在看起来就舒服多了,开始解密吧,一般情况下,解密都是从最后一句往前逆推,可是这里的代码不行,需要正向解密,就是从头到尾
一句一句的去分析。
在分析的过程中,为了方便观察过程,加上注释以及将一些有关算数的表达式直接计算出结果替换原来的表达式。
大概浏览一下可得知字符串a是我们要求的答案。
第一句是输入字符串变量a,接着给出b,c,d三个变量。
看到第一个if语句,判断的是e变量经过md5加密后和b变量经过正则替换后是否相等。
在控制台下输入即可看到结果:
可看到b变量替换后的结果为:f3313e36c611150119f5d04ff1225b3e
此时d=1
去cmd5网站解密得到:jiami
那么现在就得知,e = a.substr(8, 5)=“jiami”
字符串a从第八位开始的五位是"jiami"
做好注释:
var e = a.substr(0x8, 0x5); //e="jiami"
if ($.md5(e) == b.replace(/7/ig, ++d).replace(/8/ig, d * 0x2)) { //f3313e36c611150119f5d04ff1225b3e="jiami"
接着,var f = a.substr(0 / d, 7);
因为d=1,所以这里是var f = a.substr(0, 7);
由此可知 f 的长度为7
看第二个if语句:
if (f.substr(5, 2) == "js" && $.md5(f.substr(0 / d, d + 3)) == "d0154d5048b5a5eb10ef1646400719f1")
等价于
if (f.substr(5, 2) == "js" && $.md5(f.substr(0,4)) == "d0154d5048b5a5eb10ef1646400719f1")
到cmd5解密可得d0154d5048b5a5eb10ef1646400719f1=“wctf”
由此可知 f = "wctf?js" , 其中?为未知字符,不过做了这么多题,这个问号很明显就是"{",因为idf的题目的答案都是
"wctf{........}"这样的格式的。
那么现在就得知 a 从第0位到第12位为"wctf?js?jiami"。
r = a.substr(13);
r 是 a 从第13位开始到最后1位的字符串。
接着是第三个if语句:
if (r.charCodeAt(d) - 25 == r.charCodeAt(++d) - 25 && r.charCodeAt(--d) - 25 == r.charCodeAt(--d))
等价于
if (r.charCodeAt(1) - 25 == r.charCodeAt(2) - 25 && r.charCodeAt(1) - 25 == r.charCodeAt(0))
由此可知,r 的第0位的ascii码(10进制)比第1位的ascii码小25,第1位和第2位是相同的字符。
var g = String.fromCharCode(79);g = g.toLowerCase() + g.toLowerCase();
在控制台下运行的结果:
那么这两句就等价于:
var g="oo";
接着是第4个if语句:
if (r.substr(3,6) == g.concat("easy") && c.test(a))
可知g.concat("easy")="ooeasy",r 从第3为开始的6个字符是"ooeasy"
此时我们得知 r = “???ooeasy.........”(后面有若干位未知)
那么现在我们得知 a = "wctf?js?jiami???ooeasy........."(后面的若干位未知)
test() 方法用于检测一个字符串是否匹配某个模式。
从c.test(a)可知,a 的格式必为"??_??_??"
d= String(0x1) + String(a.length)
这时我们并不知道 a 的长度,所以无法知道 d 的值。
继续往下看。
第5个if语句:
if (a.substr(4,1) != String.fromCharCode(d) || a.substr(4,1) == "z")
由此得知 a 的第4位不是"z",而且 d 代表的字符。
好了,现在我们看看掌握的信息。
现在解得的 a 为"wctf?js?jiami???oosasy................."(后面还有若干位未知)
前面提到过idf题目的答案都是固定格式的,"wctf{..........}",那么我们就猜测 a 的第4位是"{",查看"{"的ascii码为 123,那么
我们假设d=“123”
由 d= String(0x1) + String(a.length)得知,a.length=23
那么我们数一数"wctf{js?jiami???ooeasy........"中确定的共有多少位,数了一下从w开始到y结束共有22位
可以确定 a =“wctf{js?jiami???ooeasy?”
根据格式我们猜测最后一位是"}"
那么 a = “wctf{js?jiami???ooeasy}”
由前面的test方法可知 a 的格式为??_??_?? ,那么我们继续猜测 a = "wctf{js_jiami_??ooeasy}"
前面已知, r 的第0位的ascii码比第1位小25,第1位和第2位是相同的字符此时,r 的第0位为"_",查看其ascii码是95,那么第1位
的ascii码是95+25=120,字符是“x”
到这里,a 的值我们已经全部猜解完了,a = “wctf{js_jiami_xxooeasy}”
经过注释之后的代码:
var a = prompt("输入你的flag吧,少年!", "");
var b = "f3373e36c677750779f5d04ff7885b3e";
var c = /.+_.+_.+/gi;
var d = 0;
var e = a.substr(8, 5); //e="jiami"
if ($.md5(e) == b.replace(/7/ig, ++d).replace(/8/ig, d * 2)) { //f3313e36c611150119f5d04ff1225b3e="jiami"
var f = a.substr(0,7); //f="wctf*js"
if (f.substr(5,2) == "js" && $.md5(f.substr(0,4)) == "d0154d5048b5a5eb10ef1646400719f1") {
r = a.substr(13); //len(r)=23-13=10
n=r.charCodeAt(1) - 25;
if (r.charCodeAt(1) - 25 == r.charCodeAt(2) - 25 && r.charCodeAt(1) - 25 == r.charCodeAt(0)) {
var g="oo"; //r="_xxooeasy}"
if (r.substr(3,6) == g.concat("easy") && c.test(a)) {
d = String(1) + String(a.length) //d="123" a.length=23
}
}
}
};
if (a.substr(4,1) != String.fromCharCode(d) || a.substr(4,1) == "z") {
alert("额,再去想想。。")
} else {
alert("恭喜恭喜!")
} //a="wctf{js_jiami_xxooeasy}";
现在看看我们猜得对不对吧。
结果正确。
收获:解密并不都是从尾到头逆推,还可以是从头到尾的正向解密。在本题中是正向和逆向的结合分析,锻炼了分析的能力。
时间: 2024-10-13 21:20:58