Introducción al DOM
El DOM (Document Object Model) es una interfaz de programación para documentos HTML y XML. Representa la estructura del documento como un árbol de objetos, permitiendo a los programas y scripts acceder y modificar dinámicamente el contenido, estructura y estilo del documento.
En este laboratorio, exploraremos diferentes tipos de vulnerabilidades que pueden surgir cuando se manipula el DOM de manera insegura.
const element = document.getElementById('miElemento');
element.innerHTML = 'Nuevo contenido'; // Potencialmente peligroso
Sinks de Vulnerabilidad
Los "sinks" son puntos en el código donde los datos controlados por el usuario pueden ejecutarse como código. Algunos sinks comunes incluyen:
innerHTML/outerHTMLdocument.write()eval()setTimeout()/setInterval()con cadenaslocationpropiedades (href, assign, replace)
Intenta explotar el siguiente sink para ejecutar código JavaScript.
const userInput = document.getElementById('sinkInput').value;
document.getElementById('sinkResult').innerHTML = userInput;
}
Prueba con: <img src=x onerror=alert('XSS')>
Bypass de Validación
Muchas aplicaciones implementan validaciones de entrada para prevenir ataques. Sin embargo, estas validaciones a menudo pueden ser eludidas mediante técnicas creativas.
En este desafío, intenta eludir la validación que filtra ciertas palabras clave para ejecutar código JavaScript.
let input = document.getElementById('bypassInput').value;
// Filtro básico
if (input.includes('script') || input.includes('onerror') || input.includes('javascript')) {
document.getElementById('bypassResult').innerHTML = 'Entrada bloqueada!';
return;
}
document.getElementById('bypassResult').innerHTML = input;
}
Prueba con: <img src=x onerror=alert(1)> pero modificado para evitar las palabras clave bloqueadas.
Gadgets DOM
Los gadgets DOM son funciones o propiedades del DOM que pueden ser utilizadas de manera no intencionada para ejecutar código. A menudo se encuentran en bibliotecas JavaScript o en código de aplicación.
En este desafío, identifica y utiliza un gadget DOM para ejecutar código.
const AppUtils = {
sanitizeInput: function(input) {
// Supuesta sanitización
return input.replace(/<script>/gi, '');
},
renderContent: function(elementId, content) {
const element = document.getElementById(elementId);
if (element && content) {
element.innerHTML = this.sanitizeInput(content);
}
}
};
// Tu código
function useGadget() {
const input = document.getElementById('gadgetInput').value;
AppUtils.renderContent('gadgetResult', input);
}
La función de sanitización solo elimina etiquetas <script>. ¿Qué otras etiquetas HTML pueden ejecutar JavaScript?
XSS en el DOM
El XSS basado en DOM ocurre cuando el código JavaScript de una página web procesa datos de origen no confiable de manera insegura y los escribe en el DOM.
Este desafío simula una aplicación que utiliza datos de la URL para actualizar el contenido de la página. Intenta explotar esta funcionalidad para ejecutar código malicioso.
function processURLParams() {
const urlParams = new URLSearchParams(window.location.search);
const message = urlParams.get('message');
if (message) {
document.getElementById('xssResult').innerHTML = decodeURIComponent(message);
}
}
// Ejecutar al cargar
window.onload = processURLParams;
Construye una URL que contenga tu payload en el parámetro 'message'. Por ejemplo: ?message=<svg onload=alert(1)>
DOM Clobbering
DOM Clobbering es una técnica donde se inyectan elementos HTML en una página para "sobrescribir" (clobber) propiedades y métodos JavaScript globales.
Este ataque es particularmente útil cuando no se puede ejecutar JavaScript directamente, pero se puede inyectar HTML.
function checkAdmin() {
if (window.isAdmin) {
document.getElementById('adminPanel').style.display = 'block';
document.getElementById('clobberResult').innerHTML = 'Acceso de administrador concedido!';
} else {
document.getElementById('clobberResult').innerHTML = 'Acceso denegado';
}
}
Los elementos HTML con id se convierten en propiedades globales. Prueba con: <a id="isAdmin"></a>
document.URL / document.documentURI
document.URL y document.documentURI devuelven la URL completa del documento actual. Si esta fuente se utiliza sin la sanitización adecuada, puede conducir a vulnerabilidades XSS.
En este desafío, la aplicación utiliza la URL actual para mostrar un mensaje de bienvenida. Intenta explotar esta funcionalidad.
function displayWelcome() {
const url = document.URL;
const urlObj = new URL(url);
const username = urlObj.searchParams.get('user') || 'Invitado';
document.getElementById('urlResult').innerHTML = 'Bienvenido, ' + username + '!';
}
Intenta usar: ?user=<img src=x onerror=alert(1)>
document.cookie
document.cookie permite acceder y modificar las cookies asociadas con el documento. Si el valor de una cookie se refleja en el DOM sin sanitización, puede explotarse para XSS.
Esta aplicación lee una cookie llamada 'preferences' y muestra su valor. Intenta establecer una cookie maliciosa.
function displayPreferences() {
const cookies = document.cookie.split(';');
let preferences = 'No hay preferencias guardadas';
for (let cookie of cookies) {
const [name, value] = cookie.trim().split('=');
if (name === 'preferences') {
preferences = decodeURIComponent(value);
break;
}
}
document.getElementById('cookieResult').innerHTML = 'Tus preferencias: ' + preferences;
}
Usa la consola del navegador para establecer: document.cookie = "preferences=<img src=x onerror=alert(1)>"
document.referrer
document.referrer devuelve la URI de la página que enlazó a la página actual. Si este valor se refleja sin sanitización, un atacante puede crear un enlace malicioso que conduzca a XSS.
Esta aplicación muestra la página de referencia. Intenta explotar esta funcionalidad.
function showReferrer() {
const referrer = document.referrer;
if (referrer) {
document.getElementById('referrerResult').innerHTML = 'Viniste desde: ' + referrer;
} else {
document.getElementById('referrerResult').innerHTML = 'No hay referencia detectada';
}
}
Para este desafío, crea una página HTML que enlace a esta página con un referrer malicioso.
Crea una página HTML con: <meta http-equiv="refresh" content="0;url=http://tu-dominio/lab.html?<script>alert(1)</script>">
window.name
window.name puede ser utilizado para transportar datos entre diferentes páginas y dominios. Su valor persiste durante la navegación y puede ser explotado para XSS si se refleja sin sanitización.
Esta aplicación utiliza window.name para recordar la última acción del usuario. Intenta explotar esta característica.
function restoreState() {
if (window.name) {
document.getElementById('nameResult').innerHTML = 'Estado anterior: ' + window.name;
} else {
document.getElementById('nameResult').innerHTML = 'No hay estado guardado';
}
}
function saveState() {
const state = document.getElementById('nameInput').value;
window.name = state;
document.getElementById('nameResult').innerHTML = 'Estado guardado: ' + state;
}
Establece window.name con: <img src=x onerror=alert(1)> y luego recarga la página
Web Storage (localStorage, sessionStorage)
localStorage y sessionStorage permiten almacenar datos en el navegador. Si estos datos se reflejan sin sanitización, pueden conducir a vulnerabilidades XSS persistentes.
Esta aplicación guarda y muestra el tema de preferencia del usuario. Intenta explotar el almacenamiento local.
function loadTheme() {
const theme = localStorage.getItem('userTheme') || 'predeterminado';
document.getElementById('storageResult').innerHTML = 'Tema actual: ' + theme;
}
function saveTheme() {
const theme = document.getElementById('storageInput').value;
localStorage.setItem('userTheme', theme);
document.getElementById('storageResult').innerHTML = 'Tema guardado: ' + theme;
}
Usa la consola para establecer: localStorage.setItem('userTheme', '<img src=x onerror=alert(1)>')
History API (pushState, replaceState)
history.pushState() y history.replaceState() permiten manipular el historial del navegador. Si los datos del estado se utilizan sin sanitización, pueden conducir a XSS.
Esta aplicación utiliza el estado del historial para mostrar información de navegación. Intenta explotar esta funcionalidad.
function displayState() {
if (history.state) {
document.getElementById('historyResult').innerHTML = 'Estado: ' + JSON.stringify(history.state);
} else {
document.getElementById('historyResult').innerHTML = 'No hay estado en el historial';
}
}
function setState() {
const stateData = document.getElementById('historyInput').value;
history.replaceState({data: stateData}, '', window.location);
document.getElementById('historyResult').innerHTML = 'Estado establecido: ' + stateData;
}
Usa: history.replaceState({data: "<img src=x onerror=alert(1)>"}, '', location.href) en la consola