Mini memo is a linux kernel heap challenge that is vulnerable to a 4-bytes buffer overflow of randomly generated bytes and a linked-list unlink corruption.
It's important to keep in mind that the nodes on the list have a randomly generated 4-bytes id instead of ordered indexes, and the way the algorithm selects the notes is by traversing through the list's fd pointers until it finds a node with the requested id.
Buffer overflow
In the edit command, when the user request is copied to the node's note_t object, the call to memcpy uses req.size as the size to copy, which is a user provided size. The provided size is checked against NOTE_SIZE , which is the size of the entire note_t structure (including note.id), which is 0x18 bytes long, but the data is copied to note.data, which is only 0x14 long. The check is passed if the provided is size is less than NOTE_SIZE, so 3 bytes will overflow into the 3 least significant bytes of the node's fd pointer.
if (req.size <0|| req.size >= NOTE_SIZE)break;memcpy(cur->note.data, req.data, req.size); //copies 23 bytes into 20 bytes buffer
The overflown bytes are also not controllable. Controlling the id of a chunk upon allocating it would be the only way of controlling those bytes but there is no overflow in copy_from_user when writing to the node_t structure, so the 3 bytes that will overwrite fd are the 3 least significant bytes of the randomly generated id. This is because, for the algorithm to be able to select a node in the list, the req.id field must match the desired chunk's id, so providing an arbitrary id would obviously fail.
Bruteforce LSB
A pretty simple way to work around this and have better control of the overflow is to allocate and deallocate new objects until we get and arbitrary id. In order to leverage this we would only need to control the LSB of the fd pointer, that should be enough to get the list to traverse to an arbitrary data on the slab, with the help of a heap spray we can pretty reliably control where the list traverses to and start to corrupt stuff.
If we bruteforce a LSB to garantee that the overwritten fd will point to a predictable address, we can spray msg_msg objects with a fake chunk in order to have one of them lying in our predictable address.
Defeating kASLR
Once we can make the list traverse to a fake chunk on the heap we can now fully control where it traverses to next, this allows us to, for example, to traverse to a mmaped region and have fully controllable objects in the list, which eases exploitation and helps with leaks.
For example, if we unlink a node adjacent to one of our mmap nodes we can start to leak pointers. We could leak a heap pointer, but that wouldn't be all that helpful, instead, we can try to have our controllable/readable chunk adjacent to a chunk with a pointer to the top of the list, which is an module address and can be used to defeat kASLR.
cur->bk->fd = cur->fd; cur->fd->bk = cur->bk;
At this point we have the following layout:
If we delete the corrupted chunk and the fake chunk allocated with msg_msg, we can get the mmaped chunk's bk to point to the top of the list.
Then we can just read it out of the readable mmaped region.
Arbitrary write
At this point we have gained full control of the list, we can have as many chunks as we want and read/write to them as much as we want. If we make the list traverse to an arbitrary address we can pretty much write anywhere we want using the edit ioctl command. For this exploit I simply made the list traverse to a fake chunk whom data overlaps with the modules file operations structure, this way we can replace the ioctl callback with a pointer to our shellcode since SMEP and SMAP are off.
So next time we try to call ioctl on the module our shellcode will execute.