<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rohaan</title>
    <description>The latest articles on DEV Community by Rohaan (@rohaan).</description>
    <link>https://dev.to/rohaan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4164233%2Fafa8ad27-e002-406f-9317-57f089b20148.png</url>
      <title>DEV Community: Rohaan</title>
      <link>https://dev.to/rohaan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rohaan"/>
    <language>en</language>
    <item>
      <title>Your AWS account has more admins than the IAM console shows</title>
      <dc:creator>Rohaan</dc:creator>
      <pubDate>Sat, 10 Oct 2026 20:18:18 +0000</pubDate>
      <link>https://dev.to/rohaan/your-aws-account-has-more-admins-than-the-iam-console-shows-174a</link>
      <guid>https://dev.to/rohaan/your-aws-account-has-more-admins-than-the-iam-console-shows-174a</guid>
      <description>&lt;p&gt;Open IAM, filter by the &lt;code&gt;AdministratorAccess&lt;/code&gt; policy, and you get a list. Three&lt;br&gt;
users, maybe a role or two. That is the list that gets reviewed in access&lt;br&gt;
reviews, pasted into compliance evidence, and shown to auditors.&lt;/p&gt;

&lt;p&gt;It is almost always wrong, and not by a little.&lt;/p&gt;

&lt;p&gt;Admin is not a policy you have. It is a state you can reach. Plenty of&lt;br&gt;
principals that are nowhere near that filtered list can reach it in one API&lt;br&gt;
call.&lt;/p&gt;
&lt;h2&gt;
  
  
  Permissions that grant permissions
&lt;/h2&gt;

&lt;p&gt;Most policy review looks for wide permissions: &lt;code&gt;s3:*&lt;/code&gt;, &lt;code&gt;ec2:*&lt;/code&gt;, a &lt;code&gt;Resource&lt;/code&gt; of&lt;br&gt;
&lt;code&gt;*&lt;/code&gt;. Those are worth finding. But there is a second category that gets almost no&lt;br&gt;
attention, and it is the one that matters more: permissions that let you change&lt;br&gt;
permissions.&lt;/p&gt;

&lt;p&gt;Here are the ones I keep running into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam:CreatePolicyVersion&lt;/code&gt;.&lt;/strong&gt; If you can create a new version of a policy and&lt;br&gt;
set it as the default, you can rewrite any policy you are attached to. Your&lt;br&gt;
tightly scoped policy becomes &lt;code&gt;*&lt;/code&gt; on &lt;code&gt;*&lt;/code&gt; in a single call. The policy document&lt;br&gt;
that was reviewed last quarter is not the policy document in effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam:AttachUserPolicy&lt;/code&gt;, &lt;code&gt;iam:AttachRolePolicy&lt;/code&gt;, &lt;code&gt;iam:AttachGroupPolicy&lt;/code&gt;.&lt;/strong&gt;&lt;br&gt;
The direct version. If you can attach a policy, you can attach&lt;br&gt;
&lt;code&gt;AdministratorAccess&lt;/code&gt;. To yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam:PutUserPolicy&lt;/code&gt; and &lt;code&gt;iam:PutRolePolicy&lt;/code&gt;.&lt;/strong&gt; The same thing with inline&lt;br&gt;
policies, and these are worse because inline policies do not show up where&lt;br&gt;
people look for attached managed policies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam:PassRole&lt;/code&gt; combined with a compute service.&lt;/strong&gt; This one is the most common&lt;br&gt;
and the least understood. &lt;code&gt;PassRole&lt;/code&gt; on its own does nothing. Combined with&lt;br&gt;
&lt;code&gt;lambda:CreateFunction&lt;/code&gt; and &lt;code&gt;lambda:InvokeFunction&lt;/code&gt;, or with&lt;br&gt;
&lt;code&gt;ec2:RunInstances&lt;/code&gt;, it means you can start something that runs as a role more&lt;br&gt;
privileged than you, and then use it. The permission you needed was not admin.&lt;br&gt;
It was the ability to hand a role to a service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam:UpdateAssumeRolePolicy&lt;/code&gt;.&lt;/strong&gt; You cannot assume that role today. You can&lt;br&gt;
edit its trust policy to say that you can, and then you can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam:CreateAccessKey&lt;/code&gt; on another user.&lt;/strong&gt; Mint a key for someone more&lt;br&gt;
privileged and use it. No exploit, no escalation, just an API call that is&lt;br&gt;
working as designed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam:CreateLoginProfile&lt;/code&gt; and &lt;code&gt;iam:UpdateLoginProfile&lt;/code&gt;.&lt;/strong&gt; Set a console&lt;br&gt;
password on a user who does not have one, or change one that does.&lt;/p&gt;

