picoCTF 2021 Some Assembly Required 1 Writeup

何度，心が折れても，あきらめずに粘り強く頑張った結果解けた問題なので，うれしさのあまり Writeup を残す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/49c2bdff-7bfa-2eb0-14bc-6ceefcbb13e2.png)

http://mercury.picoctf.net:26318/index.html

外見
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/f9d42555-2065-4254-274e-5797b193af96.png)

ソース

```html
<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')とか出てきそうですね。

https://developer.mozilla.org/ja/docs/Web/API/Document/getElementById

JavaScriptのソース

```javascript:G82XCw5CX3.js
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で整形してみる

https://lab.syncer.jp/Tool/JavaScript-PrettyPrint/

```javascript:G82XCw5CX3.js
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);
}
```

混乱を恐れず，自分の失敗を書いておく

Consoleで_0x402c をたたき変換表を確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/882a9dd3-f911-a0dc-edb1-d6abfddfe424.png)

```javascript:変換表（シャッフル前）
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"
```

変換表（シャッフル前）に基づいた難読化の解除

```javascript:onButtonPress()
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';
}
```

あれ？意味判らん？？？

失敗の原因

```javascript
      else _0x76dd13['push'](_0x76dd13['shift']());
    } catch (_0x41d31a) {
      _0x76dd13['push'](_0x76dd13['shift']());
```

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/push

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/shift

push や shift で配列をシャッフルしてる。
静的解析はまだ私には無理だ。
動的解析するしかない。

Submit前の _0x402c （再掲）

```javascript:変換表（シャッフル前）
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"
```

Submit後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/faa4d837-fea7-373c-44a6-cd4121702b28.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/11d48433-b508-56bf-6beb-5cd94157d566.png)


```javascript:変換表（シャッフル後）
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"
```

変換表（シャッフル後）に基づいた難読化の解除

```javascript:onButtonPress()
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してる。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export

なるほど， JavaScript モジュールを動的に作成するのね
が，中身はようわからん。
たぶん，シャッフル兼，フラグを復号しているところ

```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 が気になる

https://developer.mozilla.org/ja/docs/WebAssembly

 WebAssembly は JavaScript と並行して動作するらしい

https://www.youtube.com/watch?v=VBMHswhun-s

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/f5f2ee0c-88e2-b6c1-d314-71ff91648482.png)

exportしてたcheck_flag()があった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/686b2eb0-27c3-a8b1-cf51-57dd64b279c1.png)
copy_charも
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/75dcb3ae-4f40-e906-880f-a949f972bd84.png)
その下には。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/720a2186-6aef-8912-a97a-6ad31a709a9d.png)
ビンゴ！
あの変なJavascriptちゃんと動くのね
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/6dc43100-0bc3-31d6-911b-9e7c0714e32c.png)