Skip to content

Latest commit

 

History

History

Pkg

Pkg:Web:616pts

Shou hoards a flag in a NodeJS binary and he thinks it is safe. Prove him wrong.
Source Code

Solution

zipファイルが配られる。
解凍するとlinux、macos、winの実行ファイルが出現する。
自前のwin機でアイコンを見ると、どうやらnodeで開発されたexeのようだ。
実行するとApp listening on port 10001と10001番ポートでHTTPサーバが立ち上がる。
アクセスすると以下のようなページが表示された。
image1.png
ウサギさんが跳ねており、右側にEncrypted Flagが表示されている。
以下のような文字列であった。

V44FTEScskUnyxOlSRtWiWqrY6tGOPYtxvNOZxx6rxQD7BAJJncc86enn5FYp53hJDdbCcJDsudy39grhL7DAlUe+NPOgV+j7BN1igZRE9C+y5kORoyKF7AP0H5oErn6HdvdUK9f3ANfWJk9EzcB3M7MhcyC/zmL/xZ4Bf4VmVVicZCVDEteYCNVPA8vr0olphXJIEkBmhXG3wy9OrKTkh4VonqSjMvlBvqWELJlsWUdgvKVht2yHVErwF1K27xf

Real Flagは取得できないのでexeを解析する。
問題名からpkgのようだ。
梱包されたパッケージはSnapshot filesystemにアクセスでき、そこに各種ファイルが配置されるらしい。
天才チームメンバが「Sunshine CTF 2019 - The Whole Pkg」なる類題を見つけてくれた。
バイナリファイルのC:\\snapshotを書き換えることで、エントリーポイントをローカルのjsファイルに差し替えることができるらしい。
書き換えるためのパスを知るために、grepをかける。

$ strings binaryexpress-win.exe | grep snapshot
~~~
{"C:\\snapshot\\flag1\\server.js":{"0":[0,2112],"3":[2112,119]},"C:\\snapshot\\flag1\\package.json":{"1":[2231,436],"3":[2667,119]},"C:\\snapshot\\flag1\\views\\flag.ejs":{"1":[2786,5943],"3":[8729,120]},"C:\\snapshot\\flag1\\node_modules\\ejs\\package.json":{"1":[8849,896],"3":[9745,119]},"C:\\snapshot\\flag1\\node_modules\\ejs\\lib\\ejs.js":{"0":[9864,20904],"1":[30768,27481],"3":[58249,121]},"C:\\snapshot\\flag1\\node_modules\\express\\package.json":{"1":[58370,2623],"3":[60993,120]},"C:\\snapshot\\flag1\\node_modules\\express\\index.js":{"0":[61113,568],"1":[61681,224],"3":[61905,119]},"C:\\snapshot\\flag1\\node_modules\\node-rsa\\package.json":{"1":[62024,863],"3":[62887,119]},"C:\\snapshot\\flag1\\node_modules\\node-rsa\\src\\NodeRSA.js":{"0":[63006,11856],"1":[74862,13824],"3":[88686,121]},"C:\\snapshot\\flag1\\private_key.der":{"1":[88807,345],"3":[89152,119]},"C:\\snapshot\\flag1"
~~~
"C:\\snapshot\\flag1\\node_modules\\color-name":{"2":[3096728,27],"3":[3096755,117]},"C:\\snapshot\\flag1\\node_modules\\filelist\\node_modules":{"2":[3096872,31],"3":[3096903,117]},"C:\\snapshot\\flag1\\node_modules\\iconv-lite\\encodings\\tables":{"2":[3097020,126],"3":[3097146,117]}}
"C:\\snapshot\\flag1\\server.js"

今回はC:\\snapshot\\flag1\\server.jsC:\\snapshotC:\\snapshooに編集し、ローカルに作成したC:\snapshoo\flag1\server.jsを読み込ませる。
本来のC:\snapshot\flag1\server.jsを読み取ってしまえばフラグが書かれていると考え、以下のようなserver.jsを作成した。

const fs = require("fs");
var data = fs.readFileSync("C:\\snapshot\\flag1\\server.js").toString("utf8");
console.log(data);

エントリーポイントを書き換えたbinaryexpress-win_snapshoo.exeを実行すると以下の結果が得られた。

>binaryexpress-win_snapshoo.exe
source-code-not-available

どうやらソースは見せてくれないようだ。
復元が難しいため、grep結果より他のファイルを調査するとC:\snapshot\flag1\private_key.derが存在することがわかる。
これを読み取りEncrypted Flagを復号すればよい。
以下の通りserver.jsを書き換える。

const fs = require("fs");
//var data = fs.readFileSync("C:\\snapshot\\flag1\\server.js").toString("utf8");
var data = fs.readFileSync("C:\\snapshot\\flag1\\private_key.der").toString("hex");
console.log(data);

