Bug 1658947 (CVE-2018-19931) - CVE-2018-19931 binutils: Heap-based buffer overflow in bfd_elf32_swap_phdr_in function resulting in a denial of service
Summary: CVE-2018-19931 binutils: Heap-based buffer overflow in bfd_elf32_swap_phdr_in...
Keywords:
Status: CLOSED WONTFIX
Alias: CVE-2018-19931
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
low
low
Target Milestone: ---
Assignee: Red Hat Product Security
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks: 1658951
TreeView+ depends on / blocked
 
Reported: 2018-12-13 08:53 UTC by Andrej Nemec
Modified: 2019-09-29 15:04 UTC (History)
15 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2019-06-10 10:43:52 UTC


Attachments (Terms of Use)

Description Andrej Nemec 2018-12-13 08:53:06 UTC
An issue was discovered in the Binary File Descriptor (BFD) library (aka libbfd), as distributed in GNU Binutils through 2.31. There is a heap-based buffer overflow in bfd_elf32_swap_phdr_in in elfcode.h because the number of program headers is not restricted.

Upstream issue:

https://sourceware.org/bugzilla/show_bug.cgi?id=23942

Upstream patch:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=5f60af5d24d181371d67534fa273dd221df20c07

Comment 1 Scott Gayou 2018-12-14 17:43:21 UTC
This does not appear to impact 64bit nm.

```
(gdb) print sizeof(*i_phdr)
$5 = 64
(gdb) print i_ehdrp->e_phnum
$6 = 536870912
```

On 32-bit, 536870912 * 64 = 0. Presumably bfd_alloc returns success on an allocation of 0, and a later write triggers the heap out-of-bounds write.

On 64-bit, the calculation does not overflow with this poc.

I originally didn't understand where e_phnum is coming from though. If we run readelf on the binary:

```
  Number of program headers:         65535 (536870912)
```

I'd expect 65535, instead we have this:

```
(gdb) print i_ehdrp->e_phnum
$9 = 536870912
```

which matches readelfs argument after the number of program headers.

However:

```
/*
 * Extended Numbering
 *
 * If the real number of program header table entries is larger than
 * or equal to PN_XNUM(0xffff), it is set to sh_info field of the
 * section header at index 0, and PN_XNUM is set to e_phnum
 * field. Otherwise, the section header at index 0 is zero
 * initialized, if it exists.
 *
 * Specifications are available in:
 *
 * - Oracle: Linker and Libraries.
 *   Part No: 817–1984–19, August 2011.
 *   http://docs.oracle.com/cd/E18752_01/pdf/817-1984.pdf
 *
 * - System V ABI AMD64 Architecture Processor Supplement
 *   Draft Version 0.99.4,
 *   January 13, 2010.
 *   http://www.cs.washington.edu/education/courses/cse351/12wi/supp-docs/abi.pdf
 */
```

readelf source shows that this is a special case:

```
      if (section_headers != NULL
	  && elf_header.e_phnum == PN_XNUM
	  && section_headers[0].sh_info != 0)
	printf (" (%ld)", (long) section_headers[0].sh_info);
```

Comment 2 Scott Gayou 2018-12-14 17:48:45 UTC
and it looks like sh_info is 32bits, hence, we can't set it to a value that would overflow on 64bit platforms.

Comment 3 Scott Gayou 2018-12-14 17:50:33 UTC
Also, here's a run of the 32-bit crash.

```
nm poc
Segmentation fault (core dumped)

==28303== Memcheck, a memory error detector
==28303== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==28303== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==28303== Command: nm poc
==28303== 
==28303== Invalid write of size 4
==28303==    at 0xC39A52: bfd_elf32_swap_phdr_in (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0xC3C98E: bfd_elf32_object_p (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0xC18549: bfd_check_format_matches (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0x804B6DE: ??? (in /usr/bin/nm)
==28303==    by 0x804BCA7: ??? (in /usr/bin/nm)
==28303==    by 0x9FED35: (below main) (in /lib/libc-2.12.so)
==28303==  Address 0x40201b8 is 0 bytes after a block of size 4,064 alloc'd
==28303==    at 0x40072B2: malloc (vg_replace_malloc.c:270)
==28303==    by 0xCA0D45: objalloc_create (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0xC1AD67: _bfd_new_bfd (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0xC1B07C: bfd_fopen (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0xC1B266: bfd_openr (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0x804B601: ??? (in /usr/bin/nm)
==28303==    by 0x804BCA7: ??? (in /usr/bin/nm)
==28303==    by 0x9FED35: (below main) (in /lib/libc-2.12.so)
...
==28303== Process terminating with default action of signal 11 (SIGSEGV)
==28303==  Access not within mapped region at address 0x9066
==28303==    at 0xA63311: __GI_memcpy (in /lib/libc-2.12.so)
==28303==    by 0xA561F7: _IO_sgetn (in /lib/libc-2.12.so)
==28303==    by 0xA496AD: fread (in /lib/libc-2.12.so)
==28303==    by 0xC143D0: ??? (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0xC1392A: bfd_bread (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0xC3C9C8: bfd_elf32_object_p (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0xC18549: bfd_check_format_matches (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so)
==28303==    by 0x804B6DE: ??? (in /usr/bin/nm)
==28303==    by 0x804BCA7: ??? (in /usr/bin/nm)
==28303==    by 0x9FED35: (below main) (in /lib/libc-2.12.so)
==28303==  If you believe this happened as a result of a stack
==28303==  overflow in your program's main thread (unlikely but
==28303==  possible), you can try to increase the size of the
==28303==  main thread stack using the --main-stacksize= flag.
==28303==  The main thread stack size used in this run was 10485760.
```

64-bit run:

```
$ nm poc
nm: poc: memory exhausted
```

Comment 4 Nick Clifton 2019-01-03 13:25:23 UTC
Is it permissible to close this BZ ?

I would consider the problem itself to be very low priority.  It only
affects specially crafted, corrupt 32-bit ELF binaries, and the only
denial of service is that tools like nm and objdump will fail, and then
only if these tools themselves are 32-bit executables.

The bug has been fixed upstream and will be in the next official
release of the GNU binutils.  This in turn will make its way into
Fedora rawhide, and eventually some future RHEL release.  Backporting
the patch to other versions of Fedora and RHEL is possible, but it
does not seem like a profitable use of time.


Note You need to log in before you can comment on or make changes to this bug.