From 1b65b76bcc7cc240f2a08574893b961c59dec15c Mon Sep 17 00:00:00 2001 From: Hiroki Nomura Date: Mon, 4 Jun 2018 13:20:22 +0900 Subject: [PATCH 1/3] Add payloads and modify check for tags --- ava/actives/xss.py | 28 +++++++++++++++++++++------- requirements.txt | 1 + setup.py | 3 ++- tests/actives/test_actives_xss.py | 21 ++++++++++++++++++++- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/ava/actives/xss.py b/ava/actives/xss.py index e33131e..42504bf 100644 --- a/ava/actives/xss.py +++ b/ava/actives/xss.py @@ -1,9 +1,11 @@ import string -from urllib.parse import urlparse +from urllib.parse import urlparse, unquote from bs4 import BeautifulSoup from ava.common import utility from ava.common.check import _ValueCheck from ava.common.constant import HTTP +from pyjsparser import PyJsParser, JsSyntaxError +import re # metadata name = __name__ @@ -80,7 +82,11 @@ class CrossSiteScriptingLinkCheck(_ValueCheck): def __init__(self): """Define static payload""" payloads = [ - "javascript:{}()" + "javascript:{}()", + '");{}();//', + "%22);{}();//", + "');{}();//", + "%27);{}();//" ] # generate random and add to payloads @@ -104,11 +110,19 @@ def check(self, response, payload): # check soup = BeautifulSoup(response.text, "html.parser") - tags = soup.find("a", attrs={"href": payload}) - if tags: - return True - else: - return False + tags = soup.findAll("a", attrs={"href": re.compile(r"^javascript:")}) + parser = PyJsParser() + for tag in tags: + text = unquote(tag["href"][len("javascript:"):]) + try: + tree = parser.parse(text) + for expr in tree["body"]: + callee = expr["expression"]["callee"] + if callee["type"] == "Identifier" and callee["name"] == self._random: + return True + except JsSyntaxError: + pass + return False class CrossSiteScriptingScriptSrcCheck(_ValueCheck): diff --git a/requirements.txt b/requirements.txt index 8be3a97..ada160a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ PyYAML==3.12 requests==2.18.4 requests-toolbelt==0.8.0 urllib3==1.22 +pyjsparser==2.5.2 diff --git a/setup.py b/setup.py index a426954..8707a41 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,8 @@ 'PyYAML==3.12', 'requests==2.18.4', 'requests-toolbelt==0.8.0', - 'urllib3==1.22' + 'urllib3==1.22', + 'pyjsparser==2.5.2' ], long_description=open('README.md').read(), entry_points={ diff --git a/tests/actives/test_actives_xss.py b/tests/actives/test_actives_xss.py index e0f06ea..f87caed 100644 --- a/tests/actives/test_actives_xss.py +++ b/tests/actives/test_actives_xss.py @@ -82,7 +82,11 @@ def test_check_true_negative(self, check, response): class TestCrossSiteScriptingLinkCheck: payloads = [ - "javascript:avascan()" + "javascript:avascan()", + '");avascan();//', + "%22);avascan();//", + "');avascan();//", + "%27);avascan();//" ] @pytest.fixture @@ -103,6 +107,16 @@ def test_check_true_positive(self, check, response): test = check.check(response, check._payloads[0]) assert test + # true positive + response.text = 'Link' + test = check.check(response, check._payloads[1]) + assert test + + # true positive urlencode + response.text = 'Link' + test = check.check(response, check._payloads[2]) + assert test + def test_check_true_negative(self, check, response): response.headers = {'Content-Type': ''} @@ -122,6 +136,11 @@ def test_check_true_negative(self, check, response): test = check.check(response, check._payloads[0]) assert not test + # true negative escape + response.text = 'Link' + test = check.check(response, check._payloads[1]) + assert not test + # true negative application/json response.text = '{}' response.headers['Content-Type'] = "application/json" From 8494a0e1242fe5cf47552bc17fa1d3717720a9cd Mon Sep 17 00:00:00 2001 From: Hiroki Nomura Date: Mon, 4 Jun 2018 17:29:45 +0900 Subject: [PATCH 2/3] Use the javascript parser in AVA --- ava/actives/xss.py | 14 +++----------- requirements.txt | 1 - setup.py | 3 +-- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/ava/actives/xss.py b/ava/actives/xss.py index 42504bf..e9e621c 100644 --- a/ava/actives/xss.py +++ b/ava/actives/xss.py @@ -4,7 +4,6 @@ from ava.common import utility from ava.common.check import _ValueCheck from ava.common.constant import HTTP -from pyjsparser import PyJsParser, JsSyntaxError import re # metadata @@ -108,20 +107,13 @@ def check(self, response, payload): if 'Content-Type' in response.headers and HTTP.CONTENT_TYPE.HTML not in response.headers['Content-Type']: return False - # check + # look for payload in href attribute which starts with "javascript:" soup = BeautifulSoup(response.text, "html.parser") tags = soup.findAll("a", attrs={"href": re.compile(r"^javascript:")}) - parser = PyJsParser() for tag in tags: text = unquote(tag["href"][len("javascript:"):]) - try: - tree = parser.parse(text) - for expr in tree["body"]: - callee = expr["expression"]["callee"] - if callee["type"] == "Identifier" and callee["name"] == self._random: - return True - except JsSyntaxError: - pass + if self._random in utility.parse_javascript(text): + return True return False diff --git a/requirements.txt b/requirements.txt index ada160a..8be3a97 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,3 @@ PyYAML==3.12 requests==2.18.4 requests-toolbelt==0.8.0 urllib3==1.22 -pyjsparser==2.5.2 diff --git a/setup.py b/setup.py index 8707a41..a426954 100644 --- a/setup.py +++ b/setup.py @@ -20,8 +20,7 @@ 'PyYAML==3.12', 'requests==2.18.4', 'requests-toolbelt==0.8.0', - 'urllib3==1.22', - 'pyjsparser==2.5.2' + 'urllib3==1.22' ], long_description=open('README.md').read(), entry_points={ From d7f306830d38eb51ca5785cd04998158dcec96ef Mon Sep 17 00:00:00 2001 From: hnomura Date: Tue, 18 Sep 2018 14:41:46 +0900 Subject: [PATCH 3/3] Remove unneeded payloads --- ava/actives/xss.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ava/actives/xss.py b/ava/actives/xss.py index e9e621c..02445c0 100644 --- a/ava/actives/xss.py +++ b/ava/actives/xss.py @@ -82,9 +82,7 @@ def __init__(self): """Define static payload""" payloads = [ "javascript:{}()", - '");{}();//', "%22);{}();//", - "');{}();//", "%27);{}();//" ]