domingo, 20 de octubre de 2019

Gamehosting Hacking: Hackeando un VPS gracias a Open Game Panel.


¡Hola, muy buenas!

Ante todo, me disculpo por la prolongada ausencia de publicaciones, debido a complicaciones personales, Rolo y yo no pudimos escribir durante este tiempo. Me agrada informar que, por mi parte, puede resolver dichas complicaciones y estaré publicando contenido de forma más seguida.

No me enrollo más. En esta ocasión les vengo a compartir una experiencia personal que tuve, y de la cual pensé: Oye, que va, que podría ser una buena entrada pa'l blog.

Hace unos días un amigo me pidió si podía realizar un pentest sencillo para constatar si su VPS tenía fallos de seguridad, mediante los cuales puedan tomar control de su servidor de juego. Mi respuesta fue afirmativa, al fin de cuentas es un amigo, y toda práctica es buena.


¿Qué es Open Game Panel?
De ahora en más OGP, es un panel de control diseñado para controlar servidores de juego y voz a través de una interfaz web, sin necesidad de ejecutar comandos.

Realizando un poco de investigación, puede notar que para realizar la instalación de OGP hacen falta dos partes: Panel y Agent.

·         El panel es la interfaz web, el panel de control en sí. Contiene el código backend y frontend.

·         El agent o agente es el "controlador", es la parte que controla las acciones realizadas desde el panel. Según la Wiki de OGP, es posible instalar ambas partes en un mismo VPS, pero no es recomendable.

Desde el mismo panel tenemos diversas opciones para interactuar con nuestros servidores, desde un lector de logs hasta un cliente FTP incorporado. Todo proceso que se ejecuta a través del panel, corre bajo un usuario llamado ogpagent, y el mismo tiene permisos de sudo.

El panel no te deja ingresar comandos de ningún tipo, y dado a que estaba ejecutando la última versión, no había vulnerabilidades conocidas. Mi amigo estaba ejecutando un servidor de un juego llamado San Andreas Multiplayer. Dicho juego se trata de una modificación multijugador del famoso juego de Rockstar Games, Grand Theft Auto: San Andreas.


Investigando sobre dicho juego, averigüé que los servidores corren bajo "gamemodes" o modos de juego, que son programados bajo un lenguaje de scripting llamado Pawn. A esto se le suma la posibilidad de añadir plugins, desarrollados en algún lenguaje de programación, usualmente C++,

Si el mismo juego nos da la posibilidad de ejecutar un código nuestro, todo se resume a una simple tarea: Un plugin que nos permita ejecutar comandos del sistema, a través de la instrucción system() de C++.

Para suerte y comodidad del Nobody vago, ya existe un plugin desarrollado con las características que requería, era cuestión de subirlo a través del panel y ejecutar.

Me dispuse a programar un pequeño gamemode o filterscript, utilizando la librería recién descargada.


Con el plugin cargado y el código ejecutado, era cuestión de ingresar al juego y ejecutar una reverse shell con netcat.

¡Eureka! La reverse shell se había ejecutado, y ya disponía de una sesión por consola para ejecutar comandos.

Mediante el módulo pty de Python, logré ejecutar una shell interactiva, la cual me permitía ejecutar comandos como “sudo su”.
Para el interesado, el comando de Python que utilicé fue el siguiente:
python -c 'import pty; pty.spawn("/bin/bash")'

Investigando un poco más sobre OGP, encontré una carpeta donde posiblemente se guardaban archivos de configuración, la ruta era /usr/share/ogp_agent/Cfg.

Dentro de dicha carpeta, se encontraba un archivo con el nombre “Config.pm”, y su contenido era el siguiente:


¿sudo_password? ¿Realmente OGP guardaba una contraseña de un usuario con permisos sudo en texto plano? Para mi suerte y de poca fortuna para el resto, esto era afirmativo. Pude loguearme como root utilizando el usuario con los permisos de sudo.

En cierto punto de mi investigación, podría concluir en que realmente el servicio era vulnerable, ya que, vamos, logré obtener acceso root. Pero aún sentía que me faltaba algo, ya que si pude conseguir acceso root tan fácilmente, seguramente había más información que extraer (obviamente, en texto plano jeje).

