search PRIV & ALL sites linked here

Friday, September 18, 2009

Linux Kernel perf_counter_open() Buffer Overflow Vulnerability

Linux Kernel perf_counter_open() Buffer Overflow Vulnerability.


This issue has been reported to affect Linux kernel 2.6.31-rc1 up to (and including) 2.6.31. Credits go to Xiao Guangrong for discovering this. This bug can be found in /usr/src/linux/kernel/perf_counter.c, specifically in the perf_copy_attr routine.

...

4126 static int perf_copy_attr(struct perf_counter_attr __user *uattr,
4127 struct perf_counter_attr *attr)
4128 {
4129 int ret;
4130 u32 size;

...
4135 /*
4136 * zero the full structure, so that a short copy will be nice.
4137 */
4138 memset(attr, 0, sizeof(*attr));
4139
4140 ret = get_user(size, &uattr->size);
4141 if (ret)
4142 return ret;
4143
4144 if (size > PAGE_SIZE) /* silly large */
4145 goto err_size;
4146
4147 if (!size) /* abi compat */
4148 size = PERF_ATTR_SIZE_VER0;
4149
4150 if (size < PERF_ATTR_SIZE_VER0)
4151 goto err_size;

Okay, as you can see on line 4130, size is declared to be a 32 bit unsigned integer. On line 4138, the first call to get_user is made, and acts as a wrapper to __get_user_check.

The get_user macro provides the main single-value transfer routines, which automatically use the right sizes _if_ and only if we have the right pointer type. In the case shown above, uattr->size (the userland event type attribute) is being reused as an argument for userland to kernel direct assignment mapping, with our unsigned 32 bit integer kernel-land event type attribute size, the macro supports simple types such as char/int, but not larger data types like arrays, and structures.

PAGE_SIZE is defined in /usr/include/asm-generic/page.h to be (1 << PAGE_SHIFT) where PAGE_SHIFT is defined to be 12, resulting in 4096. So, the bug here is that we have to specify a size, and it has to be <= 4096, thus, we can pass it any amount of bytes which is <= PAGE_SIZE, which will copy it into a buffer of that size. This bug can be exploited by mmap'ing the null page, and overwriting the stored ret with 0x0's, or triggering it as a race condition so we can copy arbitrary data from "copy_to_user".

4157 if (size > sizeof(*attr)) {
4158 unsigned long val;
4159 unsigned long __user *addr;
4160 unsigned long __user *end;
4161
4162 addr = PTR_ALIGN((void __user *)uattr + sizeof(*attr),
4163 sizeof(unsigned long));
4164 end = PTR_ALIGN((void __user *)uattr + size,
4165 sizeof(unsigned long));
4166
4167 for (; addr < end; addr += sizeof(unsigned long)) {
4168 ret = get_user(val, addr); // <<< Here.
4169 if (ret)
4170 return ret;
4171 if (val)
4172 goto err_size;
4173 }

4174 }

So now, we can simply change the bytes from userland, into kernel-land with our own content

4175
4176 ret = copy_from_user(attr, uattr, size);

As you can already probably see, by mmap'ing address 0x0, as many upcoming exploits developers have done in recent times would, like so, should be enough for a barebones exploit,

/* Courtesy of Bradley Spengler */

(struct perf_counter_attr *) mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

When calling the sys_perf_counter_open syscall with these arguments. So, as you can see the bug is a pretty routine buffer overflow. Exploit developers should know their way around, from here, and I must applaud Xiao, for finding this, you have a pretty keen eye ;)

And, here's how the patch for this bug was implemented

...
__user *uattr,
if (val)
goto err_size;
}

+ size = sizeof(*attr);
}

ret = copy_from_user(attr, uattr, size);

....

The race condition can be exploited too, in order to achieve essentially the same results. You could probably read more into exploiting kernel based race conditions, and how to force a kernel path to sleep, in the very well written article written by sgrakkyu and twiz in phrack 64.

Thanks go to bob the builder, redsand and nemo for discussing this and
reviewing this for me (apparently, I can't speak english very well)

~hqi ( hqi <> efnet pe )

1 comment:

  1. After reading that, i am starting to wonder how bad MY english must seem. excellent article man.

    On another note, i will have to go back and look at that phrack article.

    ReplyDelete