Incoming details.
https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=1d4fd9fce1677572f7118cdddb05466d03c4df13 commit 1d4fd9fce1677572f7118cdddb05466d03c4df13 Author: Mike Gilbert <floppym@gentoo.org> Date: Wed Jan 9 10:28:00 2019 -0500 sys-apps/systemd: security fixes Bug: https://bugs.gentoo.org/674144 Package-Manager: Portage-2.3.54_p2, Repoman-2.3.12_p42 Signed-off-by: Mike Gilbert <floppym@gentoo.org> sys-apps/systemd/Manifest | 2 + sys-apps/systemd/systemd-239-r3.ebuild | 448 ++++++++++++++++++++++++++++++++ sys-apps/systemd/systemd-240-r3.ebuild | 457 +++++++++++++++++++++++++++++++++ 3 files changed, 907 insertions(+)
Report public
Qualys Security Advisory System Down: A systemd-journald exploit ======================================================================== Contents ======================================================================== Summary CVE-2018-16864 - Analysis - Exploitation CVE-2018-16865 - Analysis - Exploitation CVE-2018-16866 - Analysis - Exploitation Combined Exploitation of CVE-2018-16865 and CVE-2018-16866 - amd64 Exploitation - i386 Exploitation Acknowledgments Timeline Conversion, software version 7.0 -- System of a Down, "Toxicity" ======================================================================== Summary ======================================================================== We discovered three vulnerabilities in systemd-journald (https://en.wikipedia.org/wiki/Systemd): - CVE-2018-16864 and CVE-2018-16865, two memory corruptions (attacker-controlled alloca()s); - CVE-2018-16866, an information leak (an out-of-bounds read). CVE-2018-16864 was introduced in April 2013 (systemd v203) and became exploitable in February 2016 (systemd v230). We developed a proof of concept for CVE-2018-16864 that gains eip control on i386. CVE-2018-16865 was introduced in December 2011 (systemd v38) and became exploitable in April 2013 (systemd v201). CVE-2018-16866 was introduced in June 2015 (systemd v221) and was inadvertently fixed in August 2018. We developed an exploit for CVE-2018-16865 and CVE-2018-16866 that obtains a local root shell in 10 minutes on i386 and 70 minutes on amd64, on average. We will publish our exploit in the near future. To the best of our knowledge, all systemd-based Linux distributions are vulnerable, but SUSE Linux Enterprise 15, openSUSE Leap 15.0, and Fedora 28 and 29 are not exploitable because their user space is compiled with GCC's -fstack-clash-protection. This confirms https://grsecurity.net/an_ancient_kernel_hole_is_not_closed.php: "It should be clear that kernel-only attempts to solve [the Stack Clash] will necessarily always be incomplete, as the real issue lies in the lack of stack probing." [...] ------------------------------------------------------------------------ Exploitation ------------------------------------------------------------------------ For today we will take the body parts and put them on the wall -- System of a Down, "Dreaming" To leak a stack address or an mmap address from journald: - First, we send a large native message to /run/systemd/journal/socket; journald mmap()s our message, and malloc()ates a large array of iovec structures: most of these structures point into our mmap()ed message, but some of them point to the stack (in dispatch_message_real()). The contents of this iovec array (especially the mmap and stack pointers) are preserved in a heap hole after free() (after journald finishes processing our message). - Next, we send a large syslog message to /run/systemd/journal/dev-log; to receive our large message (in server_process_datagram()), journald realloc()ates its server buffer into the heap hole that previously contained the iovec array (and still contains remains of mmap and stack pointers). - Last, we send a large syslog message that exploits CVE-2018-16866; journald receives our large message in its server buffer (in the heap chunk that previously contained the iovec array), and if we carefully choose the size of our message and position its terminating ":" in front of a remaining mmap or stack pointer, then we can leak this pointer (it is mistakenly read out-of-bounds as the body of our message). From this leaked stack pointer we easily deduce journald's stack pointer before the alloca() jump, because the distance between the two depends only on journald's executable. From the leaked mmap address we can deduce libc's address, but chunks of unknown sizes are mmap()ed between the two, and we must therefore adopt different strategies based on our target architecture (i386 or amd64). ======================================================================== Combined Exploitation of CVE-2018-16865 and CVE-2018-16866 ======================================================================== Don't leave your seats now Popcorn everywhere ... -- System of a Down, "CUBErt" ------------------------------------------------------------------------ amd64 Exploitation ------------------------------------------------------------------------ - To deduce libc's address from the leaked mmap address of our native message, we arrange for this message to be mmap()ed into the 2MB hole between ld.so's read-execute and read-only segments: from this hole's address we deduce ld.so's address, and hence libc's address (with help from ldd's output). - If the resulting stack-to-libc distance is jumpable (if it is shorter than 4GB), then we proceed with our "write-what-where"; otherwise, we restart journald (we crash it with an alloca() of RLIMIT_STACK -- 8MB by default) and try again. We have a good chance of obtaining a jumpable stack-to-libc distance (and hence a root shell) after 2048 tries * 2 seconds ~= 68 minutes (by default, if journald crashes less than 5 times within 10 seconds, it is restarted automatically by systemd). - For the "write-where" part of our "write-what-where", we overwrite libc's __free_hook function pointer, whose address modulo 16 is always equal to 8 (on every amd64 distribution that we exploited). - For the "write-what" part of our "write-what-where", we overwrite __free_hook with the address of libc's system() function: whenever journald free()s data that we control, we achieve arbitrary command execution. Last-minute note: on CentOS 7, the usual function pointers in libc's read-write segment (__free_hook, __malloc_hook, etc) are not located at multiples of 16 plus 8. To circumvent this problem: - First, we overwrite the "_chain" pointer of stderr's FILE structure with the address of our own fake FILE structure (this "_chain" pointer is located at a multiple of 16 plus 8, in libc's read-write segment). - Next, we corrupt one of malloc's internal variables (also in libc's read-write segment). - Last, we force a call to malloc() or free(), which detects the corruption of its internal variable and calls abort(), which calls _IO_flush_all_lockp(), which follows stderr's overwritten "_chain" pointer to our fake FILE structure; we eventually achieve arbitrary command execution by calling libc's system() via one of the function pointers in our fake FILE structure. ------------------------------------------------------------------------ i386 Exploitation ------------------------------------------------------------------------ Our i386 exploit is very similar to the amd64 exploit, but: - The stack-to-libc distance is always jumpable (it is roughly 128MB). - There is no hole between ld.so's read-execute and read-only segments. However, libc's address is randomized in a narrow range of 1MB and is therefore brute forcible: we have a good chance of correctly guessing libc's address after 1MB / 4KB = 256 tries * 2 seconds ~= 8 minutes. - For the "write-where" part of our "write-what-where", we overwrite libc's __malloc_hook function pointer (__free_hook was never located at a multiple of 16 plus 8 or 12 on the i386 distributions that we exploited, but __malloc_hook always is). - For the "write-what" part of our "write-what-where", we overwrite __malloc_hook with the address of a "mov esp, 0x89fffa5d ; ret" gadget (or equivalent stack pivot): since our native message can be as large as 768MB, we can mmap() it at 0x89fffa5d, take control of the stack, and return into libc's execve().
x86 stable
amd64 stable
arm64 stable
ia64 stable
ppc stable
ppc64 stable
alpha stable
sparc stable
arm stable, all arches done.
This issue was resolved and addressed in GLSA 201903-07 at https://security.gentoo.org/glsa/201903-07 by GLSA coordinator Aaron Bauman (b-man).