[RFC ABRT PATCH v2] Add gdb python plugin which analyzes vulnerability
Denys Vlasenko
dvlasenk at redhat.com
Thu Jun 13 12:01:11 UTC 2013
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);
+}
--
1.8.1.4
More information about the Crash-catcher
mailing list