diff --git a/syntribos/checks/__init__.py b/syntribos/checks/__init__.py index e7051876..af6bb2be 100644 --- a/syntribos/checks/__init__.py +++ b/syntribos/checks/__init__.py @@ -15,5 +15,6 @@ from syntribos.checks.length import max_body_length as max_length from syntribos.checks.length import percentage_difference as length_diff from syntribos.checks.ssl import https_check as https_check +from syntribos.checks.string import has_string as has_string from syntribos.checks.time import percentage_difference as time_diff from syntribos.checks.time import absolute_time as time_abs diff --git a/syntribos/checks/string.py b/syntribos/checks/string.py new file mode 100644 index 00000000..923069b6 --- /dev/null +++ b/syntribos/checks/string.py @@ -0,0 +1,40 @@ +# Copyright 2016 Intel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import syntribos.signal + + +def has_string(test): + """Checks if the response consists of any failure strings + + :returns: syntribos.signal.SynSignal + """ + + slug = "FAILURE_KEYS_PRESENT" + data = { + "req": test.test_resp.request, + "resp": test.test_resp, + "failure_strings": [] + } + + failure_keys = test.failure_keys + if failure_keys: + data["failure_strings"] = [key for key in failure_keys + if key in test.test_resp.content] + + if len(data["failure_strings"]) > 0: + keys = "\n".join(map(str, data["failure_strings"])) + text = "Failure strings present " + keys + return syntribos.signal.SynSignal(check_name="has_string", text=text, + slug=slug, data=data, strength=1.0) diff --git a/syntribos/tests/fuzz/base_fuzz.py b/syntribos/tests/fuzz/base_fuzz.py index eccf2aa3..964118f8 100644 --- a/syntribos/tests/fuzz/base_fuzz.py +++ b/syntribos/tests/fuzz/base_fuzz.py @@ -43,37 +43,6 @@ class BaseFuzzTestCase(base.BaseTestCase): parser=syntribos.tests.fuzz.datagen.FuzzParser ) - @classmethod - def data_driven_failure_cases(cls): - """Checks if response contains known bad strings - - :returns: a list of strings that show up in the response that are also - defined in cls.failure_strings. - failed_strings = [] - """ - failed_strings = [] - if cls.failure_keys is None: - return [] - for line in cls.failure_keys: - if line in cls.test_resp.content: - failed_strings.append(line) - return failed_strings - - @classmethod - def data_driven_pass_cases(cls): - """Checks if response contains expected strings - - :returns: a list of assertions that fail if the response doesn't - contain a string defined in cls.success_keys as a string expected in - the response. - """ - if cls.success_keys is None: - return True - for s in cls.success_keys: - if s in cls.test_resp.content: - return True - return False - @classmethod def setUpClass(cls): """being used as a setup test not.""" diff --git a/syntribos/tests/fuzz/buffer_overflow.py b/syntribos/tests/fuzz/buffer_overflow.py index 8ec1f51e..1c8905c5 100644 --- a/syntribos/tests/fuzz/buffer_overflow.py +++ b/syntribos/tests/fuzz/buffer_overflow.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import syntribos +from syntribos.checks import has_string as has_string from syntribos.checks import time_diff as time_diff from syntribos.tests.fuzz import base_fuzz @@ -38,17 +39,20 @@ class BufferOverflowBody(base_fuzz.BaseFuzzTestCase): def test_case(self): self.test_default_issues() - failed_strings = self.data_driven_failure_cases() - if failed_strings: + self.test_signals.register(has_string(self)) + if "FAILURE_KEYS_PRESENT" in self.test_signals: + failed_strings = self.test_signals.find( + slugs="FAILURE_KEYS_PRESENT")[0].data["failed_strings"] self.register_issue( defect_type="bof_strings", severity=syntribos.MEDIUM, confidence=syntribos.LOW, - description=("The string(s): \'{0}\', known to be commonly " + description=("The string(s): '{0}', known to be commonly " "returned after a successful buffer overflow " "attack, have been found in the response. This " "could indicate a vulnerability to buffer " "overflow attacks.").format(failed_strings)) + self.diff_signals.register(time_diff(self)) if "TIME_DIFF_OVER" in self.diff_signals: self.register_issue( diff --git a/syntribos/tests/fuzz/command_injection.py b/syntribos/tests/fuzz/command_injection.py index 294370db..33035287 100644 --- a/syntribos/tests/fuzz/command_injection.py +++ b/syntribos/tests/fuzz/command_injection.py @@ -13,6 +13,7 @@ # limitations under the License. import syntribos +from syntribos.checks import has_string as has_string from syntribos.checks import time_diff as time_diff from syntribos.tests.fuzz import base_fuzz @@ -29,8 +30,10 @@ class CommandInjectionBody(base_fuzz.BaseFuzzTestCase): def test_case(self): self.test_default_issues() - failed_strings = self.data_driven_failure_cases() - if failed_strings: + self.test_signals.register(has_string(self)) + if "FAILURE_KEYS_PRESENT" in self.test_signals: + failed_strings = self.test_signals.find( + slugs="FAILURE_KEYS_PRESENT")[0].data["failed_strings"] self.register_issue( defect_type="command_injection", severity=syntribos.HIGH, diff --git a/syntribos/tests/fuzz/sql.py b/syntribos/tests/fuzz/sql.py index 323ffe2b..126ba1f1 100644 --- a/syntribos/tests/fuzz/sql.py +++ b/syntribos/tests/fuzz/sql.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import syntribos +from syntribos.checks import has_string as has_string from syntribos.checks import time_diff as time_diff from syntribos.tests.fuzz import base_fuzz @@ -42,13 +43,15 @@ class SQLInjectionBody(base_fuzz.BaseFuzzTestCase): def test_case(self): self.test_default_issues() - failed_strings = self.data_driven_failure_cases() - if failed_strings: + self.test_signals.register(has_string(self)) + if "FAILURE_KEYS_PRESENT" in self.test_signals: + failed_strings = self.test_signals.find( + slugs="FAILURE_KEYS_PRESENT")[0].data["failed_strings"] self.register_issue( defect_type="sql_strings", severity=syntribos.MEDIUM, confidence=syntribos.LOW, - description=("The string(s): \'{0}\', known to be commonly " + description=("The string(s): '{0}', known to be commonly " "returned after a successful SQL injection attack" ", have been found in the response. This could " "indicate a vulnerability to SQL injection " diff --git a/syntribos/tests/fuzz/xml_external.py b/syntribos/tests/fuzz/xml_external.py index 36ed606d..7fa9bbf2 100644 --- a/syntribos/tests/fuzz/xml_external.py +++ b/syntribos/tests/fuzz/xml_external.py @@ -14,6 +14,7 @@ from oslo_config import cfg import syntribos +from syntribos.checks import has_string as has_string from syntribos.checks import time_diff as time_diff from syntribos.tests.fuzz import base_fuzz import syntribos.tests.fuzz.datagen @@ -79,13 +80,15 @@ class XMLExternalEntityBody(base_fuzz.BaseFuzzTestCase): def test_case(self): self.test_default_issues() - failed_strings = self.data_driven_failure_cases() - if failed_strings: + self.test_signals.register(has_string(self)) + if "FAILURE_KEYS_PRESENT" in self.test_signals: + failed_strings = self.test_signals.find( + slugs="FAILURE_KEYS_PRESENT")[0].data["failed_strings"] self.register_issue( defect_type="xml_strings", severity=syntribos.MEDIUM, confidence=syntribos.LOW, - description=("The string(s): \'{0}\', known to be commonly " + description=("The string(s): '{0}', known to be commonly " "returned after a successful XML external entity " "attack, have been found in the response. This " "could indicate a vulnerability to XML external " diff --git a/syntribos/tests/fuzz/xss.py b/syntribos/tests/fuzz/xss.py index ce99550f..96f88022 100644 --- a/syntribos/tests/fuzz/xss.py +++ b/syntribos/tests/fuzz/xss.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import syntribos +from syntribos.checks import has_string as has_string from syntribos.tests.fuzz import base_fuzz @@ -23,7 +24,8 @@ class XSSBody(base_fuzz.BaseFuzzTestCase): def test_case(self): self.test_default_issues() self.failure_keys = self._get_strings() - failed_strings = self.data_driven_failure_cases() + self.test_signals.register(has_string(self)) + if 'content-type' in self.init_req.headers: content_type = self.init_req.headers['content-type'] if 'html' in content_type: @@ -32,12 +34,14 @@ class XSSBody(base_fuzz.BaseFuzzTestCase): sev = syntribos.LOW else: sev = syntribos.LOW - if failed_strings: + if "FAILURE_KEYS_PRESENT" in self.test_signals: + failed_strings = self.test_signals.find( + slugs="FAILURE_KEYS_PRESENT")[0].data["failed_strings"] self.register_issue( defect_type="xss_strings", severity=sev, confidence=syntribos.LOW, - description=("The string(s): \'{0}\', known to be commonly " + description=("The string(s): '{0}', known to be commonly " "returned after a successful XSS " "attack, have been found in the response. This " "could indicate a vulnerability to XSS "