Skip to content
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

Add kernelCTF CVE-2023-31436 #34

Merged
merged 7 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading