What is IMDS?
When cloud instances/virtual machines require access to data about itself or the cloud environment, it can query its Instance Metadata Service (IMDS) that typically listens on the IPv4 address of 169.254.169.254 as well as, in the case of Amazon Web Services (AWS) the IPv6 address of fd00:ec2::254. But what kind of information can be had? It depends on the vendor, but typically things like:
- What region the instance/VM is running in
- What subnet the instance/VM is a part of
- What image was used to launch the system
- What security groups are used to control network access to the system
- Which public SSH key was injected into the authorized_keys file when spawned
The IMDS endpoints are in a tree-like structure that you can drill into to retrieve the data of interest. For example, if you were looking for the current AWS security group names tied to this instance, you can issue the following curl command in the Linux instance:
You may think, “what’s the big deal?”, but there are some more sensitive items that can be retrieved as well, like:
- User-data passed to the system at boot time (could contain secrets)
- IAM role credentials (could allow access to the greater AWS cloud account)
- Managed identity credentials (could allow access to the Azure account)
- Service account tokens (allowing access to the GCP account)
We will focus on AWS specifically since it has a default deployment that is vulnerable (as you will see shortly). For example, this pair of curl commands can retrieve not only the name of the role attached to the EC2 instance, but also the current credentials that can be used by the instance (and attacker) to access parts of AWS:
Now, when we say access to the above cloud accounts, that means that the attacker would be able to access the cloud environment with the exact permissions tied to the role, identity, or service account. This is where an attack abusing IMDS is more likely to focus.
How can attackers leverage it?
Up to this point, you may be assuming that, to get access to IMDS, you need to have a shell session on the cloud-based system, but that is certainly not the only way to access IMDS. Attacks like the Capital One breach in 20191 can leverage a flaw in the virtual system’s software to “trick” the application into querying IMDS instead of retrieving a legitimate resource. This is an example of a Server-Side Request Forgery.
Here is an example code snippet that would be susceptible to an SSRF attack which would allow an attacker to gain access to IMDS:
This seemingly harmless code block (which will simply provide source code of the requested URL sent in as a url GET variable) will allow an attacker to retrieve IMDS data relatively easily. The attacker could craft a request to the URI of ?url=http%3A%2F%2F169.254.169.254%2Flatest%2Fmeta-data%2Fiam%2Fsecurity-credentials on this web application as shown here:
This would be followed up by another request to ?url=http%3A%2F%2F169.254.169.254%2Flatest%2Fmeta-data%2Fiam%2Fsecurity-credentials/EC2S3FullAccess.
Oh no! Exposed cloud credentials!
Once the attacker has access to the cloud credentials, it’s trivial to pivot into the cloud account and access whatever the instance/VM would normally have access to. Assuming that the attacker has the AWS CLI tools installed on their local system, they could either create an entry in their .aws/credentials file or provide the credentials as environment variables (as shown below).
After the credentials are set, the attacker can then execute AWS commands at will. Some may fail if the instance role does not have the appropriate permissions which may lead to some trial and error. But eventually, the attacker may stumble upon some requests that will work.
Uh oh! An S3 bucket that looks enticing!
And now the crown jewels are gone!
You really... REALLY should not go to that link (you’ve been warned)!
How Can Defenders Protect IMDS?
Since the above attack’s initial access was due to some vulnerable PHP web code, an obvious place to start would be to fix the code. The web application developer would start by adding input sanitization to the web code (e.g., not allowing 169.254.169.254 to be entered) or adding defense-in-depth measures like a Web Application Firewall (WAF) to thwart these suspicious web requests before they reach the web server.
There is however, another approach we can take from a cloud configuration perspective. IMDS version 1 is in place and running by default in AWS to this day which allows for these simple GET requests to be used to retrieve information. However, we can change this by enforcing IMDS version 22 instead. IMDSv2 differs in a few ways:
Why this change matters
Requestor must first request a token from IMDS before making any subsequent requests
This changes the attacker’s requirements from two simple GET requests to acquire the role name and role credentials to now needing to first request a token
When requesting the token, the HTTP method is not a GET, but a PUT
Attacker must somehow change not only the URL being requested, but the HTTP method that is used—this is often times not possible when conducting a SSRF attack
PUT request must also include an additional HTTP header of X-aws-ec2-metadata-token-ttl-seconds: <seconds> where <seconds> signifies how long the returned token will be valid
Another requirement that the attacker likely does not have the power to influence when conducting a SSRF attack
A new HTTP header, X-aws-ec2-metadata-token: <TOKEN> must be included (where <TOKEN> is the value of the returned token from the previous request)
Once more, the attacker likely does not have the ability to add HTTP headers to their request during a SSRF attack
This is what an IMDS version 2 request would look like from the command line:
And here is how to require IMDSv2 on an already deployed EC2 instance (of course, you should be deploying these from the start with only IMDSv2 enabled):
No more SSRF accessing IMDS!
Restricting which local users/groups can submit the request
Now that most SSRF attacks are taken care of, there is still the issue of an exploit which allows remote code execution. In other words, instead of just providing a URL to the web application, the web application is exploited by malicious code—allowing the attacker to execute code of their choosing (like they were on the command-line of that system) or gain shell access to the system and having direct command-line access.
In any case, the attacker can easily run the necessary curl commands to access IMDS—even with version 2 enabled. So... what else can be done?
There is one more trick up our sleeves to add as a defense-in-depth measure: host-based firewall tweaks. Since most Linux instances have iptables installed by default, we can apply a few rules restricting which user account can make an outbound request to 169.254.169.254. Let’s assume that the only user on the system that should have access to IMDS is the root user to pull the latest and greatest web code from an S3 bucket to this web server (hence the original intention of the instance role). The following iptables rules3 will prevent all other users from initiating an outbound request to the IMDS service:
The first rule allows any process that is running as the root user to reach 169.254.169.254 and the second rule prevents all other requests to IMDS.
With those rules in place, let’s test them out. Here is a request from a non-root user:
And one from the root user:
If the web application user (which should not be root) is now executing attacker-controlled commands, they should be blocked from accessing IMDS until they are able to somehow either remove the iptables rule or escalate to root. Of course, if the compromised user account is the one that requires access to IMDS, this additional work does us no good here, but would prevent other users from accessing IMDS that have no business doing so.
As course author for SANS SEC488: Cloud Security Essentials and co-author (along with Shaun McCullough) of SEC541: Cloud Security Attacker Techniques, Monitoring, and Threat Detection, I’ve found that IMDS is a major blind spot for many organizations’ security teams. If you learned anything from this, hopefully it is to at least migrate all of your current and future AWS EC2 instances to IMDS version 2. Please see the below references for even more details on this service.
 Stella, J. (2019, August 1). A Technical Analysis of the Capital One Cloud Misconfiguration Breach. Fugue. https://www.fugue.co/blog/a-technical-analysis-of-the-capital-one-cloud-misconfiguration-breach
 Amazon Web Services. (2022). Use IMDSv2. Amazon Elastic Compute Cloud.
 Nicholson, R. (2021, August 4). Neat little trick to block IMDS access from everyone except the root user. Unlike just moving to IMDS version 2. Twitter.
ABOUT THE AUTHOR
Ryan's passion for information technology started in 2001 when he found himself constantly trying to make his high school's computers and even calculators do things that they weren't exactly intended to do. They lacked games, so he learned how to create some. Yes, some may call this hacking. Ryan called it "fun", which led to attending college with intentions of becoming a software engineer. During school, Ryan obtained an internship with a very cybersecurity-minded organization -- the Defense Information Systems Agency (DISA). Ever since then, he’s been hooked on cybersecurity. Ryan is the author for SANS SEC488: Cloud Security Essentials, co-author of SEC541: Cloud Security Attacker Techniques, Monitoring, and Threat Detection, and an instructor for SEC530: Defensible Security Architecture and Engineering. Learn more about Ryan here.