PortSwigger Academy – DOM-Based Cross-Site Scripting (XSS)

¡Hola a todos! Aquí estamos de nuevo y esta vez seguimos preparando la certificación Burp Suite Certified Professional (BSCP), que proporciona una base muy buena de cara a auditorías web y gestión de la presión, sobre todo en el examen para el cual te proporcionan 4 horas y hay que explotar hasta 6 vulnerabilidades. Además, se trata de un examen muy barato (<100€) lo que la hace sumamente atractiva.

Antes de nada, recordad que PortSwigger nos permite probar 30 días su versión profesional de Burp Suite. Os recomendamos que activéis esta versión para poder probar todas las funcionalidades de la herramienta.

En esta entrada explicaremos y solucionaremos todos los laboratorios relativos a DOM-Based Cross-Site Scripting (XSS). Hay muchos walkthroughs sobre cómo resolver estos laboratorios, pero exponerlos aquí nos sirve para afianzar conceptos y enseñaros además algunos trucos de uso de Burp Suite Professional, así como comentar veces que hemos visto las vulnerabilidades estudiadas en los laboratorios en la vida real. Sin más dilación, ¡comenzamos!

Si tuviéramos que decir qué vulnerabilidad es la más prevalente en las webs que analizamos, sin duda, optaríamos por el Cross-Site Scripting, especialmente en sus formas reflejada y almacenada. Hemos visto este tipo de vulnerabilidades en aplicaciones de todo tipo y, aunque su mitigación es sencilla, en muchos casos no se aplican técnicas de desarrollo seguro que permitan ponerle freno a tiempo. Asimismo, muchas veces es tomada como inofensiva por los desarrolladores, pero sus consecuencias pueden ser bastante graves. Por ello, presta especial atención a este post.

Cabe añadir también que este tipo de vulnerabilidades son bien detectadas habitualmente por escáneres automáticos, aunque en algunos casos se pueden añadir detecciones a nivel manual. En toda esta tirada de laboratorios intentaremos hacerlo todo a mano, si bien apoyarse en herramientas para este tipo de vulnerabilidad suele ser muy útil.

Lab1 (Apprentice) – DOM XSS in document.write sink using source location.search

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve this lab, perform a cross-site scripting attack that calls the alert function.». En este laboratorio debemos identificar una vulnerabilidad de DOM-Based Cross-Site Scripting (XSS), lanzando un «alert()».

Para resolver estos laboratorios de DOM-Based XSS, lo principal es localizar lo que se denomina «sources» en las que el usuario puede controlar parte de su contenido y, posteriormente, localizar lo que se denomina «sinks» que recojan dichos valores introducidos en las fuentes y que permitan ejecutar código. En resumidas cuentas, se necesita trabajar con el DOM de la página cargada, viendo si podemos incluir un payload en un «source» que se propague hacia un «sink». Por tanto, este tipo de vulnerabilidades son más sencillas de detectar a nivel de navegador o debugging Javascript.

Afortunadamente, disponemos de extensiones como DOM Invader, incluida directamente en el navegador integrado en Burp Suite Professional. Para lanzarla, simplemente abre el navegador integrado en Burp Suite Professional y activa la extensión DOM Invader (en el icono de Burp arriba a la derecha). Esta extensión nos proporciona un valor «canary» que iremos introduciendo (ya sea a mano o de manera automática) en parámetros, URLs y demás con el objetivo de ver si se propaga desde una «source» hacia un «sink». Alternativamente, existen extensiones que se basan en localizar el uso de «sinks» tales como «Additional Scanner Checks», si bien en estos casos debemos investigar nosotros mismos la propagación.

Empezamos este laboratorio abriendo el navegador de Burp Suite Professional con DOM Invader activado y disponible y realizamos una búsqueda del valor «canary» que nos propone.

Como vemos, la propia extensión ya nos localiza un «sink» en el cual podemos controlar parte de su valor a través de los parámetros incluidos en la URL. Incluso nos presenta un botón de exploit que podemos pulsar para explotar DOM-Based XSS (spoiler, resuelve el laboratorio). Lo que hacemos es, por tanto, incluir un payload que rompa el contexto en el que se incluye el «source» dentro del «sink» y nos permita ejecutar código:

<img src="/resources/images/tracker.gif?searchTerms="><script>alert(1)</script>">	

