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!

How a simple Linux kernel memory corruption bug can lead to complete system compromise- 648

Jann Horn - Project Zero (P0)Posted 4 Years Ago
  • Linux terminal devices, such as serial consoles, are represented via the struct tty_struct. This structure contains fields used for job control features of terminals.
  • The field pgrp points to the foreground process group of the terminal. The session field points to the session associated with the terminal. Both of these fields do not point to the process/task directly though, a pid represents the numeric value of the process.
  • The vulnerability is an issue with the locking in the tiocspgrp IOCTL. The member on the terminal side would get locked. However, this lock was taken on the tty. As a result, by making a call on both side of the pseudoterminal, a race can be made on the should be locked object.
  • Regardless, one of the objects (struct pid) is decremented by 1 too many. Anytime you can cause issues with the reference counting, you got a nice looking use after free!
  • To exploit this, they needed to get the object into a part of the heap that had interesting information in it. The object that was freed only had two other objects in it, which were not usable. By going some heap feng shui (mentioned in the article), it is possible to flush the page that the object is on to be used by a different kernel allocator.
  • With the UAF object back into a more controllable heap, we can allocate it over anything that we want! The author chose to overwrite a pagetable. This is useful because we can manually give ourselves access to the .text section of a setuid binary.
  • The best part of the UAF is using the get_pid function to increment the refcount of the object itself. Using this, we can add arbitrary amounts to the address. On the page tables, this can be used to add flags that should not be set. It should be noted this bypassed ASLR entirely because of the increment primitive.
  • The rest of the article talks about mitigations for protecting against these vulnerabilities. The first one is simply attack surface reduction. Simply, if an attacker cannot hit the code, it cannot be exploited.
  • The second recommendation is to implement compile-time locking validation. Clang has some thread safety checks that can be done but they do not seem to work for C very well(only C++). It would be awesome to find locking issues in the future via compilation flags but there is still a lot of work on this front.
  • The vulnerability is a use after free, which is incredibly powerful. Memory tagging is coming in the future and will make UAFs require a serious memory leak. It should be noted that CFI and ASLR did nothing to stop this single bug exploit.
  • There are a few other mitigations mentioned in the post. Smaller portions are important data being readonly, structure layout randomization and lack of heap memory reuse.
  • Thread safety is incredibly hard to do! Overall, good post about exploiting a locking bug and the future of memory corruption.