# 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.

<details>
<summary>Script: detect CodeBuild URLs in a PR and test if they look public</summary>

```bash
#!/usr/bin/env bash
set -euo pipefail

# Usage:
#   ./check_pr_codebuild_urls.sh <owner> <repo> <pr_number>
#
# 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
```

</details>

## Quick audit checklist

```bash
# Enumerate projects
aws codebuild list-projects

# Inspect source/webhook configuration
aws codebuild batch-get-projects --names <project-name>

# 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}}