&lt;p&gt;None of these say admin. Every one of them is a path to it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why linters miss this
&lt;/h2&gt;

&lt;p&gt;Policy linters are built to answer "is this permission too broad". They flag&lt;br&gt;
wildcards, they flag &lt;code&gt;Resource: *&lt;/code&gt;, they score things against least privilege.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;iam:CreatePolicyVersion&lt;/code&gt; on one specific policy ARN is not broad. It is narrow,&lt;br&gt;
specific, and looks like exactly what a well-scoped policy should look like. It&lt;br&gt;
will pass every check you run and it is a full escalation to whatever that&lt;br&gt;
policy can be rewritten to grant.&lt;/p&gt;

&lt;p&gt;The question a linter asks is about the shape of a permission. The question that&lt;br&gt;
matters is about what the permission leads to. Those are different questions and&lt;br&gt;
the second one is a graph problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  Going and looking
&lt;/h2&gt;

&lt;p&gt;Start by listing what actually exists rather than what you remember granting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# every inline policy on every role, where the escalation paths usually hide&lt;/span&gt;
aws iam list-roles &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Roles[].RoleName'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; r&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
      &lt;/span&gt;aws iam list-role-policies &lt;span class="nt"&gt;--role-name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"PolicyNames[] | [?length(@)&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="se"&gt;\&lt;/span&gt;
        | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then search your policy documents for the specific actions above. Not for&lt;br&gt;
wildcards. For &lt;code&gt;iam:PassRole&lt;/code&gt;, &lt;code&gt;iam:CreatePolicyVersion&lt;/code&gt;, &lt;code&gt;iam:AttachRolePolicy&lt;/code&gt;&lt;br&gt;
and the rest, by name. Anything that holds one of them is effectively an admin&lt;br&gt;
with extra steps, and should be reviewed as an admin.&lt;/p&gt;

&lt;p&gt;Two things that make this harder than it sounds. Inline policies are invisible&lt;br&gt;
in most tooling and most dashboards, so they are where this accumulates.&lt;br&gt;
And &lt;code&gt;iam:PassRole&lt;/code&gt; is so routine in CI setups that it is usually granted without&lt;br&gt;
anyone asking which roles can be passed, which is the only part that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that is actually useful
&lt;/h2&gt;

&lt;p&gt;Stop counting principals with &lt;code&gt;AdministratorAccess&lt;/code&gt;. Start counting principals&lt;br&gt;
that can become an administrator, in any number of steps, using only calls they&lt;br&gt;
are already allowed to make.&lt;/p&gt;

&lt;p&gt;That number is bigger. On every account I have looked at, it has been bigger.&lt;br&gt;
And unlike the first number, it is the one that describes your actual blast&lt;br&gt;
radius.&lt;/p&gt;

&lt;p&gt;If you want to see the trust half of the same picture, who can get into the&lt;br&gt;
account from outside rather than what they can become once in, I maintain a&lt;br&gt;
read-only CLI for it called &lt;a href="https://www.secorvia.com/docs/frontdoor/" rel="noopener noreferrer"&gt;frontdoor&lt;/a&gt;.&lt;br&gt;
It is Apache 2.0, needs no account, and covers AWS, GCP and Azure.&lt;/p&gt;




&lt;p&gt;If you review IAM differently, particularly if you have found a clean way to&lt;br&gt;
handle &lt;code&gt;PassRole&lt;/code&gt; scoping in CI, I would like to hear it.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>In the cloud, lateral movement is an API call, not an exploit</title>
      <dc:creator>Rohaan</dc:creator>
      <pubDate>Wed, 07 Oct 2026 23:35:02 +0000</pubDate>
      <link>https://dev.to/rohaan/in-the-cloud-lateral-movement-is-an-api-call-not-an-exploit-4j6c</link>
      <guid>https://dev.to/rohaan/in-the-cloud-lateral-movement-is-an-api-call-not-an-exploit-4j6c</guid>
      <description>&lt;p&gt;Most of us learned attack paths on a network. Someone lands on a box, escalates&lt;br&gt;
