diff --git a/src/SUMMARY.md b/src/SUMMARY.md index df8bcfc7b..928d54295 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -260,6 +260,7 @@ - [AWS - CloudFront Post Exploitation](pentesting-cloud/aws-security/aws-post-exploitation/aws-cloudfront-post-exploitation/README.md) - [AWS - CodeBuild Post Exploitation](pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/README.md) - [AWS Codebuild - Token Leakage](pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-token-leakage.md) + - [AWS CodeBuild - Untrusted PR Webhook Bypass (CodeBreach-style)](pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-untrusted-pr-webhook-bypass.md) - [AWS - Control Tower Post Exploitation](pentesting-cloud/aws-security/aws-post-exploitation/aws-control-tower-post-exploitation/README.md) - [AWS - DLM Post Exploitation](pentesting-cloud/aws-security/aws-post-exploitation/aws-dlm-post-exploitation/README.md) - [AWS - DynamoDB Post Exploitation](pentesting-cloud/aws-security/aws-post-exploitation/aws-dynamodb-post-exploitation/README.md) diff --git a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/README.md b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/README.md index 880208649..465aba479 100644 --- a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/README.md +++ b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/README.md @@ -50,6 +50,14 @@ aws codebuild list-source-credentials aws-codebuild-token-leakage.md {{#endref}} +### Untrusted PR execution via webhook filter misconfiguration + +If webhook filters are weak, external attackers can get their PRs built in privileged CodeBuild projects and then execute arbitrary code in CI. + +{{#ref}} +aws-codebuild-untrusted-pr-webhook-bypass.md +{{#endref}} + ### `codebuild:DeleteProject` An attacker could delete an entire CodeBuild project, causing loss of project configuration and impacting applications relying on the project. @@ -85,4 +93,3 @@ aws codebuild delete-source-credentials --arn - diff --git a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-token-leakage.md b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-token-leakage.md index 3fae9a144..9641fa7f5 100644 --- a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-token-leakage.md +++ b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-token-leakage.md @@ -185,25 +185,15 @@ aws codebuild start-build --project-name > [!WARNING] > Now an attacker will be able to use the token from his machine, list all the privileges it has and (ab)use easier than using the CodeBuild service directly. -## Webhook filter ACTOR_ID regex allowlist bypass (PR-triggered privileged builds) +## Untrusted PR execution via webhook filter misconfiguration -Misconfigured CodeBuild GitHub webhooks that use unanchored `ACTOR_ID` regexes let *untrusted* PRs start privileged builds. If the allowlist is like `123456|7890123` without `^`/`$`, any ID containing one of those substrings matches. Because GitHub user IDs are sequential, an attacker can race to register an “eclipsing” ID (a superstring of a trusted ID) and trigger the build. +For the PR-triggered webhook bypass chain (`ACTOR_ACCOUNT_ID` regex + untrusted PR execution), check: -**Exploit path** - -1. Find public CodeBuild projects exposing webhook filters and extract an unanchored `ACTOR_ID` allowlist. -2. Obtain an eclipsing GitHub ID: - - Sample the global ID counter by creating/deleting GitHub orgs (org IDs share the pool). - - Pre-stage many GitHub App manifest creations and fire the confirmation URLs when the counter is within ~100 IDs of the target to burst-register a bot ID containing the trusted substring. -3. Open a PR from the eclipsing account; the regex matches the substring and the privileged build runs. -4. Use build RCE (e.g., dependency install hooks) to dump process memory handling the GitHub credential and recover the PAT/OAuth token. -5. With the token’s `repo` scope, invite your account as collaborator/admin and push/approve malicious commits or exfiltrate secrets. - -## References -- [Wiz: CodeBreach – AWS CodeBuild ACTOR_ID regex bypass and token theft](https://www.wiz.io/blog/wiz-research-codebreach-vulnerability-aws-codebuild) +{{#ref}} +aws-codebuild-untrusted-pr-webhook-bypass.md +{{#endref}} {{#include ../../../../banners/hacktricks-training.md}} - diff --git a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-untrusted-pr-webhook-bypass.md b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-untrusted-pr-webhook-bypass.md new file mode 100644 index 000000000..1b2c6fd30 --- /dev/null +++ b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-codebuild-post-exploitation/aws-codebuild-untrusted-pr-webhook-bypass.md @@ -0,0 +1,241 @@ +# AWS CodeBuild - Untrusted PR Webhook Bypass (CodeBreach-style) + +{{#include ../../../../banners/hacktricks-training.md}} + +This attack vector appears when a **public-facing PR workflow** is wired to a **privileged CodeBuild project** with weak webhook controls. + +If an external attacker can make CodeBuild execute their pull request, they can usually get **arbitrary code execution inside the build** (build scripts, dependency hooks, test scripts, etc.), and then pivot to secrets, IAM credentials, or source-provider credentials. + +## Why this is dangerous + +CodeBuild webhook filters are evaluated with regex patterns (for non-`EVENT` filters). In the `ACTOR_ACCOUNT_ID` filter, this means a weak pattern can match more users than intended. +If untrusted PRs are built in a project that has privileged AWS role permissions or GitHub credentials, this can become a full supply-chain compromise. + +Wiz showed a practical chain where: + +1. A webhook actor allowlist used an **unanchored regex**. +2. An attacker registered a GitHub ID that matched as a **superstring** of a trusted ID. +3. A malicious PR triggered CodeBuild. +4. Build code execution was used to dump memory and recover source-provider credentials/tokens. + +## Misconfigurations that allow external PR code execution + +The following are high-risk mistakes and how attackers abuse each one: + +1. **`EVENT` filters allow untrusted triggers** + - Common risky events: `PULL_REQUEST_CREATED`, `PULL_REQUEST_UPDATED`, `PULL_REQUEST_REOPENED`. + - Other events that can also become dangerous if tied to privileged builds: `PUSH`, `PULL_REQUEST_CLOSED`, `PULL_REQUEST_MERGED`, `RELEASED`, `PRERELEASED`, `WORKFLOW_JOB_QUEUED`. + - Bad: `EVENT="PUSH, PULL_REQUEST_CREATED, PULL_REQUEST_UPDATED"` in a privileged project. + - Better: use PR comment approval and minimize trigger events for privileged projects. + - Abuse: attacker opens/updates PR or pushes to a branch they control, and their code executes in CodeBuild. + +2. **`ACTOR_ACCOUNT_ID` regex is weak** + - Bad: unanchored patterns like `123456|7890123`. + - Better: exact-match anchoring `^(123456|7890123)$`. + - Abuse: regex over-match allows unauthorized GitHub IDs to pass allowlists. + +3. **Other regex filters are weak or missing** + - `HEAD_REF` + - Bad: `refs/heads/.*` + - Better: `^refs/heads/main$` (or an explicit trusted list) + - `BASE_REF` + - Bad: `.*` + - Better: `^refs/heads/main$` + - `FILE_PATH` + - Bad: no path restrictions + - Better: exclude risky files like `^buildspec\\.yml$`, `^\\.github/workflows/.*`, `(^|/)package(-lock)?\\.json$` + - `COMMIT_MESSAGE` + - Bad: trust marker with loose match like `trusted` + - Better: do not use commit message as a trust boundary for PR execution + - `REPOSITORY_NAME` / `ORGANIZATION_NAME` + - Bad: `.*` in org/global webhooks + - Better: exact repo/org matches only + - `WORKFLOW_NAME` + - Bad: `.*` + - Better: exact workflow name matches only (or avoid this as trust control) + - Abuse: attacker crafts ref/path/message/repo context to satisfy permissive regex and trigger builds. + +4. **`excludeMatchedPattern` is misused** + - Setting this flag incorrectly can invert intended logic. + - Bad: `FILE_PATH '^buildspec\\.yml$'` with `excludeMatchedPattern=false` when intent was to block buildspec edits. + - Better: same pattern with `excludeMatchedPattern=true` to deny builds touching `buildspec.yml`. + - Abuse: defenders think they deny risky events/paths/actors, but actually allow them. + +5. **Multiple `filterGroups` create accidental bypasses** + - CodeBuild evaluates groups as OR (one passing group is enough). + - Bad: one strict group + one permissive fallback group (e.g., only `EVENT=PULL_REQUEST_UPDATED`). + - Better: remove fallback groups that do not enforce actor/ref/path constraints. + - Abuse: attacker only needs to satisfy the weakest group. + +6. **Comment approval gate disabled or too permissive** + - `pullRequestBuildPolicy.requiresCommentApproval=DISABLED` is least safe. + - Overly broad approver roles reduce the control. + - Bad: `requiresCommentApproval=DISABLED`. + - Better: `ALL_PULL_REQUESTS` or `FORK_PULL_REQUESTS` with minimal approver roles. + - Abuse: fork/drive-by PRs auto-run without trusted maintainer approval. + +7. **No restrictive branch/path strategy for PR builds** + - Missing defense-in-depth with `HEAD_REF` + `BASE_REF` + `FILE_PATH`. + - Bad: only `EVENT` + `ACTOR_ACCOUNT_ID`, no ref/path controls. + - Better: combine exact `ACTOR_ACCOUNT_ID` + `BASE_REF` + `HEAD_REF` + `FILE_PATH` restrictions. + - Abuse: attacker modifies build inputs (buildspec/CI/dependencies) and gets arbitrary command execution. + +8. **Public visibility + status URL exposure** + - Public build/check URLs improve attacker recon and iterative testing. + - Bad: `projectVisibility=PUBLIC_READ` with sensitive logs/config in public builds. + - Better: keep projects private unless there is a strong business need, and sanitize logs/artifacts. + - Abuse: attacker discovers project patterns/behavior, then tunes payloads and bypass attempts. + +## Token leakage from memory + +Wiz's write-up explains that source-provider credentials are present in build runtime context and can be stolen after build compromise (for example, via memory dumping), enabling repository takeover if scopes are broad. + +AWS introduced hardening after the disclosure, but the core lesson remains: **never execute untrusted PR code in privileged build contexts** and assume attacker-controlled build code will attempt credential theft. + +For additional credential theft techniques in CodeBuild, also check: + +{{#ref}} +aws-codebuild-token-leakage.md +{{#endref}} + +## Finding CodeBuild URLs in GitHub PRs + +If CodeBuild reports commit status back to GitHub, the CodeBuild build URL usually appears in: + +1. **PR page** -> **Checks** tab (or the status line in Conversation/Commits). +2. **Commit page** -> status/checks section -> **Details** link. +3. **PR commits list** -> click the check context attached to a commit. + +For public projects, this link can expose build metadata/configuration to unauthenticated users. + +
+Script: detect CodeBuild URLs in a PR and test if they look public + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Usage: +# ./check_pr_codebuild_urls.sh +# +# Requirements: gh, jq, curl + +OWNER="${1:?owner}" +REPO="${2:?repo}" +PR="${3:?pr_number}" + +for bin in gh jq curl timeout; do + command -v "$bin" >/dev/null || { echo "[!] Missing dependency: $bin" >&2; exit 1; } +done + +tmp_commits="$(mktemp)" +tmp_urls="$(mktemp)" +trap 'rm -f "$tmp_commits" "$tmp_urls"' EXIT + +gh_api() { + timeout 20s gh api "$@" 2>/dev/null || true +} + +# Get all commit SHAs in the PR (bounded call to avoid hangs) +gh_api "repos/${OWNER}/${REPO}/pulls/${PR}/commits" --paginate --jq '.[].sha' > "$tmp_commits" +if [ ! -s "$tmp_commits" ]; then + echo "[!] No commits found (or API call timed out/failed)." >&2 + exit 1 +fi + +echo "[*] PR commits:" +cat "$tmp_commits" +echo + +echo "[*] Searching commit statuses/check-runs for CodeBuild URLs..." + +while IFS= read -r sha; do + [ -z "$sha" ] && continue + + # Classic commit statuses (target_url) + gh_api "repos/${OWNER}/${REPO}/commits/${sha}/status" \ + --jq '.statuses[]? | .target_url // empty' 2>/dev/null || true + + # GitHub Checks API (details_url) + gh_api "repos/${OWNER}/${REPO}/commits/${sha}/check-runs" \ + --jq '.check_runs[]? | .details_url // empty' 2>/dev/null || true +done < "$tmp_commits" | sort -u > "$tmp_urls" + +grep -Ei 'codebuild|codebuild\.aws\.amazon\.com|console\.aws\.amazon\.com/.*/codebuild' "$tmp_urls" || true + +echo +echo "[*] Public-access heuristic:" +echo " - If URL redirects to signin.aws.amazon.com -> likely not public" +echo " - If URL is directly reachable (HTTP 200) without auth redirect -> potentially public" +echo + +cb_urls="$(grep -Ei 'codebuild|codebuild\.aws\.amazon\.com|console\.aws\.amazon\.com/.*/codebuild' "$tmp_urls" || true)" +if [ -z "$cb_urls" ]; then + echo "[*] No CodeBuild URLs found in PR statuses/check-runs." + exit 0 +fi + +while IFS= read -r url; do + [ -z "$url" ] && continue + final_url="$(timeout 20s curl -4 -sS -L --connect-timeout 5 --max-time 20 -o /dev/null -w '%{url_effective}' "$url" || true)" + code="$(timeout 20s curl -4 -sS -L --connect-timeout 5 --max-time 20 -o /dev/null -w '%{http_code}' "$url" || true)" + + if echo "$final_url" | grep -qi 'signin\.aws\.amazon\.com'; then + verdict="NOT_PUBLIC_OR_AUTH_REQUIRED" + elif [ "$code" = "200" ]; then + verdict="POTENTIALLY_PUBLIC" + else + verdict="UNKNOWN_CHECK_MANUALLY" + fi + + printf '%s\t%s\t%s\n' "$verdict" "$code" "$url" +done <<< "$cb_urls" +``` + +Tested working with: + +```bash +bash /tmp/check_pr_codebuild_urls.sh carlospolop codebuild-codebreach-ctf-lab 1 +``` + +
+ +## Quick audit checklist + +```bash +# Enumerate projects +aws codebuild list-projects + +# Inspect source/webhook configuration +aws codebuild batch-get-projects --names + +# Inspect global source credentials configured in account +aws codebuild list-source-credentials +``` + +Review each project for: + +- `webhook.filterGroups` containing PR events. +- `ACTOR_ACCOUNT_ID` patterns that are not anchored with `^...$`. +- `pullRequestBuildPolicy.requiresCommentApproval` equal to `DISABLED`. +- Missing branch/path restrictions. +- High-privilege `serviceRole`. +- Risky source credentials scope and reuse. + +## Hardening guidance + +1. Require comment approval for PR builds (`ALL_PULL_REQUESTS` or `FORK_PULL_REQUESTS`). +2. If using actor allowlists, anchor regexes and keep them exact. +3. Add `FILE_PATH` restrictions to avoid untrusted edits to `buildspec.yml` and CI scripts. +4. Separate trusted release builds from untrusted PR builds into different projects/roles. +5. Use fine-grained, least-privileged source-provider tokens (prefer dedicated low-privilege identities). +6. Continuously audit webhook filters and source credential usage. + +## References + +- [Wiz: CodeBreach - AWS CodeBuild ACTOR_ID regex bypass and token theft](https://www.wiz.io/blog/wiz-research-codebreach-vulnerability-aws-codebuild) +- [AWS CodeBuild API - WebhookFilter](https://docs.aws.amazon.com/codebuild/latest/APIReference/API_WebhookFilter.html) +- [AWS CLI - codebuild create-webhook](https://docs.aws.amazon.com/cli/latest/reference/codebuild/create-webhook.html) +- [AWS CodeBuild User Guide - Best practices for webhooks](https://docs.aws.amazon.com/codebuild/latest/userguide/webhooks.html) + +{{#include ../../../../banners/hacktricks-training.md}}