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!
register category. If data can be read back, then it's a valid handler. Neat trick to figure out how things work! Every device that used this interface did it in a completely different way, making the reverse engineering even more fun to deal with. memcpy. Since we control BOTH the size of the overflow and the bytes with no character restrictions, this is a pretty awesome bug to exploit. socket fd. So, they brute forced the socket fd between different runs. system. We still need a place to reference a string and we do not have a stack leak. So, they used a known trick with 32-bit and mmap not being randomized so well. By making a large enough allocation in LibC, it creates an mmap call directly, making it trivial to find our string. "nvram set http_passwd=nccgroup && sleep 4 && utelnetd -d -i br0" to backdoor the device. Now, The password is reset and we can take full control of the router. slaacd is a stateless address auto configuration (SLAAC) daemon. In the IPv6 protocol stack, the Neighbor Discovery (ND) protocol is used for gathering information about network communications, gateways and many other things. One particular type is a Router Advertisement (RA) packet. This particular bug is an OpenBSD. length byte is improperly used as a signed value instead of an unsigned value. By crafting a length that has the most significant bit set to 1, it believes that it is a negative number instead of a large positive number. memcpy. memcpy, the signed integer is transparently turned into an unsigned integer. This leads to a huge write occurring within this memory space, ending up as a heap overflow. Since this is a bad size wildcopy, it is unlikely to be exploitable. entry = &flow->rule->action.entries[ctx->num_actions++];This line caught their eye because it was incrementing
ctx->num_actions and using it as an index without any bounds checks. Secondly, the struct flow->rule->action.entries had seemingly not obvious relationship with ctx->num_actions. Not a bug yet but a code smell that prompted for more review!action.entries array, what are the controls on this function and how are things initialized? All great questions to get to the point of figuring out if this is a bug or not. While diving into this, they found that the path for how things were called. Additionally, they found that the num_actions counter is ONLY incremented when the NFT_OFFLOAD_F_ACTION flag is set on the offload flags. NFT_OFFLOAD_F_ACTION. The problem is that the actions array is allocated based on the number of immediate expressions types and does NOT take into consideration that this index could be incremented. As a result, the MAX index would be different than the original allocation size. nftables. After playing around with the CLI for a while, they noticed it was adding arbitrary restrictions, which screwed up his exploitation attempts. Because of this, they use the GoLang implementation with custom overwrites. After playing with binary settings and tracing code, they were able to trigger the bug with a kernel panic. Exploitation time :) entry struct to play with. In particular, the fields id and dev were being written. To find the offsets of these structs, they used pahole. The id field writes a value of 4 or 5; dev was writing a pointer 24 bytes past the end of the array. msg_msg at offset 40 (which is hittable offset by us) to land an arbitrary kfree. msg_msg as a target, it will eventually be freed. Since the write will have our net_device pointer (dev in the code above), we have manufactured a use after free from this. In order to make this viable, a ton of heap spraying was done to get tihs to line up JUST right for the attack. net_device.netdev_ops function pointer to get code execution in the kernel when this object is overlapped. The exploit chain above has a few nice quirks:
list_head.prev inside of the msg_msg can be FOUND by calling MSG_CPY. This allows us to know which one is corrupted. /code> of a security pointer tells us WHICH one was corrupted with the ID value from above. Boom, code execution! decompressed_size field. When the copy actually happened, this size value is not used though! Instead, the data within the buffer is used instead. This creates a fairly standard buffer overflow. compressed_data field. This is then subtracted from the current pointer location. There is NO validation that the offset is smaller than the real pointer being used. Down the road, a copy is done, leading to an out of bounds read. A similar variant of this bug exists but is going forward on the offset instead of backwards. m_keyBlobBlock. If this variable is harder than 128, then a buffer global memory buffer overflow occurs. %ENV_VAR% can be used to reference environmental variables. Commonly, this is used within CMD as developers.%username%, it would return the value of the environmental variable! It is common for developers to store secret information, such as AWS_SECRET_ACCESS_KEY, Github secrets and many other things. As a result, this attack could be used to steal sensitive information from a device. window.showSaveFilePicker function, a malicious website can specify the suggstedName field for the file. pinns utility to set kernel options for a pod. Recently, pinns added support for sysctl. Pinns will blindly add kernel parameters, including sysctl without any validation. This is an interesting primitive: can we do anything with this?sysctl flag kernel.core_pattern can be used for how the kernel should react to a core dump. In this case, we will set this to a script that we control within our container to be triggered. Since this needs to be an absolute path, we also need to find the full path of the script. This can be done via the mount command. Splice is used to send all compressed files. This syscall is used for copying data between one file to another in kernel space, which helps with I/O performance. write is called to write the directory file header. This is 50 4b 01 02 1e 03 14 00. This is EXACTLY the corruption that is happening. splice followed by a call to write, simulating the zip generator. Even though the write was NOT occuring on the call to splice, the string from the second write appeared to be in the file being read. Specifically, this problem existed in pipes. pipe, a reference to a page cache entry is made via the data structure pipe_buffer. These objects have a reference to the backing memory page and have several flags for what permissions and things are allowed. The main bug is that the flags for this are NOT initialized when using pipes. In Linux 5.8, the flag PIPE_BUF_FLAG_CAN_MERGE was added, which indicates whether a page table is mergable or not. Practically, a poisoned set of flags, namely PIPE_BUF_FLAG_CAN_MERGE, allows data to be written to a page cache that the pipe does not own!PIPE_BUF_FLAG_CAN_MERGE flag in ALL of the entries.