AWS CodeBuild - Untrusted PR Webhook Bypass (CodeBreach-style)
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
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:
- A webhook actor allowlist used an unanchored regex.
- An attacker registered a GitHub ID that matched as a superstring of a trusted ID.
- A malicious PR triggered CodeBuild.
- 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:
-
EVENTfilters 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.
- Common risky events:
-
ACTOR_ACCOUNT_IDregex 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.
- Bad: unanchored patterns like
-
Other regex filters are weak or missing
HEAD_REF- Bad:
refs/heads/.* - Better:
^refs/heads/main$(or an explicit trusted list)
- Bad:
BASE_REF- Bad:
.* - Better:
^refs/heads/main$
- Bad:
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
- Bad: trust marker with loose match like
REPOSITORY_NAME/ORGANIZATION_NAME- Bad:
.*in org/global webhooks - Better: exact repo/org matches only
- Bad:
WORKFLOW_NAME- Bad:
.* - Better: exact workflow name matches only (or avoid this as trust control)
- Bad:
- Abuse: attacker crafts ref/path/message/repo context to satisfy permissive regex and trigger builds.
-
excludeMatchedPatternis misused- Setting this flag incorrectly can invert intended logic.
- Bad:
FILE_PATH '^buildspec\\.yml$'withexcludeMatchedPattern=falsewhen intent was to block buildspec edits. - Better: same pattern with
excludeMatchedPattern=trueto deny builds touchingbuildspec.yml. - Abuse: defenders think they deny risky events/paths/actors, but actually allow them.
-
Multiple
filterGroupscreate 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.
-
Comment approval gate disabled or too permissive
pullRequestBuildPolicy.requiresCommentApproval=DISABLEDis least safe.- Overly broad approver roles reduce the control.
- Bad:
requiresCommentApproval=DISABLED. - Better:
ALL_PULL_REQUESTSorFORK_PULL_REQUESTSwith minimal approver roles. - Abuse: fork/drive-by PRs auto-run without trusted maintainer approval.
-
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_PATHrestrictions. - Abuse: attacker modifies build inputs (buildspec/CI/dependencies) and gets arbitrary command execution.
- Missing defense-in-depth with
-
Public visibility + status URL exposure
- Public build/check URLs improve attacker recon and iterative testing.
- Bad:
projectVisibility=PUBLIC_READwith 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:
Finding CodeBuild URLs in GitHub PRs
If CodeBuild reports commit status back to GitHub, the CodeBuild build URL usually appears in:
- PR page -> Checks tab (or the status line in Conversation/Commits).
- Commit page -> status/checks section -> Details link.
- 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
#!/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 /tmp/check_pr_codebuild_urls.sh carlospolop codebuild-codebreach-ctf-lab 1
Quick audit checklist
# 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.filterGroupscontaining PR events.ACTOR_ACCOUNT_IDpatterns that are not anchored with^...$.pullRequestBuildPolicy.requiresCommentApprovalequal toDISABLED.- Missing branch/path restrictions.
- High-privilege
serviceRole. - Risky source credentials scope and reuse.
Hardening guidance
- Require comment approval for PR builds (
ALL_PULL_REQUESTSorFORK_PULL_REQUESTS). - If using actor allowlists, anchor regexes and keep them exact.
- Add
FILE_PATHrestrictions to avoid untrusted edits tobuildspec.ymland CI scripts. - Separate trusted release builds from untrusted PR builds into different projects/roles.
- Use fine-grained, least-privileged source-provider tokens (prefer dedicated low-privilege identities).
- Continuously audit webhook filters and source credential usage.
References
- Wiz: CodeBreach - AWS CodeBuild ACTOR_ID regex bypass and token theft
- AWS CodeBuild API - WebhookFilter
- AWS CLI - codebuild create-webhook
- AWS CodeBuild User Guide - Best practices for webhooks
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.


