Created attachment 10029 [details]
Malicious PE with invalid extended relocation sample
$objdump -x $FILE
With x option, objdump will allocate memory to store relocation data, this is done in the following function:
dump_relocs_in_section (bfd *abfd,
void *dummy ATTRIBUTE_UNUSED)
relsize = bfd_get_reloc_upper_bound (abfd, section); //can be controlled
relpp = (arelent **) xmalloc (relsize); //allocate memory
relcount = bfd_canonicalize_reloc (abfd, section, relpp, syms); //read reloc data
the relsize is computed as :
(asect->reloc_count + 1) * sizeof (arelent *) // coff_get_reloc_upper_bound(…)
In the coff standard, reoc_count is WORD, which is 16bit wide. sizeof (arelent *) is constant and small. So normally, the result is small. But when PE section contain extended relocation, everything will go bad.
PE section header is inited in the following function:
coff_set_alignment_hook (bfd * abfd ATTRIBUTE_UNUSED,
asection * section,
void * scnhdr)
/* Check for extended relocs. */
if (hdr->s_flags & IMAGE_SCN_LNK_NRELOC_OVFL)
struct external_reloc dst;
struct internal_reloc n;
file_ptr oldpos = bfd_tell (abfd);
bfd_size_type relsz = bfd_coff_relsz (abfd);
if (bfd_seek (abfd, (file_ptr) hdr->s_relptr, 0) != 0) //seek to the first reloc data
if (bfd_bread (& dst, relsz, abfd) != relsz) //read the first reloc data
coff_swap_reloc_in (abfd, &dst, &n);//fill struct
if (bfd_seek (abfd, oldpos, 0) != 0)
section->reloc_count = hdr->s_nreloc = n.r_vaddr - 1; //can be controlled
section->rel_filepos += relsz;
From the above, when pe section contain extended relocation, reloc_count is assigned from n.r_vaddr. n is a instance of struct internal_reloc as follow:
bfd_vma r_vaddr; /* Virtual address of reference */
long r_symndx; /* Index into symbol table */
unsigned short r_type; /* Relocation type */
unsigned char r_size; /* Used by RS/6000 and ECOFF */
unsigned char r_extern; /* Used by ECOFF */
unsigned long r_offset; /* Used by Alpha ECOFF, SPARC, others */
r_vaddr's type is unsigned long, it can be 32bit and 64bit, according to the os-archive. it is from the dst, which is a instance of struct external_reloc as follow:
So n.r_vaddr is really 32bit wide, section->reloc_count is also 32bit wide.
(asect->reloc_count + 1) * sizeof (arelent *) can be very large.
To control the memory size, it just need to modify the r_vaddr field of first reloc data, if can be from 0 to 0Xffffffff.
This could cause memory exhaustion to dos.
Credit:The bug was discovered by ADLab of Venustech
The master branch has been updated by Nick Clifton <email@example.com>:
Author: Nick Clifton <firstname.lastname@example.org>
Date: Tue May 2 11:54:53 2017 +0100
Prevent memory exhaustion from a corrupt PE binary with an overlarge number of relocs.
* objdump.c (dump_relocs_in_section): Check for an excessive
number of relocs before attempting to dump them.
Thanks for reporting this bug.
I have applied a patch to add a check for the reloc size being larger than
the size of the file, and hence invalid, before an attempt is made to allocate
memory for the relocs. I think that this should resolve the problem.