Skip to content

Commit

Permalink
Add kernelCTF CVE-2023-31436 (#34)
Browse files Browse the repository at this point in the history
* Add kernelCTF CVE-2023-31436

* Update pocs/linux/kernelctf/CVE-2023-31436_mitigation/metadata.json

Co-authored-by: Tamás Koczka <[email protected]>

* Update pocs/linux/kernelctf/CVE-2023-31436_mitigation/metadata.json

Co-authored-by: Tamás Koczka <[email protected]>

* Update pocs/linux/kernelctf/CVE-2023-31436_mitigation/metadata.json

Co-authored-by: Tamás Koczka <[email protected]>

* Update Makefile target

* Add option to use separate kaslr leak

* Adjust used based address

---------

Co-authored-by: Tamás Koczka <[email protected]>
  • Loading branch information
liona24 and koczkatamas authored Sep 20, 2023
1 parent 7b04842 commit 7b14d11
Show file tree
Hide file tree
Showing 8 changed files with 1,678 additions and 0 deletions.
427 changes: 427 additions & 0 deletions pocs/linux/kernelctf/CVE-2023-31436_mitigation/docs/exploit.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
As far as I am aware kernel heap data only attacks using kernel/user shared memory have
gained little to no attention yet.

The `struct xdp_mem` with the `AF_XDP` socket ([docs](https://www.kernel.org/doc/html/latest/networking/af_xdp.html))
used in this exploit seems to serve as a powerful primitive:
Advantages:
- Read / Write of "kernel" memory without restrictions (even fault shenanigans seem like a good idea,
though I did not explicitly look into this)
- No heap pointers required
- No size restrictions even though acting as a `kmalloc-128` object
- Very useful as a "fake list member"

Disadvantages:
- One level of pointer indirection
- Initial pointer restricted to `kmalloc-128`
- Requires `CAP_NET_RAW`

Depending on the primitives available unaligned pointer corruption may come in handy
when dealing with objects where the mapped member does not align with the desired pointer.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
Vulnerability Details
=====================

CVE-2023-31436: qfq_change_class in net/sched/sch_qfq.c in the Linux kernel before 6.2.13 allows an out-of-bounds write because lmax can exceed QFQ_MIN_LMAX.

This vulnerability affects the packet scheduler subsystem, specifically QFQ+.

An attacker can utilize this vulnerability to cause a slab-out-of-bounds read/write in the `(dyn-)kmalloc-8192` cache.

## Requirements

A user needs to be able to modify qdiscs, thus requiring `CAP_NET_ADMIN`.
Naturally this will be obtained through usernamespaces, thus one may require `CONFIG_USER_NS`.

The specific qdisc in question is QFQ, which needs to be enabled `CONFIG_NET_SCH_QFQ`.

## History

The fixing commit is https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3037933448f60f9acb705997eae62013ecb81e0d.
This is a fix for 3015f3d2a3cd ("pkt_sched: enable QFQ to support TSO/GSO"), which dates way back to 2011.
Based on this I assume 2.6.x+ is affected.

## Triggering the Vulnerability

In order to trigger the vulnerability an attacker needs to modify qfq classes
after modifying the MTU of the device to a large value (> 0x100000).
This can be trivially achieved for the loopback device.

When changing a class and ommitting the `TCA_QFQ_LMAX` option, the `lmax` value is chosen according to the MTU of the device, without any additional checks [1]:
```c
// qfq_change_class() in net/sched/sch_qfq.c

// ..
if (tb[TCA_QFQ_LMAX]) {
lmax = nla_get_u32(tb[TCA_QFQ_LMAX]);
if (lmax < QFQ_MIN_LMAX || lmax > (1UL << QFQ_MTU_SHIFT)) {
pr_notice("qfq: invalid max length %u\n", lmax);
return -EINVAL;
}
} else
lmax = psched_mtu(qdisc_dev(sch)); // [1]

// ..

qfq_init_agg(q, new_agg, lmax, weight); // [2]
}

// ..

qfq_add_to_agg(q, new_agg, cl); // [3]
```
`qfq_init_agg` will then set `new_agg->lmax` accordingly.
Eventually `qfq_add_to_agg()` [3] will initialize `new_agg->grp` when the call tree
reaches `qfq_update_agg()`:
```c
// qfq_update_agg() in net/sched/sch_qfq.c
agg->budgetmax = new_num_classes * agg->lmax;
new_agg_weight = agg->class_weight * new_num_classes;
agg->inv_w = ONE_FP/new_agg_weight;
if (agg->grp == NULL) {
int i = qfq_calc_index(agg->inv_w, agg->budgetmax,
q->min_slot_shift);
agg->grp = &q->groups[i]; // [4]
}
```

`qfq_calc_index()` performs some simple arithmetics to choose the final value,
but will not do any additional bounds checks.
Eventually this results in `agg->grp` pointing out-of-bounds [4] relative to the `q` object of type `struct qfq_sched` (in the `kmalloc-8192` cache).

The group of the `qfq_aggregate` is used in several places, leading to OOB reads and writes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
all: exploit.c bin
$(CC) exploit.c -o bin/exploit -O3 -static

exploit: exploit.c
$(CC) exploit.c -o exploit -O3 -static

bin:
mkdir -p bin/

run:
./bin/exploit
Binary file not shown.
Loading

0 comments on commit 7b14d11

Please sign in to comment.