ChogeLog

主にセキュリティ関係の記録やWrite-up。たまーに日記も。

#24 Rights out (Write-up)

問題

ksnctf.sweetduet.info

exeファイルが配布されている。
とりあえず実行すると以下のようなパズルゲームが表示される。

これはライツアウトというパズルで、クリックしたボタンとその上下左右が反転する。全てのボタンを反転させることができればクリアとなる。

このパズル自体は知っていたが、「ライツアウト」という名前は初めて知った。
とりあえず攻略サイトを見つつ解いてみたが、お祝いされるだけだった。

問題にも書かれているが、ただ解くだけではダメらしい。

解法

ここから解析作業に入る。
まずはstringsコマンドを実行してみるが、特に情報は得られなかった。
binwalkとかも実行してみたが特に収穫なし。

次にghidraで解析してみるが、デコンパイルに失敗しているっぽい…。

色々調べてみると、元のコードはC#で書かれているっぽい。
ということでILSpyを使ってリバエンしてみる。
ILSpyでexeを開くとあっさりとデコンパイルが完了した。

コードを読んでいくと、checkメソッドでFLAGを表示するか否かの判定を行っていることが分かった。

ざっくりと読むと、全てのボタンを反転させた場合に「Congratulations!」というメッセージが表示され、さらにとある条件も満たしている場合だけフラグを表示するようになっているらしい。
このif(flag)以降のコードを実行させてフラグを取得するのもいいが、正攻法で解きたいので条件を調べてみる。

上記のコードをよく読むと、フラグを表示させる条件はhist[i] == array[i]の場合らしい。
array配列の中身は「1, 7, 16, 11, 14, 19, 20, 18」に設定されている。
hist配列のサイズは8に設定されており、中身は下記のbutton_Clickメソッドで設定されるようになっている。

上記のコードを読むと、hist配列は直近の8回分のボタンクリックの情報を保存しているらしい。
以上のことから、hist[i] == array[i]を満たすには、直近8回にクリックしたボタンが「1, 7, 16, 11, 14, 19, 20, 18」の順番である必要がある。さらにこの順番でボタンをクリックしたタイミングで全てのボタンを反転させておく必要もある。
ちなみにボタンNoについては、左上から右に数えていく。(だからボタンに「→」の文字が書かれているっぽい)

これで条件が判明したので、あとは実際にパズルを解いていく。
まずは全てのボタンを反転させる。

その後、ボタンNoが「1, 7, 16, 11, 14, 19, 20, 18」の順番で押していく。
このとき、左上のNoは0であることに注意!(ボタンNo.1は一番上の左から2番目になる)

その後、再び「1, 7, 16, 11, 14, 19, 20, 18」の順番で押していく。同じ場所を2回押すと元の形に戻るため、これで全てのボタンが反転される。
これでフラグが表示された。

所感

100ptの問題なのでそんなに難しくはなかった。ghidraでデコンパイルできなかったところで苦戦したが…。
簡単なリバーシングは解けるようになった気がする。
「ライツアウト」という単語は初めて知った。正しくは「Lights out」なのね。

#4 Villager A (Write-up)

問題

ksnctf.sweetduet.info

SSHの情報が記載されているので、とりあえずSSHで接続する。
接続するとflag.txtq4という実行ファイルが用意されている。
もちろんflag.txtから直接フラグを取ることはできない。

解法

q4を実行してみると名前を聞かれ、その後「no」を入力するまで同じ質問が繰り返される。

実行するだけでは何も分からないので、ここからはローカルに落として解析してみる。
まずはstringsコマンドを実行する……が特に情報を得られず。

次にGhidraデコンパイルしてみる。ちなみにGhidraを使うのは初めてなので使い方はよく分かっていない('ω')
とりあえずGhidraで開き、解析結果のmain関数を確認すると以下のようになっていた。

解析結果によると、上図のlocal_180の場合にフラグを出力するっぽい。
しかし、local_18の値は常に1であるため、普通に実行してもフラグを取得することはできない。
バッファオーバーフローでもするのかな?と考えたが、違うっぽい。

ここで手詰まりになったので、Write-upを少しだけ見てヒントを確認した。
どうやらこの問題では書式文字列攻撃(フォーマット文字列攻撃)を利用するらしい。
書式文字列攻撃についての知識は全くなかったが、ハリネズミ本で紹介されていることに気づいたので、この本を参考に攻撃を行ってみた。
printfに直接文字列を入力できる場合に発生する脆弱性らしい。

とりあえずAAAA %p %p %p %p %p %p %pを引数に入れて実行すると、以下のような結果になった。

%pは対応するスタックの値を16進数で出力している。
よく見ると6番目の%pの箇所に0x41414141がされており、これはASCIIコードの「AAAA」を16進数で表した値になっている。
つまり、最初の引数で入力した「AAAA」(ローカル変数の値)が6番目のスタックに存在していることが分かる。

