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!

Turning \x00\x00 into 10000$ - CVE-2021-22555- 829

Andy Nguyen - GooglePosted 3 Years Ago
  • The author had found a vulnerability in the Linux networking stack called BleedingTooth. From there, they wanted a privilege escalation as well. While reading through old vulnerabilities like CVE-2016-3134 and CVE-2016-4997, this inspired the author to look for memcpy() and memset() in the Netfilter code. This is the start of the process.
  • When IPT_SO_SET_REPLACE or IP6T_SO_SET_REPLACE is called in capability mode, structures need to be converted from 32-bit to 64-bit in order to be processed. Since this is an error prone process, the author went through the code that does this translation.
  • The following code is ran: memset(t->data + target->targetsize, 0, pad);. The vulnerability is that the targetsize does NOT account for this offset in the allocation of the target data. This conversion creates an out of bounds memset() with nullbytes as a result. In particular, we can write 4 nullbytes to an offset of at MOST 0x4c bytes out of bounds. The offset is not DIRECTLY controlled by an attacker but can be influenced with different types of objects.
  • Notes on the primitive:
    • Write 4 nullbytes to some controlled offset.
    • The offset is NOT directly controlled. However, using different objects gives us the ability to influence the location of this; the maximum offset is 0x4c bytes.
    • The GFP_KERNEL_ACCOUNT can vary in size. This allows for the changing of malloc slabs (similar to bins) that can be attacked.
    • Only kernel objects with the GFP_KERNEL_ACCOUNT could be used because it uses its own slab.
  • With such a limited primitive, what are good targets? Reference counters, free list pointers and pointers within structs. msg_msg has the GFP_KERNEL_ACCOUNT flag and has been used in many many exploits. The author could not find any good reference counters so they went down the pointer route with msg_msg.
  • The field m_list is a linked list structure with a previous and next pointer. The messages are kept in a linked list. In general, the idea is to have a primary message in the 4096 slab (since msg_msg is dynamic in size) and a secondary message in the 1024 slab. This operation is performed over and over again as a spray in order to make the likelihood of exploitation higher.
  • Once this is setup, every 1024th primary message is freed to put a whole within the grouping of objects. After this, we trigger the vulnerability to overwrite the final 2 bytes of msg_msg->m_list.next. This will point the msg_msg next pointer to a DIFFERENT secondary message than where it should be. Since everything should be page aligned and we successfully sprayed, our exploit should be consistent.
  • Why do we want to point this? Now, we can free one of the message objects to create an unexpected use after free on this object. To make matters better, the messages can even be queried to know which one has been corrupted. Use after free achieved! Using socketpair we swap in a fake object to imitate the msg_msg object. By abusing a sort of type confusion here, the length buffer can be changed to a large size, which leaks memory from the heap.
  • Because this is a unix socket, the structure sk_buff will have a bad reference if we free the message this points to. This object is better for use after frees because the msg_msg object gets unlinked, requiring two writable pointers. If we get this reallocated and write to this buffer, we have a completely data controlled use after free. The author targets the pipe_buffer which has an object that points to function pointers.
  • The above use after free can be used to bypass KASLR. Additionally, the code pointers of the kernel can be leaked from this as well, allowing for JOP/ROP chains to be done. With this, a ROP chain can be developed with the overwritten function pointers. The ROP chain calls commit_creds to install kernel credentials and switch_task_namespaces(find_task_by_vpid(1), init_nsproxy) to change the namespace of a process to be the same as the initial process. Game over!
  • Overall, I enjoyed the diagrams and exploit explanation. The bug is subtle and it is amazing to see how this bug was taken to code execution.