if (copy_from_user
(buf, userbuf, count < sizeof(buf) ? count : sizeof(buf)))
return -EFAULT;
buf[count - 1] = '';
if (!strict_strtoul(buf, 10, &val))
gru_options = val;
return count;
}
[/sourcecode]
This function is used to handle write operations to the equivalent procfs file. As you can see, it invokes copy_from_user() using user controlled 'count' only if this is less than sizeof(buf), that is 80 according to the previous allocation. However, the NULL termination does not perform the same check and directly uses the user controlled 'count' minus one. Because of this, a user with +w access to that procfs file can write a NULL byte to arbitrary locations in kernel memory. The patch is:
[sourcecode language="c"]
+ memset(buf, 0, sizeof(buf));
if (strncpy_from_user(buf, userbuf, sizeof(buf) - 1) < 0)
return -EFAULT;
- buf[count - 1] = '';
if (!strict_strtoul(buf, 10, &val))
[/sourcecode]
Which initializes the whole 'buf' to zero, thus no need for NULL termination and removes the buggy code.

spender there are numerous ways to exploit this :)
One simple way is trigger a dereference by overwriting 1 byte of an address (offset from buf[] in this case) of a function pointer and then mmap() to that location your evil code and finally trigger a call to that function pointer.

Can you know buf 100% reliably though? I thought of an idea to know buf 100% reliably, then you would reliably overwrite the highest byte in a rarely-used (but non-zero) file_operations fptr, and then rinse and repeat the old cheddar bay code :p

No, I don’t know 100% reliably the location of buf[], using your way of course you have to overcome any mmap_min_addr since the function pointer you mention will be dereferenced to NULL. Of course, since you already have cheddar bay ready it’s not that hard :P
Nevertheless, I think it would be much better if you overwrite a function pointer outside mmap_min_addr range since you’ll not have to mess with it at all.

Why will it be dereferenced to NULL? It can’t in fact, or it wouldn’t be used. What I would do is target some fptr which I know points to some known function that I have the symbol (and thus the address for). So then I can mask off bytes using separate 0 writes and thus wind up with an address like 0x00ab0000 or something of the sort, do my mmap there (which is above mmap_min_addr) and away we go ;)

@qaaz: I’m not really sure that I get what you mean by “staying on the stack”. Probably overwriting something which is close to buf[] on the stack but as I said, I’m not 100% sure that this is what you meant.

It would be a real one but this driver only works on some special SGI x86 architecture. If anyone actually has this hardware they can make the modifications mentioned to my exploit and have something that works reliably :p

Why not use this null byte to overwrite task_struct->cred->uid directly instead of MMAPing a code and jumping there? We are going for root after all, right? :-) Of course, one must locate the address of the task’s task_struct in order to use this technique (via an info leak maybe?). I am still practicing my kernel skills, so, maybe I am wrong.

Btw, spender, can you probably tell us more about how to reliably locate buf[]? Sounds interesting.

@huku: I thought about that as well, you now, the elegant BSD way but this will require at lest two overwrites since in most cases UIDs are like 1000 or something, so in hex you have 0x000003EB. In addition to this, this is not a 100% arbitrary overwrite since write(2) doesn’t allow negative integers, this limits you to 0-0x80000000 offsets. If you can reliably find your task’s UID there then it would be neat.

I think I can answer spender’s question as well, you can also read his PoC/sample-exploit code posted above. He allocates 1GB of memory and then performs a write to the procfs file with a count in the range of his allocated space. The kernel will page this page since it was touched and using mincore(2) spender’s exploit locates the altered page. This gives more or less, the location of buf[]. Then since he knows in which page to look, he uses a for loop which fills that page with non-zero data and does a zero write to buf[] and attempts to find the zero in that page. This is what he does to find the location of his write operation in buf[] which is really cool I think. :)
spender, correct me if I’m wrong.