次に、char*型で指定されたアドレスの文字列を出力する「%s」を使ってみる。
また、%6$s と書くことで6番目の値を%sで表示させている。

上記のように入力した場合、0x41414141番地に存在する文字列を出力しようとしているため、「Segmentation fault」が発生している。
つまり、第一引数に読み込みたいアドレス値を指定することで、その中身を読み出すことができる。

ここまでは任意アドレスの読み込みだったが、「%n」を使うことで任意のアドレスへ書き込みことも可能。

この脆弱性を使って、↑のmain関数の画像の15行目にある

putchar(10);

のputchar関数のアドレスを、18行目以降のif文内の処理のアドレスに書き換えることで、19行目のフラグを取得する処理を実行することができそう。

Ghidraの解析結果を確認すると、Global Offsets Table (GOT)にputchar関数のアドレスが記述されていた。
ここでは080499e0になっている。

書き込み先のアドレスは、if文(JNZ命令?)以降の適当なアドレスを指定する。
ここでは08048691を指定してみる。

これらをまとめると、以下のような攻撃値になる。

最初の\xe0\x99\x04\x08でputchar関数のアドレスを指定している。リトルエンディアンで記述する必要があるので注意。
次の%134514317xは、書き込み先のアドレスである「080499e0」を10進数に変換(134514321)し、さらに-4した値になっている。先頭にアドレス指定用の4バイトがあるため、-4する必要があるらしい。
あとは%6$nと書くことで、\xe0\x99\x04\x08のアドレスの内容を%134514317xの内容に書き換えるようにする。

上記のコマンドを実行すると、見事フラグが出力された。

補足

これは厳密には「GOT Overwrite」という攻撃らしい。
RELROが「No RELRO」もしくは「Partial RELRO」の場合に攻撃可能とのこと。
セキュリティ機構のチェックはchecksecで行った。

所感

なんとなくは理解したが難しい。。
書式文字列攻撃だけではなく、フォーマット指定子についても全然知識がなかったので結構時間がかかった。
今回はwrite-upを少しだけ読んでしまったが、とりあえず初pwn!

#26 Sherlock Holmes (write-up)

問題

ksnctf.sweetduet.info

リンクにアクセスすると以下のような画面が表示される。httpとhttpsで特に違いはなさそう。

さらに各リンクにアクセスすると、シャーロックホームズの『A SCANDAL IN BOHEMIA』(ボヘミアの醜聞)の文章が書かれているっぽい。

解法

ディレクトリトラバーサルかな?と思いつつURLを探索し、https://ctfq.u1tramarine.blue/q26/にアクセスすると以下のようにディレクトリ内の一覧が表示された。

とりあえずflag.txtにアクセスしてみたが、予想通りフラグはなかった。

ただしヒントが書かれてあり、ソースコードを見ればいいらしい。
ということでソースコードを探してみる。
https://ctfq.u1tramarine.blue/にアクセスすると以下のような画面が表示されたので、/usr/share/nginx/htmlとかにソースコードが置かれているのかなー…と思ってファイルを探してみたが見つからなかった。

その後色々と試した結果、https://ctfq.u1tramarine.blue/q26/index.pl/index.plにアクセスするとソースコードを確認することができた。
どうやらindex.plは、index.plの次のパスに指定したファイルの中身を出力するらしい。
最初のシャーロックホームズの小説がヒントとなっており、https://ctfq.u1tramarine.blue/q26/index.pl/a_scandal_in_bohemia_1.txtにアクセスするとa_scandal_in_bohemia_1.txtの中身を出力してくれる。
よって、index.pl/index.plにアクセスすることで、index.plのソースコードを表示することができた。

ソースコードを見てみると怪しげな記述があった。

上部のコードによると、cracked.txtの中身にh@ck3d!という文字列が記載されている場合だけFLAGを表示するらしい。
ちなみにcracked.txtflag.txtと同じディレクトリに存在し、中身は空だった。

ここからどうやってcracked.txtに文字を書き込むか悩んだが、どうやらPerlのopen関数の引数の末尾にパイプ|が含まれているとOSコマンドインジェクションが発生するらしい。
このコードではhttps://ctfq.u1tramarine.blue/q26/index.plの次のパスの値をそのまま引数として利用している。

open(F,substr($ENV{'PATH_INFO'},1));

そのため、たとえばhttps://ctfq.u1tramarine.blue/q26/index.pl/|lsにアクセスするとlsコマンドが実行される。

この脆弱性を利用し、https://ctfq.u1tramarine.blue/q26/index.pl/|echo h@ck3d! >> cracked.txtにリクエストを投げることでcracked.txtに任意の文字を書き込むことができる。
その後、https://ctfq.u1tramarine.blue/q26/index.plにアクセスするとフラグを取得できた。

所感

Perlの知識が全くないので、そこで躓いてしまった。。(コードを解読するのに時間かかる…)
Perl知ってる人だと簡単に解ける問題だと思われる。