place初心者向けらしいので出てみた。Webが解けなさすぎるっぽいのでなんとかしたい。Ramenなぜかラーメン店員を検索できるWebページに隠されたフラグを探す。名前の部分文字列でヒットするので、SQLのLIKEで取ってきてるのかな。これはいわゆるあの有名なSQLインジェクションをしろと言うことなのか?まあ普段何気なしに使っている言葉でも正直実際にやったことはなかったんで試行錯誤結構大変だった。クエリを通すのが初心者には結構難しかった。メッセージが返ってくるので、PHPなのかと思った。いい加減に通るのかなとか思っていたのだけど、カンマの対応がちゃんと付いていないと受け付けてくれないみたいで、それとテーブル名とカラム名はちゃんと存在するものを入れないとfetchAll()がコケるらしかった。やると、ラーメン店員にスマホ太郎を紛れ込ませることに成功した。バックエンドが何かよく分からなかったので、でもまあこれはsqliteかpostgresかmysqlかしかないと思うので、それらを区別できそうなものを頑張ってインターネットで調べると、MySQLらしいということが分かった。いうクエリを投げてみると、テーブル一覧がだだーっと流れてきた。いうのがあったので、多分これがそうなんだろう。表示させてみたのだが、これが0になっている。いうテーブルもあって、これは3人いるはずなのに、これのtable_rowsは2になっていたので、数え方がなぜか0オリジンなのだろうか。テーブル名が分かったので、つぎにどういうカラムがあるのかを調べるクエリを投げる。いうカラムに多分フラグが入っていることが分かった。最終的に次のようなクエリでフラグが得られた。ctf4b{a_simple_sql_injection_with_union_select}ふーむこれがこういう系で一番シンプルな感じなのかなあと思った。shellcoderとりあえず与えられたバイナリを逆コンパイルしてみる。いずれかの文字が含まれていなければそれを実行してくれるプログラムらしい。プログラム中にそれらしき文字列も含まれていないし、そういうバイトが含まれないように自力でexecveを呼び出す0x28バイト以内のコード書くのめんどくさそうだなあ・・・と思ったら、インターネットに落ちてたコードを拾ってきたらそのまま動いてしまった。それをpwntoolsで送りつけて終了。ctf4b{Byp4ss_us!ng6_X0R_3nc0de}。xorしてバイパスしろという趣旨の問題だったのか。OneLineこれもとりあえず逆コンパイル。0;}バッファの0x20~0x28バイト目にwriteのポインタを書き込んで、それをバッファのポインタを引数に呼び出すコードになっているので、0x20バイトの所に呼び出したい関数のアドレスを、0x00~0x20のところにデータを書き込めば良さそう。ご親切に2回readしてくれるプログラムだ。1回目は読んだバイト数ではなく、ポインタまで含めた領域をwriteしているので、1回目で何か適当に短いクエリを投げればwriteのポインタが勝手に降ってくる。呼び出せば良さそうだ。system_addr))io.interactive()ところが、なぜかこれはSIGSEGVで動かなかった(なんでだろう)。NULLこんなのが見つかったので、これの2個目を使うとうまく行った。ctf4b{0v3rwr!t3_Func7!on_p0int3r}。関数ポインタを上書きするだけでは上手くいかなかった理由は後で調べておきたい。lineという名前なんだろう。memoまたまたとりあえず逆コンパイル。0;}やっているコードはだいぶ謎だし、うまくC言語にマッピングできなくてだいぶ変なことになっちゃってる。途中でスタックポインタをいじってるので、スタック読み書きのコードも読みづらくなってる。するなんでサイズを入力させてから固定の長さのfgetsをするのかとか、この切り上げ処理微妙に切り上げになって無くないかとか思ったり、よく分からないところは残ったが、この問題のキモは、最初の整数の入力の時に、符号無しでサイズの比較をやってしまっているので、負の数が入力出来てしまうという所だろう。負の数が入力出来てしまうと言うことによって、RSPを引き算してmainのリターンアドレスを壊せないという安全設計(?)が、逆に足し算してピンポイントで破壊できるようになってしまっている。0x10で切り上げているので、RSPの足し引きは0x10の倍数でしか行えない。このmain関数はスタックを0x50バイト使っているので、RSPを0x50足してやれば、fgetsで書き込むバッファーのちょうど8バイト目から16バイト目がmainのリターンアドレスになるように持ってこれる。16バイトほど余計にデータを書き込むことができる。値にすることで、RSPに足す値を0x50にできる。exit(0);}という関数が隠されているので、リターンアドレスここにしてやれば良さそうだ。これをそのまま読んでやるとSEGVしてしまって上手く動かない。gdbでめっちゃ頑張って追っていくと、system関数のgotエントリの遅延バインディングを行う部分のコードで落ちていることが分かって、落ちている理由はSSEのmovaps命令のアライメントがおかしいだからだと。movapsでアクセスしているアドレスはRSP依存で、ここが呼び出される前にあらかじめ16バイトアライメントに揃っていないと死ぬみたいだ。使う側がアラインしなくて良いんですかね・・・?よく分からない。分からないけどそういうことなら8バイトRSPをずらしてやれば良いので、直でhidden関数に飛ぶのではなくて、どこでもいいからret命令に飛べば、単に8バイトスタックがずらせる。ctf4b{h4ckn3y3d_574ck_b0f}。Seccompareとりあえず逆コンパイル。...}単に文字列比較してるだけなのでとても簡単。ctf4b{5tr1ngs_1s_n0t_en0ugh}。Leakage逆コンパイル。いう関数でチェックしているらしい。uVar3;}長さは0x22、convertという関数で1文字ずつあらかじめ埋め込まれているエンコード済みフラグをデコードして比較しているようだ。......ウワァァァ。is_correct関数内で0を返す瞬間には正しい文字がレジスタ上に存在しているはずなので、ブレークポイントを仕掛けてやれば、先頭から一文字ずつ正解を確定させて行くことはできそうだ。ctf4b{le4k1ng_th3_f1ag_0ne_by_0ne}。Operation今回一番苦労したのがこれ。mainの逆コンパイル。関数なんだが、これがめちゃくちゃでかい。でかすぎてなかなか逆コンパイルが終わらない。&&こういう式が大量に&&で繋げられたものだと言うことが分かる。0x55)...この部分は、要するに特定の文字のビット順を逆にしている処理だと分かる。Cのような処理だと分かる(定数を畳み込んで簡約すると)。ここまででものすごく大変だったが、つぎに、これをなんとかして実際のプログラムから抽出しなければならない。定数部分まで入れると式の形に案外バリエーションがあるのと、逆コンパイラが一貫した形の式を出してくれているわけではないので、簡単な正規表現だと難しそうだった。今回はHaskellのパターンマッチで簡約化することにした。language-cパッケージでパーズして、それを今回のコードに必要なASTだけパターンマッチを書いて、いいかげんにトラバースする。書いてて思ったのだが、language-cは、こういったいいかげんな処理を書くのはめちゃくちゃ面倒だ。これに元のでかいコードを食わせてやると、こんな感じの、見違えるほど綺麗なコードが出てくる。c;}こんな定義になっている。is_correctの全貌が明らかになったので、これを満たすデータを探す。単純な制約式なので、制約ソルバーに解かせよう。簡約化されたプログラムを適当にいじって、条件だけ抜き出したデータを作る。これを読んで先頭部分の文字の制約と合わせて、制約を組み立ててSMTソルバーに食わせるコードを書く。実行する!答えが出てくる!いや、長い道のりだった。大変だたけど、フラグが出てきた瞬間は何ともいえないすがすがしい気分になれた。出てきたフラグは、ctf4b{5ymbol1c_3xecuti0n_1s_3ffect1ve_4ga1nst_l1n34r_0p3r4ti0n}。いやあ、そうですよね。やってることは要するにそういうことなので、シンボリック実行でできないはずがない。シンボリック実行フレームワークよく知らないので、なんか自力でやることになってしまった。その辺使えるようにしておきたい。SecconPassとりあえず逆コンパイルすると、C++っぽいコードが出てきた。この問題のコンセプトはそういうアレなんだろうか。);}読んだ感じ、コマンド0で検索、コマンド1でエントリ追加、コマンド2でエントリ削除、コマンド3で終了と言った感じだ。このままだとどこにもフラグは現われないしフラグの判定をしているところもない。エントリで入力されたパスはEntry::encrypt()という関数で暗号化されて保存される。してxorを掛けているだけのようだ。いうところで使われていた。これはプログラム終了時に呼び出されるが、この中で、なぜかこれを引数にしたエントリが登録されて、すぐに削除されている。なんのためにこんなことをしているのかは分からない。分からないが、要するにこれはエンコードされた文字列だということなのだろうか。"ctf4b"とxorすると、なんとなくそう言う感じっぽかったので、先頭2文字のキーで全体をxorしたらなんとなくフラグっぽいものが出てきた。ctf4b{Impl3m3nt3d_By_Cp1u5p1u5Z。なんか後ろの方が壊れている・・・が問題自体が壊れていたらしいのでこれでOKみたいだ。dataをdecompressすると今度はbase64が出てくる。dataが出てきて、それを繰り返していくと最終的にフラグが出てきた。Partyフラグを暗号化するPythonプログラムと暗号化されたフラグが貰えるので、元のフラグを求める問題。整数が与えられるので、変数6つに式が6個あるのでまあ理屈としては普通に解ける。解けるはずだけど考えるのはめんどかったので、SMTソルバーに解いてもらうことにした。flag+a*z+b*z^2式を書くだけですんなりといてくれてもう自分で何も考えられなくなる。ctf4b{just_d0ing_sh4mir}。なんかそういうのがあるんですかね・・・。Welcome公式IRCチャンネルに繋ぐだけのやつ。containers与えられたファイルにを覗くとめっちゃ沢山PNGがそのまま入ってるように見えたので、binwalkで切り出してみると、文字が書かれたファイルが一杯出てきた。それを繋げるとフラグになった。ctf4b{e52df60c058746a66e4ac4f34db6fc81}どういう意味なんだろうか。Dumppcapのダンプと思しきファイルが与えられるので覗いてみると、webshellというやつでhexdumpでファイルを覗いてるっぽいのが出てくる。hexdumpの引数調べてどういうフォーマットで出てるのか調べてデコードすると、tar.gzファイルが出てきて、それを解答するととても爽やかな写真のjpgファイルにフラグが書かれていた。ctf4b{hexdump_is_very_useful}。|----------------8パズルの問題がたくさん降ってくるのでこれを解けという問題。やることはっきりしていて、8パズルは盤面数高々9!しかないからBFSで一瞬とわかるし別に難しい訳ではないから、まともな問題?の中ではこれが一番簡単な気がする。探索なのでRustで書いた。このぐらいの規模なら別にPythonで書いてもそんなに時間は食わない気はする。Pythonはよく分からないから凝ったコードを書きたくなかった。ソケット周りのコードがやっぱりどうしてもちょっとめんどくさくなるので、探索だけRustで書いて、通信部分はPythonのpwntoolsで書いて、Rustのプロセスを読んでも良かった気もする。これぐらいならたいしたことは無いからどっちでもいい気はする。あとRustでいい加減に通信まわりのコードを書けるようなライブラリがあったりするとこういうのやるのに良いのかなあとも思った。p)}100問解くとフラグが現われた。これもどういう意味なんだろう。cmdlineを作りました。その3)買って登録したはいいがほとんど使っていなかったVS.net。writeup月別アーカイブはてなブログをはじめよう!tanakhさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。