Eldoria Panel - Medium
Last updated
Last updated
Vamos a la siguiente categorÃa de dificultad, pero que no os engañe la palabra medium porque son retos desafiantes.
A simple vista vemos que es una aplicación en php, pero vamos a ver el DockerFile donde nos damos cuenta de que es una aplicación php
también utiliza Chromium
y que la flag se guarda en /flag.txt
Procedemos a revisar el entrypoint.sh
que se ejecuta en el docker al levantar el contenedor, en el que vemos que se cambia el nombre de la flag a flag<caracteres random>.txt
lo cual nos hace pensar que necesitamos RCE para poder leer la flag al no saber el nombre del archivo.
Ahora procederemos a ver el código de la aplicación web, una cosa que me llamo la atención es el script run_bot.py
asà que vamos a revisar que hace dicho script. Mirando el código del script nos damos cuenta que es un script que recibe una url como argumento hace unas consultas a la base de datos para sacar el usuario y contraseña del admin, inicia sesión en la aplicacion y abre el enlace pasado por argumento.
Una vez visto el bot vamos a ver en que parte del código se llama a ese script, y vemos que en el archivo routes.php
en el endpoint /api/claimQuest
se le pasa la url por un filtro y luego se la pasa para que la ejecute el script en Python y la visite.
También en este mismo archivos nos damos cuanta de una función muy curiosa llamada render()
que se ocupa de renderizar los php de la web comprobando que existen en el sistema y ejecutándolos en una instrucción eval()
Y por último vemos que el admin tiene un endpoint para cambiar la variable global templatesPath
que es la misma que utilizan las rutas estáticas que se cargan por la funcion de render()
, lo cual cambiarla podrÃa hacernos controlar que archivos de php que se renderizan lo que puede ser algo muy peligroso.
Teniendo todo lo de la revisión del código en mente, tenemos claro que nuestro objetivo es poder cambiar la templatesPath para poder cargar los .php desde el directorio que nosotros digamos. Asà que nos logeamos y vemos un botón en el menú de arriba de la página donde nos pone claim quest le damos clic y vemos que es un formulario que nos deja hacer la solicitud al endpoint que le pasa la url al bot de python.
Podemos probar localmente para ver si el bot visita nuestra url y vemos que si la visita.
Sabiendo esto, vamos a hacer un index.html para qué aceda el bot y poder cambiar el TemplatePath
haciendo una petición en su nombre
Hacemos que el bot visite nuestra url, y si hacemos un .dump a la base de datos en el docker y podemos ver que ha cambiado, primero necesitamos actualizar y instalar sqlite apt update -y;apt install sqlite3 -y
Ahora si vamos a la web podemos ver que no funciona al cambiar la Template Path
.
Pasa esto porque como vimos en el código la función render carga los archivos php desde el TemplatePath
de la base de datos al ser ahora /tmp donde no están los phps da error.
Sabiendo esto, necesitamos poner alguna ruta que tenga un login.php con el código que nosotros le demos para que se ejecute, por ejemplo si ahora en /tmp
y ponemos un login.php
la función render lo cargara y lo ejecutara desde la ruta /tmp/login.php
Sabiendo esto necesitamos meter un archivo llamado login.php
en alguna ruta del sistema para ponerla en el TemplatePath
y la rendericé. Pensando mucho nos damos cuenta de que podemos hacer que el bot descargue nuestro archivo php, asà que vamos a mandar la url de descarga al bot de nuestro index.html login.php
para ver en que directorio se descarga.
Al final nos damos cuanta que se descarga en el directorio /var/www/Downloads
pero con la extensión .crdownload
añadida, pero el contenido del fichero está intacto. Sabiendo esto nos damos cuenta de que en el template path podemos utilizar wrappers porque la función render utiliza file_get_contents()
, asà que probando y probando me di cuenta de que el wrapper phar://
me deja descomprimir archivos, asà que podemos enviar un zip poner el path del zip.crdownload
y que se añada el /login.php
de la ruta del php y ejecute el logn.php dentro del zip.
La extensión .crdownload se ocasiona por la protección que tiene chorme para descargar archivos de esta manera.
Como podemos ver el archivo se lee perfectamente y se pasara a la función eval()
, con esto podemos hacer una poc en el index.html
para que descargue nuestro zip
con nuestro login.php
webshell comprimido dentro, y luego cambiar el TemplatePath
por el payload con el wrapper phar://
pra que se le añada el /login.php
.
Para ello vamos a sacar la cadena de base64 del zip:
Y leugo lo pegamos en el payalod modificado apra que lo descargue y cambie el PathTemplate
:
Hacemos que el bot visite el index.html y comprobamos que se haya descargado bien el zip, y se haya cambiado el TemplatePath.
Ahora cuando abramos el endpoint / del reto la función file_get_contents()
deberÃa leer phar:///var/www/Downloads/login.zip.crdownload/login.php
y pasar el contenido al eval()
para que se ejecute:
Vemos que la webshell se ha ejecutado y ya podemos leer la flag tranquilamente :)
Revisando el código nos damos cuenta de algo sorprendente, el endpoint /api/admin/appSettings
no contiene ninguna verificación para saber si eres admin o no, lo que provoca que cualquiera con la petición post pueda cambiar el valor del TemplatePath
sin necesidad de abusar del bot.
Ya que podemos abusar de wrappers en al función file_get_contents()
podemos usar el wrapper ftp://
con usuario y contraseña para que cargue el login.php
desde nuestro servidor, esto serÃa válido y http://
no porque la función render()
comprueba si el archivo existe o no.
Levantamos un docker con ftp, y abrimos otra consola para meter el login.php:
Ahora hacemos el curl a el endpoint para cambiar el TemplatePath por nuestro servidor ftp con nuestro login.php:
Ahora cuando carguemos / en al web cargara nuestro login.php con la wbeshell y pwned :)