Cómo pasar de Inyección de HTML a XSS, CSRF y RCE

Al realizar pruebas de seguridad, generalmente, se identifica una vulnerabilidad, se explota para verificarla, y se documenta; y hasta ahí llega el informe de ethical hacking. No obstante, esta práctica es deficiente, ya que no representa el comportamiento de un atacante, y menos aún refleja lo que podríamos llamar como seguridad ofensiva. ¿Por qué reportar con criticidad baja una inyección de HTML cuando podría ser de severidad alta?

Veamos. En el caso de PHP, existen múltiples formas de controlar una inyección de HTML, como por ejemplo, utilizando las siguientes funciones:

  • htmlspecialchars()
  • strip_tags()
  • preg_replace()

Existen otras funciones más, por supuesto, las cuales, obviamente, según el programador y los casos de uso, serán útiles o no. Entonces, un ejemplo básico de código vulnerable es el siguiente:

Resultado al pasar por GET la típica rutina de Cross-site Scripting (XSS):

De Inyección de HTML a Inyección de Cross-site Scripting (XSS)

Aunque se dan esta clase de situaciones en desarrollo, usualmente se controlan. Si usáramos, entonces, una de estas funciones, pensando en que siempre habría una situación que permite realizar inyecciones de algún tipo, quedaría de la siguiente forma, con strip_tags():

Así, ya no sería posible utilizar el payload <script>alert(“XSS”)</script>:

Y, en su lugar, podríamos utilizar sólo el tag de HTML permitido (<p>):

Exactamente en este punto, radica mi cuestionamiento; si echamos mano a programación básica en JavaScript, podemos fácilmente convertir una inyección de HTML cuya severidad es baja, en una brecha de criticidad media (es decir, de verde o azul, a amarillo), con el siguiente payload <p onclick=”alert(‘XSS’)”>:

Algunos ejemplos básicos sobre programación en JavaScript y los eventos HTML/DOM:

De Inyección de Cross-site Scripting (XSS) a CSRF (Cross-site Request Forgery)

Ahora, supongamos que nuestro código PHP vulnerable forma parte de una aplicación web o software, y a diferencia del tipo de XSS anterior (reflected), tenemos la oportunidad de almacenar código con permanencia, ya sea invocándolo desde la base de datos, almacenándolo directamente en el código, o a través de un archivo, un escenario como éste lo ejemplificaría:

Lo primero que debemos hacer es, por supuesto, emular el comportamiento de un atacante, así es que cambiamos el evento onclick de JavaScript por onmouseover, y lo probamos, simulando la inyección de XSS almacenado por medio de archivo_malicioso.txt:

El usuario víctima tendría sólo que mover el cursor. No obstante, como se trata de un supuesto, lo ideal es siempre, realizar un ataque a través del cual el usuario víctima no tenga que realizar ningún tipo de acción que supedite la ejecución del payload, es decir, con el sólo hecho de descargar una página por medio del navegador.

De esta manera, podemos explotar nuestro Cross-site Scripting almacenado para elevar su criticidad: capturamos el request de algún endpoint crucial, en nuestro caso, el de actualización de correo; reproducimos éste último en un XMLHttpRequest, incluyendo nuestro correo de pentester-atacante:

Evidentemente, no podemos pasar el XMLHttpRequest directamente por el tag de inyección HTML (<p>), así que lo transformamos en charCode, logrando el siguiente payload para CSRF:

Inyectamos nuevamente nuestro payload, y listo: el usuario víctima con el sólo hecho de mover el cursor, ejecuta, sin saberlo, un POST request para cambiar su dirección de email por el correo del pentester-atacante:

Esta sería entonces, una de las tantas maneras de pasar del amarillo al rojo, para la criticidad de un Cross-site Scripting (XSS).

De CSRF a RCE (Remote Code Execution)

A decir verdad, después de realizar satisfactoriamente un CSRF, la gracia se cuenta sola: bastaría con repetir el proceso anterior para ejecutar deliberadamente algún archivo o endpoint vulnerable como éste, para tomar control del sistema operativo:

Una lista interesante de funciones que necesariamente debemos considerar para estos efectos (RCE):

  • system()
  • escapeshellarg()
  • shell_​exec()
  • proc_​open()
  • passthru()
  • proc_​close()
  • proc_​terminate()
  • exec()
  • proc_​get_​status()
  • proc_​nice()
  • escapeshellcmd()

Por lo tanto, sí, como decía es factible y posible pasar del verde, azul y el amarillo, al rojo, cuando de criticidad se habla. Y todo con un simple tag (<p>) de HTML.

Lecturas recomendadas

Para quienes deseen explorar y profundizar más sobre cómo pasar de un XSS a un RCE, recomiendo las siguientes lecturas: