Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Date: Tue, 25 Sep 2018 10:07:53 -0700
From: Qualys Security Advisory <qsa@...lys.com>
To: oss-security@...ts.openwall.com
Subject: 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 0x7FFFFFFF, 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 0x80000000 "items" (i.e.,
INT_MIN "items"): roughly 0x80000000 * 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              ~16GB                1MB         rsp    ~16GB
  v                                               <-------+---|----------|
  |                                                 stack | B | pointers |
  \-------------->-------------->-------------->--------------/   16GB
             0x80000000 * 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).

- The "scratch" environment strings are 1MB of safe stack space for the
  userland execution of the SUID-root binary: the integer overflow of
  "items" redirects the userland stack pointer "rsp" to the middle of
  our argument and environment strings (to an offset of 0x80000000 *
  sizeof(elf_addr_t) = 16GB) -- more precisely, to the middle of our
  "onebyte" environment strings.

- The "onebyte" environment strings are 256KB of one-byte (empty)
  environment variables that will be partly overwritten by the 4KB
  fname[] buffer in ld.so's handle_ld_preload() function.

- The "padding" environment strings occupy roughly 16GB of stack space.

- The 16GB of argument and environment "pointers" (i.e., the argv[] and
  envp[] arrays) are written on top of our "padding" environment strings
  by create_elf_tables(), after the integer overflow of "items" and the
  redirection of the userland stack pointer "rsp".

- "B" ("beta") is the amount of stack space allocated by ld.so before
  the call to handle_ld_preload(): it is approximately 9KB and is
  allocated in the middle of our "onebyte" environment strings.

As a result, ld.so partly overwrites (i.e., rewrites) our "onebyte"
environment variables with the fname[] buffer in handle_ld_preload()
(whose contents we control through our LD_PRELOAD environment variable)
and thereby nullifies process_envvars()'s filtering of UNSECURE_ENVVARS
(LD_AUDIT, LD_LIBRARY_PATH, LD_PRELOAD, etc). The exploitation of this
lack of UNSECURE_ENVVARS filtering in ld.so (via a suitable SUID-root
binary) is left as an exercise for the interested reader.

Our proof-of-concept (poc-exploit.c) exploits the integer overflow in
create_elf_tables() and the resulting lack of UNSECURE_ENVVARS filtering
in ld.so: it executes the main() of a SUID-root binary (poc-suidbin.c)
while LD_LIBRARY_PATH remains set, even though it should have been
removed from the environment variables by ld.so. Demonstration:

# gcc -O0 -o poc-suidbin poc-suidbin.c
# chown root poc-suidbin
# chmod 4555 poc-suidbin

$ gcc -o poc-exploit poc-exploit.c
$ time ./poc-exploit
...
ERROR: ld.so: object 'LD_LIBRARY_PATH=.0LD_LIBRARY_PATH=.0LD_LIBRARY_PATH=.' from LD_PRELOAD cannot be preloaded: ignored.
ERROR: ld.so: object 'LD_LIBRARY_PATH=.0LD_LIBRARY_PATH=.' from LD_PRELOAD cannot be preloaded: ignored.
ERROR: ld.so: object 'LD_LIBRARY_PATH=.' from LD_PRELOAD cannot be preloaded: ignored.
argc 2147090419
stack 0x7ffbe115008f < 0x7ffbe1150188 < 0x7fffe0e50128 < 0x7ff7e11503ea < 0x7ffbe102cdea
getenv 0x7ffbe114d83b .
0x7ffbe114d82b LD_LIBRARY_PATH=.
0x7ffbe114df60 LD_LIBRARY_PATH=.
0x7ffbe114df72 LD_LIBRARY_PATH=.
...
0x7ffbe114e69e LD_LIBRARY_PATH=.
0x7ffbe114e6b0 LD_LIBRARY_PATH=.
0x7ffbe114e6c2 LD_LIBRARY_PATH=.

real    5m38.666s
user    0m0.049s
sys     1m57.828s


========================================================================
Acknowledgments
========================================================================

We thank Red Hat Product Security and the members of
linux-distros@...openwall.org and security@...nel.org.


========================================================================
Timeline
========================================================================

2018-08-31: Contacted secalert@...hat.com.

2018-09-18: Contacted linux-distros@...openwall.org and
security@...nel.org.

2018-09-25: Coordinated Release Date (Time: 5:00 PM UTC).


View attachment "poc-suidbin.c" of type "text/plain" (1458 bytes)

View attachment "poc-exploit.c" of type "text/plain" (7582 bytes)

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.