Enunciado
Para poder realizar el ejercicio, se utilizará la aplicación Wackopicko.
Puedes encontrar esta aplicación en la máquina virtual de Vulnerable Web Apps.
Se pide
Identificar todas las vulnerabilidades que tiene la aplicación utilizando una herramienta
semiautomática como Vega u OWASP ZAP. Como resultado de este ejercicio, se
mostrará, por una parte, la cantidad de ocurrencias identificadas y clasificadas según su
nivel y, por otra, la clasificación de todas estas ocurrencias en tipologías de
vulnerabilidades.
Explotar las vulnerabilidades e indicar cómo se ha explotado cada vulnerabilidad y cuál ha
sido el resultado (adjuntando capturas de pantalla).
Proponer, al menos, una medida de prevención para cada vulnerabilidad analizada (se
entienden como medidas de prevención las modificaciones en el código donde se
encuentra el problema). Asimismo, se podrán indicar medidas adicionales sobre buenas
prácticas de seguridad relativas a la tipología de la vulnerabilidad.
Todos los pasos deben estar correctamente explicados y deben incluirse evidencias
(informe de resultado de la herramienta semiautomática, capturas de pantalla, etc).
Tabla de contenido
Enunciado.............................................................................................................................................1
Tabla de contenido................................................................................................................................2
Introducción..........................................................................................................................................3
Reporte OWASP ZAP.......................................................................................................................... 5
Vulnerabilidades Encontradas OWASP ZAP....................................................................................... 7
Cross Site Scripting (DOM Based).................................................................................................. 7
Vulnerabilidad.............................................................................................................................. 7
Remediación.................................................................................................................................8
Absence of Anti-CSRF Tokens...................................................................................................... 12
Vulnerabilidad............................................................................................................................ 12
Remediación...............................................................................................................................13
Content Security Policy (CSP) Header Not Set............................................................................. 16
Vulnerabilidad/Remediación......................................................................................................16
Missing Anti-clickjacking Header..................................................................................................18
Vulnerabilidad/Remediación......................................................................................................18
Cookie without SameSite Attribute................................................................................................19
Vulnerabilidad/Remediación......................................................................................................19
Server Leaks Information via "X-Powered-By" HTTP Response Header Field(s)....................... 20
Vulnerabilidad/Remediación......................................................................................................20
Server Leaks Version Information via "Server" HTTP Response Header Field............................ 21
Vulnerabilidad/Remediación......................................................................................................21
Timestamp Disclosure – Unix........................................................................................................22
Vulnerabilidad/Remediación......................................................................................................22
X-Content-Type-Options Header Missing..................................................................................... 24
Vulnerabilidad/Remediación......................................................................................................24
Vulnerabilidades Adicionales Encontradas........................................................................................ 25
SQL Injection................................................................................................................................. 25
Vulnerabilidad/Remediación......................................................................................................25
OS Command Injection..................................................................................................................27
Vulnerabilidad/Remediación......................................................................................................27
Directory listing..............................................................................................................................28
Vulnerabilidad/Remediación......................................................................................................28
Introducción
A continuación, vamos a empezar con identificar las vulnerabilidades de la aplicación con
OWASP ZAP, pero, antes de empezar y, tras varias pruebas, para que no se demore
demasiado, voy a aplicar la siguiente configuración:
Una vez aplicada, procedo a lanzar un escaneo automatizado a la URL
[Link] en la cual tengo levantado el Docker con la aplicación web
vulnerable WackoPicko. En esta sección podemos ver 3 pestañas principales:
Alerts: en donde podemos ver el resumen de alertas que ha detectado OWASP
ZAP
Spider: el cual nos ayuda a crear automáticamente el mapa de posibles rutas
dentro de la URL proporcionada
Active Scan: en el cual podemos ver el progreso del escaneo y los diferentes
elementos analizados en la página web.
Reporte OWASP ZAP
Una vez he ejecutado un escaneo automatizado sobre [Link] he generado
un reporte para ver a grandes rasgos que problemas encontramos:
Resumen de las alertas:
Alertas:
Sitios:
Vulnerabilidades Encontradas OWASP ZAP
Cross Site Scripting (DOM Based)
Vulnerabilidad
Este tipo de vulnerabilidades se dan cuando JavaScript toma datos de una fuente
controlable por el atacante, es decir, permite a un atacante construir un enlace para enviar
a una víctima una página confiable con una carga útil en la consulta. A continuación,
muestro cómo funciona:
Si nos situamos en la página [Link] e introducimos el siguiente código, nos
salta una ventana emergente con el PHPSESSID:
<img src onerror="alert([Link])">
La URL se ha modificado a:
[Link]
%[Link]%29%22%3E&x=0&y=0
y nos muestra por pantalla PHPSESSID=fk664ek62rkt9fto3pbiuaukl5
Si al usuario final le pilla desprevenido y no le da por revisar la URL, puede tener incluida
cualquier tipo de script malicioso para extraer datos del usuario final modificando el
navegador de la víctima en el lado del cliente.
Remediación
Para ver como remediar esta vulnerabilidad, inspeccionare la página para ver qué hay
detrás y observamos que hay un PHP llamado /pictures/[Link] que es el que puede
estar provocando este error:
Para ello, voy a revisar el PHP con VSCodium:
Podemos ver que se inserta directamente el contenido de $_GET['query'] sin escapar ni
sanitizar. Para remediarlo, voy a usar htmlspecialchars() para escaparlo antes De
mezclarlo con la salida poniendo la línea:
$safe_query = htmlspecialchars($_GET['query'], ENT_QUOTES, 'UTF-8');
para incluir esta variable en our_header en vez de incluir directamente lo que meta el
usuario:
Ahora no aparece el mensaje:
Esta medida se podría hacer para todos los formularios como Guestbook por ejemplo que
también tiene este error:
Y algún otro que se me pudiera estar escapando. Hay muchas más formas de remediar
este tipo de errores en
[Link]
[Link] pero, viendo que htmlspecialchars sirve para escapar caracteres especiales
antes de imprimirlos en el HTML, he optado por esta opción.
Adicionalmente, he modificado el código de Guestbook siguiendo la misma dinámica que
el anterior PHP para también sanitizar la entrada de valores:
Absence of Anti-CSRF Tokens
Vulnerabilidad
Para explicar que hace el CSRF he encontrado un diagrama bastante interesante:
CSRF (Cross-Site Request Forgery) aprovecha que el navegador envía cookies de sesión
automáticamente.
Un atacante persuade al usuario (p. ej. con un enlace o una página maliciosa) para que su
navegador haga una petición autenticada al sitio vulnerable (POST to /cart/[Link]?
action=delete).
Si el servidor no verifica que la petición viene de una fuente legítima/esperada, la acción
se ejecutará con los permisos del usuario.
Remediación
¿Cuándo es vulnerable a la CSRF?
Para que una ruta de aplicación o un punto final de API sean vulnerables y explotables
para CSRF, deben cumplirse las siguientes 3 condiciones principales:
La funcionalidad o característica específica debe ser privilegiada
Su ID de sesión debe ser una cookie con la política de cookies de SameSite
establecida en "None" o "Lax"
Y, por último, la solicitud HTTP no debe llevar ningún valor impredecible
Por lo tanto, simplemente con la cookie a SameSite=Lax
ya nos valdría para evitar esto, pero, para remediarlo, he creado un token anti-csrf en el
código de /users/[Link]:
<?php
require_once("../include/[Link]");
require_once("../include/html_functions.php");
require_once("../include/[Link]");
session_start();
// ------------------------Genero el token
if (empty($_SESSION['csrf_token'])) {
if (function_exists('random_bytes')) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
elseif (function_exists('openssl_random_pseudo_bytes')) {
$strong = false;
$bytes = openssl_random_pseudo_bytes(32, $strong);
if ($bytes === false || $strong === false) {
$_SESSION['csrf_token'] = bin2hex(hash('sha256', uniqid(mt_rand(), true)));
} else {
$_SESSION['csrf_token'] = bin2hex($bytes);
}
}
else {
$_SESSION['csrf_token'] = bin2hex(hash('sha256', uniqid(mt_rand(), true)));
}
}
// ------------------------comparaciones si hash_equals no existe
if (!function_exists('safe_hash_equals')) {
function safe_hash_equals($a, $b) {
if (function_exists('hash_equals')) {
return hash_equals($a, $b);
}
if (strlen($a) !== strlen($b)) {
return false;
}
$res = 0;
$len = strlen($a);
for ($i = 0; $i < $len; $i++) {
$res |= ord($a[$i]) ^ ord($b[$i]);
}
return $res === 0;
}
}
// login requires username and password both as POST.
$bad_login = !(isset($_POST['username']) && isset($_POST['password']));
if (isset($_POST['username']) && isset($_POST['password']))
{
//Valido el token CSRF antes de procesar el login
if (!isset($_POST['csrf_token']) || !safe_hash_equals($_SESSION['csrf_token'],
$_POST['csrf_token'])) {
http_response_code(403);
die("Error: solicitud no autorizada (CSRF detectado).");
}
if ($user = Users::check_login($_POST['username'], $_POST['password'], True))
{
Users::login_user($user['id']);
unset($_SESSION['csrf_token']);
if (isset($_POST['next']))
{
http_redirect($_POST['next']);
}
else
{
http_redirect(Users::$HOME_URL);
}
}
else
{
$bad_login = True;
$flash['error'] = "The username/password combination you have entered is invalid";
}
}
if ($bad_login)
{
our_header();
?>
<div class="column prepend-1 span-23 first last">
<h2>Login</h2>
<?php error_message(); ?>
<table style="width:320px" cellspacing="0">
<form action="<?=h( $_SERVER['PHP_SELF'] )?>" method="POST">
<!-- Campo oculto con token CSRF -->
<input type="hidden" name="csrf_token" value="<?= h($_SESSION['csrf_token']); ?>">
<tr><td>Username :</td><td> <input type="text" name="username" /></td></tr>
<tr><td>Password :</td><td> <input type="password" name="password" /></td></tr>
<tr><td><input type="submit" value="login" /></td><td> <a
href="/users/[Link]">Register</a></td></tr>
</form>
</table>
</div>
<?php
our_footer();
}
?>
Es una forma un poco chapucera, pero, si intento hacer un CURL:
curl -X POST [Link] -d "username=ZAP&password=ZAP"
Si, por el contrario, extraigo el token y lo almaceno en una variable para acceder por
CURL, una vez pongo lo mismo de antes, pero con el token, puedo entrar y me da un 200
pero, si vuelvo a reutilizar el mismo token, me aparece un 403.
Si en una sola línea, extraigo ese token y lo uso:
Content Security Policy (CSP) Header Not Set
Vulnerabilidad/Remediación
Configurar:
if (!headers_sent()) {
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; frame-
ancestors 'none';");
}
en los PHP. Por ejemplo, aplicándolo en [Link] podemos
ver lo siguiente al hacer un curl -I:
En donde:
default-src define el origen por defecto para todos los tipos de contenido
script-src permite con self solo los scripts alojados en el servidor
style-src aplica solo con self los estilos de su propio dominio
frame-ancestors a none controla que nadie pueda embeber la página en un
<iframe> para proteger contra clickjacking y así matamos dos pájaros de un tiro.
Toda esta información ha sido extraída de la pagina
[Link]
ml
Si, por ejemplo, pongo script-src 'unsafe-inline', me permitiría hacer un ataque XSS
inyectando un script.
Missing Anti-clickjacking Header
Vulnerabilidad/Remediación
[Link]
podría usar en este caso Content-Security-Policy: frame-ancestors 'none'; o, en su
defecto, X-Frame-Options. En este caso, para variar un poco, voy a configurar la
cabecera de la siguiente manera:
header("X-Frame-Options: SAMEORIGIN");
if (!headers_sent()) {
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';");
header("X-Frame-Options: SAMEORIGIN");
}
Sin este parámetro, podría crear una página maliciosa y, en un iframe desde un dominio
externo, hacer que realice una acción en la página comprometida como extraer dinero.
Cookie without SameSite Attribute
Vulnerabilidad/Remediación
El atributo SameSite se puede establecer al definir una cookie en el encabezado Set-
Cookie. Los valores posibles son:
• Strict: La cookie solo se enviará si la solicitud proviene del mismo origen que la
cookie.
• Lax: La cookie se enviará con solicitudes de navegación de nivel superior (como al
hacer clic en un enlace), pero no con solicitudes de origen cruzado como imágenes
o iframes.
• None: La cookie se enviará con todas las solicitudes de origen cruzado. Nota: Si se
establece en None, la cookie debe ser segura (Secure), es decir, solo se enviará a
través de conexiones HTTPS.
Ejemplo de configuración en PHP:
$cookieParams = session_get_cookie_params();
$path = $cookieParams['path'] . '; SameSite=Lax';
setcookie(
session_name(),
session_id(),
time() + $cookieParams['lifetime'],
$path,
$cookieParams['domain'],
isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
$cookieParams['httponly']
);
Server Leaks Information via "X-Powered-By" HTTP Response Header
Field(s)
Vulnerabilidad/Remediación
Nos está dando información demás, por ello, se debe deshabilitar para no dar pistas como
en este caso que el PHP es 5.5 para que vayamos a buscar exploits y probar.
Pasos para remediarlo:
nano /etc/php5/apache2/[Link]
Modificamos el siguiente valor:
expose_php = Off
Reiniciamos el servidor Apache:
service apache2 restart
Antes:
Después:
Server Leaks Version Information via "Server" HTTP Response Header Field
Vulnerabilidad/Remediación
nano /etc/apache2/conf-available/[Link]
Cambio a estos valores los siguientes parametros:
ServerTokens Prod
ServerSignature Off
service apache2 restart
Ya no aparece la versión de Apache cuando antes si me la daba porque ServerTokens
estaba a OS:
Timestamp Disclosure – Unix
Vulnerabilidad/Remediación
Corregimos el archivo [Link]
<?php
require_once("include/html_functions.php");
if (isset($_GET['date'])) {
$date = strtotime($_GET['date']);
if ($date === false) {
$date = time();
}
} else {
$date = time();
}
$cur_text = date("l jS \of F Y", $date);
$day = date("D", $date);
$is_party = ($day == "Fri" || $day == "Sat");
// add a day
$next_time = $date + (24 * 60 * 60);
$next_date_str = date("Y-m-d", $next_time);
?>
<?php our_header("calendar"); ?>
<div class="column prepend-1 span-24 first last">
<h2>WackoPicko Calendar</h2>
<p>
What is going on <?= htmlspecialchars($cur_text, ENT_QUOTES, 'UTF-8') ?>?
</p>
<?php if ($is_party) { ?>
<p>We're throwing a party!<br />
Use this coupon code: SUPERYOU21 for 10% off in celebration!
</p>
<?php } else { ?>
<p>Nothing!</p>
<?php } ?>
<p>
<a href="/[Link]?date=<?= htmlspecialchars($next_date_str, ENT_QUOTES, 'UTF-8') ?
>">What about tomorrow?</a>
</p>
</div>
<?php our_footer(); ?>
Aquí muestro el antes/después del PHP:
Si ahora ejecutamos la siguiente CURL:
curl -s [Link] | grep "What about tomorrow"
Ya no vemos formatos raros en la hora:
X-Content-Type-Options Header Missing
Vulnerabilidad/Remediación
nano /etc/apache2/conf-available/[Link]
Header set X-Content-Type-Options "nosniff"
a2enmod headers
service apache2 restart
curl -I [Link]
Vulnerabilidades Adicionales Encontradas
SQL Injection
Vulnerabilidad/Remediación
Investigando de forma manual debido a que tenía problemas para que OWASP ZAP
terminase de ejecutarse, he encontrado que en [Link] si
ponemos una ' nos da la siguiente respuesta:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for
the right syntax to use near '''' and `password` = SHA1( CONCAT('', `salt`)) limit 1' at line 1
Y, analizando la página, vemos que los campos que se piden son username y password:
Suponiendo que la conslta SQL es de la siguiente forma:
SELECT * FROM tabla_users WHERE (user =’$username AND pass=’$password’)
Intentare escapar con – o con # la contraseña para que, conociendo el usuario que es
scanner1 según la página de descarga de la aplicación web, no tener que meter la
contraseña:
Probando con esta combinación:
Acabamos de entrar sin necesidad de introducir contraseña, simplemente escapándola.
Para remediarlo, con el siguiente cambio usando mysql_real_escape_string:
Se puede ver que introducir scanner1’# sin contraseña deja de funcionar:
OS Command Injection
Vulnerabilidad/Remediación
Otra vulnerabilidad se encuentra en [Link] dado que, si no
introducimos nada, nos aparece el siguiente mensaje:
Esto nos puede estar dando pistas de que, jugando con los comandos que
introduzcamos, podemos explotar la vulnerabilidad. Por ejemplo, si introduzco: prueba$
whoami > /tmp/[Link]
veo lo siguiente:
y, si busco en /tmp en el Docker:
hemos escrito un fichero solo introduciendo parámetros por la interfaz.
Para remediar esto, introduzco en el PHP la siguiente línea y ya no funciona:
$pass = escapeshellcmd($pass);
Directory listing
Vulnerabilidad/Remediación
Otra vulnerabilidad encontrada es la de listar directorios incluyéndolos en la URL:
con la siguiente configuración usando nano /etc/apache2/[Link]:
Se resuelve el problema: