harekaze.com今後のために、せっかくなので解いた問題の記録残しておこうと思います。scramble正しいフラグを標準入力から入れるとCorrect!と表示されるプログラムが与えられるみたいなんで、そこから逆算してフラグを求める問題っぽい。入力はscrambleという関数に掛けられて、正解の値と比較される。swap(input[i/7]のi%7ビット目、input[j/7]のi%7ビット目);}みたいなことをやってることが分かったので、これの逆関数はiを逆順で回してやればいいんで、それで埋め込まれてる値を元に戻してやればフラグが出てきた。251で)掛け合わせて、その結果を16進数で繋げて出力しているプログラムのようだった。なんで、有限体上での逆行列が求まればそれで終わりっぽいんだけど、スクラッチで書くのめんどくさいし、既存の実装もよく分からなかったのでSMTソルバーに解かせることにした。c_行列積が一致しているかどうかを比較するだけのナイーブなコード。出力は行列2個分で、それが行列を右から掛けるか左から掛けるかランダムな実装になっていたので、両方試す。入力は文字列だと分かっているので制約を掛けてやらないと大きい数が出てきてしまうのでちゃんと範囲を入れてやる必要がある。これぐらいのサイズならz3で1分程度で求まった。Encodeなんかファイルパスを指定したらそれをfile_get_contents()して返してくれるPHPのWebアプリが与えられるので、フラグのファイル名与えて中身教えてもらうという問題のようだ。$content);ファイルの中身にフラグが入っていたら消されたりする処理が入っているので二段階でこれを突破する必要がある。1つ目に関しては、ファイル名のチェックがjsonのデコードの前に行われているという問題のあるコードになってるので、そこを突いて文字列をjsonのエスケープ文字でエスケープして送れば突破できる。送ってやるとcensoredされたフラグがゲットできる。2つ目に関しては、phpのストリームフィルターというもので突破できる。やると、入力に対して任意の(?)phpの関数を適用しながら読んでくれるみたいだ。やると、base64でエンコードした内容が貰えるのでチェックを回避できる。して送ればフラグをbase64エンコードしたものがゲットできる。́・_・`).oO(なんだこれ。ROPサーバーをハックして鍵ゲットしろみたいな問題。プログラムのバイナリは貰える。逆コンパイルしてみたら、こんな単純なプログラムだ。0;}どう見てもバッファオーバーフローする危ないコードだけど、実際具体的に攻撃したことはなかったのでなかなか難しかった。この問題のキモはsystem関数が呼ばれていることみたいだ。systemが呼ばれていることで、リロケーションされない本体のバイナリの固定のアドレスに、system関数へのサンク関数が作られる。格段に攻撃がやりやすくなっているみたいだ。アセンブリコードを読むと、この関数はスタック8バイト使ってるので、ripの保存先がscanfのバッファーの24バイト目からになっていることが分かる。ここに書いてやればretで好きなアドレスに飛べる。これでsystem関数に飛びたいのだが、x86_64では1つ目のポインタ変数がrdiレジスタで渡されるので、バッファオーバーフローでは直接書き込めない。rdiにpopするコードを経由してrdiに好きな値を書き込ませる。いう命令列が存在していれば、ここに飛ぶことによってrdiに好きな値を書き込ませて好きな関数をコールできることになる。そんな都合の良い命令列があるのか?というと実際にあるみたいだ。RETになるみたいだ。これを使えば、こういう順でメモリを書いてやれば、好きな引数でsystem()を呼び出せる。いうか、まさにこれを呼び出してしまえばリモートマシンでシェルを好き放題いじれる。retのアドレス+"/bin/sh"のアドレス+systemのアドレス、と言うことになる。いう、それ用の処理を抽象的に組み立てられる死ぬほど便利なツールがあるらしいのでそれを使ってみた。入出力を端末に繋いでやりたい放題io.interactive()なんだこのツールは・・・なんかいろいろ揃いすぎててやばすぎる。これで他人のマシンにshしてコマンド実行しまくれるようになったのだが、実際やってみるとまじでできちゃうんだ・・・という感じが強くて、怖い。(ROP)と呼ぶらしく、その際に使えるバイナリ中の有用(有用とは)な命令列をgadgetとか呼ぶらしい。2さっきよりちょっと難しくなったバージョン。今度はlibcが動的リンクされていて、あんまり便利な関数が使われていないので、それらへの静的なアドレスが手には入らない。0;}なんで28バイトのバッファーに堂々と0x100バイトreadしてるんだとかツッコミどころはすごいけど、攻撃するのは結構難しい。libcが動的リンクされるので、なんとかそこのsystemを呼び出したいが、最近の(最近とは)Linuxでは、動的リンクされるアドレスやスタックアドレスはセキュリティーのためにランダマイズされるので、まずはそのアドレスを特定してやらなければならない。特定した上でプロセスを終了させずに再度脆弱性のある入力を行わさせないといけない。固定アドレスが得られる関数としてはプログラム中で使われているsetvbufとreadとprintfの3つだが、このうち情報の漏洩に使えるのはprintfだけだ。このprintfで、printfのgotエントリーの値を表示させる。gotにはロードされた実体へのアドレスが実行時に解決されて入っているので、これを表示させて手に入れて、soファイル中でのオフセットから引いてやれば、libcがどこにロードされたか判明する。printf_offsetスタックにprintfのgotエントリーをprintfするように積んで、その中身を表示させる。何回か実行しても、下位1バイトは必ず0になるところにロードされるらしいので、何も表示されない(文字列null終端なので0からだと何も表示されない)。+1したアドレスから表示してもらうことにした。7,8バイト目も必ず0になるみたいなので(そういえば論理アドレス48ビットだったか)結局確定で送られてくるのは2バイト目から6バイト目までの最長5バイトである(運が悪いと途中で0が入ってしまうが、確率は高くないし何回もやれば良い)。libcのアドレスが手には入ったが、次のプロセスではまた値が変わってしまうので、同じプロセスでなんとかこの値を使ってsystemを呼び出さなければならない。すれば良いのかというと、mainを再度呼び出してしまえば良い。printfからretした後、次に実行されるのは、その+8のアドレスになるので、そこにmainのアドレスを書いてやればprintfした後にmainを呼び出させることができる。これで、サーバーはまんまとまた入力を受け付けるようになるので、再度、今度はsystem("/bin/sh")を実行させるバイト列を送ってやれば良い。端末に繋ぐio.interactive()これで乗っ取り完了。二段階で攻撃するのが結構大変で、いろいろ調べて勉強になった。これもこんなコードで乗っ取られてしまうんだなあと言う印象が強くて、思ったより世の中恐ろしいんだなと。Twenty-fiveアルファベットがシャッフルされたppencodeされたperlのコードが与えられるので、元に戻して実行しろという問題。存在する予約語から制約書いて解けば解けると思うが、めんどくさかったので、手で確定する文字から順に埋めていった。zを除く25文字しかないしね。いう風に作られてるのか分からないけれど、すべて確定的に決まっていった。[a-z().]JavaScriptで[a-z().]な文字のみを使って1337を作れという問題。コード長は200文字未満でなければいけない。JavaScriptは全然詳しくないので手探り感がすごかったが、nodeのreplでいろいろいじいじしていると、関数.nameで関数名が文字列として取れることが分かった。それの.lengthを取ればなんらかの数が作れる。数を文字列の長さとして表すなら、かけ算はa.repeat(b.length)だし、足し算はa.concat(b)だし、引き算はa.substr(b.length)である。いう式が出てきたので、これをさっきのルールで変換していくとconsole.profile.name.repeat(escape.name.length).repeat(unescape.name.length).repeat(eval.name.length).substr(console.profile.name.length).lengthというプログラムができあがった(145文字)。これはなんか最短を目指すと面白い問題な気がする。あと今気付いたけどtypeof(x)とかの方が短くなりそうな気がした。ScalaScalaで競技プログラミングを行う際に気をつけることの列挙。writeup月別アーカイブはてなブログをはじめよう!tanakhさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。