pico CTFでweb問題を解いてみる #3
picoCTF、web問題の続き。
今回は300point~
Irish-Name-Repo 1
https://play.picoctf.org/practice/challenge/80?category=1&page=1
irishって聞いて、アヤメかなぁ〜と思って指定ページを開いたら外人さんの写真がずらっと出てきた。そういえばアヤメはirisだった。
...と、そんなことはどうでもよくて、今回はアイルランド人(irish)の画像の掲示板みたいなサイトに飛ばされた。サイトの構成としては、
①画像表示ページ
②サポートページ(ユーザからきた質問とadminからの回答が書いてある)
③管理者用ログインページ
の3つで出来ている。問題文に「ログインしてみてくれ」と書いてあったのでとりあえず③に飛んでみた。するとログインフォームが表示されユーザー名とパスワードを求められる。空白にしたり適当な文字列を入力してログインを試みるも、login failedと書かれたところに飛ばされてしまう。
300点問題だしどうせ無駄かなぁと思いつつ、ダメもとで「admin' --」と入力してみるとログインできた。これなら200点問題の「web gauntlet」の方が難しかったぞ...と思った。
picoCTF{s0m3_SQL_c218b685}
Irish-Name-Repo 2
https://play.picoctf.org/practice/challenge/59?category=1&page=1
前問の亜種。50点上がって350点問題。
サイトの構造が全く同じなので、きっとエスケープ処理とかが施されてるんだろうなぁ〜なんて思いつつ、とりあえず今回も「admin' --」を入力してみる。
通ってしまった。なんやねん...
picoCTF{m0R3_SQL_plz_fa983901}
Java Script Kiddie
https://play.picoctf.org/practice/challenge/29?category=1&page=1
問題文には「画像リンクが壊れている」とだけ書かれている。指定されたページに飛ぶと小さい入力フォームだけがあった。
とりあえず適当に文字を入力するとフォームの下に画像が表示された。といっても正しく読み込めていないようで、画像ということを示すアイコンだけが表示されている状態。肝心の画像はわからない。試しに右クリックして画像リンクを取得してみると
data:image/png;base64,V4JOvABUGp2P7/lS+NTvUsNQAc8NBgEAd/NJwU4khWxVAAAOALpEAADeAPMAGK6jfgCF/ImxeQoAAADtST8AZGAUA+A7qxByAAAARQBERJOJs25wSnnuQQEAnACbAF94AOniKE7C+CxUANANKUiKO6RiRwDRAGOwYXjKAIfANmVA/FFHzQrzhR4Wfe0DXVoqSd0ZcvMAdBYEAztLvHep3aG4sgJJSectDmNmmaayzjZ/VPC/3AqjUUDOgIRmxUh/7/1OXQgW78+Sb4/vG/McAK2fxDD3HFRiPzSr1tYa6f5Bam87Sf+Ub2dbFM7eRvzHoXz1vGZRn3euM77zN/Oc+Xx9Ao+/G3eLflgS96vjSEI2+wBQq5JxrQRP09jWendz4S0YNixMK/0F62j4YAjlyEtA6dkXVyj+u2u1yLXptVHnq6VS/sTvMytyqkn5MnLJikALy5vA+eIjvJzfKNlDS2QtXWapDSLFUK/SgInJpy2MUqs41BF+cYvlf9+1DwB03brb5jjpHw/5SneYLCniPCP9rGEgiemlI7VoUNk4us3UD0BR5uaZPvv7L5eNbCAZQQv9d8mT8wsf9+k2ftmIjb/iidWD72SRl5Z3fJ/Lvj8SqtKvet/fcnw7XfWxZA85P++lkA2VIMYnNDVxYVu6TFtKz4XQAPXx9Ul6wd+fUq/xn+fNGFxLC/dNN6oHX3+PYM/yjpni8l2jbrkavASyZp9hNTq6rO8GTtdBnFqWcM1JTJWjn/ItkxDSMf5SfsgePr7mAlartcW5hKqZUr+a65M3OVz8MM92v6r9NX9ej3rm/pqXujeghH45t9mBtV//I98yRk1rZMsRPaMR45O2uE9+7xxzn/5vWvoOzrmJu43n0/H5J2ODX9Iyk/Fff2fvcaXfpPUj54Sm3PHPQ7KUHZxewkrebgDza56t1tL5VEJrKADLiqQA8Qltk89VHcw=
となっていた。
フォームに正しい文字を入力する復号できるといった感じかもしれない。
問題タイトルがJava script kiddyとなっているし、ここで一旦ソースを見てみる。
<script> var bytes = []; $.get("bytes", function(resp) { bytes = Array.from(resp.split(" "), x => Number(x)); }); function assemble_png(u_in){ var LEN = 16; var key = "0000000000000000"; var shifter; if(u_in.length == LEN){ key = u_in; } var result = []; for(var i = 0; i < LEN; i++){ shifter = key.charCodeAt(i) - 48; for(var j = 0; j < (bytes.length / LEN); j ++){ result[(j * LEN) + i] = bytes[(((j + shifter) * LEN) % bytes.length) + i] } } while(result[result.length-1] == 0){ result = result.slice(0,result.length-1); } document.getElementById("Area").src = "data:image/png;base64," + btoa(String.fromCharCode.apply(null, new Uint8Array(result))); return false; } </script>
早速読んでみる。
最初の数行で行っているのは、https://jupiter.challenges.picoctf.org/problem/17205/bytes というページから文字列を取得して、空白で区切ってから配列に格納するという処理。
お次はassemble_png()関数。ここでは入力キーをもとに複合してるっぽい。if文の中とかを見るとキーは16桁ということもわかる。
さてfor文。まずshifterという変数が登場するが、ここには入力キーのi桁目を文字から数値に変換したものが入る(48という数は'0'の文字コード)。 そして続くfor文の中では、bytes中の要素をshifterをもとにズラしてresultへ格納している。
※ "ズラし方"について説明文を書こうと30分くらい画面と格闘したのですが...文字で書くには複雑すぎてうまくまとめられませんでした。後ほどwriteupを探してみたら、アニメーションでわかりやすく解説されているページがありましたので、よければpicoCTF 2019 - JS Kiddie writeup. Solving two picoCTF web challenges for… | by @radekk | Mediumをご覧ください。
続くwhile文では「resultの最後の要素が0であれば取り除く」というのを繰り返して末尾の連続する0を取り除いており、その後最後に画像のsrcを設定。
以上がコードの中身。
ここからやるべきは正しい16桁のキーを見つけることだが、総当たりで探すには少しばかり数が多すぎる。候補を絞り込むために、pngのファイルシグネチャに着目。pngは先頭16バイトが 137 80 78 71 13 10 26 10 0 0 0 13 73 72 68 82 となるので、bytesの中身から正しい入力キーを逆算できそう。
ともあれ1行になっている状態じゃぁ読みにくいので、簡単なプログラムを書いて16列になるように整列表示
// 雑に書いてます。見辛かったらごめんなさい、 #include <iostream> #include <vector> #include <iomanip> using namespace std; int main() { int byte_num, count16=0; vector<int> splited_bytes; while(cin >> byte_num){ splited_bytes.push_back(byte_num); } for (auto it = splited_bytes.begin(); it != splited_bytes.end(); ++it){ count16++ ; cout << setw(3) << *it << " "; if(count16 == 16){ count = 0; cout << endl; } } }
出力(全45行)↓
これを見るだけで、51081803[a][b][c]63640 まで絞れる。
([a][c]は2or3or4, [b]は3or4or5or6)
このレベルなら総当たりでもいいかなって思って、試すと
51081803[4][5][3]63640 を入力した時に画像が出てきた。
読み込むと picoCTF{066cad9e69c5c7e5d2784185c0feb30b}
これを提出したら無事正解できた。長かった...
余談だけども、htmlの画像指定にbase64が使えることは今回初めて知った。「画像をhtmlに直接埋め込むことでサーバへのHTTPリクエストを削減でき高速化につながる」というメリットもあるらしい(画像が小さい場合に限る)
疲れたのでこの辺で。