Como vemos en negrita, escapamos del contexto de la imagen e incluimos un script que muestra el «alert()». Simplemente accediendo a la URL de búsqueda con el payload, resolvemos el laboratorio:

https://0ab1000903a5c265800603cb002200d3.web-security-academy.net/?search=%22%3E%3Cscript%3Ealert(1)%3C/script%3E

Lab2 (Practicioner) – DOM XSS in document.write sink using source location.search inside a select element

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve this lab, perform a cross-site scripting attack that breaks out of the select element and calls the alert function.». En este laboratorio debemos identificar una vulnerabilidad de DOM-Based Cross-Site Scripting (XSS), lanzando un «alert()».

Empezamos este laboratorio revisando las peticiones y respuestas de la web. Vemos que las herramientas automáticas nos marcan un posible «sink» aprovechable para DOM-Based XSS en la petición enviada para listar un producto cualquiera:

https://0a27003d0324db5c80f5ee7300020036.web-security-academy.net/product?productId=1

Analizamos el código Javascript que nos presenta la respuesta:

var stores = ["London","Paris","Milan"];
var store = (new URLSearchParams(window.location.search)).get('storeId');
document.write('<select name="storeId">');
if(store) {
    document.write('<option selected>'+store+'</option>');
}
for(var i=0;i<stores.length;i++) {
    if(stores[i] === store) {
        continue;
    }
    document.write('<option>'+stores[i]+'</option>');
}
document.write('</select>');

Fíjate en las líneas en negrita. Justamente es lo que buscamos, una «source» controlable por el atacante (a través de «window.location.search», que no son más que los parámetros de la URL) que se propaga hacia un «sink» explotable como «document.write». Por tanto, podemos construir un payload teniendo en cuenta, eso sí, que nuestros parámetros se incluyen en el siguiente contexto:

Contexto: <option selected>'+<URL params>+'</option>

Payload (parámetros): productId=1&storeId=1</option><script>alert(1)</script><option>2

Resultado: <option selected>1</option><script>alert(1)</script><option>2</option>

Lanzamos el payload y resolvemos el laboratorio, al propagarse desde el «source» al «sink» y ejecutarse.

Lab3 (Apprentice) – DOM XSS in innerHTML sink using source location.search

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve this lab, perform a cross-site scripting attack that calls the alert function.». En este laboratorio debemos identificar una vulnerabilidad de DOM-Based Cross-Site Scripting (XSS), lanzando un «alert()».

Comenzamos lanzando una consulta arbitraria a la web, observando que las herramientas automáticas detectan de nuevo un posible DOM-Based XSS.

En este caso, se actualiza el contenido HTML del elemento «searchMessage». Dado que los elementos HTML no soportan Javascript, debemos utilizar alternativas para ejecutar código. No obstante, esto es muy sencillo con HTML con un payload como el siguiente:

<img src=x onerror=alert(1)>

Lanzamos la búsqueda con dicho payload y resolvemos el laboratorio:

Lab4 (Apprentice) – DOM XSS in jQuery anchor href attribute sink using location.search source

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve this lab, make the «back» link alert document.cookie.». En este laboratorio debemos identificar una vulnerabilidad de DOM-Based Cross-Site Scripting (XSS), lanzando un «alert()».

Comenzamos mapeando la web y, en este caso, observamos la presencia de un script en la pestaña de enviar feedback a la misma:

Tal como podemos ver, si la abrimos con DOM Invader ya nos notifica el potencial «sink» aprovechable, que en este laboratorio es de jQuery:

Podemos decidir explotarla con DOM Invader o incluir el siguiente payload en la URL:

https://0a6f004f040c978b80d130bd002f00e5.web-security-academy.net/feedback?returnPath=javascript:alert(1)

Haciendo click en el botón «Back», ejecutamos el payload:

Lab5 (Apprentice) – DOM XSS in jQuery selector sink using a hashchange event

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, deliver an exploit to the victim that calls the print() function in their browser.». En este laboratorio debemos identificar una vulnerabilidad de DOM-Based Cross-Site Scripting (XSS), lanzando un «print()» que debemos enviar a la víctima.

En la propia raíz de la aplicación, descubrimos el código client-side vulnerable en este caso:

<script>
     $(window).on('hashchange', function(){
         var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
         if (post) post.get(0).scrollIntoView();
     });
</script>