Dentro del panel, existía un archivo de configuración (/var/www/html/includes/config.inc.php), con credenciales de acceso para una cuenta MySQL no privilegiada. Para resumir, puedo decir que OGP almacena la contraseña del usuario root de MySQL en texto plano, dentro de la base de datos accesible por el usuario no privilegiado. Aunque ya teniendo acceso root al VPS, esto no me servía de mucho.

¿Sería posible acceder a más información? Recordé que aún me faltaba por investigar el servidor de San Andreas Multiplayer de mi amigo.

Antes de seguir quiero hacer un alto, ya que la siguiente parte de la publicación va más allá de las “vulnerabilidades” de OGP, ya que esto era posible realizarse sin acceso root al VPS, y solo contando con acceso al usuario OGP correspondiente. Aún así, me parecía prudente escribir sobre esto, ya que demostraré que es posible acceder a “más”.

El servidor de mi amigo contaba con un plugin de MySQL, y una base de datos en un servidor remoto. Los datos de conexión MySQL estaban embebidos o hardcodeados dentro del código de la gamemode, del cual yo no disponía. Cabe destacar que esa información tampoco era visible, a simple vista, desensamblando el compilado de la gamemode o extrayendo la lista de strings de la misma.

Pensé en que era posible sniffear el tráfico utilizando tshark, pero luego iba a tener que romper la contraseña hasheada de todas formas, no era lo más recomendable.

Esta es la parte de la historia donde se me iluminó mi cabecita y saqué mi programador interior: El plugin de MySQL era de código abierto, simplemente tenia que modificarlo y hacer que guarde un registro de los datos de conexión. Averigüé que la versión del plugin ejecutada era la R39-6, un tanto vieja para la fecha, pero aún así el código se encuentra disponible en GitHub.

Encontré que la función de conexión se encontraba en el archivo CScripting.cpp, y ya disponía de una función de “log” de depuración, el cuál guardaba el host, usuario y base de datos, pero no la contraseña (por lógicas razones).


Editando levemente el código, reemplazando los asteriscos por el format specifier correspondiente, logré que él registro de depuración de MySQL muestre la contraseña en texto plano.

Nota: Existe una función dentro del plugin la cual permite desactivar todos los registros, o al menos el de depuración. Es posible ignorar dicha función, simplemente forzando LOG_DEBUG desde el código, esto se encuentra en el archivo CLog.h).


Ya con los datos de acceso MySQL, mi trabajo había finalizado: Había logrado ingresar a una base de datos externa al VPS. Pero, ¿qué secretos guardaba la base de datos?

La misma incluía tablas personalizadas sobre el servidor del juego, pero además contenía las tablas de un foro SMF. Recordemos que mediante el acceso al panel administrativo de SMF, podemos subir un backdoor PHP utilizando la función de “modificaciones” o “editar tema”.


Es precisamente lo que hice, y ahora sí, mi trabajo había terminado: Había conseguido acceso root a un VPS y acceso a un Webhost ajeno a dicho VPS.

Espero que esta publicación haya sido informativa, y me disculpo por la carencia de redacción. Espero haber sido suficientemente claro explicando los conceptos sobre esta “vulnerabilidad”.

También cabe destacar que no se trata de una vulnerabilidad en sí, simplemente es una forma incorrecta de guardar una contraseña.

¡Saludos desde el más allá!
Nobody.
¡Pssst! Os recuerdo que también podéis seguirme en Twitter :-).

viernes, 29 de marzo de 2019

Arbitrary File Download: el hermano menor del LFI.

¡Hola, muy buenas!

En esta ocasión les vengo a comentar sobre una vulnerabilidad a nivel web bastante frecuente, y a su vez, poco conocida. Se denomina Arbitrary File Download, y de ahora en más, AFD.

(fig 1. Arbitrary File Download).
A menudo es una vulnerabilidad que se confunde con la ya conocida Local File Inclusion, o LFI. Estás perdonado si alguna vez las confundiste, pues las similitudes que tiene son casi todas, a excepción de la metodología de trabajo. En esta entrada, me dispongo a intentar solventar todas las dudas que pueda existir sobre la diferencia de ambas vulnerabilidades.

Apéndice I: El código en PHP

Para entender más claramente las diferencias, analizaremos los códigos PHP vulnerables utilizados en ambas vulnerabilidades.

  1. <?php
  2. include($_GET['page']);
  3. // Otras funciones: include_once(), require(), require_once().
  4. ?>
(Código 1. Local File Inclusion).

Es posible combinar LFI con técnicas como Log poisoning, /proc/self/environ, phpinfo(), etc. para conseguir RCE en el servidor.


  1. <?php
  2. $file = $_GET['file'];
  3. header('Content-Type: application/octet-stream');
  4. header('Content-Disposition: attachment; filename="' .basename($file) .'"');
  5. header('Content-Length: ' . filesize($file));
  6. readfile($file);
  7. ?>
(Código 2. Arbitrary File Download).

El uso de readfile() es utilizado a lo largo de muchas páginas web para las secciones de "descarga de contenido". Esto significa que un único archivo php (llámese download.php) es el encargado de descargar los ficheros del servidor.

Apéndice II: Post-análisis del código

Luego de analizar ambos códigos, llegamos a la conclusión de que LFI permite incluir ficheros locales del servidor, y en cambio, AFD permite descargar esos ficheros.
La gran diferencia entre ambas vulnerabilidades, es que AFD no permite obtener RCE mediante técnicas como log poisoning o /proc/self/environ.

Si, muchos dirán "si es menos útil que LFI, ¿para qué la quiero?". LFI es una vulnerabilidad que poco a poco se hizo conocida y los desarrolladores tienen especial cuidado con ella. En cambio, AFD es una vulnerabilidad muy "precaría" aún, y es usual encontrarla en 7 de cada 10 sitios (vamos, que un sencillo dork en Google da toda la información).

Apéndice III: Usos de AFD

Sin menospreciar a AFD, a continuación daré una lista de usos posibles que se me han ocurrido. Todos ellos intentan garantizar conseguir una shell en el servidor victima.

  • Leer archivos de configuración MySQL. Si la página web cuenta con algún panel de administrador MySQL (phpMyAdmin, Adminer, phpLitleAdmin, etc.), es posible loguearse a dicho panel y subir un archivo a través de consultas MySQL.
  • Desde el mismo phpMyAdmin, listar los usuarios admin y sus contraseñas de un panel admin, así conseguir acceso a dicho panel, que tal vez contenga algún Unrestricted File Upload.
  • A veces, el servidor MySQL es remoto, pudiendo conectarse desde cualquier cliente MySQL. Por ejemplo, phpMyAdmin.co.
  • Leer archivos de sesión PHP. Alguna de esas sesiones, de seguro será admin.
  • Contraseñas admin hardcodeadas en el login. Es poco frecuente, pero sorprendente de cuantos programadores hardcodean las contraseñas.
  • Mucho más... Los límites son tu imaginación.
 

 Apéndice IV: Análisis final

En conclusión podemos decir que AFD tiene "más limitaciones" que LFI, pero a su vez es más frecuente, lo que lo convierte en una vulnerabilidad importante. Ambas técnicas necesitan ser combinadas con otras para conseguir un resultado útil.

Hago una pausa aquí para aclarar una cosilla. Si no lo han descubierto aún, LFI no permite leer el contenido de archivos PHP (al menos no es posible sin utilizar otra técnica, /proc/...), en cambio, AFD si permite leer el contenido de esos ficheros.

En esta parte de la entrada pensé en listar una serie de "soluciones" a esta vulnerabilidad, pero confío en la mente del lector perspicaz y ágil que logrará resolverlo por si mismo. Recuerdo una frase muy importante: Never trust the user input.

Apéndice V: Ejemplo práctico

Como toda buena entrada sobre hacking y vulnerabilidades, hay que finalizar poniendo en práctica lo recién aprendido. En este apéndice me centraré en conseguir acceso a una página web aleatoría (si, Google Dorking), utilizando AFD y en combinación de otras técnicas.

Evitaré sacar capturas de los pasos innecesarios, pero aún así los listaré:

  1. Conseguir leer el archivo "index.php", usualmente tiene mucha información útil. En este caso, tenía un require() a un archivo "mysql.inc.php"
(fig 2. Fichero mysql.inc.php).

Logré leer el fichero de conexión MySQL, el cual contaba con la información confidencial que requería. En este caso no vemos información sobre el servidor MySQL, por lo cual podemos deducir que se trata de un localhost.

Mi siguiente paso fue intentar conseguir algún tipo de panel MySQL, para darle uso a los datos recién conseguidos.








