-
Notifications
You must be signed in to change notification settings - Fork 403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add kernelCTF CVE-2024-26808_cos #113
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey!
Thank you for the submission!
I've requested a few changes below. There are 11 comments, the Github UI will only slow 10 by default and will hide 1 additional comment, please make sure to click on the "Load more..." button to see all comments.
Thanks!
|
||
mnl_attr_put_str(nlh, IFLA_IFNAME, link_name); | ||
linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO); | ||
mnl_attr_put_str(nlh, IFLA_INFO_KIND, "dummy"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please explain what this "dummy" info_kind does here? I see guess the call stack is __rtnl_newlink
-> rtnl_newlink_create
-> rtnl_create_link
, but I did not find the "dummy" kind in the code and it seems the interface tries to call request_module("rtnl-link-%s", kind)
, but I did not find rtnl-link-dummy
either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We create dummy network interfaces to reach vulnerable code path without affect other devices. (We just followed the example in rtnl source code). CONFIG_DUMMY is enabled by default in most of linux distro, it will register "dummy" kind at function dummy_init_module, please refer to drivers\net\dummy.c
With these two limited primitive, our exploit is in the following steps: | ||
|
||
1. Cross cache to overwrite `net_device` object in kmalloc-cg-4k to `packet_fanout` object in kmalloc-4k. | ||
2. `packet_fanout` object has `net` address in the first 8 byte, we leak this using UAF leak primitive. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explain here, that you are doing this, because you need to know the right net
pointer to pass the dev_net(dev) != net
check. Any the only reason to use packet_fanout
because that's an object which contains this (namespace-specific?) pointer in the first 8 bytes as your primitive can only leak the first 8 bytes.
(You are describing what you are doing, which is great, but it is easier to understand the exploit, if you also explain WHY you are doing this. I know this was mentioned that you have to leak the net
address before, but it's better to mention again, so the reader can "connect the dots".)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I add in step 2: we leak net using UAF leak primitive to satisfy condition dev_net(dev) != net when perform arbitrary free at step 6.
. the first 8 bytes leak already explained at UAF Leak section
1. Cross cache to overwrite `net_device` object in kmalloc-cg-4k to `packet_fanout` object in kmalloc-4k. | ||
2. `packet_fanout` object has `net` address in the first 8 byte, we leak this using UAF leak primitive. | ||
3. Using another UAF object, this time we overwrite `net_device` with `msg_msg` object, no need cross cache we use same kmalloc-cg-4k. | ||
4. We fill `msg_msg.next` (first 8 byte offset) with kmalloc-cg-192 by send `msg_msg` with size 0x90 for every `msg_msg` that already placed in kmalloc-cg-4k. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both msg_msg
and pipe_buffer
are elastic objects, seemingly you could've chosen any cache, why did you choose kmalloc-cg-192
? Was there any particular reason for that? Less noise?
Please also document this in the writeup.
Did you choose the size of 0x90
because that's bigger than 128, but less than 192, so it will be kmalloc-cg-192
, and any other value between 129 - 192 would've worked or was there any other condition which required you to choose exactly 0x90?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I add this in the step: kmalloc-cg-192 guarantee that the first byte doesn't contain null byte, so we can leak kmalloc-cg-192 msg_msg addr using UAF leak.
8. Reclaim it as another `pipe_buffer` B. | ||
9. Free pipe_buffer B and reclaim it as `msg_msgseg`. | ||
10. Call syscall vmsplice to vDSO memory page into `pipe_buffer` A, this method gives `struct page` addr of kernel text to `pipe_buffer`. | ||
11. Read from `msg_msgseg` to read content of `pipe_buffer` A. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be to mention that you are doing this to leak the page
field of the pipe_buffer
struct (at offset 0x00 - 0x08) because it contains the address of vdso_image_64.data
aka. raw_data
symbol.
You can add this to the writeup or code too (probably most useful near to the code where you use0x863000
):
gef➤ p vdso_image_64.data
$5 = (void *) 0xffffffff82d3b000 <raw_data>
gef➤ p &core_pattern
$6 = (char (*)[128]) 0xffffffff8359e7a0 <core_pattern>
gef➤ p 0xffffffff8359e000-0xffffffff82d3b000
$7 = 0x863000
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
10. Call syscall vmsplice to vDSO memory page into `pipe_buffer` A, this method gives `struct page` addr of kernel text to `pipe_buffer`. | ||
11. Read from `msg_msgseg` to read content of `pipe_buffer` A. | ||
12. Modify the `page` value so it will point to the `core_pattern` `page`'s addr and modify `PIPE_BUF_FLAG_CAN_MERGE`. | ||
13. Free this `msg_msgseg`/`pipe_buffer` A and overwrite again with new content. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mention that in this step you want to spray the fake pipe_buffer
you prepared in the previous step (step 12) but you cannot modify msg_msgseg, so you have to free the current one and spray the new content to reallocate your fake pipe_buffer
object at the same memory address.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
msgrcv(msqid[i + 0x2000], &msg, | ||
0x1000 - 0x30 + 0xc0 - 0x8, 6, 0); | ||
// modify struct page that will represent of `core_pattern`'s page | ||
p->page += (0x863000 >> 6); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment here that p->page
was originally pointing vdso_image_64.data
(aka. raw_data
, defined in arch/x86/entry/vdso/vdso-image-64.c
)'s vmemmap address (which contains struct page*).
Explain how this calculation work: 0x863000 >> 6
.
gef➤ p vdso_image_64.data
$5 = (void *) 0xffffffff82d3b000 <raw_data>
gef➤ p &core_pattern
$6 = (char (*)[128]) 0xffffffff8359e7a0 <core_pattern>
gef➤ p 0xffffffff8359e000-0xffffffff82d3b000
$7 = 0x863000
The >> 6
comes from that the difference between the address of the pages of vdso_image_64.data
and core_pattern
is 0x863000, but for every 4096 byte page, there is a 64 byte struct page* stored in vmemmap, so to calculate the difference between struct page*
addresses, you have to do: 0x863000 / 4096 * 64
which equals to 0x863000 >> 6
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
0x1000 - 0x30 + 0xc0 - 0x8, 6, 0); | ||
// modify struct page that will represent of `core_pattern`'s page | ||
p->page += (0x863000 >> 6); | ||
p->offset = 0x7a0 - 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mention that the address of core_pattern
is 0xffffffff8359e7a0
, so 0x7a0
is the offset within core_pattern
's page. Also mention that - 1
is because p->len == 1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
printf("msgget01 Failed 0x%x\n", i); | ||
} | ||
|
||
struct fanout_args fa = {.max_num_members = 0x100}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does max_num_members = 0x100
do? Why do you use this value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
max_num_members == 0x100 to make struct packet_fanout in kmalloc-4096 cache
match = kvzalloc(struct_size(match, arr, args->max_num_members),
GFP_KERNEL);
gef➤ p sizeof(struct packet_fanout) + sizeof(struct sock*)*0x100
$4 = 0x8c0
5. Leak kmalloc-cg-192 `msg_msg` addr using UAF leak. | ||
6. Use arbitrary free to free a chunk in kmalloc-cg-192 we got before. | ||
7. Reclaim it as `pipe_buffer` A and use arbitrary free again at same address. | ||
8. Reclaim it as another `pipe_buffer` B. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this step if not needed (from the documentation and from the exploit).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
6. Use arbitrary free to free a chunk in kmalloc-cg-192 we got before. | ||
7. Reclaim it as `pipe_buffer` A and use arbitrary free again at same address. | ||
8. Reclaim it as another `pipe_buffer` B. | ||
9. Free pipe_buffer B and reclaim it as `msg_msgseg`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mention that you are using msg_msgseg
because you a structure which cannot be easily corrupted by the pipe_buffer
which will take its place (you can also mention that you cannot use the previous msg_msg
you used before, because pipe_buffer
would overwrite its important fields and it would crash).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
No description provided.