Skip to content

Commit

Permalink
Improved performance for response splitting
Browse files Browse the repository at this point in the history
Rewrote and fixed unittests
  • Loading branch information
andresriancho committed Jun 7, 2019
1 parent 0b5c090 commit 5d0b89f
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 64 deletions.
89 changes: 46 additions & 43 deletions w3af/plugins/audit/response_splitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ class response_splitting(AuditPlugin):
'Header may not contain more than a single header, new line detected',
'Cannot modify header information - headers already sent')

def __init__(self):
AuditPlugin.__init__(self)

def audit(self, freq, orig_response, debugging_id):
"""
Tests an URL for response splitting vulnerabilities.
Expand All @@ -75,32 +72,36 @@ def _analyze_result(self, mutant, response):
if self._has_bug(mutant):
return

if self._header_was_injected(mutant, response):
desc = 'Response splitting was found at: %s' % mutant.found_at()
self._report_php_errors(mutant, response)

if not self._header_was_injected(mutant, response):
return

v = Vuln.from_mutant('Response splitting vulnerability', desc,
severity.MEDIUM, response.id,
self.get_name(), mutant)
desc = 'Response splitting was found at: %s' % mutant.found_at()
v = Vuln.from_mutant('Response splitting vulnerability', desc,
severity.MEDIUM, response.id,
self.get_name(), mutant)

self.kb_append_uniq(self, 'response_splitting', v)
self.kb_append_uniq(self, 'response_splitting', v)

# When trying to send a response splitting to php 5.1.2 I get :
def _report_php_errors(self, mutant, response):
# When trying to send a response splitting to PHP 5.1.2 I get:
# Header may not contain more than a single header, new line detected
for error in self.HEADER_ERRORS:
if error not in response:
continue

if error in response:
desc = ('The variable "%s" at URL "%s" modifies the HTTP'
' response headers, but this error was sent while'
' testing for response splitting: "%s".')
args = (mutant.get_token_name(), mutant.get_url(), error)
desc = desc % args
i = Info.from_mutant('Parameter modifies response headers',
desc, response.id, self.get_name(),
mutant)
desc = ('The variable "%s" at URL "%s" modifies the HTTP'
' response headers, but this error was sent while'
' testing for response splitting: "%s".')
args = (mutant.get_token_name(), mutant.get_url(), error)
desc %= args
i = Info.from_mutant('Parameter modifies response headers',
desc, response.id, self.get_name(),
mutant)

self.kb_append_uniq(self, 'response_splitting', i)

return
self.kb_append_uniq(self, 'response_splitting', i)
break

def _header_was_injected(self, mutant, response):
"""
Expand All @@ -111,27 +112,29 @@ def _header_was_injected(self, mutant, response):
header.
:return: True / False
"""
# Get the lower case headers
headers = response.get_lower_case_headers()
headers = response.get_headers()

for header, value in headers.iteritems():
if HEADER_NAME not in header.lower():
continue

# Analyze injection
for header, value in headers.items():
if HEADER_NAME in header and value.lower() == HEADER_VALUE:
if HEADER_VALUE in value.lower():
return True

elif HEADER_NAME in header and value.lower() != HEADER_VALUE:
msg = ('The vulnerable header was added to the HTTP response,'
' but the value is not what w3af expected (%s: %s).'
' Please verify manually.')
msg = msg % (HEADER_NAME, HEADER_VALUE)
om.out.information(msg)
#
# This is a case where we have a partial header injection
#
msg = ('The vulnerable header was added to the HTTP response,'
' but the value is not what w3af expected (%s: %s).'
' Please verify manually.')
msg %= (HEADER_NAME, HEADER_VALUE)
om.out.information(msg)

i = Info.from_mutant('Parameter modifies response headers',
msg, response.id, self.get_name(),
mutant)

i = Info.from_mutant('Parameter modifies response headers',
msg, response.id, self.get_name(),
mutant)

self.kb_append_uniq(self, 'response_splitting', i)
return False
self.kb_append_uniq(self, 'response_splitting', i)

return False

Expand All @@ -140,9 +143,9 @@ def get_long_desc(self):
:return: A DETAILED description of the plugin functions and features.
"""
return """
This plugin will find response splitting vulnerabilities.
This plugin identifies response splitting vulnerabilities.
The detection is done by sending "w3af\\r\\nVulnerable: Yes" to every
injection point, and reading the response headers searching for a header
with name "Vulnerable" and value "Yes".
Detection is performed by sending "w3af\\r\\nvulnerable073b: ae5cw3af" to
every injection point, and reading the response headers searching for a
header with name "vulnerable073b" and value "ae5cw3af".
"""
109 changes: 88 additions & 21 deletions w3af/plugins/tests/audit/test_response_splitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,55 +18,122 @@
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import re
import urllib

from nose.plugins.attrib import attr

from w3af.plugins.tests.helper import PluginTest, PluginConfig
from w3af.plugins.tests.helper import PluginTest, PluginConfig, MockResponse


class ResponseSplittingMockResponse(MockResponse):
def get_response(self, http_request, uri, response_headers):
uri = urllib.unquote(uri)
headers_to_inject = uri[uri.find('=') + 1:]
header_name_1 = 'somevalue'

try:
headers_to_inject = headers_to_inject.split('\n')
header_value_1 = headers_to_inject[0].strip()

headers_to_inject = headers_to_inject[1]
header_name_2, header_value_2 = headers_to_inject.split(':')
header_name_2 = header_name_2.strip()
header_value_2 = header_value_2.strip()
except:
return self.status, response_headers, self.body
else:
response_headers[header_name_1] = header_value_1
response_headers[header_name_2] = header_value_2
return self.status, response_headers, self.body


@attr('smoke')
class TestResponseSplitting(PluginTest):

direct_url = 'http://moth/w3af/audit/response_splitting/response_splitting.php'
error_url = 'http://moth/w3af/audit/response_splitting/response_splitting_err.php'
target_url = 'http://w3af.org/?header='
target_url_re = re.compile('http://w3af\\.org/\\?header=.*')

MOCK_RESPONSES = [ResponseSplittingMockResponse(target_url_re,
body='',
method='GET',
status=200)]
_run_configs = {
'cfg_direct': {
'target': direct_url + '?header=None',
'cfg': {
'target': target_url,
'plugins': {
'audit': (PluginConfig('response_splitting'),),
}
},

'cfg_error': {
'target': error_url + '?header=None',
'plugins': {
'audit': (PluginConfig('response_splitting'),),
}
}
}

@attr('ci_fails')
def test_found_direct(self):
cfg = self._run_configs['cfg_direct']
def test_found_response_splitting(self):
cfg = self._run_configs['cfg']
self._scan(cfg['target'], cfg['plugins'])

vulns = self.kb.get('response_splitting', 'response_splitting')
self.assertEquals(1, len(vulns), vulns)

# Now some tests around specific details of the found vuln
vuln = vulns[0]
self.assertEquals('Response splitting vulnerability', vuln.get_name())
self.assertEquals(self.direct_url, str(vuln.get_url()))
self.assertEquals('http://w3af.org/', str(vuln.get_url()))
self.assertEquals('header', vuln.get_token_name())

@attr('ci_fails')
def test_found_error(self):
cfg = self._run_configs['cfg_error']

class ResponseSplittingParameterModifiesResponseMockResponse(MockResponse):
def get_response(self, http_request, uri, response_headers):
uri = urllib.unquote(uri)
headers_to_inject = uri[uri.find('=') + 1:]

header_name_1 = 'somevalue'

try:
headers_to_inject = headers_to_inject.split('\n')
header_value_1 = headers_to_inject[0].strip()

headers_to_inject = headers_to_inject[1]
header_name_2, header_value_2 = headers_to_inject.split(':')
header_name_2 = header_name_2.strip()
header_value_2 = header_value_2.strip()
except:
return self.status, response_headers, self.body
else:
response_headers[header_name_1] = header_value_1

body = self.body
if header_name_2 and header_value_2:
body = 'Header may not contain more than a single header, new line detected'

return self.status, response_headers, body


class TestResponseSplittingParameterModifiesResponse(PluginTest):
target_url = 'http://w3af.org/?header='
target_url_re = re.compile('http://w3af\\.org/\\?header=.*')

MOCK_RESPONSES = [ResponseSplittingParameterModifiesResponseMockResponse(target_url_re,
body='',
method='GET',
status=200)]
_run_configs = {
'cfg': {
'target': target_url,
'plugins': {
'audit': (PluginConfig('response_splitting'),),
}
},
}

def test_found_response_splitting_modifies_response(self):
cfg = self._run_configs['cfg']
self._scan(cfg['target'], cfg['plugins'])

vulns = self.kb.get('response_splitting', 'response_splitting')
self.assertEquals(1, len(vulns), vulns)

# Now some tests around specific details of the found vuln
vuln = vulns[0]
self.assertEquals('Parameter modifies response headers', vuln.get_name())
self.assertEquals(self.error_url, str(vuln.get_url()))
self.assertEquals('header', vuln.get_token_name())
self.assertEquals('http://w3af.org/', str(vuln.get_url()))
self.assertEquals('header', vuln.get_token_name())

0 comments on commit 5d0b89f

Please sign in to comment.