locally, then pivots sideways to the next box. Exploit, escalate, pivot, repeat.&lt;br&gt;
It is a good model and it shaped a generation of tooling.&lt;/p&gt;

&lt;p&gt;In a cloud account it is mostly the wrong model, and the tooling that inherited&lt;br&gt;
it keeps looking in the wrong place.&lt;/p&gt;

&lt;p&gt;The pivot in cloud is usually not an exploit. It is an API call that the&lt;br&gt;
attacker is allowed to make.&lt;/p&gt;
&lt;h2&gt;
  
  
  A chain that uses exactly one vulnerability
&lt;/h2&gt;

&lt;p&gt;Here is the shape of it, with the only memorable CVE at the very start:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An app has an SSRF bug. Not even a good one. It fetches a URL you give it.&lt;/li&gt;
&lt;li&gt;You point it at the instance metadata service and read the credentials of the
role attached to the instance.&lt;/li&gt;
&lt;li&gt;Those credentials let you call &lt;code&gt;sts:AssumeRole&lt;/code&gt; on a role in the production
account, because somebody wired up cross account access a year ago and the
trust policy was never narrowed.&lt;/li&gt;
&lt;li&gt;That production role can read the bucket holding database backups.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Count the exploits. One, at step one, and it is a web bug rather than anything&lt;br&gt;
exotic. Steps two, three and four are the platform working exactly as&lt;br&gt;
configured. There is nothing to patch at steps two, three or four, which is why&lt;br&gt;
a vulnerability scanner has nothing to say about them.&lt;/p&gt;

&lt;p&gt;Now sort your findings by CVSS and see where that SSRF lands. It is a medium.&lt;br&gt;
Above it are a dozen criticals on hosts nothing can reach.&lt;/p&gt;
&lt;h2&gt;
  
  
  The edge that matters is the trust policy
&lt;/h2&gt;

&lt;p&gt;An IAM role has two halves and people pay attention to the wrong one.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;permission policy&lt;/strong&gt; says what the role can do. This is the half everyone&lt;br&gt;
reviews, the half that linters check, the half that shows up in least privilege&lt;br&gt;
talks.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;trust policy&lt;/strong&gt; says who is allowed to become the role. This is the half&lt;br&gt;
that creates the edge in the graph. A permission policy with &lt;code&gt;s3:*&lt;/code&gt; on a role&lt;br&gt;
nobody can assume is a non event. A tightly scoped permission policy on a role&lt;br&gt;
that anything in the account can assume is a path.&lt;/p&gt;

&lt;p&gt;So when you go looking, read trust policies first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam list-roles &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Roles[].{name:RoleName,trust:AssumeRolePolicyDocument}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things worth stopping on:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A principal that is a whole account.&lt;/strong&gt; &lt;code&gt;"AWS": "arn:aws:iam::111122223333:root"&lt;/code&gt;&lt;br&gt;
does not mean the root user. It means every principal in that account that also&lt;br&gt;
has permission to assume it. If 111122223333 is a sandbox where a lot of people&lt;br&gt;
have &lt;code&gt;sts:*&lt;/code&gt;, you have handed the sandbox a key to here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A missing external ID on a third party role.&lt;/strong&gt; If you gave a SaaS vendor a role&lt;br&gt;
and the trust policy names their account without an &lt;code&gt;sts:ExternalId&lt;/code&gt; condition,&lt;br&gt;
then any other customer of that vendor can potentially ask the vendor's platform&lt;br&gt;
to assume into your account. This is the confused deputy problem and it is still&lt;br&gt;
everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Federated trust with loose conditions.&lt;/strong&gt; OIDC and SAML trust is where this gets&lt;br&gt;
genuinely hard to eyeball. A GitHub Actions OIDC trust with &lt;code&gt;sub&lt;/code&gt; left as a&lt;br&gt;
wildcard means any repository in your organisation, or in some misconfigurations&lt;br&gt;
any repository anywhere, can mint credentials for that role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chains.&lt;/strong&gt; Role A can assume role B, role B can assume role C. Each hop looks&lt;br&gt;
reasonable in isolation. Nobody drew the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is a graph problem and it needs graph tools
&lt;/h2&gt;

