Vulnerabilidad en Linux (CVE-2025-71066)
Gravedad:
Pendiente de análisis
Tipo:
No Disponible / Otro tipo
Fecha de publicación:
13/01/2026
Última modificación:
19/01/2026
Descripción
En el kernel de Linux, la siguiente vulnerabilidad ha sido resuelta:<br />
<br />
net/sched: ets: Siempre eliminar la clase de la lista activa antes de eliminar en ets_qdisc_change<br />
<br />
zdi-disclosures@trendmicro.com dice:<br />
<br />
La vulnerabilidad es una condición de carrera entre &#39;ets_qdisc_dequeue&#39; y<br />
&#39;ets_qdisc_change&#39;. Conduce a UAF en el objeto &#39;struct Qdisc&#39;.<br />
El atacante requiere la capacidad de crear un nuevo usuario y un espacio de nombres de red<br />
para activar el error.<br />
Ver mi comentario adicional al final del análisis.<br />
<br />
Análisis:<br />
<br />
static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,<br />
struct netlink_ext_ack *extack)<br />
{<br />
...<br />
<br />
// (1) este bloqueo está evitando que el manejador .change (&#39;ets_qdisc_change&#39;)<br />
//compita con el manejador .dequeue (&#39;ets_qdisc_dequeue&#39;)<br />
sch_tree_lock(sch);<br />
<br />
for (i = nbands; i &lt; oldbands; i++) {<br />
if (i &gt;= q-&gt;nstrict &amp;&amp; q-&gt;classes[i].qdisc-&gt;q.qlen)<br />
list_del_init(&amp;q-&gt;classes[i].alist);<br />
qdisc_purge_queue(q-&gt;classes[i].qdisc);<br />
}<br />
<br />
WRITE_ONCE(q-&gt;nbands, nbands);<br />
for (i = nstrict; i &lt; q-&gt;nstrict; i++) {<br />
if (q-&gt;classes[i].qdisc-&gt;q.qlen) {<br />
// (2) la clase se añade a q-&gt;active<br />
list_add_tail(&amp;q-&gt;classes[i].alist, &amp;q-&gt;active);<br />
q-&gt;classes[i].deficit = quanta[i];<br />
}<br />
}<br />
WRITE_ONCE(q-&gt;nstrict, nstrict);<br />
memcpy(q-&gt;prio2band, priomap, sizeof(priomap));<br />
<br />
for (i = 0; i &lt; q-&gt;nbands; i++)<br />
WRITE_ONCE(q-&gt;classes[i].quantum, quanta[i]);<br />
<br />
for (i = oldbands; i &lt; q-&gt;nbands; i++) {<br />
q-&gt;classes[i].qdisc = queues[i];<br />
if (q-&gt;classes[i].qdisc != &amp;noop_qdisc)<br />
qdisc_hash_add(q-&gt;classes[i].qdisc, true);<br />
}<br />
<br />
// (3) el qdisc se desbloquea, ahora dequeue puede ser llamado en paralelo<br />
// al resto del manejador .change<br />
sch_tree_unlock(sch);<br />
<br />
ets_offload_change(sch);<br />
for (i = q-&gt;nbands; i &lt; oldbands; i++) {<br />
// (4) estamos reduciendo el contador de referencias para el qdisc de nuestra clase y<br />
// liberándolo<br />
qdisc_put(q-&gt;classes[i].qdisc);<br />
// (5) Si llamamos a .dequeue entre (4) y (5), tendremos<br />
// un UAF fuerte y podremos controlar RIP<br />
q-&gt;classes[i].qdisc = NULL;<br />
WRITE_ONCE(q-&gt;classes[i].quantum, 0);<br />
q-&gt;classes[i].deficit = 0;<br />
gnet_stats_basic_sync_init(&amp;q-&gt;classes[i].bstats);<br />
memset(&amp;q-&gt;classes[i].qstats, 0, sizeof(q-&gt;classes[i].qstats));<br />
}<br />
return 0;<br />
}<br />
<br />
Comentario:<br />
Esto sucede porque algunas de las clases tienen sus qdiscs asignados a<br />
NULL, pero permanecen en la lista activa. Este commit soluciona este problema al siempre<br />
eliminar la clase de la lista activa antes de eliminar y liberar su<br />
qdisc asociado.<br />
<br />
Pasos para Reproducir<br />
(versión recortada de lo que fue enviado por zdi-disclosures@trendmicro.com)<br />
<br />
```<br />
DEV="${DEV:-lo}"<br />
ROOT_HANDLE="${ROOT_HANDLE:-1:}"<br />
BAND2_HANDLE="${BAND2_HANDLE:-20:}" # child under 1:2<br />
PING_BYTES="${PING_BYTES:-48}"<br />
PING_COUNT="${PING_COUNT:-200000}"<br />
PING_DST="${PING_DST:-127.0.0.1}"<br />
<br />
SLOW_TBF_RATE="${SLOW_TBF_RATE:-8bit}"<br />
SLOW_TBF_BURST="${SLOW_TBF_BURST:-100b}"<br />
SLOW_TBF_LAT="${SLOW_TBF_LAT:-1s}"<br />
<br />
cleanup() {<br />
tc qdisc del dev "$DEV" root 2&gt;/dev/null<br />
}<br />
trap cleanup EXIT<br />
<br />
ip link set "$DEV" up<br />
<br />
tc qdisc del dev "$DEV" root 2&gt;/dev/null || true<br />
<br />
tc qdisc add dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 2<br />
<br />
tc qdisc add dev "$DEV" parent 1:2 handle "$BAND2_HANDLE" \<br />
tbf rate "$SLOW_TBF_RATE" burst "$SLOW_TBF_BURST" latency "$SLOW_TBF_LAT"<br />
<br />
tc filter add dev "$DEV" parent 1: protocol all prio 1 u32 match u32 0 0 flowid 1:2<br />
tc -s qdisc ls dev $DEV<br />
<br />
ping -I "$DEV" -f -c "$PING_COUNT" -s "$PING_BYTES" -W 0.001 "$PING_DST" \<br />
&gt;/dev/null 2&gt;&amp;1 &amp;<br />
tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 0<br />
tc qdisc change dev "$DEV" root handle "$ROOT_HANDLE" ets bands 2 strict 2<br />
tc -s qdisc ls dev $DEV<br />
tc qdisc del dev "$DEV" parent <br />
---truncated---<br />
```
Impacto
Referencias a soluciones, herramientas e información
- https://git.kernel.org/stable/c/062d5d544e564473450d72e6af83077c2b2ff7c3
- https://git.kernel.org/stable/c/06bfb66a7c8b45e3fed01351a4b087410ae5ef39
- https://git.kernel.org/stable/c/45466141da3c98a0c5fa88be0bc14b4b6a4bd75c
- https://git.kernel.org/stable/c/9987cda315c08f63a02423fa2f9a1f6602c861a0
- https://git.kernel.org/stable/c/a75d617a4ef08682f5cfaadc01d5141c87e019c9
- https://git.kernel.org/stable/c/c7f6e7cc14df72b997258216e99d897d2df0dbbd
- https://git.kernel.org/stable/c/ce052b9402e461a9aded599f5b47e76bc727f7de



