Owning the Kernel & Root

By |2018-09-24T05:00:07+00:00October 6th, 2014|

20141004-header

This time we’ll discuss how to use the basic building block of the limited form of kernel-write we found last time in order to get unrestricted write to the kernel, and ultimately root privileges.

A Quick Recap

I’ll just quickly review what we have so far. Using the futex bug, we created a scenario where the waiter list associated with a certain lock contains dangling references to the thread which called futex_wait_requeue_pi.

By calling a sendmmsg with specific parameters, we were able to make the dangling waiter object point to a fake waiter object in shared memory. The fake waiter points to another fake waiter in shared memory as its previous node.

When we add a new waiter, which is basically spawning a thread and calling futex_requeue_pi ,whose priority is greater than the dangling rt_waiter, but smaller than the fake waiter’s, it gets added between the two fake waiters, exposing the kernel address of the new waiter via the next/prev pointers of the fake waiters.

The process can be encapsulated in the following expression:

1
*(fake_waiter.prev) = kernel_address_of_added_waiter;

Now, while the first time the fake waiter pointed to shared memory, the result was a disclosure of a kernel address, we can use this method to write anywhere in the kernel provided we have a valid address, which we now do.

Extending the Reach

The form of write we have now is pretty limited for two reasons, first, we can only write, and second, we can only write values which are addresses of rt_waiter object.

Not all is lost though. First, let’s remember that the address of the waiter is an address in the kernel stack of the thread whose waiter was added to the list. Let’s call it the compromised thread for reasons which will be obvious soon enough.

In the kernel, each thread is allocated its own stack, at the bottom of which there’s a thread_info structure which describes several parameters of the thread. Among which is the addr_limit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct thread_info {
    long unsigned int flags;
    int preempt_count;
    mm_segment_t addr_limit; /* <<< this one */
    struct task_struct *task;
    struct exec_domain *exec_domain;
    __u32 cpu;
    __u32 cpu_domain;
    struct cpu_context_save cpu_context;
    __u32 syscall;
    __u8 used_cp[16];
    long unsigned int tp_value;
    struct crunch_state crunchstate;
    union fp_state fpstate;
    union vfp_state vfpstate;
    struct restart_block restart_block;
};

Every time the kernel handles a user supplied address, it verifies that this address is below addr_limit, so that a user can’t just read/write to kernel memory via:

1
2
fd = open("/some/file/", O_RDRW);
write(fd, kernel_address, 0x1000);

In the emulated kernel, the limit is set to 0xbf000000.

Now, if we could infer the address of addr_limit from the disclosed address we would be able to write to it.

Luckily we can. The kernel allocates the stack aligned to a 8KiB boundary, which means that the base of the stack, and consequently the address of the compromised thread’s thread_info is at disclosed_address & (~(8 * 1024 - 1)) = disclosed_address & 0xffffe000. The addr_limit field is just 8 bytes above that address.

Good, then we have an address, which means we can prepare the fake waiter’s prev to point to it, and when we trigger adding a new waiter to the list, we will override addr_limit with an address in the kernel. We still have several problems though:

  1. The compromised thread is sleeping. Well that’s not a big problem, we can wake it up with a signal.
  2. The values with which we override the addr_limit might be below the stack frame of the compromised thread, so it doesn’t really help us very much.

To solve the second issue, we can perform the following staggered process: on the main thread we’ll be spawning new waiter threads, after every spawn, we try to read addr_limit in the compromised thread using the write trick. We do that until we manage to sneak in a successful write. Once this happens, i.e. if we can read addr_limit, then we can also write to addr_limit using the same trick. In order to gain unrestricted read/write, we’d want to write 0xffffffff to addr_limit:

1
2
3
4
5
uint32_t max_limit = 0xffffffff;
int pipefd[2];
pipe(pipefd);
write(pipefd[1], &max_limit, sizeof(max_limit));
read(pipefd[0], thread_info.addr_limit, sizeof(max_limit));

We can now read and write anywhere in the kernel, but only from the compromised thread.

Root

If you’ll notice, the thread_info structure contains a pointer to the task structure. We can now read the address of the structure and the structure itself. There’s a particular field which interests us in the task structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct task_struct {
...
    const struct cred *real_cred;
    const struct cred *cred;
...
};
struct cred {
    atomic_t usage;
    uid_t uid;
    gid_t gid;
    uid_t suid;
    gid_t sgid;
    uid_t euid;
    gid_t egid;
    uid_t fsuid;
    gid_t fsgid;
...
};

The cred structure specifies the process’ credentials. If we were to change all the ids to 0, we’ll essentially make the process a root process, following which we can do whatever we want in the context of that process.

Afterword

Afterword

This is where this article series comes to its end. We’ve covered the inner workings of the TowelRoot exploit from its core and the bugs on which it was based, up to the gaining kernel r/w and root on the device.

There’s just one open issue left, which should be somewhat challenging. You see, all the things we did wreaked havoc on the waiter list and the waiter objects, which as you remember from the first article, leaves the kernel in a fragile state, where as soon as all the threads are released will cause the kernel to panic, which in a real-life scenario gets translated to a device (soft) reset. It would be interesting to try to locate all the orphaned waiters and mend the waiter list. I’ll leave this as an open challenge to you readers.

That’s it gals and guys. I hope you found the articles informative, insightful and ultimately an enjoyable read.

About the Author:

WordPress Video Lightbox Plugin