Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

Local Escalation of Privilege Ubuntu Desktop- 708

Flatt Security Posted 4 Years Ago
  • eBPF (extended Berkley Packet Filter) is a Linux kernel functionality that allows code to run in the context of the kernel without gaining further privileges. In order to do this, there is a verification step to ensure that all of the actions are memory safe. Because it runs in the kernel, it is faster than running it is userspace.
  • eBPF has many different map data types with one of them being a ring buffer (circular queue). When creating the file descriptor, two calls to allocate dynamic memory are made. One of them is the bpf_ringbuf_map and the other is bpf_ringbuf.
  • bpf_ringbuf_map contains a pionter to the bpf_ringbuf. bpf_ringbuf contains a consumer and producer position alignment and page aligned data, where this memory is mapped twice to give a ring buffer feel. In the eBPF bytecode, the data has a memory allocation for the data; this is essentially a call to malloc in the eBPF world but has to be a static value. The verifier knows this value because it is passed in as a const during the verification process.
  • The size of the allocation should not surpass the value passed to the verification function; this is the key to the bounds checking of the function. The main verification for the index check is the consumer and producer positions in the buffer. The check verifies that producer position - consumer position is less than the size of the buffer. This makes sense but has a terrible flaw in it.
  • The vulnerability comes from the fact that the data can be mmaped directly with the data not being validated after this alteration. The main check, mentioned above, can then be manipulated to pass by altering the producer and consumer position values so that the eBPF allocation goes much too large for the memory given to it. This leads to an out of bounds read and out of bounds write in the kernel.
  • With this primitive, the exploit strategy is fairly straight forward for the kernel. First, they create a read and write function to abstract away the vulnerability above and to only worry about exploitation. eBPF has many different protections in place to prevent exploitation, which limited the functionality that could be used in this exploit.
  • The author allocates two adjacent bpf_ringbuf structures and a kernel stack for exploitation. This is achieved by creating huge maps and then spawning a bunch of processes. Eventually, this lines up the way the attacker wants. Once the memory feng shui is done, the out of bounds read can be used to leak kernel pointers from the bpf_ringbuf object.
  • Once the randomness has been broken, we can overwrite the kernel instruction pointer on the stack in order to start a ROP chain. Since this is another process, we need to force it to pause while our full exploit happens. This is done by calling the syscall read and waiting to write to the buffer. With a full ROP chain setup, with user controllable values from the primitive above, popping a root shell is trivial.
  • One thing to note is that the read syscall function call does not have to be adjacent to our block. This is because we can search for the address of read until we find the kernel address that it would be at until the end of the function. That is super neat and takes out a lot of the system randomness.
  • Overall, this was a super interesting vulnerability in the Linux kernel! Verifying running code is extremely hard to do. Even with all of the effort and mitigations in place, eBPF has been a security problem for a little while. This shows that deeply understanding a code base allows for the discovery of vulnerabilities though :)