実行すると次の通りになる。

>binaryexpress-win_snapshoo.exe
30820155020100300d06092a864886f70d01010105000482013f3082013b020100024100986676bc7f0f74451ba334cda8789af88b023c683b1f8b6dd6e0266edb7e1dd2f2bbc39e4d1b0d42cfc5cbb2f4538c2cb7654b86076756e8f10183fb4054d2f5020301000102410084c155135457a1000658404a1a449d327edcfec40924ac6f8d2b8b2f2c728b04f6f103d28a203ec367951752097243192a6d0ad6f9eef317cea0fdc36202c9ed022100eae770f32c77135461aa7d5ada3d14b2670475984c5354b7eff06602ed80690b022100a616383d8d19faad64d14ec99a6ba589b02353078d4db2b110e235d67edd33ff0220041e4ca7a6c6ebaad60f84251ca067857d32e1d0eabda745964a53af877471e30221008a5a1e155ff21138d9afe602d8a8ed67aa1b72f1ea8a9bdd16246a16b8ed897f022049beb187600910cebb9bcc6ba9be94d54dec76aba0ffdbb5ee696595aced7539

ファイルのhexが得られたため適当にhex2binして、private_key.derに戻してやればよい。
derをそのまま読み込んでも復号可能だと思われるが、扱いやすいpemにする。

$ openssl rsa -in private_key.der -inform DER -out private_key.pem -outform PEM
writing RSA key
$ cat private_key.pem
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAJhmdrx/D3RFG6M0zah4mviLAjxoOx+LbdbgJm7bfh3S8rvDnk0b
DULPxcuy9FOMLLdlS4YHZ1bo8QGD+0BU0vUCAwEAAQJBAITBVRNUV6EABlhAShpE
nTJ+3P7ECSSsb40riy8scosE9vED0oogPsNnlRdSCXJDGSptCtb57vMXzqD9w2IC
ye0CIQDq53DzLHcTVGGqfVraPRSyZwR1mExTVLfv8GYC7YBpCwIhAKYWOD2NGfqt
ZNFOyZprpYmwI1MHjU2ysRDiNdZ+3TP/AiAEHkynpsbrqtYPhCUcoGeFfTLh0Oq9
p0WWSlOvh3Rx4wIhAIpaHhVf8hE42a/mAtio7WeqG3Lx6oqb3RYkaha47Yl/AiBJ
vrGHYAkQzrubzGupvpTVTex2q6D/27XuaWWVrO11OQ==
-----END RSA PRIVATE KEY-----

あとは暗号文を復号するだけなので、node-rsaを用いて以下のようなプログラムsolver.jsを作成する。

const NodeRSA = require("node-rsa");

const key = new NodeRSA(`
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAJhmdrx/D3RFG6M0zah4mviLAjxoOx+LbdbgJm7bfh3S8rvDnk0b
DULPxcuy9FOMLLdlS4YHZ1bo8QGD+0BU0vUCAwEAAQJBAITBVRNUV6EABlhAShpE
nTJ+3P7ECSSsb40riy8scosE9vED0oogPsNnlRdSCXJDGSptCtb57vMXzqD9w2IC
ye0CIQDq53DzLHcTVGGqfVraPRSyZwR1mExTVLfv8GYC7YBpCwIhAKYWOD2NGfqt
ZNFOyZprpYmwI1MHjU2ysRDiNdZ+3TP/AiAEHkynpsbrqtYPhCUcoGeFfTLh0Oq9
p0WWSlOvh3Rx4wIhAIpaHhVf8hE42a/mAtio7WeqG3Lx6oqb3RYkaha47Yl/AiBJ
vrGHYAkQzrubzGupvpTVTex2q6D/27XuaWWVrO11OQ==
-----END RSA PRIVATE KEY-----
`);

const encrypted = "V44FTEScskUnyxOlSRtWiWqrY6tGOPYtxvNOZxx6rxQD7BAJJncc86enn5FYp53hJDdbCcJDsudy39grhL7DAlUe+NPOgV+j7BN1igZRE9C+y5kORoyKF7AP0H5oErn6HdvdUK9f3ANfWJk9EzcB3M7MhcyC/zmL/xZ4Bf4VmVVicZCVDEteYCNVPA8vr0olphXJIEkBmhXG3wy9OrKTkh4VonqSjMvlBvqWELJlsWUdgvKVht2yHVErwF1K27xf";

const decrypted = key.decrypt(encrypted, "utf8");
console.log("flag: ", decrypted);

実行する。

$ node solver.js
flag:  we{32e0f460-710f-4a05-b716-39d1acc3a387@jU3tGue$31t}

flagが得られた。

we{32e0f460-710f-4a05-b716-39d1acc3a387@jU3tGue$31t}