This is the second of a series of posts where we’ll give our solutions (as well as source code) for some problems from Plaid CTF 2012.
Source code
Overview
sfs (Secure File System) was a 64-bit C++ binary (PIE) running under xinetd on a Debian machine with NX and ASLR enabled.
The program gives you an interface to a “filesystem” which encrypts its files with either RC4 or xor with a string. The interface looks something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ./problem
Password: Ve3yhTFW9TsffX2J
Welcome to the encrypted file system!
create [file] [xor|rc4] [key] Create file (xor or rc4)
ln [target] [linkname] Create hard link
mkdir [dir] Make directory
cat [file] View file
edit [file] [key] Edit file
rm [file] Delete file
rmdir [dir] Delete directory
ls [dir] List directory
cd [dir] Change directory
help Show list of commands
exit Exit
user@/$
A file is represented as a NormalFile class, which has a pointer to a FileData class. When a hardlink to a file is created, the hardlink shares the original file’s FileData. The number of NormalFiles referencing a FileData is kept track of with a 8 bit reference counter in the FileData class.
Vulnerability
There were (at least) two use-after-free vulnerabilities in this program. The first one (which I didn’t intend to put in) occurs when trying to create a hardlink with the target in a nonexistent directory. @sleepya_ has a great writeup of exploiting this vulnerability.
The vulnerability that I intended for people to exploit was that it was possible to cause a FileData’s reference counter to wrap back around to 1 by creating 256 hard links to a file. If we then delete one of the files, the FileData will be freed even though it is still referenced by 256 other NormalFiles.
Exploitation
To exploit this vulnerability, we first want to find out where the heap and libc are. We can then try to overwrite a FileData’s vtable in order to control rip and do ROP.
Here’s an outline of what the exploit does:
- Create a file A, and make 256 hardlinks to it (A0, A1, …).
- Create a file B, and make 256 hardlinks to it (B0, B1, …).
- Delete A and B. This causes the FileData for all of A’s and B’s hardlinks to be freed.
- Leak a heap address by reading A0.
- Leak a libc address by reading B0.
- Create a file C containing a fake vtable and ROP payload.
- Create a file D (its FileData will overlap with A0’s FileData) containing the address of our fake vtable in its FileData.
- Edit A0, causing it to call a function from our fake vtable, and thus letting us control %rip.
- ROP ROP ROP!
In order to do ROP, I used a nice gadget from the setcontext function, which basically sets all of the registers to values at offsets from its first argument.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
00000000000439c0 <setcontext>:
...
439f5: 48 8b a7 a0 00 00 00 mov 0xa0(%rdi),%rsp
439fc: 48 8b 9f 80 00 00 00 mov 0x80(%rdi),%rbx
43a03: 48 8b 6f 78 mov 0x78(%rdi),%rbp
43a07: 4c 8b 67 48 mov 0x48(%rdi),%r12
43a0b: 4c 8b 6f 50 mov 0x50(%rdi),%r13
43a0f: 4c 8b 77 58 mov 0x58(%rdi),%r14
43a13: 4c 8b 7f 60 mov 0x60(%rdi),%r15
43a17: 48 8b 8f a8 00 00 00 mov 0xa8(%rdi),%rcx
43a1e: 51 push %rcx
43a1f: 48 8b 77 70 mov 0x70(%rdi),%rsi
43a23: 48 8b 97 88 00 00 00 mov 0x88(%rdi),%rdx
43a2a: 48 8b 8f 98 00 00 00 mov 0x98(%rdi),%rcx
43a31: 4c 8b 47 28 mov 0x28(%rdi),%r8
43a35: 4c 8b 4f 30 mov 0x30(%rdi),%r9
43a39: 48 8b 7f 68 mov 0x68(%rdi),%rdi
43a3d: 31 c0 xor %eax,%eax
43a3f: c3 retq
Here is our exploit for this problem: exploit.py.