# Cloud Control Plane Reasoning

> Cloud Control-Plane Reasoning

- Skill: `annatchijova/cloud-control-plane-reasoning` (Agent Skill)
- Install (CLI): `npx skillmds@latest add annatchijova/cloud-control-plane-reasoning`
- Raw SKILL.md: https://api.skillmd.com/api/skills/annatchijova/cloud-control-plane-reasoning/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: annatchijova (https://skillmd.com/u/annatchijova)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/annatchijova/cloud-control-plane-reasoning

---


# Cloud Control-Plane Reasoning

On-prem intuition fails in cloud for one structural reason: **the control plane is an
API.** Deleting every snapshot, minting a new admin, exfiltrating a database via a
signed URL, backdooring the identity system — none of it touches the network you drew.
It is an authenticated call to `*.amazonaws.com` / `management.azure.com` /
`googleapis.com`, reachable from anywhere, authorized by a credential. The firewall
that guards the *data plane* (the running workload, the disk, the packets) does not
sit in front of the *control plane*, and the thing an attacker actually wants is the
control plane. Model the network and you have modeled the part that matters least.

The reframes this skill forces, each of which flips a severity most reviewers get
wrong:

- **An SSRF in cloud is usually credential theft, not information disclosure** — if it
  can reach the instance metadata endpoint, it reads the instance-profile credentials
  and becomes whatever that role is. Severity is set by the role, not by the SSRF.
- **Privilege escalation is an API call, not an exploit** — no memory corruption
  needed; `iam:CreatePolicyVersion`, `iam:PassRole` to a fatter service, updating a
  running Lambda's role, is escalation by design of the IAM system.
- **Persistence moved off disk and into identity** — hosts are cattle; a re-image
  removes nothing. The backdoor is an extra access key on a user, a trust-policy edit,
  a new role, a Lambda triggered on an event. (`forensic-persistence` in the cloud key.)

Composes with the library:

- **assume-breach-modeling** — this skill supplies the cloud-specific edges (assume-role chains, metadata pivots); that one runs the reachability graph
- **authorization-surface-mapping** — IAM *is* the actor×resource×action matrix; resolve "what can this identity do" there, don't eyeball the role name
- **credential-material-triage** — a captured cloud key's reach is its policy set plus everything it can assume transitively
- **forensic-logging-design** — control-plane logs (CloudTrail/Activity/Audit) and data-plane logs (S3 data events, VPC flow) are different feeds; you can be blind to one while watching the other
- **forensic-persistence** — cloud persistence lives in IAM and event triggers, not files
- **secret-lifecycle-discipline** — the instance profile / workload identity is a rotating secret whose scope is the real question

---

## Step 1 — Separate control plane from data plane, and find the real target

For the resource in question, split every action into the plane it touches:

- **Data plane** — operating on the contents: reading rows, encrypting a disk, serving
  requests, packets between pods. Guarded by network, security groups, app auth.
- **Control plane** — operating on the resource *as a cloud object* via API: snapshot
  it, change its role, make its bucket public, delete it, replicate it elsewhere,
  read it through a management API that ignores the app.

The attacker's objective is almost always control-plane: it is quieter (looks like
administration — see `dual-use-behavior-adjudication`), more complete (copy the whole
DB via snapshot, no row-by-row exfil), and outside the network controls you built. If
your threat model only defends the data plane, name that gap as the finding.

---

## Step 2 — Identity is the perimeter: draw the trust graph, not the VPC

The boundary in cloud is not the subnet; it is *who can assume what*. Build the graph
whose nodes are principals (users, roles, service accounts, workload identities,
managed identities) and whose edges are:

- **AssumeRole / trust policies** — role A trusts principal B; follow every edge,
  including transitive ones. The reachable set of `*:*` from a starting identity is the
  real blast radius.
- **PassRole** — the quiet escalator: a principal that can `iam:PassRole` a
  higher-privileged role to a service it controls (Lambda, EC2, Glue) *becomes* that
  role's authority without ever holding its credentials.
- **Resource-based policies** — an S3 bucket policy, a KMS key policy, an SQS policy
  granting *another account* access. This is how the boundary you think is the account
  edge is silently a doorway (confused deputy; check the `external ID` on third-party
  roles).
- **Federation / SSO / OIDC** — the identity provider is upstream of every role it can
  vend; compromising it, or a CI provider's OIDC trust, mints cloud identity directly.

The shortest path from any foothold identity to an administrative action is the
finding. It is usually two or three edges and never appears on a network diagram.

---

## Step 3 — The metadata pivot and the foothold-to-credential jump

Whenever a workload can be induced to make an outbound request the attacker chooses
(SSRF, a fetch of attacker-controlled URL, a misconfigured proxy), test the
control-plane consequence, not just the data one:

- **Instance metadata** — IMDSv1 (or IMDSv2 not enforced) returns the instance
  profile's credentials to anything that can hit `169.254.169.254`. That turns a
  "medium SSRF" into "attacker is now this role." Azure IMDS / GCP metadata are the
  same shape.
- **Container / pod identity** — a compromised container inherits its service account /
  workload identity / IRSA role; the node's identity may be broader still. Container
  escape is often unnecessary — the pod's token already reaches the control plane.
- **CI/CD identity** — a build job's OIDC token or stored deploy credential is a
  control-plane identity with, frequently, production rights. The pipeline is inside
  the perimeter you forgot to draw.

For each foothold, ask: *what identity does code running here hold, and what does Step 2
say that identity reaches?* That is the true severity of the foothold.

---

## Step 4 — Escalation and persistence are configuration changes

Enumerate escalation as *permitted API calls*, not vulnerabilities:

- `iam:CreatePolicyVersion` / `AttachUserPolicy` / `PutUserPolicy` — grant yourself more.
- `PassRole` + a service you can launch — inherit a fatter role.
- `UpdateFunctionConfiguration` / update a running resource's role — ride an existing
  higher-privileged identity.
- `AssumeRole` into a role whose trust policy is too broad.

And persistence as *configuration additions* that survive re-imaging: a second access
key on a user, an added trust relationship, a new IAM user/role, a Lambda on a
CloudTrail/event trigger, a modified SSO mapping. Hunt these in the control-plane log,
not on the host — the host is ephemeral and clean.

---

## Step 5 — Know which plane your logging can see

Before claiming detection or doing IR, state which feed covers which plane, because the
classic cloud blind spot is watching one and missing the other:

- **Control-plane logs** — CloudTrail (management events), Azure Activity/Entra audit,
  GCP Admin Activity: every API call, the identity, the source. This is where Step 2–4
  live; without it, control-plane compromise is invisible.
- **Data-plane logs** — S3 data events, VPC flow logs, DNS logs, app logs: often *off
  by default*, high-volume, separately enabled. Exfil via signed URL or a public bucket
  can be entirely absent from control-plane logs.

A detection or IR claim must name the plane it covers. "We have CloudTrail" does not
mean "we would see the exfil"; it means "we would see the API calls that *set up* the
exfil." Route the design through `forensic-logging-design`; do not assert coverage you
have not confirmed per plane.

---

## The one-line test

If your cloud threat model is a network diagram, you have modeled the data plane and
missed the compromise. Redraw it as the identity/trust graph: nodes are principals,
edges are assume-role and pass-role, and the shortest path to `*:*` — usually two hops
and invisible on the VPC picture — is the finding.

