== Challenge ==
namespaces with a sandbox challenge based on linux user namespaces. It gives you two options:
- run a binary in a new namespace sandbox
- run a binary in an existing sandbox
After a while, a hint was released that for the intended solution you will need to create a user namespace yourself.
If you start a new sandbox, the supervisor will
- unshare all namespaces
- change the uid and gid
- chroot to a directory in /tmp/chroots/
- execute your binary
If you attach to an existing sandbox, the supervisor will
- find the pid of the init process
- attach to all namespaces except net
- change uid/gid
- chroot
- execute your binary
The flag is outside of the chroots and only readable by uid 0.
== Bugs ==
The first issue was already mentioned, when running a new process in an existing sandbox, the supervisor doesn't attach to the network namespace. The second thing to notice is that /tmp/chroots is world-writable. And finally, attaching to the namespaces of a process is not done in a safe way if there's a process inside the user ns with capabilities.
== Exploitation ==
As mentioned in the hint, you want to be able to create your own usernamespace. It sounds easy, just call unshare. However since the processes are all chrooted, they're not allowed to create a new user namespace. You need to find a way to break out of the chroot.
First, we need to abuse the fact that the supervisor forgets about the network namespace when attaching to an existing sandbox. This allows us to communicate between different sandboxes using abstract unix domain sockets (man 7 unix) including sending file descriptors from one sandbox to another. Sending the fd of / from allows us to access files outside of the chroot. If we're chrooted to /tmp/chroots/0 and receive a file descriptor to /tmp/chroots/1, we can use that fd to traverse upwards and access the whole fs.
Next, we use our filesystem access and race a new sandbox creation. Once /tmp/chroots/2 gets created, we delete it and replace it with a symlink to /. The supervisor will then chroot to / which gives us a process that is not in a chroot anymore.
This allows us to unshare a user namespace ourselves and gain all capabilities inside. This allows us to race the new process creation. Once the supervisor attaches to our user and pid namespace, we can ptrace it and inject shellcode to read /flag.
Hi, I'm confused that your script write /proc/self/uid_map successfully. I tried to create a unshare process after call new_proc() function, but I can't modify /proc/self/uid_map or /proc/self/gid_map. And some documents tell me that only process in father user namespace or child namespace can modify uid_map of current user namespace. So it really confused me. :(