mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-06-12 19:11:44 -07:00
PRs public codebuild abuse
This commit is contained in:
@@ -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)
|
||||
|
||||
+8
-1
@@ -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 <value>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+5
-15
@@ -185,25 +185,15 @@ aws codebuild start-build --project-name <proj-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}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+241
@@ -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.
|
||||
|
||||
<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}}
|
||||
Reference in New Issue
Block a user