-
Notifications
You must be signed in to change notification settings - Fork 509
Add kernelCTF CVE-2024-46800_lts_cos_mitigation #290
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
base: master
Are you sure you want to change the base?
Add kernelCTF CVE-2024-46800_lts_cos_mitigation #290
Conversation
| @@ -0,0 +1,22 @@ | |||
| A vulnerability in the traffic control subsystem's netem qdisc (`CONFIG_NET_SCH_NETEM ` in the kernel config) can lead to a use-after-free. If a netem qdisc has a child qdisc, `netem_dequeue()` will attempt to enqueue a packet to it (for every other qdisc type this happens during enqueue; netem does it this way to allow a per packet delay). If this `qdisc_enqueue()` call returns`__NETEM_XMIT_STOLEN`, the packet will be dropped but the parent qdisc's `q.qlen` will not be updated. Then `qlen_notify()` may be skipped on the parent during destruction, leaving a dangling pointer for some classful qdiscs like DRR. | |||
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.
Please add information on capabilities needed (if any?) to trigger the vulnerability. Do you use user namespaces?
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've updated vulnerability.md to specify that user namespaces are needed.
|
|
||
| ## Triggering the Vulnerability | ||
|
|
||
| The `trigger_vuln()` function triggers the vulnerability under the passed class. A buggy netem qdisc is added as a child of the class and then removed after the vulnerability has been triggered: |
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.
So before trigger_vuln is called, we have such a situation:
[Some DRR qdisc] → [DRR class "parent"] → [default/existing child qdisc]
Is this correct?
When we call void trigger_vuln (int parent), we replace the child qdisc, which makes (those are first 2 calls in function):
[DRR qdisc] → [DRR class "parent"] → [netem] → [drr] (empty, no classes)
and after that we add filter and class like this
[DRR qdisc] → [DRR class "parent"] → [netem] → [drr] → [drr class 1]
↑
(filter with TC_ACT_STOLEN)
Then we send packet with loopback_send(). And following happens:
- Packet arrives, gets classified to
parentclass (due to outer filter set up beforetrigger_vuln) parentis added to active list of its parent DRR qdisc- Packet travels:
parent→netem→drr→drr class 1 - TC_ACT_STOLEN action drops the packet
- Bug:
parentstays on active list even though packet was dropped
Finally, we call tc_del_qd(parent); which deletes the qdisc attached to "parent" (the netem). This deletes the netem qdisc (child of parent), which causes the parent class to be freed. And
[DRR qdisc] still has active_list pointing to → [FREED "parent" class]
↑
USE-AFTER-FREE!
Is my logic correct? Could you add more information / explanations on how we actually trigger the vulnerability?
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.
Is my logic correct? Could you add more information / explanations on how we actually trigger the vulnerability?
It's all correct except for this part:
Finally, we call
tc_del_qd(parent);which deletes the qdisc attached to "parent" (the netem). This deletes the netem qdisc (child ofparent), which causes theparentclass to be freed.
parent is not freed in trigger_vuln(), it is only put in a buggy state such that it will remain on the active list after being freed. The free happens when the class itself is deleted, here:
tc_del_cl(b1); // b1 freed
tbfp = b2 = drr_spray_and_find(b2);
and here:
tc_del_cl(b2); // b2 freed
b3 = drr_spray_and_find(b3);
I've updated the the docs to make this clearer.
I also noticed that the DRR qdisc does not actually need to have a class for the TC_ACT_STOLEN filter to work and updated trigger_vuln() to not create the class.
artmetla
left a comment
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.
Hello @hexfoureight
Thanks for submitting the exploit and writeup. Please, have a look at my comments and fix them.
No description provided.