# RCE on build server via misconfigured pip install

Unclaimed internal package name yelp-cgeom was uploaded to public PyPI; a Yelp build server installed it, executing attacker setup.py and confirming RCE.

- Payout: hidden
- Severity: critical
- Program: Yelp
- Found by: alexbirsan
- Disclosed: 2021-02-09
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/946409
- Library page: https://bugbountylibrary.com/r/h1%3A946409
- Tags: area:cloud domain:infra impact:internal-access impact:rce kind:bounty platform:hackerone sev:critical tech:python technique:recon technique:source-review vuln:dependency-confusion vuln:rce

**Target:** Internal PyPI package name resolved against public pypi.org on a Jenkins build server
**Root cause:** Build server pip configuration resolved an internal-only package name from the public PyPI registry instead of Yelp's internal registry, and the name was unclaimed publicly.
**Steps:**
1. Identify internal package name `yelp-cgeom` referenced by Yelp build configs.
2. Confirm the name is unclaimed on pypi.org.
3. Publish a package `yelp-cgeom` to PyPI with a `setup.py` that beacons out (IP, hostname, cwd) on install.
4. Wait for a build server to run `pip install yelp-cgeom` and receive the callback.
5. Observe callback from `54.71.19.248`, hostname `...yelpcorp.com`, cwd `/ephemeral/tmpdir/pip-install-.../yelp-cgeom`.
**How found:** Recognized that internal package names can be squatted on public registries; enumerated a known internal Yelp package name and checked it was unclaimed on PyPI, then uploaded a beaconing package to detect misconfigured installs.
**Exploitation:** Attacker registers the internal package name on public PyPI; when the misconfigured build server runs pip install, the attacker's setup.py executes with build-server privileges, enabling arbitrary code execution and backdooring build artifacts. Here only a benign callback was performed.
**Escalation:** Could pivot from build server into injecting backdoors into built projects/artifacts.
**Insight:** Internal package names visible in lockfiles, requirements, or error messages are candidates for public-registry squatting; a benign callback in setup.py confirms execution without violating scope.

