An attacker who reaches a cloud account rarely needs an exploit. They need a credential, a role that trusts it, and a few minutes with the provider's own command-line tools. The API will then do the rest: list the storage, read the secrets, assume whatever roles the credential is permitted to assume, and start whatever compute it likes. There is no memory corruption and no malware. There are only identities, the permissions attached to them, and the chains that link one identity to the next.
That is why identity and access management is where most cloud breaches are decided. The perimeter of a cloud estate is not a firewall; it is the sum of every principal that can call the API and everything each one is allowed to do. When we run a cloud red team, we spend most of the engagement reading policies, not scanning ports.
This article is for the people who own that policy surface: platform and security engineers, the CTO who signed off on the migration, and anyone who has inherited an account they did not build. We walk through the misconfigurations that turn a single credential into full compromise, the mechanism behind each one, the concrete defence, and how we test for it.
Over-privileged roles are the default
Nobody sets out to grant administrator rights to a log-shipping function. It happens because the function needed one more permission late on a Friday, the wildcard worked, and nobody narrowed it afterwards. Multiply that by every service, pipeline and developer, and after two years the account's effective permissions bear no relation to the architecture diagram.
The mechanism is accumulation. Permissions are added and almost never removed. Broad managed policies get attached because they are convenient. Inline policies are copied from an answer that was itself too wide. A role created for a proof of concept is reused in production because it already works.
Why it matters: when an attacker compromises any one principal, its permissions define their starting position. If that principal can read every storage bucket, every bucket is now readable. If it can call the identity service's full action set, the engagement is over in the first hour.
The defence is unglamorous. Generate least-privilege policies from actual usage — the providers expose access-analyser and last-accessed data precisely for this — review them on a schedule, and treat a wildcard in the action or resource field as a finding that needs written justification. Permission boundaries and organisation-level service control policies set a ceiling on what any role can ever be granted, so a single mistaken policy cannot escalate past it.
How we test: we enumerate every principal and attached policy, resolve the effective permissions, and compare what each role actually does, from the logs, with what it may do, from the policy. The gap between the two is the attack surface.
Long-lived keys are liabilities
A static access key is a password that never expires, is used by machines, and ends up in places humans forget. We find them in commit history, in build logs, in images pulled from public registries, in infrastructure-state files left in a bucket, in chat messages, and in the environment of virtual machines nobody has logged into for a year.
The failure mode is not some elaborate theft. It is that a key generated for a one-off task lived on and was eventually exposed by accident. The identity it belongs to usually carries far more permission than the task needed, because it was created in a hurry and dropped into an existing group.
The defence is to stop issuing long-lived keys wherever a short-lived alternative exists. Workloads should obtain temporary credentials from the environment rather than carrying a stored secret: instance and workload identity on the provider side, federated identity for external systems. Where a static key is unavoidable, scope it to one task, rotate it automatically, and monitor it for use from unexpected locations. A secret scanner on every repository and image build catches the accidents before an outsider does.
How we test: we scan the artefacts a real attacker would reach first — public repositories, old images, exposed state files — and, for any key we are given, we check how old it is, how broad it is, and whether its use would stand out in the logs at all.
The attack chain, from one key to the whole account
It helps to see the steps, because each is a place to break the chain. A credential is exposed in a public artefact. The attacker confirms who it belongs to, then lists the permissions. Those let it read configuration and secrets, which yield more credentials. One of the roles it can now assume trusts it more broadly, so it assumes that role and enumerates again from a stronger position. A few hops later it reaches a role with identity-administration rights, creates its own persistent principal, and no longer depends on the original key.
Every arrow in that chain is optional. The key did not have to be long-lived, the identity did not have to be over-privileged, the trust relationship did not have to be so loose, and monitoring could have fired on the first out-of-pattern call. Defence in depth means no single mistake carries the attacker all the way to the end.
Metadata-service abuse
Every cloud instance can ask a local metadata endpoint who it is, and that endpoint will hand back the instance's temporary credentials. This is a convenience that becomes a weapon the moment an application on the instance can be made to fetch a URL of the attacker's choosing. A server-side request forgery in a web app, pointed at the metadata address, returns the instance role's credentials to the attacker — and that role is often far more privileged than the web app needed.
The older metadata protocol answered any process that asked, with a simple unauthenticated request. The hardened version requires the caller to first obtain a short-lived token with a specific header, which blocks the naive forgery. Enforce the hardened version account-wide, set the response hop limit to one so a container cannot reach the host's endpoint, and give instance roles the minimum they need, so that even a successful theft yields little.
How we test: wherever an application fetches URLs, we try to point it at the metadata endpoint, and we check whether the account still permits the unauthenticated protocol anywhere.
Cross-account trust policy mistakes
Assuming a role in another account is normal and useful. The danger is in the trust policy that says who may assume it. One that names a whole external account, rather than a specific principal in it, trusts every identity that account ever creates. One with a wildcard principal, perhaps added during debugging, trusts the entire internet.
A subtler class involves the external identifier, the shared value a third party presents when assuming your role. Without it, any customer of the same service provider can be tricked into assuming your role on an attacker's behalf — the confused-deputy problem. We check that every cross-account trust names a specific principal, carries a condition tightening it to the intended source, and uses an external identifier wherever a third party is involved.
Role chaining and the pass-role class
The permission to assume a role, and the permission to give a role to a service, are both quiet escalation paths. If a principal may pass a high-privilege role to a compute service and then run a workload under it, the principal has effectively acquired that role's permissions without ever being granted them directly. The permission that makes this work governs attaching a role to a resource; a principal that holds it, together with the ability to launch the resource, can escalate to any role it is allowed to pass.
Role chaining is the same idea across trust relationships: principal A assumes role B, which may assume role C, which can administer identities. No single link looks alarming, but the path from A to C is a full escalation, and it is invisible in a per-policy review — it appears only when you resolve the whole graph.
The defence is to constrain the pass-role permission to specific, named roles rather than a wildcard, and to audit the assume-role graph for paths that end at an administrative identity. We map that graph on every engagement and report the shortest path to an administrative role.
Service accounts, managed identities, and the equivalents everywhere
The vocabulary differs by provider but the risks rhyme. On one platform the unit is a service account, which can be impersonated, can mint its own access tokens, and can be bound to a workload; the dangerous permissions are the ones that let a principal act as a service account or create a key for it, because both hand over that account's identity. On another the unit is a managed identity attached to a resource, where the escalation lever is the permission to assign roles, which lets a principal grant itself more.
In every case the pattern is the same: a workload identity with more permission than the workload needs, plus a permission that lets some principal borrow or extend it. We enumerate these per provider, because the escalation primitives are provider-specific even though the shape of the mistake is not.
CI/CD runner identities
A build pipeline runs arbitrary code, often from a pull request, with a credential that can deploy to production. That makes the runner's identity one of the most valuable targets in the estate, and one of the least watched. A pipeline that holds a broad deployment role, triggers on external contributions, and prints its environment into a log is a direct route from an outside contributor to production.
The defence is to give the pipeline a short-lived, federated identity rather than a stored key, scope it to exactly the resources the deployment touches, and separate the credentials available to untrusted pull-request builds from those for trusted main-branch deployments. Secrets should be injected at the step that needs them, never exported into the whole job's environment. We cover this and the surrounding build-system risks in Securing Your Software Supply Chain.
Public by accident
Storage made public, a snapshot shared with everyone, a database exposed to the open internet, a key-management policy that trusts a wildcard principal: these are the misconfigurations that need no credential at all. They are often created in a moment of debugging and never reverted, and they are the first thing any opportunistic scanner on the internet will find.
The mechanism is that cloud resources default to private, but it is easy to open them and hard to notice they stayed open. A resource policy with a public principal, a storage grant to all users, or a network rule allowing every source turns a private asset into a public one with no further warning.
The defence is preventive controls that make accidental exposure impossible rather than merely detectable: account-level public-access blocks on storage, organisation policies that forbid public resource grants, and network defaults that deny inbound access, with continuous checks that flag any newly public resource within minutes. We enumerate public exposure from the outside, as an unauthenticated attacker would, before we use any credential at all.
Detection: assume the first mistake will be made
Prevention fails eventually, so the account must be instrumented to notice an attacker working through the chain above. The management-event log is the spine of this: it records every API call, who made it, from where, and whether it succeeded. The signals worth alerting on are behavioural. A principal that suddenly enumerates identities and policies. A role assumed from a region or address it has never used. A first-ever call to the identity-administration actions. A spike in access-denied errors, which is what enumeration looks like before the attacker finds the one that works.
The common failure is that the logs exist but nobody reads them, and the alerting is tuned to resource metrics rather than identity behaviour. Make sure the log cannot be disabled or deleted by the identities it watches — keep it append-only in a separate account — because turning off logging is one of the first things a capable attacker does.
How we test for this
A cloud IAM assessment from us starts cold, with the access a realistic attacker would have: often a single low-privileged credential, sometimes only the account's public footprint. We enumerate principals, policies and trust relationships, then resolve them into an effective-permission graph and an assume-role graph. Against those graphs we look for the escalation classes above — the pass-role lever, the loose trust policy, the over-privileged workload identity, the exposed metadata path — and we chain them into the shortest route to account administration.
For anything we rate high or critical we demonstrate it, carrying out the escalation in a scoped, non-destructive way so there is no argument about whether it is real. We finish by replaying the chain against your logging to answer the question that matters most: would you have seen us? The deliverable is the graph, the proven paths, the policy changes that cut them, and the detections that would have fired.
Next steps
Start with the three questions that find the most, fastest. Which identities can reach administrative permissions, directly or through a chain? Which credentials are long-lived, and where might they already be exposed? What is public right now that should not be? Answer those and you have closed the routes behind most real cloud breaches.
If you want us to map it properly — the full permission graph, the proven escalation paths, and the detections to match — our scope and process are on the services page, and you can reach the team on Telegram.