&lt;p&gt;You cannot find chains by reading policies one at a time. Three hops is already&lt;br&gt;
beyond what anyone holds in their head, and real accounts have more.&lt;/p&gt;

&lt;p&gt;What you want is every principal as a node, every trust relationship as an edge,&lt;br&gt;
and then a walk from anything externally reachable to anything you would be sad&lt;br&gt;
to lose. The complication is that the edges have preconditions: you only take the&lt;br&gt;
IAM edge if you first got code execution on the thing holding the role, which is&lt;br&gt;
why this is not a plain shortest path.&lt;/p&gt;

&lt;p&gt;I ended up writing a tool for the trust half of it, because reading&lt;br&gt;
&lt;code&gt;AssumeRolePolicyDocument&lt;/code&gt; by hand across three providers got old. It is called&lt;br&gt;
&lt;a href="https://www.secorvia.com/docs/frontdoor/" rel="noopener noreferrer"&gt;frontdoor&lt;/a&gt;, it is Apache 2.0, it maps&lt;br&gt;
federated and cross account trust into AWS, GCP and Azure, and it is read only&lt;br&gt;
and needs no account. One binary. Point it at your credentials and it prints who&lt;br&gt;
can walk in from outside.&lt;/p&gt;

&lt;p&gt;That covers the trust graph. Joining it to reachability and to your&lt;br&gt;
vulnerability data is the harder half, and that is the product I work on rather&lt;br&gt;
than the free tool, so take that part as disclosure rather than advice: build it&lt;br&gt;
yourself if you have the time, because you will understand your own account&lt;br&gt;
better than any tool will explain it to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing I would tell my earlier self
&lt;/h2&gt;

&lt;p&gt;Stop asking "what is the highest severity finding". Start asking "what can an&lt;br&gt;
attacker reach, and what can they do once they are there".&lt;/p&gt;

&lt;p&gt;The second question has a much shorter answer, and it is the one you can actually&lt;br&gt;
finish.&lt;/p&gt;




&lt;p&gt;If you handle federated trust differently, particularly OIDC conditions across&lt;br&gt;
multiple clouds, I would like to hear it. That is the part I am least confident&lt;br&gt;
about.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Your cloud security tool is sorting by the wrong number</title>
      <dc:creator>Rohaan</dc:creator>
      <pubDate>Mon, 05 Oct 2026 15:43:12 +0000</pubDate>
      <link>https://dev.to/rohaan/your-cloud-security-tool-is-sorting-by-the-wrong-number-npm</link>
      <guid>https://dev.to/rohaan/your-cloud-security-tool-is-sorting-by-the-wrong-number-npm</guid>
      <description>&lt;p&gt;Every cloud security tool I have used opens on the same screen: a list of findings, sorted by severity, with the criticals at the top in red. It feels like a priority queue. It is not one.&lt;/p&gt;

&lt;p&gt;Here is the problem with it, using two findings I had in front of me last month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding A.&lt;/strong&gt; A critical CVE, CVSS 9.8, remote code execution, on a container image. The image runs in a private subnet. No ingress, no public load balancer, no NAT gateway on the route table. The task role can read one S3 bucket of build artifacts. Nothing else in the account can reach it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding B.&lt;/strong&gt; A medium misconfiguration, CVSS 5.3. An EC2 instance with IMDSv1 still enabled. The instance sits behind an ALB that is open to the internet and runs an app with a path traversal bug nobody had got round to fixing. Its instance profile can assume a role in the production account, and that role can read the database credentials.&lt;/p&gt;

&lt;p&gt;Sorted by severity, A is at the top and B is twelve screens down. Sorted by what an attacker can actually do, B is the only one that matters this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Severity is a property of the vulnerability, not of your environment
&lt;/h2&gt;

&lt;p&gt;This is the whole issue in one line. CVSS scores a vulnerability in the&lt;br&gt;
abstract. It has no idea whether the affected host is reachable, what the compromised identity can do next, or whether the blast radius is one build artifact or your customer database.&lt;/p&gt;