(fig 3. Ingreso phpMyAdmin).


Afortunadamente la página web contaba con un panel phpMyAdmin en un subdominio (phpmyadmin.domain.tld), siendo posible el ingreso con los datos anteriormente obtenidos.






(fig 4. Datos administrativos).

 Al buscar entre todas las tablas, llegué a la que me interesaba a mi. El siguiente paso era claro: obtener el URL del panel admin. Afortunadamente a muchos programadores se les da por dejarle como /admin, o simplemente dejarlo en robots.txt.


(fig 5. Panel administrativo).

 ¡Y voilà! Ya hemos accedido al panel administrativo. Si mis intenciones fuesen malas, buscaría algún uploader y subiría una shell, pero como prueba de concepto, creo que debería detenerme aquí.







  

Hasta aquí la entrada de hoy. Empezaré a publicar contenido más seguido.😁😁😁

Be happy and never (never) trust the user input ;-)
Nobody.

Pssst: Recuerda seguirme en Twitter :-): https://twitter.com/n0bodysec

lunes, 25 de marzo de 2019

Iframe Injection - De Phishing a XSS

Muy buenas a todos,

hoy quería explicar un fallo de seguridad que encontré hace unos días, y que posible explotación tendría esta vulnerabilidad. Yo he llamado a esta vulnerabilidad como "Iframe Injection", ya que no se si tenía ya un nombre antes.



Esta vulnerabilidad ocurre cuando puedes modificar el source de un iframe a tu antojo, pudiendo cargar un contenido de una página maliciosa. En la POC partiremos de una aplicación no vulnerable a XSS. Os voy a dejar el código vulnerable aquí.

  1. <html>
  2. <head>
  3. <title>Mensaje de ayuda</title>
  4. </head>
  5. <body>
  6. <h1>Este es el menu de ayuda</h1>
  7. <?php
  8.         $mensaje_ayuda = htmlentities($_GET['ayuda']);
  9.        
  10.         $errors = array("javascript");
  11.        
  12.         foreach($errors as $error) {
  13.         $res = strpos($mensaje_ayuda, $error);
  14.         if ($res !== false){
  15.                         header('location: error.html');
  16.                 }
  17.         }
  18.         echo '<iframe src="' . $mensaje_ayuda  . '"></iframe>';
  19. ?>
  20. <form method="GET" action="ayuda.php">
  21. <p>NUMERO DE AYUDA</p>
  22. <input name="ayuda">
  23. <button type="submit">ENVIAR</button>
  24. </form>
  25. </body>
  26. </html>

Como vemos lo que tenemos es una supuesta página de ayuda, dependiendo de la página que queramos ver se enviará por parámetro y lo introducirá dentro de un iframe, así quedaría un ejemplo legítimo.


El problema surge cuando la aplicación no controla que el contenido introducido en el iframe pertezca a su propio dominio, el ejemplo más típico sería introducir la URL de nuestra página maliciosa donde tendríamos alojado un phishing para que el usuario introdujera sus datos.


A partir de aquí se podrían enviar correos electrónicos de Phishing más sofisticados ya que la URL introducida tendría el dominio legítimo.

Bypasseando filtros con diferentes URIS

Ahora vamos a imaginar que para evitar esta vulnerabilidad el programador pone algunos filtros que "evitan" que podamos introducir sources externos al dominio.

¿Que formas tenemos de introducir URIs completas para poder jugar con los diferentes filtros que pueda crear un programador?

  1. $errors = array("javascript", "http");
  • Podemos introducir la URL con o sin http indiferentemente, el navegador de ambas formas no lo interpretará como nosotros queremos.
  1. $errors = array("javascript", "http", "//");
  • Podemos usar los símbolos "/" y "\" indistintamente y cuantas veces queramos, el único requisito es que sean mínimo dos.


  • En caso de que la aplicación tenga una regex que busque los símbolos "\" y "/" seguido de "dominio.int" podemos añadir los caráctares ":@" o tan solo "@" al inicio del dominio.

 Jugando con otras URIS

