何度,心が折れても,あきらめずに粘り強く頑張った結果解けた問題なので,うれしさのあまり Writeup を残す。
ソース
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="http://gc.kis.v2.scr.kaspersky-labs.com/FD126C42-EBFA-4E12-B309-BB3FDD723AC1/main.js?attr=zfDykIDMiQvcx9foL-veRUG-WvILWzA6D1-Oe8lKtGgKHIAIGJ5pQGpISvCBVnEvZLIsS9ByPXs0qLQMYgQ9jw" charset="UTF-8"></script><script src="G82XCw5CX3.js"></script>
</head>
<body>
<h4>Enter flag:</h4>
<input type="text" id="input"/>
<button onclick="onButtonPress()">Submit</button>
<p id="result"></p>
</body>
</html>
JavaScript で getElementById('input')とか出てきそうですね。
JavaScriptのソース
const _0x402c=['value','2wfTpTR','instantiate','275341bEPcme','innerHTML','1195047NznhZg','1qfevql','input','1699808QuoWhA','Correct!','check_flag','Incorrect!','./JIFxzHyW8W','23SMpAuA','802698XOMSrr','charCodeAt','474547vVoGDO','getElementById','instance','copy_char','43591XxcWUl','504454llVtzW','arrayBuffer','2NIQmVj','result'];const _0x4e0e=function(_0x553839,_0x53c021){_0x553839=_0x553839-0x1d6;let _0x402c6f=_0x402c[_0x553839];return _0x402c6f;};(function(_0x76dd13,_0x3dfcae){const _0x371ac6=_0x4e0e;while(!![]){try{const _0x478583=-parseInt(_0x371ac6(0x1eb))+parseInt(_0x371ac6(0x1ed))+-parseInt(_0x371ac6(0x1db))*-parseInt(_0x371ac6(0x1d9))+-parseInt(_0x371ac6(0x1e2))*-parseInt(_0x371ac6(0x1e3))+-parseInt(_0x371ac6(0x1de))*parseInt(_0x371ac6(0x1e0))+parseInt(_0x371ac6(0x1d8))*parseInt(_0x371ac6(0x1ea))+-parseInt(_0x371ac6(0x1e5));if(_0x478583===_0x3dfcae)break;else _0x76dd13['push'](_0x76dd13['shift']());}catch(_0x41d31a){_0x76dd13['push'](_0x76dd13['shift']());}}}(_0x402c,0x994c3));let exports;(async()=>{const _0x48c3be=_0x4e0e;let _0x5f0229=await fetch(_0x48c3be(0x1e9)),_0x1d99e9=await WebAssembly[_0x48c3be(0x1df)](await _0x5f0229[_0x48c3be(0x1da)]()),_0x1f8628=_0x1d99e9[_0x48c3be(0x1d6)];exports=_0x1f8628['exports'];})();function onButtonPress(){const _0xa80748=_0x4e0e;let _0x3761f8=document['getElementById'](_0xa80748(0x1e4))[_0xa80748(0x1dd)];for(let _0x16c626=0x0;_0x16c626<_0x3761f8['length'];_0x16c626++){exports[_0xa80748(0x1d7)](_0x3761f8[_0xa80748(0x1ec)](_0x16c626),_0x16c626);}exports['copy_char'](0x0,_0x3761f8['length']),exports[_0xa80748(0x1e7)]()==0x1?document[_0xa80748(0x1ee)](_0xa80748(0x1dc))[_0xa80748(0x1e1)]=_0xa80748(0x1e6):document[_0xa80748(0x1ee)](_0xa80748(0x1dc))[_0xa80748(0x1e1)]=_0xa80748(0x1e8);}
Syncerで整形してみる
const _0x402c = ['value', '2wfTpTR', 'instantiate', '275341bEPcme', 'innerHTML', '1195047NznhZg', '1qfevql', 'input', '1699808QuoWhA', 'Correct!', 'check_flag', 'Incorrect!', './JIFxzHyW8W', '23SMpAuA', '802698XOMSrr', 'charCodeAt', '474547vVoGDO', 'getElementById', 'instance', 'copy_char', '43591XxcWUl', '504454llVtzW', 'arrayBuffer', '2NIQmVj', 'result'];
const _0x4e0e = function (_0x553839, _0x53c021) {
_0x553839 = _0x553839 - 0x1d6;
let _0x402c6f = _0x402c[_0x553839];
return _0x402c6f;
};
(function (_0x76dd13, _0x3dfcae) {
const _0x371ac6 = _0x4e0e;
while (!![]) {
try {
const _0x478583 = -parseInt(_0x371ac6(0x1eb)) + parseInt(_0x371ac6(0x1ed)) + -parseInt(_0x371ac6(0x1db)) * -parseInt(_0x371ac6(0x1d9)) + -parseInt(_0x371ac6(0x1e2)) * -parseInt(_0x371ac6(0x1e3)) + -parseInt(_0x371ac6(0x1de)) * parseInt(_0x371ac6(0x1e0)) + parseInt(_0x371ac6(0x1d8)) * parseInt(_0x371ac6(0x1ea)) + -parseInt(_0x371ac6(0x1e5));
if (_0x478583 === _0x3dfcae) break;
else _0x76dd13['push'](_0x76dd13['shift']());
} catch (_0x41d31a) {
_0x76dd13['push'](_0x76dd13['shift']());
}
}
}(_0x402c, 0x994c3));
let exports;
(async() => {
const _0x48c3be = _0x4e0e;
let _0x5f0229 = await fetch(_0x48c3be(0x1e9)),
_0x1d99e9 = await WebAssembly[_0x48c3be(0x1df)](await _0x5f0229[_0x48c3be(0x1da)]()),
_0x1f8628 = _0x1d99e9[_0x48c3be(0x1d6)];
exports = _0x1f8628['exports'];
})();
function onButtonPress() {
const _0xa80748 = _0x4e0e;
let _0x3761f8 = document['getElementById'](_0xa80748(0x1e4))[_0xa80748(0x1dd)];
for (let _0x16c626 = 0x0; _0x16c626 < _0x3761f8['length']; _0x16c626++) {
exports[_0xa80748(0x1d7)](_0x3761f8[_0xa80748(0x1ec)](_0x16c626), _0x16c626);
}
exports['copy_char'](0x0, _0x3761f8['length']), exports[_0xa80748(0x1e7)]() == 0x1 ? document[_0xa80748(0x1ee)](_0xa80748(0x1dc))[_0xa80748(0x1e1)] = _0xa80748(0x1e6) : document[_0xa80748(0x1ee)](_0xa80748(0x1dc))[_0xa80748(0x1e1)] = _0xa80748(0x1e8);
}
混乱を恐れず,自分の失敗を書いておく
0: "instance"
1: "copy_char"
2: "43591XxcWUl"
3: "504454llVtzW"
4: "arrayBuffer"
5: "2NIQmVj"
6: "result"
7: "value"
8: "2wfTpTR"
9: "instantiate"
10: "275341bEPcme"
11: "innerHTML"
12: "1195047NznhZg"
13: "1qfevql"
14: "input"
15: "1699808QuoWhA"
16: "Correct!"
17: "check_flag"
18: "Incorrect!"
19: "./JIFxzHyW8W"
20: "23SMpAuA"
21: "802698XOMSrr"
22: "charCodeAt"
23: "474547vVoGDO"
24: "getElementById"
変換表(シャッフル前)に基づいた難読化の解除
function onButtonPress() {
const _0xa80748 = _0x4e0e;
let _0x3761f8 = document['getElementById']('802698XOMSrr')['input'];
for (let _0x16c626 = 0x0; _0x16c626 < _0x3761f8['length']; _0x16c626++) {
exports['2wfTpTR'](_0x3761f8['arrayBuffer'](_0x16c626), _0x16c626);
}
exports['copy_char'](0x0, _0x3761f8['length']), exports['getElementById']() == 0x1 ? document['result']('1qfevql')['Incorrect!'] = '474547vVoGDO' : document['result']('1qfevql')['Incorrect!'] = 'instance';
}
あれ?意味判らん???
失敗の原因
else _0x76dd13['push'](_0x76dd13['shift']());
} catch (_0x41d31a) {
_0x76dd13['push'](_0x76dd13['shift']());
push や shift で配列をシャッフルしてる。
静的解析はまだ私には無理だ。
動的解析するしかない。
Submit前の _0x402c (再掲)
0: "instance"
1: "copy_char"
2: "43591XxcWUl"
3: "504454llVtzW"
4: "arrayBuffer"
5: "2NIQmVj"
6: "result"
7: "value"
8: "2wfTpTR"
9: "instantiate"
10: "275341bEPcme"
11: "innerHTML"
12: "1195047NznhZg"
13: "1qfevql"
14: "input"
15: "1699808QuoWhA"
16: "Correct!"
17: "check_flag"
18: "Incorrect!"
19: "./JIFxzHyW8W"
20: "23SMpAuA"
21: "802698XOMSrr"
22: "charCodeAt"
23: "474547vVoGDO"
24: "getElementById"
0: "instance"
1: "copy_char"
2: "43591XxcWUl"
3: "504454llVtzW"
4: "arrayBuffer"
5: "2NIQmVj"
6: "result"
7: "value"
8: "2wfTpTR"
9: "instantiate"
10: "275341bEPcme"
11: "innerHTML"
12: "1195047NznhZg"
13: "1qfevql"
14: "input"
15: "1699808QuoWhA"
16: "Correct!"
17: "check_flag"
18: "Incorrect!"
19: "./JIFxzHyW8W"
20: "23SMpAuA"
21: "802698XOMSrr"
22: "charCodeAt"
23: "474547vVoGDO"
24: "getElementById"
変換表(シャッフル後)に基づいた難読化の解除
function onButtonPress() {
const _0xa80748 = _0x4e0e;
let _0x3761f8 = document['getElementById']("input")["value"];
for (let _0x16c626 = 0x0; _0x16c626 < _0x3761f8['length']; _0x16c626++) {
exports["copy_char"](_0x3761f8["charCodeAt"](_0x16c626), _0x16c626);
}
exports['copy_char'](0x0, _0x3761f8['length']), exports["check_flag"]() == 0x1 ? document["getElementById"]("result")["innerHTML"] = "Correct!" : document["getElementById"]("result")["innerHTML"] = "Incorrect!";
}
意味判るようになった。
check_flagという関数とcopy_charという関数をexportしてる。
なるほど, JavaScript モジュールを動的に作成するのね
が,中身はようわからん。
たぶん,シャッフル兼,フラグを復号しているところ
(function (_0x76dd13, _0x3dfcae) {
const _0x371ac6 = _0x4e0e;
while (!![]) {
try {
const _0x478583 = -parseInt(_0x371ac6(0x1eb)) + parseInt(_0x371ac6(0x1ed)) + -parseInt(_0x371ac6(0x1db)) * -parseInt(_0x371ac6(0x1d9)) + -parseInt(_0x371ac6(0x1e2)) * -parseInt(_0x371ac6(0x1e3)) + -parseInt(_0x371ac6(0x1de)) * parseInt(_0x371ac6(0x1e0)) + parseInt(_0x371ac6(0x1d8)) * parseInt(_0x371ac6(0x1ea)) + -parseInt(_0x371ac6(0x1e5));
if (_0x478583 === _0x3dfcae) break;
else _0x76dd13['push'](_0x76dd13['shift']());
} catch (_0x41d31a) {
_0x76dd13['push'](_0x76dd13['shift']());
}
}
}(_0x402c, 0x994c3));
let exports;
(async() => {
const _0x48c3be = _0x4e0e;
let _0x5f0229 = await fetch(_0x48c3be(0x1e9)),
_0x1d99e9 = await WebAssembly[_0x48c3be(0x1df)](await _0x5f0229[_0x48c3be(0x1da)]()),
_0x1f8628 = _0x1d99e9[_0x48c3be(0x1d6)];
exports = _0x1f8628['exports'];
})();
この中では WebAssembly が気になる
WebAssembly は JavaScript と並行して動作するらしい
exportしてたcheck_flag()があった。
copy_charも
その下には。。。
ビンゴ!
あの変なJavascriptちゃんと動くのね