tl;dr
- Execute shellcode on parent and write to child’s memory using
/proc/<pid of child>/mem
- Overwrite return address of child with execve shellcode and pop shell.
Category: Sandbox
Challenge Points: 140
Solves: 87
Solved By: Sherl0ck, Cyb0rG, 3agl3
We had a really great time this weekend playing this year’s edition of Google CTF. Although we were able to solve only 2 of the pwn challenges , here’s the intended writeup for the challenge WriteOnly.
Challenge description
Here’s the list of all the allowed syscalls.
1 | void setup_seccomp() { |
A surprise that the challenge has for us is that Read
syscall is not allowed.
Idea of exploit
Again, going through source code , we see a few things happening.
- A child is created which opens and reads 4 bytes of flag constantly.
- The parent sets up seccomp, asks for a length and takes our shellcode as input and gives us code execution right away.
1 | int main(int argc, char *argv[]) { |
An important thing to notice here is , seccomp is enabled in the parent after the child has been created. Hence , the child does not inherit seccomp, so how cool is that?
The only thing that we could think of was to somehow write to the child’s memory and get code execution in child too. But the question was , what do we write to child and how do we do it?
Delving into the exploit
As suggested by Sherl0ck, we could open a pseudo file called /proc/<pid of child>/mem
and write to any segment of memory of the child , just like editing a binary in plain ghex :P. And what more , the program already prints the PID of child coupled with PIE being disabled , hence confirming our approach to exploitation.
So now , another question that should pop in our minds now is , where do we write in the memory of child to get code execution? We can blithely overwrite the return address of child with our shellcode. There’s another problem , remember that child will die if parent dies , so we have to make sure that the parent is alive throughout our journey of popping shell through child.
Overwriting return address of child
tl;dr of the plan is :
- Open
/proc/<pid of child>/mem
with read-write permissions. - using lseek syscall to seek to the return address of child.
- Write shellcode to return address and finally loop parent so that it doesn’t die out.
1 | from pwn import * |
Conclusion
The challenge taught me yet another way of escaping seccomp sandbox through writing to child’s memory. kudos to Google CTF for such a good challenge.