6 - Exploiting DOM clobbering to enable XSS
https://www.youtube.com/watch?v=eWD4LH5W2Es
El DOM Clobbering es una vulnerabilidad de seguridad en aplicaciones web que ocurre cuando elementos del Documento Object Model (DOM) de una página web sobrescriben o "clobber" (destruyen o alteran) objetos o variables globales de JavaScript en el entorno del navegador.
¿Cómo ocurre?
En HTML, los elementos pueden tener atributos id o name, y estos nombres pueden crear referencias globales en el entorno JavaScript. Por ejemplo, si un elemento tiene un id llamado form, en JavaScript podría generarse automáticamente una variable global window.form que apunta a ese elemento. Sin embargo, si la aplicación ya usa una variable llamada form, esta referencia puede ser sobrescrita por el elemento DOM, lo que puede causar comportamientos inesperados o vulnerabilidades de seguridad.
Ejemplo básico
Supongamos que una aplicación tiene un código JavaScript que utiliza una variable form para referirse a un objeto que maneja la lógica de formularios:
var form = new FormHandler();
form.submit();
Ahora, si en el HTML se define un elemento con id="form":
<form id="form" action="/submit">
...
</form>
En algunos navegadores, la variable form en JavaScript será sobrescrita por la referencia al elemento DOM <form>. Esto se conoce como clobbering. Ahora, si se intenta llamar a form.submit(), en lugar de ejecutar la lógica del objeto FormHandler, se llamará al método submit() del elemento <form>, lo que podría llevar a un comportamiento no deseado.
loadCommentsWithDomClobbering.js
function loadComments(postCommentPath) {
- Descripción: Es la función principal que se ejecuta cuando se desea cargar los comentarios de un post.
- Parámetro:
postCommentPathes la ruta del servidor desde la que se obtendrán los comentarios.
let xhr = new XMLHttpRequest();
- Descripción: Crea un nuevo objeto
XMLHttpRequest, que se utiliza para realizar una solicitud HTTP para obtener datos sin recargar la página.
xhr.onreadystatechange = function() {
- Descripción: Define una función que se ejecuta cada vez que el estado (
readyState) de la solicitud cambia.
if (this.readyState == 4 && this.status == 200) {
- Condición: Si
readyStatees 4 (la solicitud está completa) y elstatuses 200 (OK), entonces se procesan los comentarios.
let comments = JSON.parse(this.responseText);
- Acción: La respuesta de la solicitud (asumida como JSON) se convierte en un objeto JavaScript usando
JSON.parse.
displayComments(comments);
- Acción: Se llama a la función
displayCommentspara mostrar estos comentarios.
}
};
- Fin de la función
onreadystatechange.
xhr.open("GET", postCommentPath + window.location.search);
- Descripción: Configura la solicitud para ser de tipo
GETy construye la URL de destino agregando los parámetros de la URL actual (window.location.search) apostCommentPath.
xhr.send();
- Descripción: Envía la solicitud al servidor.
function escapeHTML(data) {
return data.replace(/[<>'"]/g, function(c){
return '&#' + c.charCodeAt(0) + ';';
});
}
- Descripción: Es una función auxiliar para evitar que caracteres especiales como
<,>,', y"sean interpretados como HTML, evitando así inyecciones de código. - Uso: Reemplaza caracteres problemáticos por sus equivalentes HTML.
function displayComments(comments) {
- Descripción: Es la función que se encarga de generar y mostrar los comentarios en el DOM (Document Object Model).
- Parámetro:
commentses un array de objetos, donde cada objeto representa un comentario.
let userComments = document.getElementById("user-comments");
- Descripción: Obtiene el elemento del DOM con el ID "user-comments" donde se agregarán los comentarios.
for (let i = 0; i < comments.length; ++i) {
- Descripción: Un bucle
forrecorre cada comentario encomments.
comment = comments[i];
- Descripción: Se accede al comentario actual en la iteración del bucle.
let commentSection = document.createElement("section");
commentSection.setAttribute("class", "comment");
- Descripción: Se crea un elemento
sectionpara encapsular cada comentario y se le asigna la clasecomment.
let firstPElement = document.createElement("p");
- Descripción: Se crea un párrafo (
<p>) para contener el nombre del autor, el avatar y la fecha.
let defaultAvatar = window.defaultAvatar || {avatar: '/resources/images/avatarDefault.svg'};
let avatarImgHTML = '<img class="avatar" src="' + (comment.avatar ? escapeHTML(comment.avatar) : defaultAvatar.avatar) + '">';
- Descripción: Si hay un avatar en el comentario, se utiliza; si no, se muestra un avatar por defecto. ¿comment.avatar? si -> escape(avatar) , no -> default avatar
escapeHTML: Se utiliza para evitar posibles inyecciones a través de URLs del avatar.
let divImgContainer = document.createElement("div");
divImgContainer.innerHTML = avatarImgHTML;
- Descripción: Se crea un
divpara contener la imagen del avatar.
if (comment.author) {
if (comment.website) {
let websiteElement = document.createElement("a");
websiteElement.setAttribute("id", "author");
websiteElement.setAttribute("href", comment.website);
firstPElement.appendChild(websiteElement);
}
- Descripción: Si el autor está presente y tiene un sitio web, se crea un enlace (
<a>) que apunta a ese sitio.
let newInnerHtml = firstPElement.innerHTML + DOMPurify.sanitize(comment.author);
firstPElement.innerHTML = newInnerHtml;
}
- Descripción: Se "sanitiza" el nombre del autor y se actualiza el contenido de
firstPElement.
if (comment.date) {
let dateObj = new Date(comment.date);
let month = '' + (dateObj.getMonth() + 1);
let day = '' + dateObj.getDate();
let year = dateObj.getFullYear();
- Descripción: Si hay una fecha, se convierte en un objeto
Datepara su manipulación.
if (month.length < 2) month = '0' + month;
if (day.length < 2) day = '0' + day;
- Descripción: Se asegura que el día y el mes tengan dos dígitos.
dateStr = [day, month, year].join('-');
- Descripción: La fecha se formatea como
dd-mm-yyyy.
let newInnerHtml = firstPElement.innerHTML + " | " + dateStr;
firstPElement.innerHTML = newInnerHtml;
}
- Descripción: Se agrega la fecha al contenido de
firstPElement.
firstPElement.appendChild(divImgContainer);
- Descripción: Se añade el
divcon la imagen del avatar afirstPElement.
commentSection.appendChild(firstPElement);
- Descripción: Se agrega
firstPElementa lasectiondel comentario.
if (comment.body) {
let commentBodyPElement = document.createElement("p");
commentBodyPElement.innerHTML = DOMPurify.sanitize(comment.body);
- Descripción: Si hay un cuerpo de texto, se crea un nuevo párrafo con el contenido del comentario. Este contenido se "sanitiza" con
DOMPurify.
commentSection.appendChild(commentBodyPElement);
}
commentSection.appendChild(document.createElement("p"));
- Descripción: Se añade el párrafo del cuerpo del comentario a la
sectiondel comentario y se agrega otro párrafo vacío como separación.
userComments.appendChild(commentSection);
}
}
- Descripción: Cada comentario procesado se inserta en
userComments.
};
- Fin de la función
loadComments.
Solución :
<a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:"onerror=alert(1)//">
Estamos clobbeando default avatar,si nos fijamos en la elección del default avatar (se evalúan los OR de izquierda a derecha):
let defaultAvatar = window.defaultAvatar || {avatar: '/resources/images/avatarDefault.svg'};
¿Existe comment.avatar? No -> el src será el atributo avatar del objeto defaultAvatar:
let avatarImgHTML = '<img class="avatar" src="' + (comment.avatar ? escapeHTML(comment.avatar) : defaultAvatar.avatar) + '">';
¿Porque dos anchor? -> Si el primer elemento con id="defaultAvatar" no sobrescribe completamente la variable global (porque el DOM trata de mantener una referencia a ambos), el segundo elemento podría asegurar que la propiedad avatar sea clobberada, ya que ahora está intentando sobrescribir una referencia a un grupo de elementos en lugar de un único elemento. (Ponerlo por si acaso)
El valor href="cid:"onerror=alert(1)//" es un intento de inyección maliciosa de código en un atributo HTML, con la intención de ejecutar un ataque XSS (Cross-Site Scripting) utilizando el evento onerror de una etiqueta <img>.
Vamos a desglosar lo que hace cada parte:
1. href="cid:"onerror=alert(1)//"
-
href="cid::hrefes un atributo comúnmente usado en elementos como<a>o<img>. Aquí, el valor comienza concid:, que es un esquema URI que se usa a veces para identificar contenido incrustado en correos electrónicos (como imágenes).- En este caso,
cid:se explica abajo.
-
":"es la entidad HTML que representa una comilla doble ("). Está siendo utilizada para "cerrar" el atributohrefen el HTML interpretado, terminando la URL de forma prematura. Es un truco para salir del contexto de la cadena delhrefy comenzar a introducir código arbitrario.
-
onerror=alert(1)//:onerrores un evento que se dispara en ciertos elementos HTML, como imágenes (<img>), cuando ocurre un error en la carga del recurso.alert(1)es un código JavaScript que simplemente muestra un cuadro de alerta con el número1.//es un comentario en JavaScript que hace que todo lo que sigue en la misma línea se ignore, lo que asegura que no haya errores sintácticos por el código malicioso inyectado.
Como podemos ver usar el esquema URI cid: implica que la comilla sea url encodeada o no lo sea.