Como vemos, se usa el selector $(window), por lo que dado que controlamos el valor «window.location.hash.slice», podemos incluir un payload tal como el siguiente para explotar la vulnerabilidad:

https://0af3007e045609ea83bbf07c00a10013.web-security-academy.net#<img src=1 onerror=alert(1)>

Sin embargo, para que esto funcione, tiene que haber un «hashchange». Por tanto, generamos el siguiente payload que almacenaremos en nuestro «Exploit Server»:

<iframe src="https://0af3007e045609ea83bbf07c00a10013.web-security-academy.net#" onload="this.src+='<img src=1 onerror=print()>'">

Fíjate cómo con el «onload» conseguimos cambiar forzar el «hashchange». Almacenamos el código anterior en «Exploit Server» y, al enviarlo a la víctima, solucionamos el laboratorio.

Lab6 (Practicioner) – DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve this lab, perform a cross-site scripting attack that executes an AngularJS expression and calls the alert function.». En este laboratorio debemos identificar una vulnerabilidad de DOM-Based Cross-Site Scripting (XSS), lanzando un «alert()».

La solución es bastante sencilla, dado que al enviar una petición de búsqueda de un valor arbitrario detectamos que dicho valor se refleja en la respuesta que, a su vez, es una aplicación AngularJS ya que uno de los elementos principales del HTML (el body) tiene su atributo «ng-app»:

Por tanto, podemos utilizar código AngularJS dentro de la respuesta, tal como el siguiente:

{{$on.constructor('alert(1)')()}}

Simplemente lanzamos la petición con dicho payload y resolvemos el laboratorio:

Lab7 (Practicioner) – Reflected DOM XSS

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve this lab, create an injection that calls the alert() function.». En este laboratorio debemos identificar una vulnerabilidad de DOM-Based Cross-Site Scripting (XSS), lanzando un «alert()».

Tal como podemos ver, cuando lanzamos una consulta con valor «randomvalue» en la búsqueda del blog, por debajo se realiza una petición al endpoint «/search-results».

Dado que asimismo existe un código Javascript que coge el valor de la respuesta a dicha petición y lo incluye en un «sink» denominado «eval()», podemos explotarlo:

La idea es que el valor «this.responseText» que se debe interpretar contenga un Javascript que se ejecute en el contexto del navegador víctima. Para ello, nos fijamos en la composición de la respuesta a la búsqueda:

{"results":[],"searchTerm":"randomvalue"}

En base a ella, construimos un payload válido:

{"results":[],"searchTerm":""};alert(1)//"}

Payload: "};alert(1)//

No obstante, lo lanzamos y vemos que no tenemos éxito, porque el servidor escapa las comillas dobles:

Probamos a hacer nosotros un escapado, ya que si el backslash no se escapa, existe un doble backslash y la comilla quedará intacta:

{"results":[],"searchTerm":"\"};alert(1)//"}

Payload: \"};alert(1)//

Lanzamos y resolvemos el laboratorio:

Lab8 (Practicioner) – Stored DOM XSS

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «o solve this lab, exploit this vulnerability to call the alert() function.». En este laboratorio debemos identificar una vulnerabilidad de DOM-Based Cross-Site Scripting (XSS), lanzando un «alert()».

Analizamos de nuevo el código Javascript. En este caso, al cargar comentarios en cualquiera de los post nos topamos con una petición al siguiente fragmento de código en el recurso «/resources/js/loadCommentsWithVulnerableEscapeHtml.js»

let commentBodyPElement = document.createElement("p");
commentBodyPElement.innerHTML = escapeHTML(comment.body);

Como vemos, esto es vulnerable a DOM-Based XSS, ya que podemos incluir un comentario que incluya el siguiente payload:

<img src=x onerror=alert(1)>

No obstante, todo sería muy sencillo si no hubiera la función de escapado:

function escapeHTML(html) {
    return html.replace('<', '&lt;').replace('>', '&gt;');
}

Sin embargo, esta función contiene una vulnerabilidad en sí misma, ya que solamente reemplaza las primeras ocurrencias de «<» y «>». Por tanto, podemos utilizar el siguiente payload como comentario y resolver el laboratorio:

<><img src=x onerror=alert(1)>

Simplemente posteando un comentario y accediendo de nuevo al post, resolvemos el laboratorio.

~km0xu95

Comments are closed.