Bug 19296 - memory crush(Out of bound read error)caused by insufficient check for parsed_size in archive.c
Summary: memory crush(Out of bound read error)caused by insufficient check for parsed_...
Status: RESOLVED DUPLICATE of bug 17512
Alias: None
Product: binutils
Classification: Unclassified
Component: binutils (show other bugs)
Version: 2.23
: P2 critical
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-11-26 07:26 UTC by 0yangke0
Modified: 2015-11-29 04:25 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
exploit input file for objdump and nm (79.02 KB, application/x-archive)
2015-11-26 07:26 UTC, 0yangke0
Details

Note You need to log in before you can comment on or make changes to this bug.
Description 0yangke0 2015-11-26 07:26:31 UTC
Created attachment 8814 [details]
exploit input file for objdump and nm

Hi,
We find an memory crush bug of objdump and nm at least before binuitls-2.24,and 
This bug is caused by missing check of read variable "parsed_size" in archive.c:481(The following description use binutils-2.23 as an example).
Also we find that this bug still exists in versions after binutils-2.23 by reviewing the source code, but it doesn't crash in our test in Ubuntu 14.04 as for the dynamic allocation area is all flushed by zero (00 00 00 00).

We create a trigger input file for this bug and we make it as an attachment.

object -t option and default option and nm default 
e.g #objdump -t exploit_file
    #objdump exploit_file
    #nm exploit_file


In line archive.c:481, parsed_size is assigned by the hdr.ar_size(in function:_bfd_generic_read_ar_hdr_mag).

481:  scan = sscanf (hdr.ar_size, "%" BFD_VMA_FMT "u", &parsed_size);

Thus if we construct "30 00 00 00 00 00 00 00 00 00" for hdr.ar_size, then we get a "0"string(30 is the ASCII number of zero),and the parsed_size will then become zero. 
In line 562 the parse_size is assigned to ared->parsed_size:
562	  ared->parsed_size = parsed_size;

The struct pointer ared is returned and assigned to mapdata in line 883 in function _bfd_generic_read_ar_hdr:

883:     mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);

In line 886, mapdata->parsed_size is assigned to parsed_size(note that we are in function _bfd_generic_read_ar_hdr now):

886:	  parsed_size = mapdata->parsed_size;

In line 889, parsed_size is used as the length to apply for dynamic memory.

889:      raw_armap = (bfd_byte *) bfd_zalloc (abfd, parsed_size);

As parsed_size is zero so pointer raw_armap is not NULL and points to some padding bytes.  
It passed the following check as the parsed_size is zero now and the condition is false.

893:	  if (bfd_bread (raw_armap, parsed_size, abfd) != parsed_size)

In line 902, ardata->symdef_count is assigned by the former applied  dynamic padding bytes.The expanded macro H_GET_32 will call bfd_getl32 to convert the four bytes to integer.

902:	  ardata->symdef_count = H_GET_32 (abfd, raw_armap) / BSD_SYMDEF_SIZE;
903:
904:      if (ardata->symdef_count * BSD_SYMDEF_SIZE >
905:          parsed_size - BSD_SYMDEF_COUNT_SIZE)

BSD_SYMDEF_SIZE expanded to 8.
The following error check doesn't work as "parsed_size-BSD_SYMDEF_COUNT_SIZE"
will be a very large unsigned number 0xfffffffc.

The following for statement in 922 then use ardata->symdef_count as the loop bound, thus cause a memory overread crush in line 927.

922:  for (counter = 0, set = ardata->symdefs;
923:       counter < ardata->symdef_count;
924:       counter++, set++, rbase += BSD_SYMDEF_SIZE)
925:    {
926:      set->name = H_GET_32 (abfd, rbase) + stringbase;
927:      set->file_offset = H_GET_32 (abfd, rbase + BSD_SYMDEF_OFFSET_SIZE);
928:    }

Here paste the gdb crash stack:

Program received signal SIGSEGV, Segmentation fault.
bfd_getl32 (p=0x811b000) at libbfd.c:623
623	  v |= (unsigned long) addr[1] << 8;
(gdb) bt
#0  bfd_getl32 (p=0x811b000) at libbfd.c:623
#1  0x0804d5cd in do_slurp_bsd_armap (abfd=abfd@entry=0x80faa50)
    at archive.c:927
#2  0x0804e877 in bfd_slurp_armap (abfd=0x80faa50) at archive.c:1119
#3  0x0804e5cb in bfd_generic_archive_p (abfd=0x80faa50) at archive.c:809
#4  0x08056720 in bfd_check_format_matches (abfd=abfd@entry=0x80faa50, 
    format=format@entry=bfd_archive, matching=matching@entry=0x0)
    at format.c:215
#5  0x08056a8f in bfd_check_format (abfd=abfd@entry=0x80faa50, 
    format=format@entry=bfd_archive) at format.c:95
#6  0x0804bf81 in display_file (filename=0xbfffeabe "exploit_file")
    at nm.c:1210
#7  0x0804a115 in main (argc=2, argv=0xbfffe814) at nm.c:1688

The attacker may create more sophisticated exploit to cause a memory leak, thus access privileged information nearby and other unknown effect.

Sincerely,
Ke Yang,Institute of Software Chinese Academy of Sciences
Hengtai Ma,Institute of Software Chinese Academy of Sciences
Xuefei Wang,Institute of Software Chinese Academy of Sciences
Comment 1 0yangke0 2015-11-26 07:32:18 UTC
Forgot to say our test platform: Ubuntu14.04i386 
#uname -a
#Linux ubuntu 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:12 UTC 2014 i686 i686 i686 GNU/Linux

We think this is a critical problem because it may cause the leak of critical information.
Comment 2 Nick Clifton 2015-11-27 12:01:51 UTC
Hi Guys,

  Thanks very much for reporting this problem.
 
> We find an memory crush bug of objdump and nm at least before
> binuitls-2.24,and 

  You are correct - this bug exists in 2.24 and earlier versions of the binutils.  Fortunately however the bug has been fixed in the 2.25 release and the forthcoming 2.26 release, so it is only a problem for legacy installations.

  At this point I think that it is up to distribution maintainers to decide if and how they want to patch this bug - assuming of course that they are using the 2.24 (or earlier) sources.

Cheers
  Nick

PS. This problem, and others like it, were reported in PR 17512.

*** This bug has been marked as a duplicate of bug 17512 ***
Comment 3 0yangke0 2015-11-29 04:25:08 UTC
Thanks for your attention!
Wow, it's a really hot comment list. 

Regards,
Ke Yang