&lt;p&gt;That is not a criticism of CVSS. It was never meant to be a work queue. The specification says so. We turned it into one because it was the only number every tool agreed on, and because sorting by it is easy to implement.&lt;/p&gt;

&lt;p&gt;EPSS helps, because it estimates the probability a vulnerability gets exploited in the wild in the next thirty days. KEV helps more, because it is a list of things that definitely are being exploited right now. Both are still properties of the vulnerability. Neither one knows anything about your network.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually makes something urgent
&lt;/h2&gt;

&lt;p&gt;Three things, and none of them are in the CVE record:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reachability.&lt;/strong&gt; Can anything an attacker controls get to this asset? That means route tables, security groups, NACLs, load balancers, peering, and whatever your service mesh is doing. In practice most critical findings in a well segmented account are not reachable from anywhere an attacker starts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identity.&lt;/strong&gt; If this asset is compromised, what can the attacker do next? An instance profile that can call &lt;code&gt;sts:AssumeRole&lt;/code&gt; into another account is worth more to an attacker than a dozen RCEs on isolated hosts. Over permissioned roles are the real lateral movement path in cloud, not kernel exploits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blast radius.&lt;/strong&gt; What is on the other side of that movement? There is a&lt;br&gt;
difference between reaching a staging bucket and reaching the production&lt;br&gt;
database, and no severity score captures it.&lt;/p&gt;

&lt;p&gt;Put those three together and findings stop being a list. They become a graph, and the question changes from "what is the highest score" to "which of these chain together into a route to something that matters".&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it yourself
&lt;/h2&gt;

&lt;p&gt;You can get a surprising distance with the APIs you already have. Roughly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull your inventory. &lt;code&gt;ec2:DescribeInstances&lt;/code&gt;, &lt;code&gt;ecs:ListTasks&lt;/code&gt;,
&lt;code&gt;rds:DescribeDBInstances&lt;/code&gt;, the equivalents on Azure and GCP.&lt;/li&gt;
&lt;li&gt;Pull network configuration. Route tables, security groups, NACLs, load balancer listeners and target groups. Build the reachability edges from that rather than from tags, because tags lie.&lt;/li&gt;
&lt;li&gt;Pull IAM. Roles, attached and inline policies, trust policies, instance profiles. The trust policy is the edge you care about, because it is what makes cross account movement possible.&lt;/li&gt;
&lt;li&gt;Pull your vulnerability data from wherever it already lives, and join it onto the inventory by instance id or image digest.&lt;/li&gt;
&lt;li&gt;Mark your crown jewels by hand. Nothing automatic gets this right, and it is a short list.&lt;/li&gt;
&lt;li&gt;Walk the graph from every internet reachable node and see which crown jewels you can reach, and what you had to exploit on the way.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 6 is where it gets interesting and where it gets slow. The graph is not large by graph standards, but the path search is not a simple shortest path, because each edge has a precondition: you only traverse the IAM edge if you first got code execution on the node that holds the role.&lt;/p&gt;

&lt;p&gt;I built this twice by hand before deciding it was a product. If you want to see what it looks like when it is finished, that is what we do at&lt;br&gt;
&lt;a href="https://www.secorvia.com" rel="noopener noreferrer"&gt;Secorvia&lt;/a&gt;: a live graph across AWS, Azure, Google Cloud and DigitalOcean, with findings ranked by exploitability and blast radius instead of by CVSS. The scanning is read only, and the free tier does not expire. Build it yourself if you have the time, though. You will understand your own environment far better than any tool will explain it to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing I got wrong for a long time
&lt;/h2&gt;

&lt;p&gt;I used to think the goal was to get the critical count to zero. It is not, and chasing it actively hurts, because you spend your week on unreachable criticals and the reachable mediums stay open.&lt;/p&gt;

&lt;p&gt;A better target: zero findings that sit on a path from the internet to anything you would be sad to lose. That number is usually small enough to actually fix, which is the entire point.&lt;/p&gt;




&lt;p&gt;If you do this differently, I would genuinely like to hear it, particularly how you handle reachability through service meshes. That is the part I am least happy with.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>aws</category>
      <category>devops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
