diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dab3d91 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:2.7.16-alpine3.10 + +ENV WORKDIR /src +RUN mkdir -p ${WORKDIR} +WORKDIR ${WORKDIR} + +COPY ./requirements.txt ${WORKDIR}/requirements.txt +RUN pip install -r requirements.txt + +COPY ./ ${WORKDIR}/ + +ENTRYPOINT ["python", "XssPy.py"] diff --git a/README.md b/README.md index 7be5711..085b439 100644 --- a/README.md +++ b/README.md @@ -4,27 +4,43 @@ A tool by Fsecurify Author: Faizan Ahmad https://pk.linkedin.com/in/faizan-ahmad-015964118 -#Great News: Xsspy was recently used by an engineer at microsoft to find a bug in Pentagon's Bug Bounty Program. +# Great News: Xsspy was recently used by an engineer at microsoft to find a bug in Pentagon's Bug Bounty Program. http://holisticinfosec.blogspot.com/2016/06/toolsmith-tidbit-xsspy.html -#How to Use: +# How to Use: http://fsecurify.com/xsspy-web-application-xss-scanner/ -#Installation: +# Installation: Type the following in the terminal. -git clone https://github.com/faizann24/XssPy/ /opt/xsspy +`git clone https://github.com/faizann24/XssPy/` /opt/xsspy The tool works on Python 2.7 and you should have mechanize installed. If mechanize is not installed, type "pip install mechanize" in the terminal. -#Usage: -python XssPy.py website.com (Do not write www.website.com OR http://www.website.com) - -#Payloads +You will also need the mechanize distribution, you can install it with pip: +```pip install mechanize``` + +# Usage: +`python XssPy.py website.com` (Do not write www.website.com OR http://www.website.com) + +# Docker +Advantage of Docker is that is will run on every machine. You don't need to install Pip packages or use a Venv. +Package versions are pinned. This ensures that XssPy will also run in the future. Regardless which Python-Version you've running on you machine. +## Docker build +``` +docker build -t xsspy . +``` +## Docker usage +After you build +``` +docker run -t xsspy -u website.com +``` + +# Payloads If you have found a XSS vulnerability, you can try the following payloads. http://pastebin.com/J1hCfL9J -#Description: +# Description: XssPy is a python tool for finding Cross Site Scripting vulnerabilities in websites. This tool is the first of its kind. Instead of just checking one page as most of the tools do, this tool traverses the website and find all the links and subdomains first. After that, it starts scanning each and every input on each and every page that it found while its traversal. It uses small yet effective payloads to search for XSS vulnerabilities. The tool has been tested parallel with paid Vulnerability Scanners and most of the scanners failed to detect the vulnerabilities that the tool was able to find. Moreover, most paid tools scan only one site whereas XSSPY first finds a lot of subdomains and then scan all the links altogether. The tool comes with: @@ -37,7 +53,7 @@ The tool has been tested parallel with paid Vulnerability Scanners and most of t With this tool, Cross Site Scripting vulnerabilities have been found in the websites of MIT, Stanford, Duke University, Informatica, Formassembly, ActiveCompaign, Volcanicpixels, Oxford, Motorola, Berkeley and many more. -#NOTE: +# NOTE: Mail me if you encounter any errors (fsecurify@gmail.com). You can also post your problems on the website. I'll try my best to respond as soon as possible. Best Regards diff --git a/XssPy.py b/XssPy.py index e7647b9..05b16b9 100644 --- a/XssPy.py +++ b/XssPy.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python import mechanize import sys import httplib @@ -5,32 +6,45 @@ import logging from urlparse import urlparse -br = mechanize.Browser() #initiating the browser -br.addheaders = [('User-agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11)Gecko/20071127 Firefox/2.0.0.11')] +br = mechanize.Browser() # initiating the browser +br.addheaders = [ + ('User-agent', + 'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11)Gecko/20071127 Firefox/2.0.0.11') +] br.set_handle_robots(False) br.set_handle_refresh(False) +payloads = ['', '" onfocus="alert(1);', 'javascript:alert(1)'] +blacklist = ['.png', '.jpg', '.jpeg', '.mp3', '.mp4', '.avi', '.gif', '.svg', + '.pdf'] +xssLinks = [] # TOTAL CROSS SITE SCRIPTING FINDINGS + class color: - RED = '\033[91m' - GREEN = '\033[92m' - YELLOW = '\033[93m' - BOLD = '\033[1m' - END = '\033[0m' - @staticmethod - def log(lvl, col, msg): - logger.log(lvl, col + msg + color.END) + BLUE = '\033[94m' + RED = '\033[91m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + BOLD = '\033[1m' + END = '\033[0m' + + @staticmethod + def log(lvl, col, msg): + logger.log(lvl, col + msg + color.END) + print color.BOLD + color.RED + """ XssPy - Finding XSS made easier Author: Faizan Ahmad (Fsecurify) Email: fsecurify@gmail.com -Usage: pythonXssPy.py website.com (Do not write www.website.com OR http://www.website.com) -Comprehensive Scan: python XssPy.py website.com -e +Usage: XssPy.py website.com (Not www.website.com OR http://www.website.com) +Comprehensive Scan: python XssPy.py -u website.com -e +Verbose logging: python XssPy.py -u website.com -v +Cookies: python XssPy.py -u website.complex -c name=val name=val -Description: XssPy is a python tool for finding Cross Site Scripting +Description: XssPy is a python tool for finding Cross Site Scripting vulnerabilities in websites. This tool is the first of its kind. -Instead of just checking one page as most of the tools do, this tool +Instead of just checking one page as most of the tools do, this tool traverses the website and find all the links and subdomains first. After that, it starts scanning each and every input on each and every page that it found while its traversal. It uses small yet effective @@ -46,166 +60,138 @@ def log(lvl, col, msg): lh.setFormatter(formatter) parser = argparse.ArgumentParser() -parser.add_argument('-u', action='store', dest='url', help='The URL to analyze') -parser.add_argument('-e', action='store_true', dest='compOn', help='Enable comprehensive scan') -parser.add_argument('-v', action='store_true', dest='verbose', help='Enable verbose logging') +parser.add_argument('-u', action='store', dest='url', + help='The URL to analyze') +parser.add_argument('-e', action='store_true', dest='compOn', + help='Enable comprehensive scan') +parser.add_argument('-v', action='store_true', dest='verbose', + help='Enable verbose logging') +parser.add_argument('-c', action='store', dest='cookies', + help='Space separated list of cookies', + nargs='+', default=[]) results = parser.parse_args() logger.setLevel(logging.DEBUG if results.verbose else logging.INFO) -def initializeAndFind(firstDomains): - - dummy = 0 #dummy variable for doing nothing - firstDomains = [] #list of domains - if not results.url: #if the url has been passed or not - color.log(logging.INFO, color.GREEN, 'Url not provided correctly') - return 0 - - smallurl = results.url #small url is the part of url without http and www - - allURLS = [] - allURLS.append(smallurl) #just one url at the moment - largeNumberOfUrls = [] #in case one wants to do comprehensive search - - color.log(logging.INFO, color.GREEN, 'Doing a short traversal.') #doing a short traversal if no command line argument is being passed - for url in allURLS: - x = str(url) - smallurl = x - - try: # Test HTTPS/HTTP compatibility. Prefers HTTPS but defaults to HTTP if any errors are encountered - test = httplib.HTTPSConnection(smallurl) - test.request("GET", "/") - response = test.getresponse() - if (response.status == 200) | (response.status == 302): - url = "https://www." + str(url) - elif response.status == 301: - loc = response.getheader('Location') - url = loc.scheme + '://' + loc.netloc - else: - url = "http://www." + str(url) - except: - url = "http://www." + str(url) +def testPayload(payload, p, link): + br.form[str(p.name)] = payload + br.submit() + # if payload is found in response, we have XSS + if payload in br.response().read(): + color.log(logging.DEBUG, color.BOLD + color.GREEN, 'XSS found!') + report = 'Link: %s, Payload: %s, Element: %s' % (str(link), + payload, str(p.name)) + color.log(logging.INFO, color.BOLD + color.GREEN, report) + xssLinks.append(report) + br.back() + + +def initializeAndFind(): + + if not results.url: # if the url has been passed or not + color.log(logging.INFO, color.GREEN, 'Url not provided correctly') + return [] + + firstDomains = [] # list of domains + allURLS = [] + allURLS.append(results.url) # just one url at the moment + largeNumberOfUrls = [] # in case one wants to do comprehensive search + + # doing a short traversal if no command line argument is being passed + color.log(logging.INFO, color.GREEN, 'Doing a short traversal.') + for url in allURLS: + smallurl = str(url) + # Test HTTPS/HTTP compatibility. Prefers HTTPS but defaults to + # HTTP if any errors are encountered + try: + test = httplib.HTTPSConnection(smallurl) + test.request("GET", "/") + response = test.getresponse() + if (response.status == 200) | (response.status == 302): + url = "https://www." + str(url) + elif response.status == 301: + loc = response.getheader('Location') + url = loc.scheme + '://' + loc.netloc + else: + url = "http://www." + str(url) + except: + url = "http://www." + str(url) + try: + br.open(url) + for cookie in results.cookies: + color.log(logging.INFO, color.BLUE, + 'Adding cookie: %s' % cookie) + br.set_cookie(cookie) + br.open(url) + color.log(logging.INFO, color.GREEN, + 'Finding all the links of the website ' + str(url)) + for link in br.links(): # finding the links of the website + if smallurl in str(link.absolute_url): + firstDomains.append(str(link.absolute_url)) + firstDomains = list(set(firstDomains)) + except: + pass + color.log(logging.INFO, color.GREEN, + 'Number of links to test are: ' + str(len(firstDomains))) + if results.compOn: + color.log(logging.INFO, color.GREEN, + 'Doing a comprehensive traversal. This may take a while') + for link in firstDomains: try: - br.open(url) - color.log(logging.INFO, color.GREEN, 'Finding all the links of the website ' + str(url)) - try: - for link in br.links(): #finding the links of the website - if smallurl in str(link.absolute_url): - firstDomains.append(str(link.absolute_url)) - firstDomains = list(set(firstDomains)) - except: - dummy = 0 + br.open(link) + # going deeper into each link and finding its links + for newlink in br.links(): + if smallurl in str(newlink.absolute_url): + largeNumberOfUrls.append(newlink.absolute_url) except: - dummy = 0 - color.log(logging.INFO, color.GREEN, 'Number of links to test are: ' + str(len(firstDomains))) - - if results.compOn: - color.log(logging.INFO, color.GREEN, 'Doing a comprehensive traversal. This could take a while') - for link in firstDomains: - try: - br.open(link) - try: - for newlink in br.links(): #going deeper into each link and finding its links - if smallurl in str(newlink.absolute_url): - largeNumberOfUrls.append(newlink.absolute_url) - except: - dummy = 0 - except: - dummy = 0 - - firstDomains = list(set(firstDomains + largeNumberOfUrls)) - color.log(logging.INFO, color.GREEN, 'Total Number of links to test have become: ' + str(len(firstDomains))) #all links have been found - return firstDomains + pass + firstDomains = list(set(firstDomains + largeNumberOfUrls)) + color.log(logging.INFO, color.GREEN, + 'Total Number of links to test have become: ' + + str(len(firstDomains))) # all links have been found + return firstDomains def findxss(firstDomains): - color.log(logging.INFO, color.GREEN, 'Started finding XSS') #starting finding XSS - xssLinks = [] #TOTAL CROSS SITE SCRIPTING FINDINGS - count = 0 #to check for forms - dummyVar = 0 #dummy variable for doing nothing - if len(firstDomains) > 0: #if there is atleast one link - for link in firstDomains: - y = str(link) - color.log(logging.DEBUG, color.YELLOW, str(link)) - if 'jpg' in y: #just a small check - color.log(logging.DEBUG, color.RED, '\tNot a good url to test') - elif 'pdf' in y: - color.log(logging.DEBUG, color.RED, '\tNot a good url to test') - else: - try: - br.open(str(link)) #open the link - except: - dummyVar = 0 - try: - for form in br.forms(): #check its forms - count = count + 1 - except: - dummyVar = 0 - if count > 0: #if a form exists, submit it - try: - params = list(br.forms())[0] #our form - except: - dummyVar = 0 - try: - br.select_form(nr=0) #submit the first form - except: - dummyVar = 0 - for p in params.controls: - par = str(p) - if 'TextControl' in par: #submit only those forms which require text - color.log(logging.DEBUG, color.YELLOW, '\tParam: ' + str(p.name)) - try: - br.form[str(p.name)] = '' #our payload - except: - dummyVar = 0 - try: - br.submit() - except: - dummyVar = 0 - try: - if '' in br.response().read(): #if payload is found in response, we have XSS - color.log(logging.INFO, color.BOLD+color.GREEN, 'Xss found and the link is ' + str(link) + ' And the payload is ') - xssLinks.append(link) - else: - dummyVar = 0 - except: - color.log(logging.INFO, color.RED, '\tcould not read the page') - try: - br.back() - except: - dummyVar = 0 - - #SECOND PAYLOAD - - try: - br.form[str(p.name)] = 'javascript:alert(1)' #second payload - except: - dummyVar = 0 - try: - br.submit() - except: - dummyVar = 0 - try: - if '