|
1 |
| -## Sandbox (Pwn, 5p) |
| 1 | +## Sandbox (pwn, 5p) |
2 | 2 |
|
3 | 3 | 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 |
5 | 5 | the flag at /home/sandbox/flag
|
6 | 6 |
|
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 | +``` |
9 | 11 |
|
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. |
0 commit comments