Add kernelCTF CVE-2024-46800_lts_cos_mitigation#290
Add kernelCTF CVE-2024-46800_lts_cos_mitigation#290hexfoureight wants to merge 10 commits intogoogle:masterfrom
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.
Please add information on capabilities needed (if any?) to trigger the vulnerability. Do you use user namespaces?
There was a problem hiding this comment.
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.
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.
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.
Hello @hexfoureight
Thanks for submitting the exploit and writeup. Please, have a look at my comments and fix them.
|
Hey! I've left a few comments on #261, I guess they apply to here as well, but let's discuss them there and when we finished there, let's apply the changes here as well. |
No description provided.