xorl %eax, %eax

CVE-2009-0269: Linux eCryptFS off-by-one Underflow

with one comment

This is a common off-by-one overflow case which is known to almost any code auditor. The issue we’ll discuss here affects Linux kernel up to release and it was disclosed by Duane Griffin on 22 December 2008. eCryptfs is a POSIX-compliant cryptographic filesystem. The next code snippets were taken from Linux kernel 2.6.28 release and the vulnerability is located on the source code responsible for following symbolic links in an eCryptFS filesystem. To be more specific, here is the vulnerable code as seen at fs/encryptfs/inode.c:

659 static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd)
660 {
661        char *buf;
662        int len = PAGE_SIZE, rc;
663        mm_segment_t old_fs;

This function is invoked when the filesystem is asked to follow a symbolic link file. Here I found it useless to use a signed integer (len) to store size for allocation but since this is out of the scope of this vulnerability I’m going to move on and describe the function.. So, it continues like this:

665        /* Released in ecryptfs_put_link(); only release here on error */
666        buf = kmalloc(len, GFP_KERNEL);
667        if (!buf) {
668                rc = -ENOMEM;
669                goto out;
670        }

Just allocate some space (PAGE_SIZE) and check the return value. Notice the missing check for ZERO_PTR_SIZE but it is impossible to make len equal to zero so this is not anything important. Next..

671        old_fs = get_fs();
672        set_fs(get_ds());
673        ecryptfs_printk(KERN_DEBUG, "Calling readlink w/ "
674                        "dentry->d_name.name = [%s]\n", dentry->d_name.name);

Fix the current filesystem registers and print a debugging message which includes the symbolic link to be followed and then:

675        rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len);
676        buf[rc] = '\0';
677        set_fs(old_fs);
678        if (rc < 0)
679                goto out_free;

Using a function pointer at line 675 call readlink(2) and append the contents of dentry to buf, however as everyone knows readlink(2) does not NULL terminate the buffer so at line 676 it NULL terminates it manually. Now, line 678 implies the obvious.. which can be read from the man page of readlink(2):

       The call returns the count of characters placed in the buffer if it succeeds, or a -1 if an error occurs,
       placing the error code in errno.

But if readlink() fails the code at line 676 will attempt to write to:

    buf[-1] = '\0';

This is a classic off-by-one which is documented at least since 2006 when Ilja van Sprundel wrote on his blog about it. Our case is only vulnerable to an off-by-one underflow according to ilja’s post. For completeness, the code ends like this:

680        rc = 0;
681        nd_set_link(nd, buf);
682        goto out;
683 out_free:
684        kfree(buf);
685 out:
686        return ERR_PTR(rc);
687 } 

And now the question that arises is… How can readlink(2) fail? Oh.. there are plenty of ways to achieve this…

     The readlink() system call will fail if:

     [ENOTDIR]          A component of the path prefix is not a directory.

     [ENAMETOOLONG]     A component of a pathname exceeded 255 characters, or
                        an entire path name exceeded 1023 characters.

     [ENOENT]           The named file does not exist.

     [EACCES]           Search permission is denied for a component of the
                        path prefix.

     [ELOOP]            Too many symbolic links were encountered in translat-
                        ing the pathname.

     [EINVAL]           The named file is not a symbolic link.

     [EIO]              An I/O error occurred while reading from the file sys-

     [EFAULT]           The buf argument extends outside the process's allo-
                        cated address space.

     In addition to the errors returned by the readlink(), the readlinkat()
     may fail if:

     [EBADF]            The path argument does not specify an absolute path
                        and the fd argument is neither AT_FDCWD nor a valid
                        file descriptor open for searching.

     [ENOTDIR]          The path argument is not an absolute path and fd is
                        neither AT_FDCWD nor a file descriptor associated with
                        a directory.

This means that trigger of this off-by-one is fairly simple. Just create a malicious symbolic link which has at least one of the above properties and use it in a eCryptFS partition. The patch to this bug was:

        rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len);
-       buf[rc] = '\0';
        if (rc < 0)
                goto out_free;
+       else
+               buf[rc] = '\0';
        rc = 0;

Exploitation of such vulnerabilities is not something trivial. As MaXX of synnergy showed on 2001 at phrack #57, exploitation of simple off-by-ones like this one can lead to reliable code execution since heap uses control structures but on the other hand this needs an in depth knowledge of the dynamic memory management allocator used. This might not be simple but it is still exploitable.

Written by xorl

January 29, 2009 at 14:18

Posted in linux, vulnerabilities

One Response

Subscribe to comments with RSS.

  1. […] bien explican aquí, la vulnerabilidad se debe a que el valor de retorno de la función readlink puede ser negativo y […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s