# We wanted to use Baseten for inference. We ended up with admin access to Baseten GitHub repos

Public Harbor project exposed a Baseten image whose build history contained a live GitHub PAT with admin rights on internal repos.

- Payout: Research
- Severity: critical
- Program: Baseten
- Write-up by: Strix
- Disclosed: 2026-09-01
- Write-up quality: 7/10
- Original: https://www.strix.ai/blog/baseten-harbor-github-pat-takeover
- Library page: https://bugbountylibrary.com/r/blog%3Astrix-ai-blog-baseten-harbor-github-pat-takeover
- Tags: area:cloud domain:cloud impact:integrity impact:internal-access kind:research platform:research sev:critical tech:docker tech:github-actions technique:recon technique:source-review vuln:misconfiguration vuln:secrets-leak

**Target:** Public Harbor container registry and image build history
**Root cause:** A GITHUB_TOKEN build arg was expanded into a RUN command, so Docker recorded the token value in the image config's history[].created_by; the token was still live when found in July 2026, over three years after the March 2023 build.
**Steps:**
1. Enumerate subdomains of *.baseten.co via cert logs; find gcp-us-east4-zlw.registry.baseten.co
2. List public Harbor projects and repositories anonymously; obtain anonymous pull token scoped to repository:baseten/baseten-app:pull
3. Pull manifest and blobs for baseten/baseten-app via signed storage redirect
4. Run TruffleHog on layers; AWS keys found but dead (sts:GetCallerIdentity returned InvalidClientTokenId)
5. Inspect image config history[].created_by; find GITHUB_TOKEN expanded in a RUN command
6. Validate token with read-only `GET /user` on GitHub; returns 200 as basetenbot with X-OAuth-Scopes: repo
7. Enumerate per-repo permissions read-only; admin+push on main product, GitOps and Homebrew repos, read/write on other private repos including per-customer repos
**How found:** Recon of subdomains and cert logs surfaced an unfamiliar Harbor registry; rather than reporting exposure, the agent tested whether anonymous pull actually worked, then hunted for credentials inside pulled images, pivoting from dead AWS keys to the build history config.
**Exploitation:** No weaponization performed; researchers stopped after confirming read-only that the token belonged to basetenbot in basetenlabs and held admin/push on core repos. A real attacker could have pushed malicious code to product/GitOps repos or read private customer repos.
**Escalation:** Chained anonymous registry pull to image config extraction to live credential to org-wide repo permission enumeration.
**Insight:** Docker image config history[].created_by persists expanded build args even when the credential file is deleted from layers; always pull the config blob and grep history, not just filesystem layers, and test whether leaked tokens are still live and what scope they hold.

## Proof of concept (copied exactly from the original report)

**Read-only GitHub token validation request**

```
Strix used the token for a read-only GET /user request to GitHub and… VOILÀ. 200, with the account name basetenbot.
```

**Repository permission enumeration output**

```
basetenlabs/b***admin: true, push: true
basetenlabs/f***admin: true, push: true
basetenlabs/h***admin: true, push: true
basetenlabs/r***Private, read/write
basetenlabs/b***Private, read/write
```

