Monday, February 14, 2011

Fuzzing the ELF32 file format with Peach

Introduction:

In light of the recent announcement by Hex Rays of their bug bounty program (http://www.hex-rays.com/bugbounty.shtml), we will be attempting to fuzz the ELF32 file format and IDA Pro. The hope is to find a bug in IDA Pro when it opens an ELF32 file, specifically in it's file format parsing functionality. We will use the common Peach fuzzer (www.pachfuzzer.com) to accomplish this. When using Peach, one must first create what's called a "Data Model" (http://peachfuzzer.com/TutorialFileFuzzing/CreateDataModel). Data Models describe how the struct of the file (or protocol) that you're fuzzing should look. Typically you create a Data Model that conforms with what you're fuzzing (be it ELF32 binaries, HTTP communication, BGP packets). After that, you start changing bits around, hoping to catch the program off-guard and finding a condition the program wasn't expecting.


References:
(1) http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
(2) http://freebsd.active-venture.com/FreeBSD-srctree/newsrc/sys/elf32.h.html (32-bit)
(3) http://en.wikipedia.org/wiki/C_variable_types_and_declarations#Size (Variable types and their size in bytes)
(4) http://www.skyfree.org/linux/references/ELF_Format.pdf (32-bit ELF file format specification)
(5) http://ivan215.blogspot.com/2009/08/linux-elf.html (64-bit)


Methodology:

The ELF file format is the common file type for executable programs (see Reference 1), like Microsoft's PE file for files with the file extension "exe". The ELF header is set up as a C structure (see Reference 2, the comment that says ELF Header). The same reference will describe the length of each variable type definition (you see it on that page as typedef).


The ELF Header structure:

/*
* ELF definitions common to all 32-bit architectures.
*/

typedef u_int32_t Elf32_Addr; // 4 bytes
typedef u_int16_t Elf32_Half; // 2 bytes
typedef u_int32_t Elf32_Off; // 8 bytes
typedef int32_t Elf32_Sword; // 8 bytes
typedef u_int32_t Elf32_Word; // 8 bytes
typedef u_int32_t Elf32_Size; // 8 bytes

/*
* ELF header.
*/

typedef struct {
unsigned char e_ident[EI_NIDENT]; /* File identification. */
Elf32_Half e_type; /* File type. */
Elf32_Half e_machine; /* Machine architecture. */
Elf32_Word e_version; /* ELF format version. */
Elf32_Addr e_entry; /* Entry point. */
Elf32_Off e_phoff; /* Program header file offset. */
Elf32_Off e_shoff; /* Section header file offset. */
Elf32_Word e_flags; /* Architecture-specific flags. */
Elf32_Half e_ehsize; /* Size of ELF header in bytes. */
Elf32_Half e_phentsize; /* Size of program header entry. */
Elf32_Half e_phnum; /* Number of program header entries. */
Elf32_Half e_shentsize; /* Size of section header entry. */
Elf32_Half e_shnum; /* Number of section header entries. */
Elf32_Half e_shstrndx; /* Section name strings section. */
} Elf32_Ehdr;




Sample file: /usr/bin/top
see: man top(1)


$ readelf -h /usr/bin/top
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8049740
Start of program headers: 52 (bytes into file)
Start of section headers: 61808 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 32
Section header string table index: 31




standard elf header e_ident:
magic number: 0x7f
E = 45
L = 4C
F = 46

configurable:
EI_CLASS = 01 (32-bit objects)
1 byte
Means it's capable of 32-bit
Valid input is integers 0-2

EI_DATA = 01 (ELFDATA2LSB)
1 byte
2's complement, meaning little endian
Valid input is integers 0-2

EI_VERSION = 01
Must be the value of EV_CURRENT

EI_PAD = 00
Set to 0's, it's the beginning of unused bytes for the rest of e_ident

** end of e_ident **

e_type = 02 00
2 bytes
EXEC (Executable file)
From EI_DATA, we know this is little endian format. So it actually reads "00 02".
Valid input is 0-4, 0xff00, 0xffff.

e_machine = 03 00 (again in little endian, "00 03")
2 bytes
00 03 has the name "EM_386" meaning Intel 80386
Valid input is 0-8.

e_version = 01 00 00 00
4 bytes
Valid input is 0 and 1

e_entry = 40 97 04 08
4 bytes
Virtual Address to transfer control to the system

e_phoff = 34 00 00 00
4 bytes
Offset to the program's header table in bytes

e_shoff = 70 F1 00 00
4 bytes
Section header table's file offset in bytes

e_flags = 00 00 00 00
4 bytes
Machine flag information.
This is read 1 byte at a time, valid bytes must be 00 or 01. ie.: 00 01 00 01, 01 00 00 00, 00 01 01 00. Page 1-12 of Reference 4.

e_ehsize = 34 00
2 bytes
The size of the ELF header (in bytes).

e_phentsize = 20 00
2 bytes
This describes the size of an entry in the file's program header table, with all entries in that table being of the exact same size.

e_phnum = 08 00
2 bytes
This describes the number of entries in the program's header table. Additional tidbit: The header's table size in bytes can be determined by multiplying e_phentsize and e_phnum.

e_shentsize = 28 00
2 bytes
This is the size of the section headers (in bytes). All section headers are the same size.

e_shnum = 20 00
2 bytes
Number of entries in the section header table. Additional tidbit: Multiply e_shentsize and e_shnum, you have the section header's table size (again, in bytes).

e_shstrndx = 1F 00
2 bytes
The index of the section header table entry, containing the section name string table.
Valid input is 00. I believe any combination is also valid. Or in Reference 4, page 1-16, Figure 1-15.



On the next post, we will post the Data Model for use with Peach using the above as a template. As this is a new field of exploration for me, please note inaccuracies may occur. If any are spotted, feel free to help us and others out by commenting!

No comments:

Post a Comment