[FD] Qualys Security Advisory - CVE-2015-3245 userhelper - CVE-2015-3246 libuser
Hello, it is July 23, 2015, 17:00 UTC, the Coordinated Release Date for CVE-2015-3245 and CVE-2015-3246. Please find our advisory below, and our exploit attached. Qualys Security Advisory CVE-2015-3245 userhelper chfn() newline filtering CVE-2015-3246 libuser passwd file handling --[ Summary ]- The libuser library implements a standardized interface for manipulating and administering user and group accounts, and is installed by default on Linux distributions derived from Red Hat's codebase. During an internal code audit at Qualys, we discovered multiple libuser-related vulnerabilities that allow local users to perform denial-of-service and privilege-escalation attacks. As a proof of concept, we developed an unusual local root exploit against one of libuser's applications. [ Vulnerability #1 (CVE-2015-3245 userhelper chfn() newline filtering) We discovered a bug in userhelper, a setuid-root program from the usermode package that provides a basic interface to change a user's password, gecos information, and shell; its -f (Full Name), -o (Office), -p (Office Phone) and -h (Home Phone) command-line options are equivalent to those of the traditional chfn program. userhelper's chfn() function verifies that the fields it was given on the command-line are sane (i.e., contain no forbidden characters). Unfortunately, these forbidden characters (:,=) do not include '\n' and allow local attackers to inject newline characters into /etc/passwd and alter this file in unexpected ways. To the best of our knowledge, this bug is a local denial-of-service only: we were not able to turn it into a local root exploit, but maybe some creative minds will. There is another, secondary aspect of this bug: userhelper depends on libuser to modify /etc/passwd, and libuser's format_generic() and generic_setpass() functions reject fields containing a ':' that would be interpreted as a field separator. Vulnerability #1 could have been prevented if libuser had also rejected '\n' characters. [ Vulnerability #2 (CVE-2015-3246 libuser passwd file handling) We discovered a bug in libuser itself: even though traditional programs like passwd, chfn, and chsh work on a temporary copy of /etc/passwd and eventually rename() it, libuser modifies /etc/passwd directly. Unfortunately, if anything goes wrong during these modifications, libuser may leave /etc/passwd in an inconsistent state. This bug is not just another local denial-of-service: we were able to turn it into a local root exploit against userhelper and chfn (if linked with libuser). There is also another, secondary aspect of this bug: glibc modules like nss and nscd do not expect /etc/passwd to be directly modified while they parse its contents, and programs from packages like shadow-utils and util-linux use lckpwdf() locks that are incompatible with libuser's fcntl() locks. --[ Exploitation Overview ]--- In this section, we outline our userhelper exploit against libuser's Vulnerability #2; later in this advisory, we explain how it can be easily adapted to chfn (if linked with libuser). Our ultimate goal is to inject an arbitrary line into /etc/passwd (for example, the a-line \na::0:0::/:\n) but we first need to understand how libuser's generic_mod() function modifies our own user's line in /etc/passwd: - open() /etc/passwd for reading and writing (O_RDWR, but not O_APPEND nor O_TRUNC); - acquire the file's fcntl() write-lock (an exclusive, but advisory lock); - read() the file's contents (into a g_malloc()ated buffer); - lseek() the file to the beginning of our user's line (and skip the unmodified lines that precede); - write() our user's new, modified line (and the rest of the unmodified lines that follow) to the file; - ftruncate() the file (if our user's new, modified line is shorter than the old one); - release the file's fcntl() write-lock; - close() the file. Surprisingly, we only need two things in our toolbox in order to exploit this function and inject the a-line into /etc/passwd: - a pencil and eraser that allows us to repeatedly write() and re-write() our own GECOS field (its length and last character in particular) in /etc/passwd: the userhelper program itself; - a pair of scissors that allows us to interrupt write() with byte precision and avoid ftruncate(): the resource limit RLIMIT_FSIZE, The maximum size of files that the process may create. Attempts to extend a file beyond this limit result in delivery of a SIGXFSZ signal. By default, this signal terminates a process, but a process can catch this signal instead, in which case the relevant system call (e.g., write(2), truncate(2)) fails with the error EFBIG. For each character in the a-line (beginning with its last character and ending with its first character), we fork() a new process and execve() userhelper with: - a GECOS field that allows us
[FD] Qualys Security Advisory - OpenSMTPD Audit Report
(Sorry for the "CVE-2015-ABCD" place-holders in the report, but OpenSMTPD's developers were ready with the patches before MITRE was ready with the CVE-IDs.) Qualys Security Advisory OpenSMTPD Audit Report Contents Summary Approach Local Vulnerabilities Remote Vulnerabilities Inter-Process Vulnerabilities Miscellaneous Bugs Acknowledgments Summary For the past few months, one of our background projects has been to audit OpenSMTPD, a free implementation of the server-side Simple Mail Transfer Protocol (SMTP). OpenSMTPD replaces Sendmail as OpenBSD's default Mail Transfer Agent (MTA) since OpenBSD 5.6, released on November 1, 2014. OpenSMTPD was designed to be secure, reliable, performant, and easy to configure. Indeed, its codebase lives up to OpenBSD's reputation: it is clean, modular, privilege-separated, and made our audit easy and really enjoyable. However, the project is pretty much in its infancy (the first stable version, 5.3, was released on March 17, 2013), which explains why we discovered various vulnerabilities during our security assessment: - an oversight in the portable version of fgetln() that allows attackers to read and write out-of-bounds memory; - multiple denial-of-service vulnerabilities that allow local users to kill or hang OpenSMTPD; - a stack-based buffer overflow that allows local users to crash OpenSMTPD, or execute arbitrary code as the non-chrooted _smtpd user; - a hardlink attack (or race-conditioned symlink attack) that allows local users to unset the chflags() of arbitrary files; - a hardlink attack that allows local users to read the first line of arbitrary files (for example, root's hash from /etc/master.passwd); - a denial-of-service vulnerability that allows remote attackers to fill OpenSMTPD's queue or mailbox hard-disk partition; - an out-of-bounds memory read that allows remote attackers to crash OpenSMTPD, or leak information and defeat the ASLR protection; - a use-after-free vulnerability that allows remote attackers to crash OpenSMTPD, or execute arbitrary code as the non-chrooted _smtpd user; - multiple inter-process vulnerabilities that allow attackers to escalate from one (already-compromised) OpenSMTPD process to another. Approach The OpenSMTPD version that we audited is available at: https://www.opensmtpd.org/archives/opensmtpd-5.4.4p1.tar.gz and is installed by default on OpenBSD's latest release (OpenBSD 5.7, released on May 1, 2015). Unless otherwise noted, the vulnerabilities that we discovered in OpenSMTPD 5.4.4p1 affect OpenSMTPD's latest release as well (OpenSMTPD 5.7.1p1, released on June 30, 2015). The "hybrid approach" that we adopted to review OpenSMTPD is described in the bible of code auditing, "The Art of Software Security Assessment" (by Mark Dowd, John McDonald, and Justin Schuh): - We started with a "top-down approach" and reviewed the high-level information that we gathered on OpenSMTPD: READMEs, manual pages, web pages (https://www.opensmtpd.org/presentations/asiabsdcon2013-smtpd/ and https://www.poolp.org/). This approach allowed us to quickly understand OpenSMTPD's design (seven privilege-separated, long-running, and event-driven processes that communicate through UNIX sockets and the imsg API) and identify its attack surface (local, remote, and inter-process entry points). - We continued with a "bottom-up approach" and reviewed OpenSMTPD's implementation: the lowest-level code first (openbsd-compat/ and smtpd/mproc.c), followed by the higher-level code. This approach allowed us to quickly identify complex vulnerabilities: the remote out-of-bounds memory read and use-after-free are actually a combination of several low-level and high-level bugs. Privilege Separation --[ PROC_PARENT ]--- User: root Chroot: no Peers: PROC_CONTROL, PROC_LKA, PROC_QUEUE, PROC_CA, PROC_PONY PROC_PARENT, the "[priv]" process, spawns the six other long-running processes at startup (by calling fork_peers() from main()), and the transient Mail Delivery Agent (MDA) processes on demand (by calling forkmda() from parent_imsg()). If any of its long-running children dies, PROC_PARENT calls parent_shutdown(), kill()s its remaining children, and exit()s, but does not restart automatically: if we try to exp
[FD] Qualys Security Advisory - CVE-2017-1000367 in Sudo's get_process_ttyname() for Linux
Qualys Security Advisory CVE-2017-1000367 in Sudo's get_process_ttyname() for Linux Contents Analysis Exploitation Example Acknowledgments Analysis We discovered a vulnerability in Sudo's get_process_ttyname() for Linux: this function opens "/proc/[pid]/stat" (man proc) and reads the device number of the tty from field 7 (tty_nr). Unfortunately, these fields are space-separated and field 2 (comm, the filename of the command) can contain spaces (CVE-2017-1000367). For example, if we execute Sudo through the symlink "./ 1 ", get_process_ttyname() calls sudo_ttyname_dev() to search for the non-existent tty device number "1" in the built-in search_devs[]. Next, sudo_ttyname_dev() calls the function sudo_ttyname_scan() to search for this non-existent tty device number "1" in a breadth-first traversal of "/dev". Last, we exploit this function during its traversal of the world-writable "/dev/shm": through this vulnerability, a local user can pretend that his tty is any character device on the filesystem, and after two race conditions, he can pretend that his tty is any file on the filesystem. On an SELinux-enabled system, if a user is Sudoer for a command that does not grant him full root privileges, he can overwrite any file on the filesystem (including root-owned files) with his command's output, because relabel_tty() (in src/selinux.c) calls open(O_RDWR|O_NONBLOCK) on his tty and dup2()s it to the command's stdin, stdout, and stderr. This allows any Sudoer user to obtain full root privileges. Exploitation To exploit this vulnerability, we: - create a directory "/dev/shm/_tmp" (to work around /proc/sys/fs/protected_symlinks), and a symlink "/dev/shm/_tmp/_tty" to a non-existent pty "/dev/pts/57", whose device number is 34873; - run Sudo through a symlink "/dev/shm/_tmp/ 34873 " that spoofs the device number of this non-existent pty; - set the flag CD_RBAC_ENABLED through the command-line option "-r role" (where "role" can be our current role, for example "unconfined_r"); - monitor our directory "/dev/shm/_tmp" (for an IN_OPEN inotify event) and wait until Sudo opendir()s it (because sudo_ttyname_dev() cannot find our non-existent pty in "/dev/pts/"); - SIGSTOP Sudo, call openpty() until it creates our non-existent pty, and SIGCONT Sudo; - monitor our directory "/dev/shm/_tmp" (for an IN_CLOSE_NOWRITE inotify event) and wait until Sudo closedir()s it; - SIGSTOP Sudo, replace the symlink "/dev/shm/_tmp/_tty" to our now-existent pty with a symlink to the file that we want to overwrite (for example "/etc/passwd"), and SIGCONT Sudo; - control the output of the command executed by Sudo (the output that overwrites "/etc/passwd"): . either through a command-specific method; . or through a general method such as "--\nHELLO\nWORLD\n" (by default, getopt() prints an error message to stderr if it does not recognize an option character). To reliably win the two SIGSTOP races, we preempt the Sudo process: we setpriority() it to the lowest priority, sched_setscheduler() it to SCHED_IDLE, and sched_setaffinity() it to the same CPU as our exploit. Example We will publish our Sudoer-to-root exploit (Linux_sudo_CVE-2017-1000367.c) in the near future: [john@localhost ~]$ head -n 8 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt [john@localhost ~]$ sudo -l [sudo] password for john: ... User john may run the following commands on localhost: (ALL) /usr/bin/sum [john@localhost ~]$ ./Linux_sudo_CVE-2017-1000367 /usr/bin/sum $'--\nHELLO\nWORLD\n' [sudo] password for john: [john@localhost ~]$ head -n 8 /etc/passwd /usr/bin/sum: unrecognized option '-- HELLO WORLD ' Try '/usr/bin/sum --help' for more information. ogin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin Acknowledgments =
[FD] Qualys Security Advisory - Buffer overflow in glibc's ld.so
Qualys Security Advisory Buffer overflow in glibc's ld.so Contents Summary Memory Leak Buffer Overflow Exploitation Acknowledgments Summary We have discovered a memory leak and a buffer overflow in the dynamic loader (ld.so) of the GNU C Library (glibc): - the memory leak (CVE-2017-1000408) first appeared in glibc 2.1.1 (released on May 24, 1999) and can be reached and amplified through the LD_HWCAP_MASK environment variable; - the buffer overflow (CVE-2017-1000409) first appeared in glibc 2.5 (released on September 29, 2006) and can be triggered through the LD_LIBRARY_PATH environment variable. Further investigation showed that: - the buffer overflow is not exploitable if /proc/sys/fs/protected_hardlinks is enabled (it is not enabled by default on vanilla Linux kernels, but most Linux distributions turn it on by default); - the memory leak and the buffer overflow are not exploitable if the glibc is patched against CVE-2017-1000366, because this patch ignores the LD_HWCAP_MASK and LD_LIBRARY_PATH environment variables when SUID binaries are executed (CVE-2017-1000366 was first patched in glibc 2.26, released on August 2, 2017, but most Linux distributions had already backported this patch on June 19, 2017). We have therefore rated the impact of these vulnerabilities as Low. Nevertheless, we give a brief analysis of the vulnerable function, and present a simple method for exploiting a SUID binary on the command line and obtaining full root privileges (if /proc/sys/fs/protected_hardlinks is not enabled, and CVE-2017-1000366 is not patched). Memory Leak (CVE-2017-1000408) Analysis In _dl_init_paths(), ld.so malloc()ates "rtld_search_dirs.dirs[0]", a cache of information about the system's trusted directories (typically "/lib" and "/usr/lib" on 32-bit or "/lib64" and "/usr/lib64" on 64-bit). To compute the number of system directories, ld.so uses the classic C idiom "sizeof (system_dirs) / sizeof (system_dirs[0])": 691 rtld_search_dirs.dirs[0] = (struct r_search_path_elem *) 692 malloc ((sizeof (system_dirs) / sizeof (system_dirs[0])) 693 * round_size * sizeof (struct r_search_path_elem)); Unfortunately, "system_dirs" is not a classic array: it is not an array of strings (pointers to characters), but rather an array of characters, the concatenation of all system directories, separated by null bytes: 109 static const char system_dirs[] = SYSTEM_DIRS; where "SYSTEM_DIRS" is generated by "gen-trusted-dirs.awk" (typically "/lib/\0/usr/lib/" on 32-bit or "/lib64/\0/usr/lib64/" on 64-bit). As a result, the number of system directories is overestimated, and too much memory is allocated for "rtld_search_dirs.dirs[0]": if "system_dirs" is "/lib/\0/usr/lib/" for example, the number of system directories is 2, but 16 is used instead (the number of characters in "system_dirs") to compute the size of "rtld_search_dirs.dirs[0]". This extra memory is never accessed, never freed, and mostly filled with null bytes, because only the information about "nsystem_dirs_len" system directories (the correct number of system directories) is written to "rtld_search_dirs.dirs[0]", and because the minimal malloc() implementation in ld.so calls mmap(), but never munmap(). Moreover, this memory leak can be amplified through the LD_HWCAP_MASK environment variable, because ld.so uses "ncapstr" (the total number of hardware-capability combinations) to compute the size of "rtld_search_dirs.dirs[0]": 687 round_size = ((2 * sizeof (struct r_search_path_elem) - 1 688 + ncapstr * sizeof (enum r_dir_status)) 689 / sizeof (struct r_search_path_elem)); History We tracked down this vulnerability to: commit ab7eb292307152e706948a7b19164ff5e6d593d4 Date: Mon May 3 21:59:35 1999 + Update. * elf/Makefile (trusted-dirs.st): Use gen-trusted-dirs.awk. * elf/gen-trusted-dirs.awk: New file. * elf/dl-load.c (systems_dirs): Moved into file scope. Initialize from SYSTEM_DIRS macro.
[FD] Qualys Security Advisory - Procps-ng Audit Report
Qualys Security Advisory Procps-ng Audit Report Contents Summary 1. FUSE-backed /proc/PID/cmdline 2. Unprivileged process hiding 3. Local Privilege Escalation in top (Low Impact) 4. Denial of Service in ps 5. Local Privilege Escalation in libprocps (High Impact) 5.1. Vulnerability 5.2. Exploitation 5.3. Exploitation details 5.4. Non-PIE exploitation 5.5. PIE exploitation Acknowledgments Patches.tar.gz.b64 Summary We performed a complete audit of procps-ng, the "command line and full screen utilities for browsing procfs, a 'pseudo' file system dynamically generated by the [Linux] kernel to provide information about the status of entries in its process table" (https://gitlab.com/procps-ng/procps). procps-ng contains the utilities free, kill, pgrep, pidof, pkill, pmap, ps, pwdx, skill, slabtop, snice, sysctl, tload, top, uptime, vmstat, w, watch, and the necessary libprocps library. We discovered and submitted patches for more than a hundred bugs and vulnerabilities in procps-ng; for reference, our patches are available at: https://www.qualys.com/2018/05/17/procps-ng-audit-report-patches.tar.gz and base64-encoded at the end of this advisory. In the remainder of this advisory, we present our most interesting findings: 1. FUSE-backed /proc/PID/cmdline (CVE-2018-1120) An attacker can block any read() access to /proc/PID/cmdline by mmap()ing a FUSE file (Filesystem in Userspace) onto this process's command-line arguments. The attacker can therefore block pgrep, pidof, pkill, ps, and w, either forever (a denial of service), or for some controlled time (a synchronization tool for exploiting other vulnerabilities). 2. Unprivileged process hiding (CVE-2018-1121) An unprivileged attacker can hide a process from procps-ng's utilities, by exploiting either a denial of service (a rather noisy method) or a race condition inherent in reading /proc/PID entries (a stealthier method). 3. Local Privilege Escalation in top (CVE-2018-1122) top reads its configuration file from the current working directory, without any security check, if the HOME environment variable is unset or empty. In this very unlikely scenario, an attacker can carry out an LPE (Local Privilege Escalation) if an administrator executes top in /tmp (for example), by exploiting one of several vulnerabilities in top's config_file() function. 4. Denial of Service in ps (CVE-2018-1123) An attacker can overflow the output buffer of ps, when executed by another user, administrator, or script: a denial of service only (not an LPE), because ps mmap()s its output buffer and mprotect()s its last page with PROT_NONE (an effective guard page). 5. Local Privilege Escalation in libprocps (CVE-2018-1124) An attacker can exploit an integer overflow in libprocps's file2strvec() function and carry out an LPE when another user, administrator, or script executes a vulnerable utility (pgrep, pidof, pkill, and w are vulnerable by default; other utilities are vulnerable if executed with non-default options). Moreover, an attacker's process running inside a container can trigger this vulnerability in a utility running outside the container: the attacker can exploit this userland vulnerability and break out of the container or chroot. We will publish our proof-of-concept exploits in the near future. Additionally, CVE-2018-1125 has been assigned to 0008-pgrep-Prevent-a-potential-stack-based-buffer-overflo.patch, and CVE-2018-1126 to 0035-proc-alloc.-Use-size_t-not-unsigned-int.patch. 1. FUSE-backed /proc/PID/cmdline (CVE-2018-1120) In this experiment, we add a sleep(60) to hello_read() in https://github.com/libfuse/libfuse/blob/master/example/hello.c and compile it, mount it on /tmp/fuse, and mmap() /tmp/fuse/hello onto the command-line arguments of a simple proof-of-concept: $ gcc -Wall hello.c `pkg-config fuse --cflags --libs` -o hello $ mkdir /tmp/fuse $ ./hello /tmp/fuse $ cat > fuse-backed-cmdline.c << "EOF" #include #include #include #include #include #include #include #include #define die() do { \ fprintf(stderr, "died in %s: %u\n", __func__, __LINE__); \ exit(EXIT_FAILURE); \ } while (0) #define PAGESZ ((size_t)4096) int main(const int argc, const char * const argv[]) { if (argc <= 0) die(); const char * const arg_start = argv[0]; const char * const last_arg = argv[argc-1]; const char * const arg_end = last_arg + strlen(last_arg) + 1; if (arg_end <= arg
[FD] Integer overflow in Linux's create_elf_tables() (CVE-2018-14634)
Qualys Security Advisory Mutagen Astronomy: Integer overflow in Linux's create_elf_tables() (CVE-2018-14634) Contents Summary Analysis Exploitation Acknowledgments Timeline Summary We discovered an integer overflow in the Linux kernel's create_elf_tables() function: on a 64-bit system, a local attacker can exploit this vulnerability via a SUID-root binary and obtain full root privileges. Only kernels with commit b6a2fea39318 ("mm: variable length argument support", from July 19, 2007) but without commit da029c11e6b1 ("exec: Limit arg stack to at most 75% of _STK_LIM", from July 7, 2017) are exploitable. Most Linux distributions backported commit da029c11e6b1 to their long-term-supported kernels, but Red Hat Enterprise Linux and CentOS (and Debian 8, the current "oldstable" version) have not, and are therefore vulnerable and exploitable. Analysis 150 #define STACK_ROUND(sp, items) \ 151 (((unsigned long) (sp - items)) &~ 15UL) ... 165 create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, ... 169 int argc = bprm->argc; 170 int envc = bprm->envc; 171 elf_addr_t __user *sp; ... 178 int items; ... 190 p = arch_align_stack(p); ... 287 items = (argc + 1) + (envc + 1) + 1; 288 bprm->p = STACK_ROUND(sp, items); ... 295 sp = (elf_addr_t __user *)bprm->p; "argc", the number of command-line arguments passed to the execve() system call, is limited to MAX_ARG_STRINGS (in fs/exec.c); "envc", the number of environment variables passed to execve(), is also limited to MAX_ARG_STRINGS; but because MAX_ARG_STRINGS is 0x7FFF, we can overflow the integer "items" (at line 287) and make it negative. As a result, we can increase the userland stack pointer instead of decreasing it (at lines 288 and 295 -- the stack normally grows down on x86_64), redirect the userland stack to the middle of our argument and environment strings (which were copied to the top of the stack in fs/exec.c), and hence overwrite these strings during the userland execution of a SUID-root binary. Exploitation We execve() a SUID-root binary with exactly 0x8000 "items" (i.e., INT_MIN "items"): roughly 0x8000 * sizeof(char *) = 16GB of argument pointers, 16GB of argument strings, and 16GB of environment strings. Our exploit requires "only" 2 * 16GB = 32GB of memory, instead of 3 * 16GB = 48GB or more, because we use a few tricks to reduce its memory footprint (for example, we replace the nearly 16GB of equal argument pointers with equivalent file-backed mappings that consume practically no memory). The following diagram represents the layout of our userland stack when the execution of the SUID-root binary starts, in ld.so: | argument strings | environment strings | --|---||-+-|-+-+-+-|-- | A | sprand | protect | padding | protect | scratch | onebyte | padding | --|---||-+-|-+-+--^--+-|-- | 0-8192 ~16GB1MB rsp~16GB v <---+---|--| | stack | B | pointers | \-->-->-->--/ 16GB 0x8000 * sizeof(elf_addr_t) = 16GB - "A" ("alpha") is the amount of stack space allocated by create_elf_tables() between lines 190 and 287 exclusive (for example, the platform and base-platform capability strings): it is approximately 512 bytes. - "sprand" is a random amount of stack space allocated by create_elf_tables() at line 190: it varies from 0 to 8192 bytes. - The "protect" argument strings are vital command-line arguments and options that must be safe from memory corruption (for example, argv[0], the filename of the SUID-root binary). - The "padding" argument strings occupy roughly 16GB of stack space. - The "protect" environment strings are vital environment variables that must be safe from memory corruption (for example, our LD_PRELOAD environment variable, which will be processed by ld.so's handle_ld_preload() function
[FD] System Down: A systemd-journald exploit
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." CVE-2018-16864 Analysis The waves all keep on crashing by -- System of a Down, "Suggestions" We accidentally discovered CVE-2018-16864 while working on the exploit for Mutagen Astronomy (CVE-2018-14634); if we pass several megabytes of command-line arguments to a program that calls syslog(), then journald crashes: systemd-journal[472]: segfault at 7ffe9a077420 ip 7f45f6174877 sp 7ffe9a0773f0 error 6 in systemd-journald[7f45f6169000+3f000] (gdb) disassemble 0x7f45f6174877 - 0x7f45f6169000 Dump of assembler code for function dispatch_message_real.4064: ... 0xb82c <+988>: callq 0x2bd10 0xb831 <+993>: test %eax,%eax 0xb833 <+995>: js 0xb8ea 0xb839 <+1001>: mov-0x218(%rbp),%rbx 0xb840 <+1008>: test %rbx,%rbx 0xb843 <+1011>: je 0xd31b 0xb849 <+1017>: mov%rbx,%rdi 0xb84c <+1020>: callq 0x5360 0xb851 <+1025>: add$0xa,%eax 0xb854 <+1028>: cltq 0xb856 <+1030>: add$0x1e,%rax 0xb85a <+1034>: and$0xfff0,%rax 0xb85e <+1038>: sub%rax,%rsp 0xb861 <+1041>: movabs $0x454e494c444d435f,%rax 0xb86b <+1051>: lea0x37(%rsp),%r15 0xb870 <+1056>: and$0xfff0,%r15 0xb874 <+1060>: test %rbx,%rbx 0xb877 <+1063>: mov%rax,(%r15) 0xb87a <+1066>: mov$0x3d,%eax 0xb87f <+1071>: mov%ax,0x8(%r15) 0xb884 <+1076>: lea0x9(%r15),%rax 0xb888 <+1080>: je 0xb895 0xb88a <+1082>: mov%rbx,%rsi 0xb88d <+1085>: mov%rax,%rdi 0xb890 <+1088>: callq 0x5370 538 static void dispatch_message_real( ... 604 r = get_process_cmdline(ucred->pid, 0, false, ); 605 if (r >= 0) { 606 x = strjoina("_CMDLINE=", t); 919 #define strjoina(a, ...)\ 920 ({ \ 921 const char *_appendees_[] = { a, __VA_ARGS__ }; \ 922 char *_d_, *_p_;\ 923 in
Re: [FD] System Down: A systemd-journald exploit
Hi all, Our systemd-journald exploit for CVE-2018-16865 and CVE-2018-16866 is now available at: https://www.qualys.com/2019/05/09/system-down/system-down.tar.gz It is also attached to this email. A few notes about this exploit: - It supports several targets by default (vulnerable versions of Debian, Ubuntu, Fedora, CentOS), and it should be relatively easy to add more targets. - When adding a new amd64 target, use the "free_hook" method if possible (if located at a multiple of 16 plus 8, as explained in our advisory); for various reasons, the alternative "stderr_chain" method is not as reliable as "free_hook" and may therefore take longer to succeed. - When adding and testing a new target, you may want to set "StartLimitInterval=1s" and "StartLimitBurst=10" (for example) in "systemd-journald.service": the exploit will detect this and brute-force faster. - If the exploit dies because "No journal files were opened due to insufficient permissions", the "wall" method can be used instead (via the "-w" switch). Our exploit currently implements the wall method "ssh 127.0.0.1", but alternative methods can be implemented ("utempter" and "gnome-pty-helper", for example). - To test the default information-leak method even if "No journal files were opened due to insufficient permissions", it is enough to create /var/log/journal/ (as explained in "man systemd-journald"). Thank you very much! With best regards, -- the Qualys Security Advisory team system-down.tar.gz Description: application/gzip ___ Sent through the Full Disclosure mailing list https://nmap.org/mailman/listinfo/fulldisclosure Web Archives & RSS: http://seclists.org/fulldisclosure/
[FD] Local Privilege Escalation in OpenBSD's dynamic loader (CVE-2019-19726)
Qualys Security Advisory Local Privilege Escalation in OpenBSD's dynamic loader (CVE-2019-19726) == Contents == Summary Analysis Demonstration Acknowledgments == Summary == We discovered a Local Privilege Escalation in OpenBSD's dynamic loader (ld.so): this vulnerability is exploitable in the default installation (via the set-user-ID executable chpass or passwd) and yields full root privileges. We developed a simple proof of concept and successfully tested it against OpenBSD 6.6 (the current release), 6.5, 6.2, and 6.1, on both amd64 and i386; other releases and architectures are probably also exploitable. == Analysis == In this section, we analyze a step-by-step execution of our proof of concept: -- 1/ We execve() the set-user-ID /usr/bin/chpass, but first: 1a/ we set the LD_LIBRARY_PATH environment variable to one single dot (the current working directory) and approximately ARG_MAX colons (the maximum number of bytes for the argument and environment list); as described in man ld.so: LD_LIBRARY_PATH A colon separated list of directories, prepending the default search path for shared libraries. This variable is ignored for set-user-ID and set-group-ID executables. 1b/ we set the RLIMIT_DATA resource limit to ARG_MAX * sizeof(char *) (2MB on amd64, 1MB on i386); as described in man setrlimit: RLIMIT_DATA The maximum size (in bytes) of the data segment for a process; this includes memory allocated via malloc(3) and all other anonymous memory mapped via mmap(2). -- 2/ Before the main() function of chpass is executed, the _dl_boot() function of ld.so is executed and calls _dl_setup_env(): 262 void 263 _dl_setup_env(const char *argv0, char **envp) 264 { ... 271 _dl_libpath = _dl_split_path(_dl_getenv("LD_LIBRARY_PATH", envp)); ... 283 _dl_trust = !_dl_issetugid(); 284 if (!_dl_trust) { /* Zap paths if s[ug]id... */ 285 if (_dl_libpath) { 286 _dl_free_path(_dl_libpath); 287 _dl_libpath = NULL; 288 _dl_unsetenv("LD_LIBRARY_PATH", envp); 289 } -- 3/ At line 271, _dl_getenv() returns a pointer to our LD_LIBRARY_PATH environment variable and passes it to _dl_split_path(): 23 char ** 24 _dl_split_path(const char *searchpath) 25 { .. 35 pp = searchpath; 36 while (*pp) { 37 if (*pp == ':' || *pp == ';') 38 count++; 39 pp++; 40 } .. 45 retval = _dl_reallocarray(NULL, count, sizeof(*retval)); 46 if (retval == NULL) 47 return (NULL); -- 4/ At line 45, count is approximately ARG_MAX (the number of colons in our LD_LIBRARY_PATH) and _dl_reallocarray() returns NULL (because of our low RLIMIT_DATA); at line 47, _dl_split_path() returns NULL. -- 5/ As a result, _dl_libpath is NULL (line 271) and our LD_LIBRARY_PATH is ignored, but it is not deleted from the environment (CVE-2019-19726): although _dl_trust is false (_dl_issetugid() returns true because chpass is set-user-ID), _dl_unsetenv() is not called (line 288) because _dl_libpath is NULL (line 285). -- 6/ Next, the main() function of chpass is executed, and it: 6a/ calls setuid(0), which sets the real and effective user IDs to 0; 6b/ calls pw_init(), which resets RLIMIT_DATA to RLIM_INFINITY; 6c/ calls pw_mkdb(), which vfork()s and execv()s /usr/sbin/pwd_mkdb (unlike execve(), execv() does not reset the environment). -- 7/ Before the main() function of pwd_mkdb is executed, the _dl_boot() function of ld.so is executed and calls _dl_setup_env(): 7a/ at line 271, _dl_getenv() returns a pointer to our LD_LIBRARY_PATH environment variable (because it was not deleted from the environment in step 5, and because execv() did not reset the environment in step 6c)
[FD] Authentication vulnerabilities in OpenBSD
Qualys Security Advisory Authentication vulnerabilities in OpenBSD == Contents == 1. CVE-2019-19521: Authentication bypass 1.1. Analysis 1.2. Case study: smtpd 1.3. Case study: ldapd 1.4. Case study: radiusd 1.5. Case study: sshd 1.6. Case study: su 2. CVE-2019-19520: Local privilege escalation via xlock 3. CVE-2019-19522: Local privilege escalation via S/Key and YubiKey 4. CVE-2019-19519: Local privilege escalation via su 5. Acknowledgments == 1. CVE-2019-19521: Authentication bypass == We discovered an authentication-bypass vulnerability in OpenBSD's authentication system: this vulnerability is remotely exploitable in smtpd, ldapd, and radiusd, but its real-world impact should be studied on a case-by-case basis. For example, sshd is not exploitable thanks to its defense-in-depth mechanisms. == 1.1. Analysis == From the manual page of login.conf: -- OpenBSD uses BSD Authentication, which is made up of a variety of authentication styles. The authentication styles currently provided are: ... passwd Request a password and check it against the password in the master.passwd file. See login_passwd(8). ... skey Send a challenge and request a response, checking it with S/Key (tm) authentication. See login_skey(8). ... yubikeyAuthenticate using a Yubico YubiKey token. See login_yubikey(8). ... For any given style, the program /usr/libexec/auth/login_style is used to perform the authentication. The synopsis of this program is: /usr/libexec/auth/login_style [-v name=value] [-s service] username class -- This is the first piece of the puzzle: if an attacker specifies a username of the form "-option", they can influence the behavior of the authentication program in unexpected ways. From the manual page of login_passwd: -- login_passwd [-s service] [-v wheel=yes|no] [-v lastchance=yes|no] user [class] ... The service argument specifies which protocol to use with the invoking program. The allowed protocols are login, challenge, and response. (The challenge protocol is silently ignored but will report success as passwd- style authentication is not challenge-response based). -- This is the second piece of the puzzle: if an attacker specifies the username "-schallenge" (or "-schallenge:passwd" to force a passwd-style authentication), then the authentication is automatically successful and therefore bypassed. == 1.2. Case study: smtpd == To demonstrate how smtpd's authentication can be bypassed, we follow the instructions from the manual page of smtpd.conf: -- In this second example, the aim is to permit mail delivery and relaying only for users that can authenticate (using their normal login credentials). ... listen on egress tls pki mail.example.com auth ... match auth from any for any action "outbound" -- and we restart smtpd. Then, with our remote-attacker hat on: -- $ printf '\0-schallenge\0whatever' | openssl base64 AC1zY2hhbGxlbmdlAHdoYXRldmVy $ openssl s_client -connect 192.168.56.121:25 -starttls smtp ... EHLO client.example.com ... AUTH PLAIN AC1zY2hhbGxlbmdlAHdoYXRldmVy 235 2.0.0 Authentication succeeded -- == 1.3. Case study: ldapd == From the manual page of ldapd: -- ldapd can authenticate users via simple binds or SASL with the PLAIN mechanism. ... When
[FD] Local information disclosure in OpenSMTPD (CVE-2020-8793)
Qualys Security Advisory Local information disclosure in OpenSMTPD (CVE-2020-8793) == Contents == Summary Analysis Exploitation POKE 47196, 201 Acknowledgments == Summary == We discovered a minor vulnerability in OpenSMTPD, OpenBSD's mail server: an unprivileged local attacker can read the first line of an arbitrary file (for example, root's password hash in /etc/master.passwd) or the entire contents of another user's file (if this file and /var/spool/smtpd/ are on the same filesystem). We developed a proof of concept and successfully tested it against OpenBSD 6.6 (the current release). This vulnerability is generally not exploitable on Linux, because /proc/sys/fs/protected_hardlinks is 1 by default on most distributions. Surprisingly, however, it is exploitable on Fedora (31) and yields full root privileges. == Analysis == In October 2015 we published the results of an exhaustive OpenSMTPD audit (https://www.qualys.com/2015/10/02/opensmtpd-audit-report.txt); one of our key findings was: -- Multiple hardlink attacks in the offline directory ... In the world-writable "/var/spool/smtpd/offline" directory, local users can create hardlinks to files they do not own, and wait until the server reboots (or, crash OpenSMTPD with a denial-of-service and wait until the administrator restarts it) to carry out assorted attacks. ... 2/ The following code in offline_enqueue() allows an attacker to execvp() "/usr/sbin/smtpctl" as "sendmail", with a command-line argument that is the hardlinked file's first line (CVE-2015-ABCD): ... For example, an attacker can hardlink /etc/master.passwd to the offline directory, and retrieve its first line (root's encrypted password) by running ps (or a small program that simply calls sysctl() with KERN_FILE_BYUID and KERN_PROC_ARGV) in a loop: ... 4/ If an attacker is able to reach another user's file (i.e., +x on all directories that lead to the file) but not read it, he can hardlink the file to the offline directory, and wait for savedeadletter() to create a world-readable copy of the file in this other user's home directory: -- OpenBSD's patch for this vulnerability was threefold: a/ They removed the world-writable and sticky bits from /var/spool/smtpd/offline, changed its group to "_smtpq", and made /usr/sbin/smtpctl set-group-ID _smtpq: -- drwxrwx--- 2 root _smtpq 512 Oct 12 10:34 /var/spool/smtpd/offline -r-xr-sr-x 1 root _smtpq 217736 Oct 12 10:34 /usr/sbin/smtpctl -- b/ They added an _smtpq group check to offline_scan(): -- 1543 /* offline file group must match parent directory group */ 1544 if (e->fts_statp->st_gid != e->fts_parent->fts_statp->st_gid) 1545 continue; 1553 if (offline_add(e->fts_name)) { 1554 log_warnx("warn: smtpd: " 1555 "could not add offline message %s", e->fts_name); 1556 continue; 1557 } -- This check (at line 1544) effectively prevents offline_scan() from adding the filename of a hardlink to the offline queue (at line 1553), because no interesting file on the filesystem belongs to the group _smtpq. c/ They added a hardlink check to offline_enqueue() (at line 1631), which is called by offline_add(): -- 1615 if ((fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK)) == -1) { 1616 log_warn("warn: smtpd: open: %s", path); 1617 _exit(1); 1618 } 1619 1620 if (fstat(fd, ) == -1) { 1621 log_warn("warn: smtpd: fstat: %s", path); 1622 _exit(1); 1623 } 1631 if (sb.st_nlink != 1) { 1632 log_warnx("warn: smtpd: file %s is hard-link", path); 1633 _exit(1); 1634
[FD] LPE and RCE in OpenSMTPD's default install (CVE-2020-8794)
Qualys Security Advisory LPE and RCE in OpenSMTPD's default install (CVE-2020-8794) == Contents == Summary Analysis ... Acknowledgments == Summary == We discovered a vulnerability in OpenSMTPD, OpenBSD's mail server. This vulnerability, an out-of-bounds read introduced in December 2015 (commit 80c6a60c, "when peer outputs a multi-line response ..."), is exploitable remotely and leads to the execution of arbitrary shell commands: either as root, after May 2018 (commit a8e22235, "switch smtpd to new grammar"); or as any non-root user, before May 2018. Because this vulnerability resides in OpenSMTPD's client-side code (which delivers mail to remote SMTP servers), we must consider two different scenarios: - Client-side exploitation: This vulnerability is remotely exploitable in OpenSMTPD's (and hence OpenBSD's) default configuration. Although OpenSMTPD listens on localhost only, by default, it does accept mail from local users and delivers it to remote servers. If such a remote server is controlled by an attacker (either because it is malicious or compromised, or because of a man-in-the-middle, DNS, or BGP attack -- SMTP is not TLS-encrypted by default), then the attacker can execute arbitrary shell commands on the vulnerable OpenSMTPD installation. - Server-side exploitation: First, the attacker must connect to the OpenSMTPD server (which accepts external mail) and send a mail that creates a bounce. Next, when OpenSMTPD connects back to their mail server to deliver this bounce, the attacker can exploit OpenSMTPD's client-side vulnerability. Last, for their shell commands to be executed, the attacker must (to the best of our knowledge) crash OpenSMTPD and wait until it is restarted (either manually by an administrator, or automatically by a system update or reboot). We developed a simple exploit for this vulnerability and successfully tested it against OpenBSD 6.6 (the current release), OpenBSD 5.9 (the first vulnerable release), Debian 10 (stable), Debian 11 (testing), and Fedora 31. At OpenBSD's request, and to give OpenSMTPD's users a chance to patch their systems, we are withholding the exploitation details and code until Wednesday, February 26, 2020. Last-minute note: we tested our exploit against the recent changes in OpenSMTPD 6.6.3p1, and our results are: if the "mbox" method is used for local delivery (the default in OpenBSD -current), then arbitrary command execution as root is still possible; otherwise (if the "maildir" method is used, for example), arbitrary command execution as any non-root user is possible. == Analysis == SMTP clients connect to SMTP servers and send commands such as EHLO, MAIL FROM, and RCPT TO. SMTP servers respond with either single-line or multiple-line replies: - the first lines begin with a three-digit code and a hyphen ('-'), followed by an optional text (for example, "250-ENHANCEDSTATUSCODES"); - the last line begins with the same three-digit code, followed by an optional space (' ') and text (for example, "250 HELP"). In OpenSMTPD's client-side code, these multiline replies are parsed by the mta_io() function: -- 1098 static void 1099 mta_io(struct io *io, int evt, void *arg) 1100 { 1133 case IO_DATAIN: 1134 nextline: 1135 line = io_getline(s->io, ); 1146 if ((error = parse_smtp_response(line, len, , ))) { -- - the first lines (when line[3] == '-') are concatenated into a 2KB replybuf: -- 1177 if (cont) { 1178 if (s->replybuf[0] == '\0') 1179 (void)strlcat(s->replybuf, line, sizeof s->replybuf); 1180 else { 1181 line = line + 4; 1187 (void)strlcat(s->replybuf, line, sizeof s->replybuf); 1188 } 1189 goto nextline; 1190 } -- - the last line (when line[3] != '-') is also concatenated into replybuf: -- 1195
[FD] LPE and RCE in OpenSMTPD (CVE-2020-7247)
Qualys Security Advisory LPE and RCE in OpenSMTPD (CVE-2020-7247) == Contents == Summary Analysis Exploitation Acknowledgments == Summary == We discovered a vulnerability in OpenSMTPD, OpenBSD's mail server. This vulnerability is exploitable since May 2018 (commit a8e222352f, "switch smtpd to new grammar") and allows an attacker to execute arbitrary shell commands, as root: - either locally, in OpenSMTPD's default configuration (which listens on the loopback interface and only accepts mail from localhost); - or locally and remotely, in OpenSMTPD's "uncommented" default configuration (which listens on all interfaces and accepts external mail). We developed a simple proof of concept and successfully tested it against OpenBSD 6.6 (the current release) and Debian testing (Bullseye); other versions and distributions may be exploitable. == Analysis == OpenSMTPD's smtp_mailaddr() function is responsible for validating sender (MAIL FROM) and recipient (RCPT TO) mail addresses: -- 2189 static int 2190 smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args, 2191 const char *domain) 2192 { 2218 if (!valid_localpart(maddr->user) || 2219 !valid_domainpart(maddr->domain)) { 2234 return (0); 2235 } 2236 2237 return (1); 2238 } -- - it calls valid_domainpart() to validate the domain name (after the @ sign) of a mail address -- this function only accepts IPv4 and IPv6 addresses, and alpha-numeric, '.', '-', and '_' characters; - it calls valid_localpart() to validate the local part (before the @ sign) of a mail address -- this function only accepts alpha-numeric, '.', and MAILADDR_ALLOWED characters (a white list from RFC 5322): 71 #define MAILADDR_ALLOWED"!#$%&'*/?^`{|}~+-=_" Among the characters in MAILADDR_ALLOWED, the ones that are also in MAILADDR_ESCAPE are later transformed into ':' characters (escaped) by mda_expand_token(): 72 #define MAILADDR_ESCAPE "!#$%&'*?`{|}~" smtp_mailaddr()'s white-listing and mda_expand_token()'s escaping are fundamental to OpenSMTPD's security -- they prevent dangerous characters from reaching the shell that executes MDA commands (in mda_unpriv()): execle("/bin/sh", "/bin/sh", "-c", mda_command, (char *)NULL, mda_environ); Mail Delivery Agents (MDAs) are responsible for delivering mail to local recipients; for example, OpenSMTPD's default MDA method is "mbox", and the corresponding MDA command is (in parse.y): asprintf(>u.local.command, "/usr/libexec/mail.local -f %%{mbox.from} %%{user.username}"); where %{user.username} is the name of an existing local user (the local part of the recipient address), and %{mbox.from} is the sender address (which would be under the complete control of an attacker if it were not for smtp_mailaddr()'s white-listing and mda_expand_token()'s escaping). Unfortunately, we discovered a vulnerability in smtp_mailaddr() (CVE-2020-7247): -- 2189 static int 2190 smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args, 2191 const char *domain) 2192 { 2218 if (!valid_localpart(maddr->user) || 2219 !valid_domainpart(maddr->domain)) { 2229 if (maddr->domain[0] == '\0') { 2230 (void)strlcpy(maddr->domain, domain, 2231 sizeof(maddr->domain)); 2232 return (1); 2233 } 2234 return (0); 2235 } 2236 2237 return (1); 2238 } -- If the local part of an address is invalid (line 2218) and if its domain name is empty (line 2229), then smtp_mailaddr() adds the default domain automatically (line 2230) and returns 1 (line 2232), although it should return 0 because the local part of the address is invalid (for example, because it contains invalid characters). As a result, an attacker can pass dangerous characters that are not in MAILADDR_ALLOWED and not in MAILADDR_ESCAPE (';' and ' ' in particular) to the shell that executes the MD
[FD] Remote Code Execution in qmail (CVE-2005-1513)
Qualys Security Advisory 15 years later: Remote Code Execution in qmail (CVE-2005-1513) Contents Summary Analysis Exploitation qmail-verify - CVE-2020-3811 - CVE-2020-3812 Mitigations Acknowledgments Patches Summary TLDR: In 2005, three vulnerabilities were discovered in qmail but were never fixed because they were believed to be unexploitable in a default installation. We recently re-discovered these vulnerabilities and were able to exploit one of them remotely in a default installation. In May 2005, Georgi Guninski published "64 bit qmail fun", three vulnerabilities in qmail (CVE-2005-1513, CVE-2005-1514, CVE-2005-1515): http://www.guninski.com/where_do_you_want_billg_to_go_today_4.html Surprisingly, we re-discovered these vulnerabilities during a recent qmail audit; they have never been fixed because, as stated by qmail's author Daniel J. Bernstein (in https://cr.yp.to/qmail/guarantee.html): "This claim is denied. Nobody gives gigabytes of memory to each qmail-smtpd process, so there is no problem with qmail's assumption that allocated array lengths fit comfortably into 32 bits." Indeed, the memory consumption of each qmail-smtpd process is severely limited by default (by qmail-smtpd's startup script); for example, on Debian 10 (the latest stable release), it is limited to roughly 7MB. Unfortunately, we discovered that these vulnerabilities also affect qmail-local, which is reachable remotely and is not memory-limited by default (we investigated many qmail packages, and *all* of them limit qmail-smtpd's memory, but *none* of them limits qmail-local's memory). As a proof of concept, we developed a reliable, local and remote exploit against Debian's qmail package in its default configuration. This proof of concept requires 4GB of disk space and 8GB of memory, and allows an attacker to execute arbitrary shell commands as any user, except root (and a few system users who do not own their home directory). We will publish our proof-of-concept exploit in the near future. About our new discovery, Daniel J. Bernstein issues the following statement: "https://cr.yp.to/qmail/guarantee.html has for many years mentioned qmail's assumption that allocated array lengths fit comfortably into 32 bits. I run each qmail service under softlimit -m12345678, and I recommend the same for other installations." Finally, we also discovered two minor vulnerabilities in qmail-verify (a third-party qmail patch that is included in, for example, Debian's qmail package): CVE-2020-3811 (a mail-address verification bypass), and CVE-2020-3812 (a local information disclosure). Analysis We decided to exploit Georgi Guninski's vulnerability "1. integer overflow in stralloc_readyplus" (CVE-2005-1513). There are, in fact, four potential integer overflows in stralloc_readyplus; three in the GEN_ALLOC_readyplus() macro (which generates the stralloc_readyplus() function), at line 21 (n += x->len), line 23 (x->a = base + n + ...), and line 24 (x->a * sizeof(type)): 17 #define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \ 18 int ta_rplus(x,n) register ta *x; register unsigned int n; \ 19 { register unsigned int i; \ 20 if (x->field) { \ 21 i = x->a; n += x->len; \ 22 if (n > i) { \ 23 x->a = base + n + (n >> 3); \ 24 if (alloc_re(>field,i * sizeof(type),x->a * sizeof(type))) return 1; \ 25 x->a = i; return 0; } \ 26 return 1; } \ 27 x->len = 0; \ 28 return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); } and, in theory, one integer overflow in the alloc() function itself (which is called by the alloc_re() function), at line 18: 14 /*@null@*//*@out@*/char *alloc(n) 15 unsigned int n; 16 { 17 char *x; 18 n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */ .. 20 x = malloc(n); .. 22 return x; 23 } In practice, the integer overflows at line 21 (in GEN_ALLOC_readyplus()) and line 18 (in alloc()) are very hard to trigger; and the one at line 24 (in GEN_ALLOC_readyplus()) is irrelevant to stralloc_readyplus's
Re: [FD] Remote Code Execution in qmail (CVE-2005-1513)
Hi all, Our Linux exploit for CVE-2005-1513 in qmail is attached to this email. Alternatively, it will be available at: https://www.qualys.com/research/security-advisories/ A few notes about this exploit: - It works as-is against a default, unpatched installation of qmail on Debian 10 (amd64). It requires roughly 4GB of disk space and 8GB of memory on the target machine, and creates a file in /tmp when successful. - It can be ported to other Linux distributions (if the qmail-local binary is not full-RELRO) by modifying the lines marked with XXX in the exploit code. - To obtain the mmap layout described in our advisory, the exploit simulates the qmail-local program, and must therefore be executed on the same type of Linux distribution as the target. For example, in our tests, we executed the exploit on a Debian 10.0 machine and remotely attacked a Debian 10.3 machine. The exploit parameters can probably be calculated without the qmail-local simulation, and can certainly be precalculated, but we wanted to keep our exploit as general as possible. For the local exploit (LPE), there are only two command-line arguments: - "user": the name of the target user (on a default Debian installation, this can be "man", "root", "avahi-autoipd", or any real user account). - "domain": by default, the hostname in "/var/lib/qmail/control/me". For the command line of the remote exploit (RCE), there are three mandatory options, three arguments, and one optional option: - "-i client_ip": the IP address of the attacking machine, as seen by the target machine. - "-h client_host": the hostname of the attacking machine (if it has no reverse DNS, the empty string can be specified, and the exploit will use qmail's default, "unknown"). - "-s server_host": the hostname of the target machine (by default, the same as the "domain" below). - "user": the name of the target user. - "domain": by default, the hostname in "/var/lib/qmail/control/me" on the target machine (and hence the hostname in qmail's SMTP banner). - "server_ip": the IP address of the target machine. - "-d homedir": the home directory of the target user, if known (otherwise, the exploit uses a reasonable default). We are at your disposal for questions, comments, and further discussions. Thank you very much! With best regards, -- the Qualys Security Advisory team [https://d1dejaj6dcqv24.cloudfront.net/asset/image/email-banner-384-2x.png]<https://www.qualys.com/email-banner> This message may contain confidential and privileged information. If it has been sent to you in error, please reply to advise the sender of the error and then immediately delete it. If you are not the intended recipient, do not read, copy, disclose or otherwise use this message. The sender disclaims any liability for such unauthorized use. NOTE that all incoming emails sent to Qualys email accounts will be archived and may be scanned by us and/or by external service providers to detect and prevent threats to our systems, investigate illegal or inappropriate behavior, and/or eliminate unsolicited promotional emails (“spam”). If you have any concerns about this process, please contact us. CVE-2005-1513.tar.gz Description: application/gzip ___ Sent through the Full Disclosure mailing list https://nmap.org/mailman/listinfo/fulldisclosure Web Archives & RSS: http://seclists.org/fulldisclosure/
[FD] Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156)
Qualys Security Advisory Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156) Contents Summary Analysis Exploitation Acknowledgments Timeline Summary We discovered a heap-based buffer overflow in Sudo (https://www.sudo.ws/). This vulnerability: - is exploitable by any local user (normal users and system users, sudoers and non-sudoers), without authentication (i.e., the attacker does not need to know the user's password); - was introduced in July 2011 (commit 8255ed69), and affects all legacy versions from 1.8.2 to 1.8.31p2 and all stable versions from 1.9.0 to 1.9.5p1, in their default configuration. We developed three different exploits for this vulnerability, and obtained full root privileges on Ubuntu 20.04 (Sudo 1.8.31), Debian 10 (Sudo 1.8.27), and Fedora 33 (Sudo 1.9.2). Other operating systems and distributions are probably also exploitable. Analysis If Sudo is executed to run a command in "shell" mode (shell -c command): - either through the -s option, which sets Sudo's MODE_SHELL flag; - or through the -i option, which sets Sudo's MODE_SHELL and MODE_LOGIN_SHELL flags; then, at the beginning of Sudo's main(), parse_args() rewrites argv (lines 609-617), by concatenating all command-line arguments (lines 587-595) and by escaping all meta-characters with backslashes (lines 590-591): 571 if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) { 572 char **av, *cmnd = NULL; 573 int ac = 1; ... 581 cmnd = dst = reallocarray(NULL, cmnd_size, 2); ... 587 for (av = argv; *av != NULL; av++) { 588 for (src = *av; *src != '\0'; src++) { 589 /* quote potential meta characters */ 590 if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$') 591 *dst++ = '\\'; 592 *dst++ = *src; 593 } 594 *dst++ = ' '; 595 } ... 600 ac += 2; /* -c cmnd */ ... 603 av = reallocarray(NULL, ac + 1, sizeof(char *)); ... 609 av[0] = (char *)user_details.shell; /* plugin may override shell */ 610 if (cmnd != NULL) { 611 av[1] = "-c"; 612 av[2] = cmnd; 613 } 614 av[ac] = NULL; 615 616 argv = av; 617 argc = ac; 618 } Later, in sudoers_policy_main(), set_cmnd() concatenates the command-line arguments into a heap-based buffer "user_args" (lines 864-871) and unescapes the meta-characters (lines 866-867), "for sudoers matching and logging purposes": 819 if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { ... 852 for (size = 0, av = NewArgv + 1; *av; av++) 853 size += strlen(*av) + 1; 854 if (size == 0 || (user_args = malloc(size)) == NULL) { ... 857 } 858 if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) { ... 864 for (to = user_args, av = NewArgv + 1; (from = *av); av++) { 865 while (*from) { 866 if (from[0] == '\\' && !isspace((unsigned char)from[1])) 867 from++; 868 *to++ = *from++; 869 } 870 *to++ = ' '; 871 } ... 884 } ... 886 } Unfortunately, if a command-line argument ends with a single backslash character, then: - at line 866, "from[0]" is the backslash character, and "from[1]" is the argument's null terminator (i.e., not a space character); - at line 867, "from" is incremented and points to the null terminator; - at line 868, the null terminator is copied to the "user_args" buffer, and "from" is incremented again and points to the first character after the null terminator (i.e., out of the argument's bounds); - the "while" loop at lines 865-869 reads and copies out-of-bounds characters to the "user_args" buffer. In other words, set_cmnd() is vulnerable to a heap-based buffer overflow, because the out-of-bounds characters th
[FD] CVE-2023-4911: Local Privilege Escalation in the glibc's ld.so
Qualys Security Advisory Looney Tunables: Local Privilege Escalation in the glibc's ld.so (CVE-2023-4911) Contents Summary Analysis Proof of concept Exploitation Acknowledgments Timeline Summary The GNU C Library's dynamic loader "find[s] and load[s] the shared objects (shared libraries) needed by a program, prepare[s] the program to run, and then run[s] it" (man ld.so). The dynamic loader is extremely security sensitive, because its code runs with elevated privileges when a local user executes a set-user-ID program, a set-group-ID program, or a program with capabilities. Historically, the processing of environment variables such as LD_PRELOAD, LD_AUDIT, and LD_LIBRARY_PATH has been a fertile source of vulnerabilities in the dynamic loader. Recently, we discovered a vulnerability (a buffer overflow) in the dynamic loader's processing of the GLIBC_TUNABLES environment variable (https://www.gnu.org/software/libc/manual/html_node/Tunables.html). This vulnerability was introduced in April 2021 (glibc 2.34) by commit 2ed18c ("Fix SXID_ERASE behavior in setuid programs (BZ #27471)"). We successfully exploited this vulnerability and obtained full root privileges on the default installations of Fedora 37 and 38, Ubuntu 22.04 and 23.04, Debian 12 and 13; other distributions are probably also vulnerable and exploitable (one notable exception is Alpine Linux, which uses musl libc, not the glibc). We will not publish our exploit for now; however, this buffer overflow is easily exploitable (by transforming it into a data-only attack), and other researchers might publish working exploits shortly after this coordinated disclosure. Analysis At the very beginning of its execution, ld.so calls __tunables_init() to walk through the environment (at line 279), searching for GLIBC_TUNABLES variables (at line 282); for each GLIBC_TUNABLES that it finds, it makes a copy of this variable (at line 284), calls parse_tunables() to process and sanitize this copy (at line 286), and finally replaces the original GLIBC_TUNABLES with this sanitized copy (at line 288): 269 void 270 __tunables_init (char **envp) 271 { 272 char *envname = NULL; 273 char *envval = NULL; 274 size_t len = 0; 275 char **prev_envp = envp; ... 279 while ((envp = get_next_env (envp, , , , 280_envp)) != NULL) 281 { 282 if (tunable_is_name ("GLIBC_TUNABLES", envname)) 283 { 284 char *new_env = tunables_strdup (envname); 285 if (new_env != NULL) 286 parse_tunables (new_env + len + 1, envval); 287 /* Put in the updated envval. */ 288 *prev_envp = new_env; 289 continue; 290 } The first argument of parse_tunables() (tunestr) points to the soon-to-be-sanitized copy of GLIBC_TUNABLES, while the second argument (valstring) points to the original GLIBC_TUNABLES environment variable (in the stack). To sanitize the copy of GLIBC_TUNABLES (which should be of the form "tunable1=aaa:tunable2=bbb"), parse_tunables() removes all dangerous tunables (the SXID_ERASE tunables) from tunestr, but keeps SXID_IGNORE and NONE tunables (at lines 221-235): 162 static void 163 parse_tunables (char *tunestr, char *valstring) 164 { ... 168 char *p = tunestr; 169 size_t off = 0; 170 171 while (true) 172 { 173 char *name = p; 174 size_t len = 0; 175 176 /* First, find where the name ends. */ 177 while (p[len] != '=' && p[len] != ':' && p[len] != '\0') 178 len++; 179 180 /* If we reach the end of the string before getting a valid name-value 181 pair, bail out. */ 182 if (p[len] == '\0') 183 { 184 if (__libc_enable_secure) 185 tunestr[off] = '\0'; 186 return; 187 } 188 189 /* We did not find a valid name-value pair before encountering the 190 colon. */ 191 if (p[len]== ':') 192 { 193 p += len + 1; 194 continue; 195 } 196 197 p += len + 1; 198 199 /* Take the value from the valstring since we need to NULL terminate it. */ 200 char *value = [p - tunestr]; 201 len = 0; 202 203 while (p[len] != ':' && p[len] != '\0') 204
[FD] Authorization bypass and symlink attack in multipathd (CVE-2022-41974 and CVE-2022-41973)
Qualys Security Advisory Leeloo Multipath: Authorization bypass and symlink attack in multipathd (CVE-2022-41974 and CVE-2022-41973) Contents Summary CVE-2022-41974: Authorization bypass CVE-2022-41973: Symlink attack Acknowledgments Timeline Summary We discovered two local vulnerabilities (an authorization bypass and a symlink attack) in multipathd, a daemon that is running as root in the default installation of (for example) Ubuntu Server: https://ubuntu.com/server/docs/device-mapper-multipathing-introduction https://github.com/opensvc/multipath-tools We combined these two vulnerabilities with a third vulnerability, in another package that is also installed by default on Ubuntu Server, and obtained full root privileges on Ubuntu Server 22.04; other releases are probably also exploitable. We will publish this third vulnerability, and the complete details of this local privilege escalation, in an upcoming advisory. The authorization bypass (CVE-2022-41974) was introduced in February 2017 (version 0.7.0) by commit 9acda0c ("Perform socket client uid check on IPC commands"), but earlier versions perform no authorization checks at all: any unprivileged local user can issue any privileged command to multipathd. The symlink attack (CVE-2022-41973) was introduced in May 2018 (version 0.7.7) by commit 65d0a63 ("functions to indicate mapping failure in /dev/shm"); the vulnerable code was hardened significantly in May 2020 (version 0.8.5) by commit 40ee3ea ("simplify failed wwid code"), but it remains exploitable nonetheless. CVE-2022-41974: Authorization bypass The multipathd daemon listens for client connections on an abstract Unix socket (conveniently, the multipathd binary itself can act as a client, if executed with non-option arguments; we use this feature extensively in this advisory to connect and send commands to the multipathd daemon): $ ps -ef | grep 'multipath[d]' root 377 1 0 13:55 ?00:00:00 /sbin/multipathd -d -s $ ss -l -x | grep 'multipathd' u_str LISTEN 0 4096@/org/kernel/linux/storage/multipathd 18105 The commands sent by a client to multipathd are composed of keywords, and internally, each keyword is identified by a different bit; for example, "list" is 1 (1<<0), "add" is 2 (1<<1), and "path" (which requires a parameter) is 65536 (1<<16): 155 load_keys (void) ... 163 r += add_key(keys, "list", LIST, 0); 164 r += add_key(keys, "show", LIST, 0); 165 r += add_key(keys, "add", ADD, 0); ... 183 r += add_key(keys, "path", PATH, 1); 53 #define LIST(1ULL << __LIST) 54 #define ADD (1ULL << __ADD) .. 69 #define PATH(1ULL << __PATH) 6 enum { 7 __LIST, /* 0 */ 8 __ADD, .. 23 __PATH, In turn, each command is associated with a handler (a C function) by its fingerprint -- the bitwise OR of its constituent keywords; for example, the command "list path PARAM" is associated with cli_list_path() by the fingerprint 65537 (LIST+PATH=1+65536), and the command "add path PARAM" is associated with cli_add_path() by the fingerprint 65538 (ADD+PATH=2+65536): 1522 void init_handler_callbacks(void) 1527 set_handler_callback(LIST+PATH, cli_list_path); 1549 set_handler_callback(ADD+PATH, cli_add_path); 321 static uint64_t 322 fingerprint(const struct _vector *vec) ... 325 uint64_t fp = 0; ... 331 vector_foreach_slot(vec, kw, i) 332 fp += kw->code; 333 334 return fp; 89 static struct handler * 90 find_handler (uint64_t fp) .. 95 vector_foreach_slot (handlers, h, i) 96 if (h->fingerprint == fp) 97
[FD] Race condition in snap-confine's must_mkdir_and_open_with_perms() (CVE-2022-3328)
Qualys Security Advisory Race condition in snap-confine's must_mkdir_and_open_with_perms() (CVE-2022-3328) Contents Summary Background Exploitation Acknowledgments Timeline I can't help but feel a missed opportunity to integrate lyrics from one of the best songs ever: [SNAP! - The Power (Official Video)] -- https://twitter.com/spendergrsec/status/1494420041076461570 Summary We discovered a race condition (CVE-2022-3328) in snap-confine, a SUID-root program installed by default on Ubuntu. In this advisory, we tell the story of this vulnerability (which was introduced in February 2022 by the patch for CVE-2021-44731) and detail how we exploited it in Ubuntu Server (a local privilege escalation, from any user to root) by combining it with two vulnerabilities in multipathd (an authorization bypass and a symlink attack, CVE-2022-41974 and CVE-2022-41973): https://www.qualys.com/2022/10/24/leeloo-multipath/leeloo-multipath.txt Background Like the crack of the whip, I Snap! attack Radical mind, day and night all the time -- SNAP! - The Power In February 2022, we published CVE-2021-44731 in our "Lemmings" advisory (https://www.qualys.com/2022/02/17/cve-2021-44731/oh-snap-more-lemmings.txt): to set up a snap's sandbox, snap-confine created the temporary directory /tmp/snap.$SNAP_NAME or reused it if it already existed, even if it did not belong to root; a local attacker could race against snap-confine, retain control over /tmp/snap.$SNAP_NAME, and eventually obtain full root privileges. This vulnerability was patched by commit acb2b4c ("cmd/snap-confine: Prevent user-controlled race in setup_private_mount"), which introduced a new helper function, must_mkdir_and_open_with_perms(): 142 static void setup_private_mount(const char *snap_name) ... 169 sc_must_snprintf(base_dir, sizeof(base_dir), "/tmp/snap.%s", snap_name); ... 176 base_dir_fd = must_mkdir_and_open_with_perms(base_dir, 0, 0, 0700); 55 static int must_mkdir_and_open_with_perms(const char *dir, uid_t uid, gid_t gid, 56 mode_t mode) .. 61 mkdir: .. 67 if (mkdir(dir, 0700) < 0 && errno != EEXIST) { .. 70 fd = open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); .. 81 if (fstat(fd, ) < 0) { .. 84 if (st.st_uid != uid || st.st_gid != gid 85 || st.st_mode != (S_IFDIR | mode)) { ... 130 if (rename(dir, random_dir) < 0) { ... 135 goto mkdir; - the temporary directory /tmp/snap.$SNAP_NAME is created at line 67, if it does not exist already; - if it already exists, and if it does not belong to root (at line 84), then it is moved out of the way (at line 130) by rename()ing it to a random directory in /tmp, and its creation is retried (at line 135). When we reviewed this patch back in December 2021, we felt very nervous about this rename() call (because it allows a local attacker to rename() a directory they do not own), and we advised the Ubuntu Security Team to either not reuse the directory /tmp/snap.$SNAP_NAME at all, or to create it in a non-world-writable directory instead of /tmp, or at least to use renameat2(RENAME_EXCHANGE) instead of rename(). Unfortunately, all of these ideas were deemed impractical (for example, renameat2() is not supported by older kernel and glibc versions); moreover, we (Qualys) failed to come up with a feasible attack plan against this rename() call, so the patch was kept in its current form. After the release of Ubuntu 22.04 in April 2022, we decided to revisit snap-confine and its recent hardening changes, and we finally found a way to exploit the rename() call in must_mkdir_and_open_with_perms(). Exploitation It's getting, it's getting, it's getting kinda heavy It's getting, it's getting, it's getting kinda hectic -- SNAP! - The Power The three key ideas to exploit the rename() of /tmp/snap.$SNAP_NAME are: 1/ snap-confine operates in /tmp to create a snap's temporary directory (/tmp/snap.$SNAP_NAME in setup_private_mount()), but it also operates in /
[FD] LPE and RCE in RenderDoc: CVE-2023-33865, CVE-2023-33864, CVE-2023-33863
Qualys Security Advisory LPE and RCE in RenderDoc: CVE-2023-33865, CVE-2023-33864, CVE-2023-33863 Contents Summary CVE-2023-33865, a symlink vulnerability in /tmp/RenderDoc - Analysis - Exploitation CVE-2023-33864, an integer underflow to heap-based buffer overflow - Analysis - Exploitation CVE-2023-33863, an integer overflow to heap-based buffer overflow - Analysis Acknowledgments Summary "RenderDoc is a free MIT licensed stand-alone graphics debugger that allows quick and easy single-frame capture and detailed introspection of any application using Vulkan, D3D11, OpenGL & OpenGL ES or D3D12 across Windows, Linux, Android, or Nintendo Switch(TM)." (https://renderdoc.org/) To capture a frame on Linux, RenderDoc LD_PRELOADs the shared library librenderdoc.so into the application to be debugged, and this library immediately starts a server thread that listens on TCP port 38920 (on all network interfaces) and waits for clients to connect. Unfortunately, we discovered three vulnerabilities in this server's implementation: - CVE-2023-33865, a symlink vulnerability that is exploitable by any unprivileged local attacker to obtain the privileges of the user who runs RenderDoc. The exact details of this symlink vulnerability made it quite interesting and challenging to exploit. - CVE-2023-33864, an integer underflow that results in a heap-based buffer overflow that is exploitable by any remote attacker to execute arbitrary code on the machine that runs RenderDoc. The unusual malloc exploitation technique that we used to exploit this vulnerability is reliable, one-shot, and works despite all the latest glibc, ASLR, PIE, NX, and stack-canary protections. - CVE-2023-33863, an integer overflow that results in a heap-based buffer overflow and may be exploitable by a remote attacker to execute arbitrary code on the machine that runs RenderDoc (but we have not tried to exploit this vulnerability). All three vulnerabilities were fixed on May 19, 2023 by the following commits (i.e., RenderDoc <= v1.26 is vulnerable, but v1.27 is fixed): https://github.com/baldurk/renderdoc/commit/601ed56111ce3803d8476d438ade1c92d6092856 https://github.com/baldurk/renderdoc/commit/e0464fea4f9a7f149c4ee1d84e5ac57839a4a862 https://github.com/baldurk/renderdoc/commit/1f72a09e3b4fd8ba45be4b0db4889444ef5179e2 https://github.com/baldurk/renderdoc/commit/203fc8382a79d53d2035613d9425d966b1d4958e https://github.com/baldurk/renderdoc/commit/771aa8e769b72e6a36b31d6e2116db9952dcbe9b Last-minute note: RenderDoc also listens on TCP port 39920, but only allows connections from private IPs there (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), and can be configured to further restrict this allow- list; on the other hand, RenderDoc allows anyone to connect to TCP port 38920 (the port that we exploited), and cannot be configured to restrict who can connect there. CVE-2023-33865, a symlink vulnerability in /tmp/RenderDoc Analysis As soon as librenderdoc.so is LD_PRELOADed into the application to be debugged, its library_loaded() function: - creates the directory /tmp/RenderDoc, or reuses it if it already exists, even if it does not belong to the user who runs RenderDoc (Alice, in this advisory); - opens (and possibly creates) a log file of the form /tmp/RenderDoc/RenderDoc_app_.MM.DD_hh.mm.ss.log, and writes to it in append mode: 507 open(filename.c_str(), O_APPEND | O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); Consequently, a local attacker can create /tmp/RenderDoc before Alice runs RenderDoc, and can populate this directory with numerous symlinks (of the form /tmp/RenderDoc/RenderDoc_app_.MM.DD_hh.mm.ss.log) that point to an arbitrary file in the filesystem; when Alice runs RenderDoc, this file will be created (if it does not exist already) and written to, with Alice's privileges. The attacker can write arbitrary strings into this file (by sending these strings to RenderDoc on TCP port 38920), but unfortunately for the attacker, RenderDoc prepends each of these strings with a header that is not controlled by the attacker (and if the attacker sends a string that contains \n characters, then RenderDoc splits this str
[FD] CVE-2023-38408: Remote Code Execution in OpenSSH's forwarded ssh-agent
Qualys Security Advisory CVE-2023-38408: Remote Code Execution in OpenSSH's forwarded ssh-agent Contents Summary Background Experiments Results Discussion Acknowledgments Timeline Summary "ssh-agent is a program to hold private keys used for public key authentication. Through use of environment variables the agent can be located and automatically used for authentication when logging in to other machines using ssh(1). ... Connections to ssh-agent may be forwarded from further remote hosts using the -A option to ssh(1) (but see the caveats documented therein), avoiding the need for authentication data to be stored on other machines." (https://man.openbsd.org/ssh-agent.1) "Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host ... can access the local agent through the forwarded connection. ... A safer alternative may be to use a jump host (see -J)." (https://man.openbsd.org/ssh.1) Despite this warning, ssh-agent forwarding is still widely used today. Typically, a system administrator (Alice) runs ssh-agent on her local workstation, connects to a remote server with ssh, and enables ssh-agent forwarding with the -A or ForwardAgent option, thus making her ssh-agent (which is running on her local workstation) reachable from the remote server. While browsing through ssh-agent's source code, we noticed that a remote attacker, who has access to the remote server where Alice's ssh-agent is forwarded to, can load (dlopen()) and immediately unload (dlclose()) any shared library in /usr/lib* on Alice's workstation (via her forwarded ssh-agent, if it is compiled with ENABLE_PKCS11, which is the default). (Note to the curious readers: for security reasons, and as explained in the "Background" section below, ssh-agent does not actually load such a shared library in its own address space (where private keys are stored), but in a separate, dedicated process, ssh-pkcs11-helper.) Although this seems safe at first (because every shared library in /usr/lib* comes from an official distribution package, and no operation besides dlopen() and dlclose() is generally performed by ssh-agent on a shared library), many shared libraries have unfortunate side effects when dlopen()ed and dlclose()d, and are therefore unsafe to be loaded and unloaded in a security-sensitive program such as ssh-agent. For example, many shared libraries have constructor and destructor functions that are automatically executed by dlopen() and dlclose(), respectively. Surprisingly, by chaining four common side effects of shared libraries from official distribution packages, we were able to transform this very limited primitive (the dlopen() and dlclose() of shared libraries from /usr/lib*) into a reliable, one-shot remote code execution in ssh-agent (despite ASLR, PIE, and NX). Our best proofs of concept so far exploit default installations of Ubuntu Desktop plus three extra packages from Ubuntu's "universe" repository. We believe that even better results can be achieved (i.e., some operating systems might be exploitable in their default installation): - we only investigated Ubuntu Desktop 22.04 and 21.10, we have not looked into any other versions, distributions, or operating systems; - the "fuzzer" that we wrote to test our ideas is rudimentary and slow, and we ran it intermittently on a single laptop, so we have not tried all the combinations of shared libraries and side effects; - we initially had only one attack vector in mind (i.e., one specific combination of side effects from shared libraries), but we discovered six more while analyzing the results of our fuzzer, and we are convinced that more attack vectors exist. In this advisory, we present our research, experiments, reproducible results, and further ideas to exploit this "dlopen() then dlclose()" primitive. We will also publish the source code of our crude fuzzer at https://www.qualys.com/research/security-advisories/ (warning: this code might hurt the eyes of experienced fuzzing practitioners, but it gave us quick answers to our many questions; it is provided "as is", in the hope that it will be useful). Background The ability to load and unload shared libraries in ssh-agent was developed in 2010 to support the addition and deletion of PKCS#11 keys: ssh-agent forks and executes a long-running ssh-pkcs11-helper process that dlopen()s PKCS#11 provi
[FD] Out-of-bounds read & write in the glibc's qsort()
Qualys Security Advisory For the algorithm lovers: Nontransitive comparison functions lead to out-of-bounds read & write in glibc's qsort() Contents Summary Background Experiments Analysis Patch Discussion Acknowledgments Timeline CUT MY LIST IN TWO PIECES THAT'S HOW YOU START QUICK SORT -- https://twitter.com/QuinnyPig/status/1710447650112438710 Summary We discovered a memory corruption in the glibc's qsort() function, due to a missing bounds check. To be vulnerable, a program must call qsort() with a nontransitive comparison function (a function cmp(int a, int b) that returns (a - b), for example) and with a large number of attacker- controlled elements (to cause a malloc() failure inside qsort()). We have not tried to find such a vulnerable program in the real world. All glibc versions from at least September 1992 (glibc 1.04) to the current release (glibc 2.38) are affected, but the glibc's developers have independently discovered and patched this memory corruption in the master branch (commit b9390ba, "stdlib: Fix array bounds protection in insertion sort phase of qsort") during a recent refactoring of qsort(). About our advisory, the glibc security team issues the following statement: This memory corruption in the GNU C Library through the qsort function is invoked by an application passing a non-transitive comparison function, which is undefined according to POSIX and ISO C standards. As a result, we are of the opinion that the resulting CVE, if any, should be assigned to any such calling applications and subsequently fixed by passing a valid comparison function to qsort and not to glibc. We however acknowledge that this is a quality of implementation issue and we fixed this in a recent refactor of qsort. We would like to thank Qualys for sharing their findings and helping us validate our recent changes to qsort. Background While browsing through Postfix's HISTORY file, we stumbled across a puzzling entry from February 2002: Bugfix: make all recipient comparisons transitive, because Solaris qsort() causes SIGSEGV errors otherwise. Victor Duchovni, Morgan Stanley. File: *qmgr/qmgr_message.c. Segmentation faults in qsort()? Transitive comparison functions? As explained in the manual page for qsort(), "The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second." Of course, such a comparison function cmp() must be transitive: - if a < b (i.e., if cmp(pointer_to(a), pointer_to(b)) < 0); - and if b < c (i.e., if cmp(pointer_to(b), pointer_to(c)) < 0); - then necessarily a < c (i.e., cmp(pointer_to(a), pointer_to(c)) < 0). For example, the following comparison function (which compares integers) is transitive (and perfectly correct): int cmp(const void * const pa, const void * const pb) { const int a = *(const int *)pa; const int b = *(const int *)pb; if (a > b) return +1; if (a < b) return -1; return 0; } A shorter and more efficient version of this comparison function could simply "return (a > b) - (a < b);" and still be transitive and perfectly correct: - if a > b, it returns 1 - 0 = +1; - if a < b, it returns 0 - 1 = -1; - if a = b, it returns 0 - 0 = 0. The question, then, is: how can a comparison function be nontransitive? A comparison function cmp() is nontransitive if there exist a, b, and c such that: - a < b (because cmp(pointer_to(a), pointer_to(b)) < 0); - b < c (because cmp(pointer_to(b), pointer_to(c)) < 0); - but a >= c (because cmp(pointer_to(a), pointer_to(c)) >= 0 by mistake). Although the following comparison function seems correct at first, it is in fact nontransitive, because the subtraction in "return (a - b);" is prone to integer overflows: int cmp(const void * const pa, const void * con
[FD] CVE-2023-6246: Heap-based buffer overflow in the glibc's syslog()
Qualys Security Advisory CVE-2023-6246: Heap-based buffer overflow in the glibc's syslog() Contents Summary Analysis Proof of concept Exploitation Acknowledgments Timeline Summary We discovered a heap-based buffer overflow in the GNU C Library's __vsyslog_internal() function, which is called by both syslog() and vsyslog(). This vulnerability was introduced in glibc 2.37 (in August 2022) by the following commit: https://sourceware.org/git?p=glibc.git;a=commit;h=52a5be0df411ef3ff45c10c7c308cb92993d15b1 and was also backported to glibc 2.36 because this commit was a fix for another, minor vulnerability in __vsyslog_internal() (CVE-2022-39046, an "uninitialized memory [read] from the heap"): https://sourceware.org/bugzilla/show_bug.cgi?id=29536 For example, we confirmed that Debian 12 and 13, Ubuntu 23.04 and 23.10, and Fedora 37 to 39 are vulnerable to this buffer overflow. Furthermore, we successfully exploited an up-to-date, default installation of Fedora 38 (on amd64): a Local Privilege Escalation, from any unprivileged user to full root. Other distributions are probably also exploitable. To the best of our knowledge, this vulnerability cannot be triggered remotely in any likely scenario (because it requires an argv[0], or an openlog() ident argument, longer than 1024 bytes to be triggered). Last-minute note: in December 1997 Solar Designer published information about a very similar vulnerability in the vsyslog() of the old Linux libc (https://insecure.org/sploits/linux.libc.5.4.38.vsyslog.html). Analysis In the glibc, both syslog() and vsyslog() call the vulnerable function __vsyslog_internal(): 122 __vsyslog_internal (int pri, const char *fmt, va_list ap, 123 unsigned int mode_flags) 124 { 125 /* Try to use a static buffer as an optimization. */ 126 char bufs[1024]; 127 char *buf = NULL; 128 size_t bufsize = 0; ... 171 #define SYSLOG_HEADER(__pri, __timestamp, __msgoff, pid) \ 172 "<%d>%s%n%s%s%.0d%s: ",\ 173 __pri, __timestamp, __msgoff, \ 174 LogTag == NULL ? __progname : LogTag, \ 175 "[" + (pid == 0), pid, "]" + (pid == 0) ... 182 l = __snprintf (bufs, sizeof bufs, 183 SYSLOG_HEADER (pri, timestamp, , pid)); ... 187 if (0 <= l && l < sizeof bufs) 188 { ... 202 } 203 204 if (buf == NULL) 205 { 206 buf = malloc ((bufsize + 1) * sizeof (char)); ... 213 __snprintf (buf, l + 1, 214 SYSLOG_HEADER (pri, timestamp, , pid)); ... 221 __vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc, 222 mode_flags); - at lines 182-183, SYSLOG_HEADER() includes __progname (the basename() of argv[0]) if LogTag is NULL (e.g., if openlog() was not called, or called with a NULL ident argument); - because a local attacker fully controls argv[0] and hence __progname (even when executing a SUID-root program such as su), at line 187 l (the return value of __snprintf()) can be larger than sizeof bufs (1024), in which case the code block at lines 188-202 is skipped; - consequently, at line 203 buf is still NULL and bufsize is still 0, and at line 206 a very small 1-byte buf is malloc()ated (because bufsize is 0); - at lines 213-214 this small buf is overflowed with the attacker- controlled __progname (because l is larger than 1024), and at lines 221-222 this small buf is further overflowed (because bufsize - l + 1 is 0 - l + 1, a very large size_t). Proof of concept $ (exec -a "`printf '%0128000x' 1`" /usr/bin/su < /dev/null) Password: Segmentation fault (core dumped) Exploitation We decided to exploit this vulnerability through su (the most common SUID-root program) on Fedora 38. To authenticate a user, su calls the PAM library, and if the password provided by the user is incorrect, then PAM calls the glibc's syslog() function without calling openlog() fir