minimemo was a heap note like kernel challenge, but it only allowed to add/edit/delete notes on the kernel, so no easy leaks here.
The main bug for this challenge was in the edit function:
The edit function checks the size of our requests, but compares it with NOTE_SIZE (which will be 0x18), but the note.data field has only a size of 0x14, so we can overwrite 4 bytes after note.data.
But in our request, we’ll need to specify the data to copy in the first 0x14 bytes followed by the id of the note to edit. This means, we can overwrite the bytes after note.data only with the id from the note itself.
After note.data will be cur->fd, so we can overwrite the lower 4 bytes of the fd pointer to point somewhere else (for now just with the id of the note).
Since we have no kernel leaks by now, the best idea would be to arrange some objects in such a way, that we only need to overwrite the LSB of the fd pointer to point it to somewhere useful.
To get an initial leak, I created two notes and a msg_seq struct behind the second note (by creating an oversized msg_msg).
To get a proper value for overwriting cur->fd, I kept deleting and recreating the second note until the LSB of its id matched the target address byte.
The last edit, will overwrite the LSB of the fd pointer with 0xc0 (from our forged id). This way, note2->fd will point to the msg_seq struct. In the msg_seq struct, I prepared a fd/bk pointer, pointing into an mmapped region, so the unlink from CMD_DEL will not crash (it just needs two valid pointers).
Since the first qword of the msg_seq struct will be 0x0, we can then delete the note with id 0x0 (delete will traverse the node list, and since we corrupted the fd pointer to the msg_seq struct, it will try to delete that and unlink it from the note list).
After forging note 2 with a proper id and allocating msg_seq heap looks like
After overwriting the LSB of note2->fd
After deleting the note with id 0x0
The unlink functionality from CMD_DEL now updated the bk pointer of our fake chunk.
We can now just receive the complete msg_msg again and get the first leak from it :)
Having a leak to the note heap itself gives us more control, since we can now overwrite complete addresses. To have something useful on the note heap to begin with, I just did a shmem spray to get some kernel addresses there (preferably an address in the same memory region as modprobe_path).
After that, I used the fd lsb overwrite again, but this time, I let the fd pointer point into the note itself, so that we can overwrite the complete fd pointer with an arbitrary value.
Though manipulating the fd pointer alone will not help us much, since we cannot show notes, so we cannot use it to “directly” leak something.
For this, I also put a msg_msg struct in the same heap, and then used the arbitrary overwrite to point the fd of the note into the msg_msg struct, so we can overwrite msg_size and msg_next. With this we can then do an arbitrary read (by pointing msg_next to an address near the address we want to read and setting msg_size > 0xfd0).
Let’s see, what happens there…
After recreating note2 with a proper id:
After overwriting the fd of note 2 with 0x90 and allocating another msg_msg:
After the next edit of note 2, we will have the fd overwritten with a pointer into the msg_msg struct.
So, we now have a fake note inside msg_msg with id = 0x1. We can now edit this fake note to overwrite msg_size and msg_next.
So we now have a fake msg_msg, which has a msg_seq, which points to an address, which will be in the same memory region as modprobe_path, perfect :)
Just receive the complete message:
Now, we can abuse the lsb fd overwrite one last time to overwrite the fd pointer of a note with a pointer above modprobe_path.
This time, I moved it a bit, so that the fd pointer of the note will point to note + 0xc. By doing this, we can overwrite the id of the note and the fd pointer in one go (it must not have an id of 0x0, since that will be the id of our precious modprobe chunk).
So, again, we recreate the note until the lsb of id has a good value (0x8c in this case). We’ll use it again to overwrite the fd to make it point into the note itself (this fake chunk will have id 0x0 for now).
And we then edit this fake chunk, to overwrite the id with something != 0x0 and let the fd point slightly above modprobe_path.
After overwriting fd lsb
After editing the fake note (id 0x0)
Everything prepared, we can now just overwrite modprobe_path and do the “usual” modprobe copy flag exploit :)
As always, my initial exploit didn’t work remote, so I had to completely rewrite it (like shown in this writeup).
But still, it was a little bit off on remote, so I had to allocate an additional 0x40 chunk at the start to make the exploit work (differed between local and remote execution via argc, so the exploit has to be run with pwn 1 when running remote).
That was a very nice and fun challenge (as expected from ptr-yudai ;))
Though from reading the flag, this was maybe not completely the intended way, since I used the unlink just for the initial leak.
In hindsight, doing writes via abusing the unlink might have been much easier (since there are no checks on fd/bk), but even after completely rewriting the exploit after it failed remote, it was still enough for first blood, so… shrug :)