Además de "http" existen otros tipos de URIs con las que podemos jugar, aconsejo mirar aquí y ver si hay algo que pueda resultar interesante, yo voy a mostrar aquí un par de ideas.

  • Se podría abrir el correo electrónico con la URI mailto, encargada de enviar correos electrónicos de la siguiente forma: "mailto:correo@electronico?body=mensaje"

  • Se podría ejecutar código JavaScript gracias a la URI data, la cual simplemente responde con el contenido especificado en la propiar URI, podríamos hacerlo de la siguiente forma: data:text/html,<script>alert("XSS")</script> 


NOTA: Si se introduce JavaScript dentro del servidor Web malicioso no se podrá interactuar con el padre por las CORS, que es lo que nos interesa para poder robar cookies o realizar otros ataques, de esta forma el código si será ejecutado en el padre.
Para entender un poco mejor las CORS aconsejo leer este post https://www.whateversec.com/2019/03/jugando-con-xss-cookies-y-cors.html

Por hoy ya he terminado, podeis seguir jugando con tras URIs diferentes que seguro que sale algo interesante, para cualquier duda o búsqueda de más información podeis comentar.

También me podéis seguir en Twitter para ver nuevos Posts y así echarme de paso una manita.

Saluti.

Lah vulnerabilidadeh de cliente molan tela.

lunes, 18 de marzo de 2019

Jugando con XSS, Cookies y CORS

Muy buenas a todos,

el otro día estaba discutiendo con alguien como de peligrosa puede llegar a ser una XSS, a donde ese "alguien" argumentaba que en caso de ser una aplicación sin usuarios el riesgo de una XSS no será ninguno.

A esto tengo que decir que no es del todo cierto y voy a explicar por qué, este post será tan solo teórico para explicar algunos conceptos y entenderlos bien, pero no haré una prueba de concepto como suelo hacer para que podáis jugar.

El caso en el que me enfocaba yo para poder decir que una XSS en un lugar sin usuarios es el caso de que existan subdominios que si tuvieran usuarios (lo cual es algo muy común).



Para entender como podrías atacar a un subdominio a través de una XSS en el dominio principal hace falta saber en que consisten los siguientes dos valores de las cookies:

  • HTTPOnly: Este valor indica si JavaScript puede o no interactuar con una cookie, es el valor que indica si podremos hacer un robo de sesión a través de una XSS o no.
  • Domain: Este valor indica el domio de la cookie en cuestión. Cuando un subdomio crea una nueva cookie tiene la opción de hacerlo como "subdominio.dominio.int" o como ".dominio.int"
En el caso de que el el flag del dominio apunte a ".dominio.int", cualquier parte del dominio entero podría acceder a ella, de esta forma sería posible robarla en caso de tener el flag "HTTPOnly" desactivado con una XSS en una parte del dominio sin autenticación.

Además de esto existiría otro vector de ataque, este sería a través de peticiones "XMLHttpRequest" en AJAX.

Esto es una explotación ya conocida en las XSS, el ataque consistiría en obtener la respuesta de una petición, por ejemplo "modificarPerfil.html", obtener el token CSRF, y una vez con este enviar otra petición a "modificarPerfil.php" para poder evadir esta medida de seguridad.

Para evitar que esto se pudiera hacer desde un dominio malicioso se crearon las CORS, que son unas cabeceras del servidor que le dicen a tu navegador si el tratamiento de estas peticiones se debe o no hacer dependiendo de donde provenga esta petición.


En principio, si el servidor no indica nada no se podrán realizar estas peticiones (ya sea desde un dominio externo o un subdominio), la cuestión es que pueden estar implementadas varias cabeceras que indican desde donde se podrían realizar estas peticiones.

  • Access-Control-Allow-Origin: Indica desde que dominio se pueden realizar peticiones de tipo XMLHttpRequest.
  •  Access-Control-Allow-Credentials: Esta cabecera indica si se puede o no enviar las cookies a la hora de realizar estas peticiones.


Como veis es necesario que se especifique que se pueden realizar peticiones desde este dominio, sin embargo no es extraño encontrarse estas cabeceras, ya que al ser un dominio del que también se tiene control se trata como un dominio seguro.


Por hoy ya he terminado, ha sido una explicación un poco superficial pero espero que hayan quedado claros los conceptos, para cualquier duda o búsqueda de más información podeis comentar.

También me podéis seguir en Twitter para ver nuevos Posts y así echarme de paso una manita.

Saluti.

Ci no configurah vien lah cosah po te jaqueo.