[RFC ABRT PATCH v2] Add gdb python plugin which analyzes vulnerability

Jiri Moskovcak jmoskovc at redhat.com
Thu Jun 13 14:03:24 UTC 2013


On 06/13/2013 03:48 PM, Jiri Moskovcak wrote:
> After a quick glance at the code:
>
> - all the functions taking instance as parameter called "self" and then
> working with it should be methods

- after a bit detailed reading on _is_exploitable() seems to change the 
state of the instance and should be fixed to not do so, since it's not a 
method
- the other functions which takes the instance and just read it should 
have the parameter renamed - "self" is really misleading

--Jirka

>
> --Jirka
>
> On 06/13/2013 02:01 PM, Denys Vlasenko wrote:
>> This is an RFC, please do not commit.
>>
>> Usage:
>> Run abrt-action-analyze-vulnerability in a directory which contains
>> ./coredump file.
>> If crash looks exploitable, the tool creates ./exploitable file.
>> Example such a file:
>>
>> """
>> Likely crash reason: Write to an invalid address
>> Exploitable rating (0-9 scale): 6
>> """
>>
>> This patch adds abrt-action-analyze-vulnerability invocation to
>> "EVENT=post-create analyzer=CCpp".
>>
>> TODO:
>> * improve error detection (e.g. what to do if gdb failed to run?)
>> * suppress stray gdb output
>> * set $SIGNO_OF_THE_COREDUMP to work around non-working
>> $_siginfo.si_signo
>> * decide whether to push for $_signo support in gdb (I have a tested
>> gdb patch)
>> * instruction analyzer is x86 specific now, make it per-arch
>>    (how to get arch name???)
>>
>> v2: improved instruction analyzer, added more exploibable cases to
>> detect,
>> added tests
>>
>> Signed-off-by: Denys Vlasenko <dvlasenk at redhat.com>
>> ---
>>   abrt.spec.in                                       |   2 +
>>   src/plugins/Makefile.am                            |  10 +-
>>   src/plugins/abrt-action-analyze-vulnerability      |   9 +
>>   src/plugins/abrt-gdb-exploitable                   | 502
>> +++++++++++++++++++++
>>   src/plugins/ccpp_event.conf                        |   5 +-
>>   tests/abrt-exploitable/.gitignore                  |  10 +
>>   tests/abrt-exploitable/Makefile                    |  67 +++
>>   tests/abrt-exploitable/testDivideByZero.c          |  10 +
>>   tests/abrt-exploitable/testExecuteInvalid.c        |  17 +
>>   .../abrt-exploitable/testFloatingPointException.c  |  10 +
>>   tests/abrt-exploitable/testReadNull.c              |  12 +
>>   tests/abrt-exploitable/testReadRandom.c            |  14 +
>>   tests/abrt-exploitable/testSignalAbort.c           |  10 +
>>   tests/abrt-exploitable/testSignalIll.c             |  10 +
>>   tests/abrt-exploitable/testStackBufferOverflow.c   |  23 +
>>   tests/abrt-exploitable/testStackRecursion.c        |  15 +
>>   tests/abrt-exploitable/testWriteRandom.c           |  15 +
>>   17 files changed, 738 insertions(+), 3 deletions(-)
>>   create mode 100755 src/plugins/abrt-action-analyze-vulnerability
>>   create mode 100755 src/plugins/abrt-gdb-exploitable
>>   create mode 100644 tests/abrt-exploitable/.gitignore
>>   create mode 100644 tests/abrt-exploitable/Makefile
>>   create mode 100644 tests/abrt-exploitable/testDivideByZero.c
>>   create mode 100644 tests/abrt-exploitable/testExecuteInvalid.c
>>   create mode 100644 tests/abrt-exploitable/testFloatingPointException.c
>>   create mode 100644 tests/abrt-exploitable/testReadNull.c
>>   create mode 100644 tests/abrt-exploitable/testReadRandom.c
>>   create mode 100644 tests/abrt-exploitable/testSignalAbort.c
>>   create mode 100644 tests/abrt-exploitable/testSignalIll.c
>>   create mode 100644 tests/abrt-exploitable/testStackBufferOverflow.c
>>   create mode 100644 tests/abrt-exploitable/testStackRecursion.c
>>   create mode 100644 tests/abrt-exploitable/testWriteRandom.c
>>
>> diff --git a/abrt.spec.in b/abrt.spec.in
>> index fec5b56..ce5f944 100644
>> --- a/abrt.spec.in
>> +++ b/abrt.spec.in
>> @@ -670,6 +670,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor
>> &>/dev/null || :
>>   %{_initrddir}/abrt-ccpp
>>   %endif
>>   %{_libexecdir}/abrt-hook-ccpp
>> +%{_libexecdir}/abrt-gdb-exploitable
>>
>>   # attr(6755) ~= SETUID|SETGID
>>   %attr(6755, abrt, abrt)
>> %{_libexecdir}/abrt-action-install-debuginfo-to-abrt-cache
>> @@ -677,6 +678,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor
>> &>/dev/null || :
>>   %{_bindir}/abrt-action-analyze-c
>>   %{_bindir}/abrt-action-trim-files
>>   %{_bindir}/abrt-action-analyze-core
>> +%{_bindir}/abrt-action-analyze-vulnerability
>>   %{_bindir}/abrt-action-install-debuginfo
>>   %{_bindir}/abrt-action-generate-backtrace
>>   %{_bindir}/abrt-action-generate-core-backtrace
>> diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
>> index 767c045..20e0297 100644
>> --- a/src/plugins/Makefile.am
>> +++ b/src/plugins/Makefile.am
>> @@ -3,6 +3,7 @@
>>   bin_SCRIPTS = \
>>       abrt-action-install-debuginfo \
>>       abrt-action-analyze-core \
>> +    abrt-action-analyze-vulnerability \
>>       abrt-action-analyze-vmcore \
>>       abrt-action-list-dsos \
>>       abrt-action-perform-ccpp-analysis \
>> @@ -29,9 +30,12 @@ bin_PROGRAMS += \
>>       abrt-bodhi
>>   endif
>>
>> -libexec_PROGRAMS = abrt-action-install-debuginfo-to-abrt-cache
>> +libexec_PROGRAMS = \
>> +    abrt-action-install-debuginfo-to-abrt-cache
>>
>> -libexec_SCRIPTS = abrt-action-ureport
>> +libexec_SCRIPTS = \
>> +    abrt-action-ureport \
>> +    abrt-gdb-exploitable
>>
>>   #dist_pluginsconf_DATA = Python.conf
>>
>> @@ -68,6 +72,7 @@ PYTHON_FILES = \
>>       abrt-action-install-debuginfo.in \
>>       abrt-action-list-dsos \
>>       abrt-action-analyze-core \
>> +    abrt-action-analyze-vulnerability \
>>       abrt-action-analyze-vmcore.in \
>>       abrt-action-perform-ccpp-analysis.in
>>
>> @@ -84,6 +89,7 @@ EXTRA_DIST = \
>>       abrt-action-analyze-vmcore \
>>       abrt-action-save-kernel-data \
>>       abrt-action-ureport \
>> +    abrt-gdb-exploitable \
>>       https-utils.h \
>>       post_report.xml.in \
>>       abrt-action-analyze-ccpp-local
>> diff --git a/src/plugins/abrt-action-analyze-vulnerability
>> b/src/plugins/abrt-action-analyze-vulnerability
>> new file mode 100755
>> index 0000000..f2a410e
>> --- /dev/null
>> +++ b/src/plugins/abrt-action-analyze-vulnerability
>> @@ -0,0 +1,9 @@
>> +#!/bin/sh
>> +
>> +if type gdb >/dev/null 2>&1; then
>> +    # gdb is avaliable
>> +    gdb --batch \
>> +        -ex 'python execfile("/usr/libexec/abrt-gdb-exploitable")' \
>> +        -ex 'core-file ./coredump' \
>> +        -ex 'abrt-exploitable 5 ./exploitable'
>> +fi
>> diff --git a/src/plugins/abrt-gdb-exploitable
>> b/src/plugins/abrt-gdb-exploitable
>> new file mode 100755
>> index 0000000..4f73e0b
>> --- /dev/null
>> +++ b/src/plugins/abrt-gdb-exploitable
>> @@ -0,0 +1,502 @@
>> +#!/usr/bin/python
>> +# This is a GDB plugin.
>> +# Usage:
>> +# gdb --batch -ex 'python execfile("THIS_FILE")' -ex run -ex
>> abrt-exploitable PROG
>> +# or
>> +# gdb --batch -ex 'python execfile("THIS_FILE")' -ex 'core COREDUMP'
>> -ex abrt-exploitable
>> +
>> +import gdb
>> +import os
>> +import signal
>> +
>> +_writing_instr = {
>> +    # insn:N, where N:
>> +    # -1: this insn always writes to memory
>> +    # -2: writes to memory if any operand is a memory operand
>> +    # 2:  writes to memory if 2nd (or later) operand is a memory operand
>> +    #
>> +    # Two-operand insns
>> +    "add":2,
>> +    "adc":2,
>> +    "sub":2,
>> +    "sbb":2,
>> +    "and":2,
>> +    "xor":2,
>> +    "or":2,
>> +    "xadd":2,
>> +    "cmpxchg":2,
>> +    # One-operand insns. Can use 1 or -2
>> +    "inc":-2,
>> +    "dec":-2,
>> +    "neg":-2,
>> +    "not":-2,
>> +    "pop":-2,
>> +    # "Set byte on condition". One-operand insns.
>> +    "seta":-2,
>> +    "setae":-2,
>> +    "setb":-2,
>> +    "setbe":-2,
>> +    "setc":-2,
>> +    "sete":-2,
>> +    "setg":-2,
>> +    "setge":-2,
>> +    "setl":-2,
>> +    "setle":-2,
>> +    "setna":-2,
>> +    "setnae":-2,
>> +    "setnb":-2,
>> +    "setnbe":-2,
>> +    "setnc":-2,
>> +    "setne":-2,
>> +    "setng":-2,
>> +    "setnge":-2,
>> +    "setnl":-2,
>> +    "setnle":-2,
>> +    "setno":-2,
>> +    "setnp":-2,
>> +    "setns":-2,
>> +    "setnz":-2,
>> +    "seto":-2,
>> +    "setp":-2,
>> +    "setpe":-2,
>> +    "setpo":-2,
>> +    "sets":-2,
>> +    "setz":-2,
>> +    # Shifts.
>> +    # sarl $2,(%rcx)
>> +    # sarl (%rax) - *implicit* operand (shift count) 1.
>> +    # shld 11,%ecx,(%rdi) - *third* operand is r/m.
>> +    # Luckily, any memory operand is a destination, can use -2.
>> +    "shl":-2,
>> +    "shr":-2,
>> +    "sal":-2,
>> +    "sar":-2,
>> +    "rol":-2,
>> +    "ror":-2,
>> +    "rcl":-2,
>> +    "rcr":-2,
>> +    "shld":-2,
>> +    "shrd":-2,
>> +    # Bit tests. Any memory operand is a destination, can use -2.
>> +    "bts":-2,
>> +    "btr":-2,
>> +    "btc":-2,
>> +    # One-operand (register pair is another, implicit operand).
>> +    "cmpxchg8b":-2,
>> +    "cmpxchg16b":-2,
>> +
>> +    # Either mem operand indicates write to mem.
>> +    "xchg":-2,
>> +
>> +    # String store insns.
>> +    # Look similar to widening signed move "movs[bwl][wlq]",
>> +    # but aliasing doesn't happen since widening move has two siffixes
>> +    "movs":-1,
>> +    "stos":-1,
>> +    # Widening moves never store to mem.
>> +    # May look like we need to list them because otherwise they get
>> caught
>> +    # by "movXXX", but thankfully their 2nd operand is never a memory
>> reference,
>> +    # which "movXXX" wildcard checks.
>> +    #"mov[sz][bwl][wlq]":0,
>> +
>> +    # One-operand insn.
>> +    # These are system insns, but they do NOT cause exception in
>> userspace.
>> +    "smsw":-2,
>> +    "sgdt":-2,
>> +    "sidt":-2,
>> +    "sldt":-2,
>> +    "str":-2,
>> +
>> +    # FPU/SIMD madness follows.
>> +
>> +    # FPU store insns. One-operand.
>> +    "fsts":-2,
>> +    "fstl":-2,
>> +    #"fstt" doesn't exist
>> +    "fstps":-2,
>> +    "fstpl":-2,
>> +    "fstpt":-2,
>> +    # Saving state. One-operand insns.
>> +    "fstcw":-2,
>> +    "fnstcw":-2,
>> +    "fstsw":-2,
>> +    "fnstsw":-2,
>> +    "fstenv":-2,
>> +    "fnstenv":-2,
>> +    "fsave":-2,
>> +    "fnsave":-2,
>> +    "fxsave":-2,
>> +    "xsave":-2,
>> +    "xsaveopt":-2,
>> +    "fsave64":-2,
>> +    "fnsave64":-2,
>> +    "fxsave64":-2,
>> +    "xsave64":-2,
>> +    "xsaveopt64":-2,
>> +    "stmxcsr":-2,
>> +    "vstmxcsr":-2,
>> +    # SIMD store insns.
>> +    # Three-operand insns. Any memory operand is a destination.
>> +    "vcvtps2ph":-2,
>> +    "extractps":-2,
>> +    "vextractps":-2,
>> +    #[v]extractpd does not exist
>> +    "vextractf128":-2,
>> +    "vextracti128":-2,
>> +    "pextr":-2,       # covers pextr[bwq]
>> +    "pextrd":-2,
>> +    "vpextr":-2,
>> +    "vpextrd":-2,
>> +    "vmaskmovpd":-2,
>> +    "vmaskmovps":-2,
>> +    "vpmaskmovd":-2,
>> +    "vpmaskmovq":-2,
>> +    # These insns have implicit (%edi) dest operand:
>> +    "maskmovq":-1,    # mmx version
>> +    "maskmovdqu":-1,
>> +    "vmaskmovdqu":-1,
>> +
>> +    # check binutils/gas/testsuite/gas/i386/* for more weird insns
>> +    # Instruction Set Reference, A-M and N-Z:
>> +    # http://download.intel.com/products/processor/manual/253666.pdf
>> +    # http://download.intel.com/products/processor/manual/253667.pdf
>> +    # SSE4:
>> +    #
>> http://software.intel.com/sites/default/files/m/0/3/c/d/4/18187-d9156103.pdf
>>
>> +    # Instruction Set Extensions:
>> +    #
>> http://download-software.intel.com/sites/default/files/319433-014.pdf
>> +    # Xeon Phi:
>> +    #
>> http://download-software.intel.com/sites/default/files/forum/278102/327364001en.pdf
>>
>> +
>> +    #"vmovXXX" - special-cased in the code
>> +    "mov":2
>> +
>> +    # Note: stack-writing instructions are omitted
>> +}
>> +
>> +_pushing_instr = {
>> +    "push":-1,
>> +    "pusha":-1,
>> +    "pushf":-1,
>> +    "enter":-1,
>> +    "call":-1,
>> +    "lcall":-1
>> +}
>> +
>> +_intdiv_instr = {
>> +    "div":-1,
>> +    "idiv":-1
>> +}
>> +
>> +_jumping_instr = {
>> +    "jmp":-1,  # indirect jumps/calls with garbage data
>> +    "call":-1, # call: also possible that stack is exhausted
>> (infinite recursion)
>> +    "ljmp":-1,
>> +    "lcall":-1,
>> +    # Yes, lret/iret isn't used in normal userspace code,
>> +    # but it does work (compile with "gcc -nostartfiles -nostdlib
>> -m32"):
>> +    #
>> +    #_start: .globl  _start
>> +    #        pushf
>> +    #        push    %cs
>> +    #        push    $next
>> +    #        iret            # lret or ret would work too
>> +    #next:
>> +    #        movl    $42, %ebx
>> +    #        movl    $1, %eax
>> +    #        int     $0x80   # exit(42)
>> +    #
>> +    "iret":-1,
>> +    "lret":-1,
>> +    "ret":-1
>> +}
>> +
>> +_return_instr = {
>> +    "iret":-1,
>> +    "lret":-1,
>> +    "ret":-1   # stack smashed if we crash on this
>> +}
>> +
>> +def _fetch_insn_from_table(ins, table):
>> +    if not ins:
>> +        return None
>> +    if ins in table.keys():
>> +        return table[ins]
>> +    # Drop common byte/word/long/quad suffix and try again
>> +    if ins[-1] in ("b", "w", "l", "q"):
>> +        ins = ins[:-1]
>> +        if ins in table.keys():
>> +            return table[ins]
>> +    return None
>> +
>> +def _instruction_is_writing(self):
>> +    operand = _fetch_insn_from_table(self.mnemonic, _writing_instr)
>> +    if not operand:
>> +        if not self.mnemonic:
>> +            return False
>> +        # There are far too many SSE store instructions,
>> +        # don't want to pollute the table with them.
>> +        # Special-case the check for MOVxxx
>> +        # and its SIMD cousins VMOVxxx:
>> +        if self.mnemonic[:3] != "mov" and self.mnemonic[:4] != "vmov":
>> +            return False
>> +        operand = 2
>> +
>> +    if operand == -1:  # no need to check operands, it's a write
>> +        return True
>> +
>> +    # Memory operands look like this: [%seg:][[-]0xHEXNUM][(%reg[,...])]
>> +    # Careful with immediate operands which are $0xHEXNUM
>> +    # and FPU register references which are st(N).
>> +    if self.operands.startswith("0x") or
>> self.operands.startswith("-0x") or self.operands.startswith("("):
>> +        mem_op_pos = 0
>> +    else:
>> +        mem_op_pos = self.operands.find(",0x")
>> +        if mem_op_pos < 0:
>> +            mem_op_pos = self.operands.find(",-0x")
>> +        if mem_op_pos < 0:
>> +            mem_op_pos = self.operands.find(":0x")
>> +        if mem_op_pos < 0:
>> +            mem_op_pos = self.operands.find(":-0x")
>> +        if mem_op_pos < 0:
>> +            mem_op_pos = self.operands.find(",(")
>> +        if mem_op_pos < 0:
>> +            mem_op_pos = self.operands.find(":(")
>> +        if mem_op_pos < 0:
>> +            return False # no memory operands
>> +        mem_op_pos += 1
>> +
>> +    if operand == -2:  # any mem operand indicates write
>> +        return True
>> +
>> +    comma = self.operands.find(",")
>> +    if mem_op_pos < comma:
>> +        # "%cs:0x0(%rax,%rax,1),foo" - 1st operand is memory
>> +        # "%cs:0x0(%rax),foo" - 1st operand is memory
>> +        memory_operand = 1
>> +    elif comma < 0:
>> +        # "%cs:0x0(%rax)" - 1st operand is memory
>> +        memory_operand = 1
>> +    else:
>> +        # mem_op_pos is after comma
>> +        # "foo,%cs:0x0(%rax,%rax,1)" - 2nd operand is memory
>> +        # (It also can be a third, fourth etc operand)
>> +        memory_operand = 2
>> +
>> +    if operand == memory_operand:
>> +        return True
>> +    return False
>> +
>> +def _instruction_is_pushing(self):
>> +    if _fetch_insn_from_table(self.mnemonic, _pushing_instr):
>> +        return True
>> +    return False
>> +
>> +def _instruction_is_division(self):
>> +    if _fetch_insn_from_table(self.mnemonic, _intdiv_instr):
>> +        return True
>> +    return False
>> +
>> +def _instruction_is_jumping(self):
>> +    if _fetch_insn_from_table(self.mnemonic, _jumping_instr):
>> +        return True
>> +    return False
>> +
>> +def _instruction_is_return(self):
>> +    if _fetch_insn_from_table(self.mnemonic, _return_instr):
>> +        return True
>> +    return False
>> +
>> +class _Signal_and_Insn:
>> +    pass
>> +
>> +def _get_signal_and_instruction():
>> +    si = _Signal_and_Insn()
>> +    si.signo = None
>> +    try:
>> +        sig = gdb.parse_and_eval("$_signo") # ("$_siginfo.si_signo")
>> +        # type(sig) = <type 'gdb.Value'>
>> +        # sig is 8 (for SIGFPE)
>> +        si.signo = int(sig)
>> +    except gdb.error:
>> +        # Python Exception <class 'gdb.error'> Attempt to extract a
>> component of a value that is not a structure.:
>> +        # Possible reasons why $_siginfo doesn't exist:
>> +        # program is still running, program exited normally,
>> +        # we work with a coredump from an old kernel.
>> +        #
>> +        # HACK_ALERT: kernels before 3.?.? do not record siginfo in
>> coredumps,
>> +        # so $_siginfo isn't present.
>> +        # Lets see whether we are running from the abrt and it
>> provided us with signal number
>> +        #
>> +        try:
>> +            si.signo = int(os.environ["SIGNO_OF_THE_COREDUMP"])
>> +        except KeyError:
>> +            return None
>> +
>> +    si.current_instruction = None
>> +    si.mnemonic = None
>> +    si.operands = ""
>> +    try:
>> +        # just "disassemble $pc" won't work if $pc doesn't point
>> +        # inside a known function
>> +        instructions = gdb.execute("disassemble $pc,$pc+32",
>> to_string=True)
>> +        # type(instructions) = <type 'str'>
>> +    except gdb.error:
>> +        # For example, if tracee already exited normally.
>> +        # Another observed case is if $pc points to unmapped area.
>> +        # Python Exception <class 'gdb.error'> No registers.:
>> +        return si
>> +
>> +    raw_instructions = instructions
>> +    #print instructions
>> +    instructions = []
>> +    current = None
>> +    for line in raw_instructions.split("\n"):
>> +        # line can be:
>> +        # "Dump of assembler code from 0xAAAA to 0xBBBB:"
>> +        # "[=>] 0x00000000004004dc[ <+0>]:  push   %rbp"
>> +        #   (" <+0>" part is present when we run on a live process,
>> +        #   on coredump it is absent)
>> +        # "End of assembler dump."
>> +        # "" (empty line)
>> +        if line.startswith("=>"):
>> +            line = line[2:]
>> +            current = len(instructions)
>> +        line = line.split(":", 1)
>> +        if len(line) < 2:        # no ":"?
>> +            continue
>> +        line = line[1]           # drop "foo:"
>> +        line = line.strip()      # drop leading/trailing whitespace
>> +        if line:
>> +            instructions.append(line)
>> +    if current == None:
>> +        # not False! we determined that $pc points to a bad address,
>> +        # which is an interesting fact.
>> +        return si
>> +
>> +    si.current_instruction = instructions[current]
>> +    # TODO: too simplistic.
>> +    # consider this example:
>> +    # "data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)"
>> +    t = si.current_instruction.split(None,2)
>> +    si.mnemonic = t[0]
>> +    if len(t) > 1:
>> +        si.operands = t[1]
>> +    return si
>> +
>> +#Our initial set of testing will use the list Apple included in their
>> +#CrashWrangler announcement:
>> +#
>> +#Exploitable if:
>> +#        Crash on write instruction
>> +#        Crash executing invalid address
>> +#        Crash calling an invalid address
>> +#        Crash accessing an uninitialized or freed pointer as
>> indicated by
>> +#            using the MallocScribble environment variable
>> +#        Illegal instruction exception
>> +#        Abort due to -fstack-protector, _FORTIFY_SOURCE, heap
>> corruption
>> +#            detected
>> +#        Stack trace of crashing thread contains certain functions
>> such as
>> +#            malloc, free, szone_error, objc_MsgSend, etc.
>> +
>> +def _is_exploitable(self):
>> +    self.exploitable_rating = 3
>> +    self.exploitable_desc = ""
>> +
>> +    if 0:
>> +        pass
>> +    # SIGABRT Abort signal from abort(3)
>> +    # SIGQUIT Quit from keyboard
>> +    # SIGXCPU CPU time limit exceeded
>> +    # SIGXFSZ File size limit exceeded
>> +    # SIGTRAP Trace/breakpoint trap
>> +    # SIGSYS  Bad argument to routine (SVr4)
>> +    # SIGFPE  Floating point exception
>> +    # SIGILL  Illegal Instruction
>> +    # SIGSEGV Invalid memory reference
>> +    # SIGBUS  Bus error (bad memory access)
>> +    elif self.signo == signal.SIGABRT:
>> +        self.exploitable_rating = 0
>> +        self.exploitable_desc = "ABRT signal (abort() was called?)"
>> +    elif self.signo == signal.SIGQUIT:
>> +        self.exploitable_rating = 0
>> +        self.exploitable_desc = "QUIT signal (Ctrl-\ pressed?)"
>> +    elif self.signo == signal.SIGXCPU:
>> +        self.exploitable_rating = 0
>> +        self.exploitable_desc = "XCPU signal (over CPU time limit)"
>> +    elif self.signo == signal.SIGXFSZ:
>> +        self.exploitable_rating = 0
>> +        self.exploitable_desc = "XFSZ signal (over file size limit)"
>> +    elif self.signo == signal.SIGTRAP:
>> +        self.exploitable_rating = 0
>> +        self.exploitable_desc = "TRAP signal (can be a bug in a
>> debugger/tracer)"
>> +    elif self.signo == signal.SIGSYS:
>> +        self.exploitable_rating = 1
>> +        self.exploitable_desc = "SYS signal (unknown/masked syscall
>> was called?)"
>> +
>> +    elif self.signo == signal.SIGFPE:
>> +        self.exploitable_rating = 1
>> +        self.exploitable_desc = "Arithmetic exception occurred"
>> +        if _instruction_is_division(self):
>> +            self.exploitable_rating = 0
>> +            self.exploitable_desc = "Division by zero occurred"
>> +    elif self.signo == signal.SIGILL:
>> +        self.exploitable_rating = 5
>> +        self.exploitable_desc = "Illegal instruction (jump to a
>> random address?)"
>> +
>> +    # TODO: check that sig is SIGSEGV/SIGBUS?
>> +
>> +    elif _instruction_is_pushing(self):
>> +        self.exploitable_rating = 4
>> +        self.exploitable_desc = "Stack overflow"
>> +    elif _instruction_is_writing(self):
>> +        self.exploitable_rating = 6
>> +        self.exploitable_desc = "Write to an invalid address"
>> +    elif _instruction_is_return(self):
>> +        self.exploitable_rating = 7
>> +        self.exploitable_desc = "Subroutine return to an invalid
>> address (corrupted stack?)"
>> +    # Note: we check "ret" first, _then_ jumps.
>> +    # Corrupted stack is different from corrupted data.
>> +    elif _instruction_is_jumping(self):
>> +        self.exploitable_rating = 6
>> +        self.exploitable_desc = "Jump to an invalid address"
>> +    elif not self.current_instruction:
>> +        self.exploitable_rating = 6
>> +        self.exploitable_desc = "Jump to an invalid address"
>> +    elif self.signo == signal.SIGBUS:
>> +        self.exploitable_rating = 5
>> +        self.exploitable_desc = "Access past the end of mapped file,
>> invalid address, unaligned access, etc"
>> +    #elif self.signo = signal.SIGfoo:
>> +
>> +class AbrtExploitable(gdb.Command):
>> +    "Analyze a crash to determine exploitability"
>> +    def __init__(self):
>> +        super(AbrtExploitable, self).__init__(
>> +                "abrt-exploitable",
>> +                gdb.COMMAND_SUPPORT, # command class
>> +                gdb.COMPLETE_NONE,   # completion method
>> +                False  # => it's not a prefix command
>> +        )
>> +
>> +    # Called when the command is invoked from GDB
>> +    def invoke(self, args, from_tty):
>> +        si = _get_signal_and_instruction()
>> +        #print "w", _instruction_is_writing(self)
>> +        f = sys.stdout
>> +        min_rating = 0
>> +        if args:
>> +            args = args.split(None, 1)
>> +            min_rating = int(args[0])
>> +            if len(args) > 1:
>> +                f = open(args[1], 'w')
>> +        if not si:
>> +            sys.stderr.write("Can't get signal no and do
>> exploitability analysis\n")
>> +            return
>> +        _is_exploitable(si)
>> +        if si.exploitable_desc:
>> +            if si.exploitable_rating >= min_rating:
>> +                f.write("Likely crash reason: " + si.exploitable_desc
>> + "\n")
>> +                f.write("Exploitable rating (0-9 scale): " +
>> str(si.exploitable_rating) + "\n")
>> +        else:
>> +            sys.stderr.write("Exploitability analysis on this crash
>> came up empty\n")
>> +
>> +AbrtExploitable()
>> diff --git a/src/plugins/ccpp_event.conf b/src/plugins/ccpp_event.conf
>> index dfc4908..aa8cdb3 100644
>> --- a/src/plugins/ccpp_event.conf
>> +++ b/src/plugins/ccpp_event.conf
>> @@ -15,10 +15,13 @@ EVENT=post-create analyzer=CCpp
>>               exit 1
>>           fi
>>           # Try generating backtrace, if it fails we can still use
>> -        # the UUID generated by abrt-action-analyze-c
>> +        # the hash generated by abrt-action-analyze-c
>>           ##satyr migration:
>>           #satyr abrt-create-core-stacktrace "$DUMP_DIR"
>>           abrt-action-generate-core-backtrace
>> +        # Run GDB plugin to see if crash looks exploitable
>> +        abrt-action-analyze-vulnerability
>> +        # Generate hash
>>           abrt-action-analyze-c &&
>>           abrt-action-list-dsos -m maps -o dso_list &&
>>           (
>> diff --git a/tests/abrt-exploitable/.gitignore
>> b/tests/abrt-exploitable/.gitignore
>> new file mode 100644
>> index 0000000..b0c3b29
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/.gitignore
>> @@ -0,0 +1,10 @@
>> +testDivideByZero
>> +testExecuteInvalid
>> +testFloatingPointException
>> +testReadNull
>> +testReadRandom
>> +testSignalAbort
>> +testSignalIll
>> +testStackBufferOverflow
>> +testStackRecursion
>> +testWriteRandom
>> diff --git a/tests/abrt-exploitable/Makefile
>> b/tests/abrt-exploitable/Makefile
>> new file mode 100644
>> index 0000000..fe4078d
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/Makefile
>> @@ -0,0 +1,67 @@
>> +# Copyright (C) 2010, 2011  Red Hat, Inc.
>> +#
>> +# This file is part of ABRT.
>> +#
>> +# This program is free software; you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License as published by
>> +# the Free Software Foundation; either version 2 of the License, or
>> +# (at your option) any later version.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License
>> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
>> +
>> +CFLAGS=-g
>> +
>> +TESTS=\
>> + testDivideByZero\
>> + testReadNull\
>> + testStackRecursion\
>> + testReadRandom\
>> + testSignalAbort\
>> + testSignalIll\
>> + testWriteRandom\
>> + testExecuteInvalid\
>> + testStackBufferOverflow\
>> + testFloatingPointException\
>> +
>> +all: $(TESTS)
>> +
>> +clean:
>> +    rm -f $(TESTS)
>> +
>> +testlive:
>> +    for t in $(TESTS); do \
>> +        echo "====="; \
>> +        echo "Test: $$t"; \
>> +        gdb --batch \
>> +        -ex 'python
>> execfile("../../src/plugins/abrt-gdb-exploitable")' \
>> +        -ex 'run' \
>> +        -ex 'disas $$pc-16,$$pc+16' \
>> +        -ex 'abrt-exploitable' \
>> +        -ex 'cont' \
>> +        -ex 'quit' \
>> +        ./$$t; \
>> +    done 2>&1 | tee testlive.log
>> +
>> +testcore:
>> +    rm ./core* 2>/dev/null; \
>> +    ulimit -c unlimited; \
>> +    for t in $(TESTS); do \
>> +        echo "====="; \
>> +        echo "Test: $$t"; \
>> +        ./$$t && { echo "No crash???"; continue; }; \
>> +        mv core* core || { echo "No corefile???"; continue; }; \
>> +        gdb --batch \
>> +        -ex 'python
>> execfile("../../src/plugins/abrt-gdb-exploitable")' \
>> +        -ex 'core ./core' \
>> +        -ex 'disas $$pc-16,$$pc+16' \
>> +        -ex 'abrt-exploitable' \
>> +        -ex 'quit' \
>> +        ; \
>> +        rm ./core; \
>> +    done 2>&1 | tee testcore.log
>> diff --git a/tests/abrt-exploitable/testDivideByZero.c
>> b/tests/abrt-exploitable/testDivideByZero.c
>> new file mode 100644
>> index 0000000..06d1551
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testDivideByZero.c
>> @@ -0,0 +1,10 @@
>> +/*
>> + * Test a divide by zero error
>> + * This error is not exploitable
>> + */
>> +
>> +#include <stdio.h>
>> +
>> +int main(int argc, char *argv[]) {
>> +    printf("%d\n", 7/0);
>> +}
>> diff --git a/tests/abrt-exploitable/testExecuteInvalid.c
>> b/tests/abrt-exploitable/testExecuteInvalid.c
>> new file mode 100644
>> index 0000000..86ce515
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testExecuteInvalid.c
>> @@ -0,0 +1,17 @@
>> +/*
>> + * Test a crash attempting to execute an invalid address
>> + * This error is exploitable
>> + */
>> +
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +int (*function_pointer)();
>> +
>> +int main(int argc, char *argv[]) {
>> +    char *a;
>> +    a = malloc(1024);
>> +    a = (size_t)a * 1024;   // This should put us well outside the
>> valid memory range
>> +    function_pointer = a;
>> +    function_pointer();
>> +}
>> diff --git a/tests/abrt-exploitable/testFloatingPointException.c
>> b/tests/abrt-exploitable/testFloatingPointException.c
>> new file mode 100644
>> index 0000000..53e8762
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testFloatingPointException.c
>> @@ -0,0 +1,10 @@
>> +/*
>> + * Test the illegal instruction signal
>> + * This error is exploitable
>> + */
>> +
>> +#include <signal.h>
>> +
>> +int main(int argc, char *argv[]) {
>> +    raise(SIGFPE);
>> +}
>> diff --git a/tests/abrt-exploitable/testReadNull.c
>> b/tests/abrt-exploitable/testReadNull.c
>> new file mode 100644
>> index 0000000..548cf73
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testReadNull.c
>> @@ -0,0 +1,12 @@
>> +/*
>> + * Test a NULL read
>> + * This error is not exploitable
>> + */
>> +
>> +#include <stdio.h>
>> +
>> +int main(int argc, char *argv[]) {
>> +    char *a;
>> +    a = 0x0;
>> +    puts(a);
>> +}
>> diff --git a/tests/abrt-exploitable/testReadRandom.c
>> b/tests/abrt-exploitable/testReadRandom.c
>> new file mode 100644
>> index 0000000..8493078
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testReadRandom.c
>> @@ -0,0 +1,14 @@
>> +/*
>> + * Test a crash attempting to read invalid memory
>> + * This error is not exploitable
>> + */
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +
>> +int main(int argc, char *argv[]) {
>> +    char *a;
>> +    a = malloc(1024);
>> +    a = (size_t)a * 1024;   // This should put us well outside the
>> valid memory range
>> +    printf("%s\n", a);
>> +}
>> diff --git a/tests/abrt-exploitable/testSignalAbort.c
>> b/tests/abrt-exploitable/testSignalAbort.c
>> new file mode 100644
>> index 0000000..fc41227
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testSignalAbort.c
>> @@ -0,0 +1,10 @@
>> +/*
>> + * Test the abort signal
>> + * This error is not exploitable
>> + */
>> +
>> +#include <signal.h>
>> +
>> +int main(int argc, char *argv[]) {
>> +    raise(SIGABRT);
>> +}
>> diff --git a/tests/abrt-exploitable/testSignalIll.c
>> b/tests/abrt-exploitable/testSignalIll.c
>> new file mode 100644
>> index 0000000..7783263
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testSignalIll.c
>> @@ -0,0 +1,10 @@
>> +/*
>> + * Test the illegal instruction signal
>> + * This error is exploitable
>> + */
>> +
>> +#include <signal.h>
>> +
>> +int main(int argc, char *argv[]) {
>> +    raise(SIGILL);
>> +}
>> diff --git a/tests/abrt-exploitable/testStackBufferOverflow.c
>> b/tests/abrt-exploitable/testStackBufferOverflow.c
>> new file mode 100644
>> index 0000000..fcbd95f
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testStackBufferOverflow.c
>> @@ -0,0 +1,23 @@
>> +/*
>> + * Test a stack buffer overflow
>> + * This test could be exploitable (it needs further analysis)
>> + */
>> +
>> +#include <stdio.h>
>> +
>> +int i;
>> +
>> +int my_function() {
>> +    char a[2];
>> +
>> +    for (i = 0; i < 1024; i++) {
>> +        a[i] = 'A';
>> +    }
>> +    printf("%s\n", a);
>> +}
>> +
>> +int main(int argc, char *argv[]) {
>> +    my_function();
>> +    my_function();
>> +    return 0;
>> +}
>> diff --git a/tests/abrt-exploitable/testStackRecursion.c
>> b/tests/abrt-exploitable/testStackRecursion.c
>> new file mode 100644
>> index 0000000..dddf583
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testStackRecursion.c
>> @@ -0,0 +1,15 @@
>> +/*
>> + * Test a crash from stack recursion
>> + * This error is not exploitable
>> + */
>> +
>> +#include <stdio.h>
>> +
>> +void my_function() {
>> +    char a[1024];
>> +    my_function();
>> +}
>> +
>> +int main(int argc, char *argv[]) {
>> +    my_function();
>> +}
>> diff --git a/tests/abrt-exploitable/testWriteRandom.c
>> b/tests/abrt-exploitable/testWriteRandom.c
>> new file mode 100644
>> index 0000000..812f94c
>> --- /dev/null
>> +++ b/tests/abrt-exploitable/testWriteRandom.c
>> @@ -0,0 +1,15 @@
>> +/*
>> + * Test a crash attempting to write invalid memory
>> + * This error is exploitable
>> + */
>> +
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +int main(int argc, char *argv[]) {
>> +    char *a;
>> +    char b[] = "pwnt";
>> +    a = malloc(1024);
>> +    a = (size_t)a * 1024;   // This should put us well outside the
>> valid memory range
>> +    strcpy(a, b);
>> +}
>>
>



More information about the Crash-catcher mailing list