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!

CVE-2021-22555: Turning \x00\x00 into 10000$- 561

Andy Nguyen - GooglePosted 4 Years Ago
  • While looking at old bugs in the Linux kernel, the author of this post decided to simply grep for sketchy looking calls to memcpy or memset. This led the author to a call that had broken logic on the size of the buffer.
  • When IPT_SO_SET_REPLACE is called in compatibility mode and needs to convert the data from 32bit to 64bit in order to be processed by the native functions. Converting data is complicated and tends to be error prone!
  • In the function xt_compat_target_from_user the call to memset is called with an offset that is not accounted for during the allocation. This leads us to an out of bounds write with a few bytes. The size of the overwrite is not directly controllable by the user; but, choosing different structs carefully allows to make the overwrite as big as 0x4C bytes long.
  • The primitive is small though: an out of bounds write with only 4 zeros. In the current structure, we can write up to 0x4C bytes passed the buffer or slightly before. Because of the lack of the control on the bytes being written, we must turn this into another primitive first.
  • Is there anything help we can overwrite? The author mentions reference counters, free list pointers in the heap allocators and a pointer in a struct. In the end, he choose the msg_msg struct to gain a use after free (UAF). This is a good object to spray as well because they are controllable and easy to create.
  • The msg_msg object contains a linked list to the next message in the list at the beginning of the object. The goal of the spraying is to create an insane amount of these messages, overwrite one of the pointer with our vulnerability to point to another one of the messages. Eventually, we will have two pointers pointing to the same message. This is all done by 00ing out a small of the pointer!
  • How do we know which message is the corrupted one though? They tag every message with the index of the message in the queue. If the two tags are different, then we know that the corruption occurred on this chunk.
  • With two pointers pointing to the same message, we can free one of them. Now, we have a use after free on the other object with the forced free that happened. How do we turn this use after free, with controlled values, into something useful?
  • First, we need an information disclosure, such as a heap leak. Using Unix sockets, the author sprayed messages of size 1024 and imitated the struct msg_msg header. By replacing the msg_msg->m_ts value with something large from the socket code, we can leak a substantial amount of information struct with an OOB read.
  • The next pointer in the block above is what we are trying to read. By reading this, we can gather where we are located on the heap. With this heap leak, the double linked list (next and prev from before), can now be re-created. With the pointer fixed, we can free this again when the sk_buff struct is allocated over the top to create a more powerful UAF.
  • The sk_buff buff is better for the uAF because we can use it to free any kind of object in the heap slab. This gives us an even better use after free primitive. They choose the pipe_buffer object because it contains function pointers. A struct within this structure also contains pointers to the .data sections, which is needed for bypassing the code randomization. Reading this is trivial with the current setup.
  • With heap randomization partially broken and the code randomization broken, we can move onto creating a JOP/ROP chain. We overwrite the function pointer within the pipe_buffer object in order to start the chain.
  • The chain calls commit_creds(prepare_kernel_cred(NULL)) to install kernel credentials and switch_task_namespaces(find_task_by_vpid(1), init_nsproxy) to switch namespaces of process 1 to the init process. Now, back in userland, we have root permissions to change process namespaces for Kubernetes.
  • This exploit turns a restricted value write with a limited length into code execution with clever targets and spraying techniques. Wonderful write up!