Commit d0afce03 by Thomas Barabosch Committed by Enkelmann

Improve ida pro support and fix issue #24 (#25)

* This commit improves the cwe_checker_to_ida tool. First, it fixes
issue #24. Second, it introduces some unit tests for
cwe_checker_to_ida. Third, cwe_checker_to_ida parses newer cwe checks
like cwe415 or cwe787. Forth, updated description of
cwe_checker_to_ida in README.md.
parent bd0de62d
...@@ -32,9 +32,12 @@ Its main focus are ELF binaries that are commonly found on Linux and Unix operat ...@@ -32,9 +32,12 @@ Its main focus are ELF binaries that are commonly found on Linux and Unix operat
Please note that some of the above analyses only are partially implemented at the moment. Furthermore, false positives are to be expected due to shortcuts and the nature of static analysis as well as over-approximation. Please note that some of the above analyses only are partially implemented at the moment. Furthermore, false positives are to be expected due to shortcuts and the nature of static analysis as well as over-approximation.
*cwe_checker* comes with a script called `cwe_checker_to_ida`, which parses the output of *cwe_checker* and generates a IDAPython script. This script annotates the found CWEs in IDA Pro, which helps during manual analysis of a binary. The following screenshot shows some results: *cwe_checker* comes with a script called `cwe_checker_to_ida`, which parses the output of *cwe_checker* and generates a IDAPython script. This script annotates the found CWEs in IDA Pro, which helps during manual analysis of a binary. The colors represent the severeness of the found issues (yellow, orange, or red). The following screenshot shows some results:
<p align="center">
<img src="doc/images/example_ida_anotation.png" alt="IDA Pro anotation" width="50%" height="50%"/>
</p>
![](https://github.com/fkie-cad/cwe_checker/raw/master/doc/images/example_ida_anotation.png "IDA Pro anotation")
## Why use cwe_checker? ## ## Why use cwe_checker? ##
The following arguments should convince you to give *cwe_checker* a try: The following arguments should convince you to give *cwe_checker* a try:
- it is very easy to setup, just build the Docker container! - it is very easy to setup, just build the Docker container!
......
import logging import logging
colors = {"CWE190": "0xBBBBBB", RED = 0x6666FF
"CWE215": None, ORANGE = 0x6699FF
"CWE243": None, YELLOW = 0xC0FFFF
"CWE332": None,
"CWE367": "0xFF15AA", colors = {'CWE190': YELLOW,
"CWE426": "0xAA15FF", 'CWE215': None,
"CWE457": "0x4286F4", 'CWE243': None,
"CWE467": "0xFFD400", 'CWE248': YELLOW,
"CWE476": "0x2AFF00", 'CWE332': None,
"CWE676": "0x8833FF", 'CWE367': ORANGE,
"CWE-782": "0xCC00BB", 'CWE415': RED,
'CWE416': RED,
'CWE426': ORANGE,
'CWE457': YELLOW,
'CWE467': ORANGE,
'CWE476': ORANGE,
'CWE560': YELLOW,
'CWE676': RED,
'CWE782': ORANGE,
'CWE787': RED,
} }
...@@ -43,7 +52,7 @@ class CweWarningParser(object): ...@@ -43,7 +52,7 @@ class CweWarningParser(object):
try: try:
splitted_line = warning.split('WARN') splitted_line = warning.split('WARN')
cwe_warning = splitted_line[1].replace( cwe_warning = splitted_line[1].replace(
'u32', '').replace("64u", '').replace(':', '') 'u32', '').replace('64u', '').replace(':', '')
cwe_name = self._remove_color(cwe_warning.split(')')[0]) + ')' cwe_name = self._remove_color(cwe_warning.split(')')[0]) + ')'
cwe_name = cwe_name.split('{')[0].strip() + ' ' + cwe_name.split('}')[1].strip() cwe_name = cwe_name.split('{')[0].strip() + ' ' + cwe_name.split('}')[1].strip()
...@@ -55,7 +64,7 @@ class CweWarningParser(object): ...@@ -55,7 +64,7 @@ class CweWarningParser(object):
return CweWarning(cwe_name, plugin_version, cwe_message) return CweWarning(cwe_name, plugin_version, cwe_message)
except Exception as e: except Exception as e:
logging.error('[CweWarningParser] Error while parsing CWE warning: %s.' % str(e)) logging.error('[CweWarningParser] Error while parsing CWE warning: %s.', str(e))
return None return None
...@@ -63,74 +72,95 @@ class Parser(object): ...@@ -63,74 +72,95 @@ class Parser(object):
def __init__(self, result_path): def __init__(self, result_path):
self._result_path = result_path self._result_path = result_path
self._parsers = {"CWE190": self._parse_cwe190, self._parsers = {'CWE125': self.parse_path,
"CWE215": self._not_highlighted, 'CWE190': self.parse_cwe190,
"CWE243": self._not_highlighted, 'CWE215': self.not_highlighted,
"CWE332": self._not_highlighted, 'CWE243': self.not_highlighted,
"CWE367": self._parse_at, 'CWE248': self.parse_at,
"CWE426": self._parse_at, 'CWE332': self.not_highlighted,
"CWE457": self._parse_cwe457, 'CWE367': self.parse_at,
"CWE467": self._parse_cwe467, 'CWE415': self.parse_path,
"CWE476": self._parse_cwe476, 'CWE416': self.parse_path,
"CWE676": self._parse_cwe676, 'CWE426': self.parse_at,
"CWE782": self._parse_cwe782, 'CWE457': self.parse_cwe457,
'CWE467': self.parse_cwe467,
'CWE476': self.parse_cwe476,
'CWE560': self.parse_cwe560,
'CWE676': self.parse_cwe676,
'CWE782': self.parse_cwe782,
'CWE787': self.parse_path,
} }
def _read_in_config(self): def _read_in_config(self):
lines = None lines = None
with open(self._result_path, "r") as f: with open(self._result_path, 'r') as f:
lines = f.readlines() lines = f.readlines()
if not lines: if not lines:
print("[Parser] Could not read in file %s" % self._result_path) print('[Parser] Could not read in file %s' % self._result_path)
raise Exception() raise Exception()
return lines return lines
@staticmethod @staticmethod
def _not_highlighted(warning): def not_highlighted(warning):
warning.highlight = False warning.highlight = False
return warning return warning
@staticmethod @staticmethod
def _parse_at(warning): def parse_at(warning):
warning.address = warning.warning.split("at ")[-1].split()[0].strip() warning.address = warning.warning.split('at ')[-1].split()[0].strip()
return warning return warning
@staticmethod @staticmethod
def _parse_cwe190(warning): def parse_path(warning):
if "multiplication" in warning.warning: warning.address = warning.warning.split('->')[-1].strip()
warning.address = warning.warning.split("multiplication ")[1].split()[0] return warning
@staticmethod
def parse_cwe190(warning):
if 'multiplication' in warning.warning:
warning.address = warning.warning.split('multiplication ')[1].split()[0]
else: else:
warning.address = warning.warning.split("addition ")[1].split()[0] warning.address = warning.warning.split('addition ')[1].split()[0]
return warning
@staticmethod
def parse_cwe248(warning):
warning.address = warning.warning.split('at ')[-1].split(':')[0].strip()
return warning return warning
@staticmethod @staticmethod
def _parse_cwe457(warning): def parse_cwe457(warning):
warning.address = warning.warning.split("at ")[-1].split(":")[0].strip() warning.address = warning.warning.split('at ')[-1].split(':')[0].strip()
return warning return warning
@staticmethod @staticmethod
def _parse_cwe467(warning): def parse_cwe467(warning):
warning.address = warning.warning.split("at ")[1].split()[0] warning.address = warning.warning.split('at ')[1].split()[0]
return warning return warning
@staticmethod @staticmethod
def _parse_cwe476(warning): def parse_cwe476(warning):
warning.address = warning.warning.split("at ")[1].split()[0] warning.address = warning.warning.split('at ')[1].split()[0]
return warning return warning
@staticmethod @staticmethod
def _parse_cwe676(warning): def parse_cwe560(warning):
warning.address = warning.warning.split("(")[1].split(")")[0].split(":")[0] warning.address = warning.warning.split('Function ')[1].split()[0]
return warning return warning
@staticmethod @staticmethod
def _parse_cwe782(warning): def parse_cwe676(warning):
warning.address = warning.warning.spit("(")[1].split(")")[0].strip() warning.address = warning.warning.split('(')[-1].split(')')[0].split(':')[0]
return warning
@staticmethod
def parse_cwe782(warning):
warning.address = warning.warning.split('(')[1].split(')')[0].strip()
return warning return warning
@staticmethod @staticmethod
def _extract_cwe_number(name): def _extract_cwe_number(name):
tmp = name.split("]")[0] tmp = name.split(']')[0]
return tmp[1:] return tmp[1:]
def parse(self): def parse(self):
...@@ -140,18 +170,14 @@ class Parser(object): ...@@ -140,18 +170,14 @@ class Parser(object):
for line in lines: for line in lines:
line = line.strip() line = line.strip()
if 'WARN' in line: if 'WARN' in line:
try:
warning = cwe_parser.parse(line) warning = cwe_parser.parse(line)
cwe_number = self._extract_cwe_number(warning.name) cwe_number = self._extract_cwe_number(warning.name)
warning.cwe_number = cwe_number warning.cwe_number = cwe_number
if cwe_number in self._parsers: if cwe_number in self._parsers:
warning = self._parsers[cwe_number](warning) warning = self._parsers[cwe_number](warning)
warning.color = colors[warning.cwe_number] warning.color = colors[warning.cwe_number]
if warning.address != "UNKNOWN": if warning.address != 'UNKNOWN':
result.append(warning) result.append(warning)
else: else:
print("Warning: %s not supported." % cwe_number) print('Warning: %s not supported.' % cwe_number)
except Exception as e:
print('Error while parsing: %s' % str(e))
print(line)
return result return result
import unittest
import CweCheckerParser
class TestCweCheckerParser(unittest.TestCase):
def setUp(self):
self.parser = CweCheckerParser.Parser('RESULT_PATH')
def test_parse_cwe125(self):
cwe125_warning = '2019-07-04 12:59:18.189 WARN : [CWE125] {0.1} (Out-of-bounds Read) 0x6e9 -> 0x6f7 -> 0x707 -> 0x713 -> 0x71f'
cwe_warning = CweCheckerParser.CweWarning('CWE125', '0.1', cwe125_warning)
expect_res = '0x71f'
res = self.parser.parse_path(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe190(self):
cwe190_warning = '2019-07-04 11:13:05.299 WARN : [CWE190] {0.1} (Integer Overflow or Wraparound) Potential overflow due to multiplication 0x6BC:32u (malloc).'
cwe_warning = CweCheckerParser.CweWarning('CWE190', '0.1', cwe190_warning)
expect_res = '0x6BC:32u'
res = self.parser.parse_cwe190(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe248(self):
cwe248_warning = '2019-07-04 11:22:27.579 WARN : [CWE248] {0.1} (Possibly Uncaught Exception) (Exception thrown at 0xC77:64u).'
cwe_warning = CweCheckerParser.CweWarning('CWE248', '0.1', cwe248_warning)
expect_res = '0xC77'
res = self.parser.parse_cwe248(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe415(self):
cwe415_warning = '2019-07-04 12:59:18.189 WARN : [CWE415] {0.1} (Double Free) 0x6e9 -> 0x6f7 -> 0x707 -> 0x713 -> 0x71f'
cwe_warning = CweCheckerParser.CweWarning('CWE415', '0.1', cwe415_warning)
expect_res = '0x71f'
res = self.parser.parse_path(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe416(self):
cwe416_warning = '2019-07-04 13:04:12.542 WARN : [CWE416] {0.1} (Use After Free) 0x6ee -> 0x6fc -> 0x70c -> 0x718 -> 0x72e'
cwe_warning = CweCheckerParser.CweWarning('CWE415', '0.1', cwe416_warning)
expect_res = '0x72e'
res = self.parser.parse_path(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe457(self):
cwe457_warning = '2019-07-04 11:13:05.303 WARN : [CWE457] {0.1} (Use of Uninitialized Variable) Found potentially unitialized stack variable (FP + 0xFFFFFFFC:32u) in function __do_global_dtors_aux at 0x662:32u'
cwe_warning = CweCheckerParser.CweWarning('CWE457', '0.1', cwe457_warning)
expect_res = '0x662'
res = self.parser.parse_cwe457(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe476(self):
cwe476_warning = '2019-07-04 11:13:05.306 WARN : [CWE476] {0.2} (NULL Pointer Dereference) There is no check if the return value is NULL at 0x6BC:32u (@malloc).'
cwe_warning = CweCheckerParser.CweWarning('CWE476', '0.1', cwe476_warning)
expect_res = '0x6BC:32u'
res = self.parser.parse_cwe476(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe560(self):
cwe560_warning = '2019-07-04 10:57:14.599 WARN : [CWE560] {0.1} (Use of umask() with chmod-style Argument) Function 0x5FC:32u calls umask with argument 666'
cwe_warning = CweCheckerParser.CweWarning('CWE560', '0.1', cwe560_warning)
expect_res = '0x5FC:32u'
res = self.parser.parse_cwe560(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe676(self):
cwe676_warning = '2019-07-04 11:13:05.306 WARN : [CWE676] {0.1} (Use of Potentially Dangerous Function) @make_table (0x6F3:32u) -> @memcpy.'
cwe_warning = CweCheckerParser.CweWarning('CWE676', '0.1', cwe676_warning)
expect_res = '0x6F3'
res = self.parser.parse_cwe676(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe787(self):
cwe787_warning = '2019-07-04 12:59:18.189 WARN : [CWE787] {0.1} (Out-of-bounds Write) 0x6e9 -> 0x6f7 -> 0x707 -> 0x713 -> 0x71f'
cwe_warning = CweCheckerParser.CweWarning('CWE787', '0.1', cwe787_warning)
expect_res = '0x71f'
res = self.parser.parse_path(cwe_warning)
self.assertEqual(res.address, expect_res)
doc/images/example_ida_anotation.png

119 KB | W: | H:

doc/images/example_ida_anotation.png

321 KB | W: | H:

doc/images/example_ida_anotation.png
doc/images/example_ida_anotation.png
doc/images/example_ida_anotation.png
doc/images/example_ida_anotation.png
  • 2-up
  • Swipe
  • Onion skin
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment