[RFC ABRT PATCH v2] Add gdb python plugin which analyzes vulnerability
Jiri Moskovcak
jmoskovc at redhat.com
Thu Jun 13 13:42:49 UTC 2013
Would be great if you push it into a separate branch, so we don't have
to manually apply every change you do. In your current tree run:
$ git checkout -b exploitcheck # this will create the branch
$ git push origin exploitcheck # this will push it to the server
others can just:
$ git clone .. && git checkout exploitcheck
Whenever you do some changes others will just git pull instead of the
annoying mail saving&applying ...
Thanks,
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