From 865f6d87f3bce7f569343382c3dfd1bc68dcacee Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 27 Dec 2018 23:35:39 +0100
Subject: Displayed mitigations for Elf files using a new Python plugin.

---
 configure.ac                           |   1 +
 plugins/python/Makefile.am             |   2 +-
 plugins/python/checksec/Makefile.am    |   9 +++
 plugins/python/checksec/__init__.py    |   4 +
 plugins/python/checksec/mitigations.py | 135 +++++++++++++++++++++++++++++++++
 plugins/python/checksec/plugin.py      |  39 ++++++++++
 6 files changed, 189 insertions(+), 1 deletion(-)
 create mode 100644 plugins/python/checksec/Makefile.am
 create mode 100644 plugins/python/checksec/__init__.py
 create mode 100644 plugins/python/checksec/mitigations.py
 create mode 100644 plugins/python/checksec/plugin.py

diff --git a/configure.ac b/configure.ac
index 3943a2f..b514ea5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -394,6 +394,7 @@ AC_CONFIG_FILES([Makefile
                  plugins/pychrysalide/mangling/Makefile
                  plugins/python/Makefile
                  plugins/python/apkfiles/Makefile
+                 plugins/python/checksec/Makefile
                  plugins/readdex/Makefile
                  plugins/readelf/Makefile
                  plugins/readmc/Makefile
diff --git a/plugins/python/Makefile.am b/plugins/python/Makefile.am
index efbb704..1560913 100644
--- a/plugins/python/Makefile.am
+++ b/plugins/python/Makefile.am
@@ -1,2 +1,2 @@
 
-SUBDIRS = apkfiles
+SUBDIRS = apkfiles checksec
diff --git a/plugins/python/checksec/Makefile.am b/plugins/python/checksec/Makefile.am
new file mode 100644
index 0000000..eecee28
--- /dev/null
+++ b/plugins/python/checksec/Makefile.am
@@ -0,0 +1,9 @@
+
+checksecdir = $(pluginsdatadir)/python/checksec
+
+checksec_DATA = 							\
+	__init__.py								\
+	mitigations.py							\
+	plugin.py
+
+EXTRA_DIST = $(checksec_DATA)
diff --git a/plugins/python/checksec/__init__.py b/plugins/python/checksec/__init__.py
new file mode 100644
index 0000000..691b58d
--- /dev/null
+++ b/plugins/python/checksec/__init__.py
@@ -0,0 +1,4 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from checksec.plugin import CheckSec as AutoLoad
diff --git a/plugins/python/checksec/mitigations.py b/plugins/python/checksec/mitigations.py
new file mode 100644
index 0000000..1539fa7
--- /dev/null
+++ b/plugins/python/checksec/mitigations.py
@@ -0,0 +1,135 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from pychrysalide.format.elf import ElfFormat
+
+
+class ElfMitigations():
+
+    def __init__(self, fmt):
+        """Look for mitigations in a loaded Elf format."""
+
+        self._fmt = fmt
+
+        self._nx = self._get_nx_status()
+
+        self._pie = self._get_pie_status()
+
+        self._relro = self._get_reloc_status()
+
+        self._canary = self._get_canary_status()
+
+
+    def _get_nx_status(self):
+        """Find information about the stack status."""
+
+        # Cf. https://wiki.gentoo.org/wiki/Hardened/GNU_stack_quickstart
+        # ...#Causes_of_executable_stack_markings
+
+        # -Wl,-z,execstack / -Wl,-z,noexecstack
+
+        stack = self._fmt.find_program_by_type(ElfFormat.PT_GNU_STACK)
+
+        status = stack is None or stack.p_flags & ElfFormat.PF_X
+
+        return 'No' if status else 'Yes'
+
+
+    def _get_pie_status(self):
+        """Check for taking advantage of ASLR."""
+
+        # Cf. https://stackoverflow.com/questions/2463150/what-is-the-fpie-option-for-position-independent-executables-in-gcc-and-ld/5030518#5030518
+
+        # -pie -fPIE
+
+        hdr = self._fmt.get_header()
+
+        status = hdr.e_type == ElfFormat.ET_DYN
+
+        return 'Yes' if status else 'No'
+
+
+    def _get_reloc_status(self):
+        """Track protections for the GOT."""
+
+        # Cf. https://wiki.debian.org/Hardening
+        # ...#DEB_BUILD_HARDENING_RELRO_.28ld_-z_relro.29
+
+        # -Wl,-z,relro / -Wl,-z,now
+
+        prgm = self._fmt.find_program_by_type(ElfFormat.PT_GNU_RELRO)
+
+        entry = self._fmt.find_dynamic_item_by_type(ElfFormat.DT_BIND_NOW)
+
+        if prgm is None and entry is None:
+            status = 'No'
+
+        elif not(prgm is None) and entry is None:
+            status = 'Partial'
+
+        elif prgm is None and not(entry is None):
+            status = 'Full'
+
+        else:
+            status = 'Unknown'
+
+        return status
+
+
+    def _get_canary_status(self):
+        """Look for a canary as stack protection."""
+
+        # Cf. https://outflux.net/blog/archives/2014*01/24/fstack-protector-strong/
+
+        # -fno-stack-protector / -fstack-protector / -fstack-protector-all / -fstack-protector-strong
+
+        sym = self._fmt.find_symbol_by_label('__stack_chk_fail@plt')
+
+        status = sym is None
+
+        return 'No' if status else 'Yes'
+
+
+    def __str__(self):
+        """Output a mitigation summary."""
+
+        desc = fmt.content.describe(True) + ':'
+
+        desc += '\n'
+
+        desc += '-' * (len(desc) - 1)
+
+        desc += '\n'
+
+        desc += 'NX: %s' % self._nx
+
+        desc += '\n'
+
+        desc += 'PIE: %s' % self._pie
+
+        desc += '\n'
+
+        desc += 'RelRO: %s' % self._relro
+
+        desc += '\n'
+
+        desc += 'Canary: %s' % self._canary
+
+        return desc
+
+
+if __name__ == '__main__':
+
+    from pychrysalide.features import *
+    import sys
+
+    cnt = FileContent(sys.argv[1])
+    fmt = ElfFormat(cnt)
+
+    binary = LoadedBinary(fmt)
+
+    status = binary.analyze_and_wait()
+
+    if status :
+        m = ElfMitigations(binary.format)
+        print(m)
diff --git a/plugins/python/checksec/plugin.py b/plugins/python/checksec/plugin.py
new file mode 100644
index 0000000..6efb9b5
--- /dev/null
+++ b/plugins/python/checksec/plugin.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from .mitigations import ElfMitigations
+from pychrysalide import PluginModule
+from pychrysalide.core import log_message, LMT_INFO
+from pychrysalide.format.elf import ElfFormat
+
+
+class CheckSec(PluginModule):
+    """Check for Elf mititgations."""
+
+    def get_interface(self):
+        """Provide the full plugin description."""
+
+        desc = {
+
+            'name' : 'CheckSec',
+            'desc' : 'Output the exploit mitigations compiled with a loaded binary',
+            'version' : '0.1',
+
+            'actions' : [ PluginModule.PGA_FORMAT_POST_ANALYSIS_ENDED ]
+
+        }
+
+        return desc
+
+
+    def handle_format_analysis(self, action, format, gid, status):
+        """Get notified at the end of format analysis."""
+
+        if type(format) == ElfFormat:
+
+            m = ElfMitigations(format)
+
+            msg = 'Elf mitigations: NX: <b>%s</b>  PIE: <b>%s</b>  RelRO: <b>%s</b>  Canary: <b>%s</b>' \
+                  % (m._nx, m._pie, m._relro, m._canary)
+
+            self.log_message(LMT_INFO, msg)
-- 
cgit v0.11.2-87-g4458