+
+
-
"""
- return form0x02
+ return test_form_0x02
diff --git a/core/inputin.py b/core/inputin.py
index 3436fea..9dd2ff5 100644
--- a/core/inputin.py
+++ b/core/inputin.py
@@ -9,8 +9,8 @@
#This module requires XSRF-Probe
#https://github.com/0xInfection/XSRF-Probe
-import sys
-import socket
+import socket, requests
+from tld import get_fld
from core.colors import *
from files.config import SITE_URL
@@ -22,15 +22,37 @@ def inputin():
if 'http' not in web: # add protocol to site
web = 'http://' + web
- web0 = web.split('//')[1]
+ web0 = get_fld(web)
try:
- print(O+'Testing site status...')
+ print(O+'Testing site '+color.GREY+web0+color.END+' status...')
socket.gethostbyname(web0) # test whether site is up or not
print(color.GREEN+' [+] Site seems to be up!'+color.END)
except socket.gaierror: # if site is down
print(R+'Site seems to be down...')
- sys.exit(0)
-
- if not web.endswith('/'): # check
- web = web + '/' # make sure the site address ends with '/'
- return web
+ quit()
+ try:
+ print(O+'Testing '+color.CYAN+web.split('//')[1].replace(web0,'')+color.END+' endpoint status...')
+ requests.get(web)
+ print(color.GREEN+' [+] Endpoint seems to be up!'+color.END)
+ except requests.exceptions.MissingSchema as e:
+ verbout(R, 'Exception at: '+color.GREY+url)
+ verbout(R, 'Error: Invalid URL Format')
+ ErrorLogger(url, e.__str__())
+ quit()
+ except requests.exceptions.HTTPError as e: # if error
+ verbout(R, "HTTP Error : "+main_url)
+ ErrorLogger(main_url, e.__str__())
+ quit()
+ except requests.exceptions.ConnectionError as e:
+ verbout(R, 'Connection Aborted : '+main_url)
+ ErrorLogger(main_url, e.__str__())
+ quit()
+ except Exception as e:
+ verbout(R, "Exception Caught: "+e.__str__())
+ ErrorLogger(main_url, e.__str__())
+ quit() # if at all nothing happens :(
+ if not web0.endswith('/'):
+ web0 = web0 + '/'
+ if web.split('//')[1] == web0:
+ return web, ''
+ return (web, web0)
diff --git a/core/logger.py b/core/logger.py
index f2cb8ed..9ac1458 100644
--- a/core/logger.py
+++ b/core/logger.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -13,16 +13,21 @@
from core.colors import *
from files.config import *
from core.verbout import verbout
+from files.discovered import INTERNAL_URLS, FILES_EXEC, SCAN_ERRORS
+from files.discovered import VULN_LIST, FORMS_TESTED, REQUEST_TOKENS, STRENGTH_LIST
def logger(filename, content):
'''
This module is for logging all the stuff we found
while crawling and scanning.
'''
- output_file = OUTPUT_DIR + filename + '.txt'
+ output_file = OUTPUT_DIR + filename + '.log'
with open(output_file, 'w+', encoding='utf8') as f:
- for m in content:
- f.write(m+'\n')
+ if type(content) is tuple or type(content) is list:
+ for m in content: # if it is list or tuple, it is iterable
+ f.write(m+'\n')
+ else:
+ f.write(content) # else we write out as it is... ;)
f.write('\n')
def pheaders(tup):
@@ -35,3 +40,31 @@ def pheaders(tup):
for key, val in tup.items():
verbout(' ',color.CYAN+key+': '+color.ORANGE+val)
verbout('','')
+
+def GetLogger():
+ if INTERNAL_URLS:
+ logger('internal-links', INTERNAL_URLS)
+ if SCAN_ERRORS:
+ logger('errored', SCAN_ERRORS)
+ if FILES_EXEC:
+ logger('files-found', FILES_EXEC)
+ if REQUEST_TOKENS:
+ logger('anti-csrf-tokens', REQUEST_TOKENS)
+ if FORMS_TESTED:
+ logger('forms-tested', FORMS_TESTED)
+ if VULN_LIST:
+ logger('vulnerabilities', VULN_LIST)
+ if STRENGTH_LIST:
+ logger('strengths', STRENGTH_LIST)
+
+def ErrorLogger(url, error):
+ con = '(i) '+url+' -> '+error.__str__()
+ SCAN_ERRORS.append(con)
+
+def VulnLogger(url, vuln, content=''):
+ tent = '[!] '+url+' -> '+vuln+'\n\n'+str(content)+'\n\n'
+ VULN_LIST.append(tent)
+
+def NovulLogger(url, strength):
+ tent = '[+] '+url+' -> '+strength
+ STRENGTH_LIST.append(tent)
diff --git a/core/main.py b/core/main.py
index 69794e4..b2e24d1 100644
--- a/core/main.py
+++ b/core/main.py
@@ -5,7 +5,7 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -25,7 +25,7 @@
print("\033[1;91m [-] \033[1;93mXSRFProbe\033[0m isn't compatible with Python 2.x versions.\n\033[1;91m [-] \033[0mUse Python 3.x to run \033[1;93mXSRFProbe.")
quit()
try:
- import requests, stringdist, lxml, bs4
+ import requests, stringdist, bs4
except ImportError:
print(' [-] Required dependencies are not installed.\n [-] Run \033[1;93mpip3 install -r requirements.txt\033[0m to fix it.')
@@ -35,11 +35,15 @@
from core.inputin import inputin
from core.request import Get, Post
from core.verbout import verbout
-from core.forms import form10, form20
+from core.prettify import formPrettify
from core.banner import banner, banabout
+from core.forms import testFormx1, testFormx2
+from core.logger import ErrorLogger, GetLogger
+from core.logger import VulnLogger, NovulLogger
# Imports from files
from files.config import *
+from files.discovered import FORMS_TESTED
# Imports from modules
from modules import Debugger
@@ -63,125 +67,230 @@ def Engine(): # lets begin it!
os.system('clear') # Clear shit from terminal :p
banner() # Print the banner
banabout() # The second banner
- web = inputin() # Take the input
- form1 = form10() # Get the form 1 ready
- form2 = form20() # Get the form 2 ready
-
+ web, fld = inputin() # Take the input
+ form1 = testFormx1() # Get the form 1 ready
+ form2 = testFormx2() # Get the form 2 ready
# For the cookies that we encounter during requests...
Cookie0 = http.cookiejar.CookieJar() # First as User1
Cookie1 = http.cookiejar.CookieJar() # Then as User2
resp1 = build_opener(HTTPCookieProcessor(Cookie0)) # Process cookies
resp2 = build_opener(HTTPCookieProcessor(Cookie1)) # Process cookies
-
actionDone = [] # init to the done stuff
-
csrf = '' # no token initialise / invalid token
- ref_detect = 0x00 # Null Char
- ori_detect = 0x00
- init1 = web # get the starting page
+ ref_detect = 0x00 # Null Char Flag
+ ori_detect = 0x00 # Null Char Flags
form = Debugger.Form_Debugger() # init to the form parser+token generator
+ bs1 = BeautifulSoup(form1).findAll('form', action=True)[0] # make sure the stuff works properly
+ bs2 = BeautifulSoup(form2).findAll('form', action=True)[0] # same as above
+ init1 = web # First init
+ resp1.open(init1) # Makes request as User2
+ resp2.open(init1) # Make request as User1
- bs1=BeautifulSoup(form1).findAll('form',action=True)[0] # make sure the stuff works properly
- bs2=BeautifulSoup(form2).findAll('form',action=True)[0] # same as above
-
- action = init1 # First init
-
- resp1.open(action) # Makes request as User2
- resp2.open(action) # Make request as User1
-
- verbout(GR, "Initializing crawling and scanning...")
- crawler = Crawler.Handler(init1, resp1) # Init to the Crawler handler
-
+ # Now there are 2 different modes of scanning and crawling here.
+ # 1st -> Testing a single endpoint without the --crawl flag.
+ # 2nd -> Testing all endpoints with the --crawl flag.
try:
- while crawler.noinit(): # Until 0 urls left
- url = next(crawler) # Go for next!
-
- print(C+'Crawling :> '+color.CYAN+url) # Display what url its crawling
-
+ # Implementing the first mode. [NO CRAWL]
+ if not CRAWL_SITE:
+ url = web
+ response = Get(url).text
try:
- soup = crawler.process(web) # Start the parser
- if not soup:
- continue # Making sure not to end the program yet...
- i = 0 # Set count = 0
- if REFERER_ORIGIN_CHECKS:
- # Referer Based Checks if True...
- verbout(O, 'Checking endpoint request validation via '+color.GREY+'Referer'+color.END+' Checks...')
- if Referer(url):
- ref_detect = 0x01
- verbout(O, 'Confirming the vulnerability...')
-
- # We have finished with Referer Based Checks, lets go for Origin Based Ones...
- verbout(O, 'Confirming endpoint request validation via '+color.GREY+'Origin'+color.END+' Checks...')
- if Origin(url):
- ori_detect = 0x01
-
- if COOKIE_BASED:
- Cookie(url)
-
- # Now lets get the forms...
- verbout(O, 'Retrieving all forms on ' +color.GREY+url+color.END+'...')
- for m in Debugger.getAllForms(soup): # iterating over all forms extracted
- action = Parser.buildAction(url,m['action']) # get all forms which have 'action' attribute
- if not action in actionDone and action!='': # if url returned is not a null value nor duplicate...
- # If form submission is kept to True
- if FORM_SUBMISSION:
+ verbout(O,'Trying to parse response...')
+ soup = BeautifulSoup(response) # Parser init
+ except HTMLParser.HTMLParseError:
+ verbout(R,'BeautifulSoup Error: '+url)
+ i = 0 # Init user number
+ if REFERER_ORIGIN_CHECKS:
+ # Referer Based Checks if True...
+ verbout(O, 'Checking endpoint request validation via '+color.GREY+'Referer'+color.END+' Checks...')
+ if Referer(url):
+ ref_detect = 0x01
+ verbout(O, 'Confirming the vulnerability...')
+ # We have finished with Referer Based Checks, lets go for Origin Based Ones...
+ verbout(O, 'Confirming endpoint request validation via '+color.GREY+'Origin'+color.END+' Checks...')
+ if Origin(url):
+ ori_detect = 0x01
+ # Now lets get the forms...
+ verbout(O, 'Retrieving all forms on ' +color.GREY+url+color.END+'...')
+ for m in Debugger.getAllForms(soup): # iterating over all forms extracted
+ verbout(O,'Testing form:\n'+color.CYAN)
+ formPrettify(m.prettify())
+ verbout('', '')
+ FORMS_TESTED.append('(i) '+url+':\n\n'+m.prettify()+'\n')
+ try:
+ if m['action']:
+ pass
+ except KeyError:
+ m['action'] = '/' + url.rsplit('/', 1)[1]
+ ErrorLogger(url, 'No standard form "action".')
+ action = Parser.buildAction(url, m['action']) # get all forms which have 'action' attribute
+ if not action in actionDone and action!='': # if url returned is not a null value nor duplicate...
+ # If form submission is kept to True
+ if FORM_SUBMISSION:
+ try:
+ # NOTE: Slow connections may cause read timeouts which may result in AttributeError
+ # So the idea here is tp make requests pretending to be 3 different users.
+ # Now a series of requests will be targeted against the site with different
+ # identities. Refer to XSRFProbe wiki for more info.
+ #
+ # NOTE: Slow connections may cause read timeouts which may result in AttributeError
+ result, genpoc = form.prepareFormInputs(m) # prepare inputs as user 1
+ r1 = Post(url, action, result) # make request with token values generated as user1
+ result, genpoc = form.prepareFormInputs(m) # prepare inputs as user 2
+ r2 = Post(url, action, result) # again make request with token values generated as user2
+ # Go for cookie based checks
+ if COOKIE_BASED:
+ Cookie(url, r1)
+ # Go for token based entropy checks...
+ try:
+ if m['name']:
+ query, token = Entropy(result, url, r1.headers, m.prettify(), m['action'], m['name'])
+ except KeyError:
+ query, token = Entropy(result, url, r1.headers, m.prettify(), m['action'])
+ # Now its time to detect the encoding type (if any) of the Anti-CSRF token.
+ fnd, detct = Encoding(token)
+ if fnd == 0x01 and detct:
+ VulnLogger(url, 'Token is a string encoded value which can be probably decrypted.', '[i] Encoding: '+detct)
+ else:
+ NovulLogger(url, 'Anti-CSRF token is not a string encoded value.')
+ # Go for token parameter tamper checks.
+ if (query and token):
+ txor = Tamper(url, action, result, r2.text, query, token)
+ o2 = resp2.open(url).read() # make request as user2
try:
- result = form.prepareFormInputs(m) # prepare inputs
- r1 = Post(url, action, result).text # make request with token values generated as user1
- result = form.prepareFormInputs(m) # prepare the input types
- r2 = Post(url, action, result).text # again make request with token values generated as user2
- # Go for token based entropy checks...
+ form2 = Debugger.getAllForms(BeautifulSoup(o2))[i] # user2 gets his form
+ except IndexError:
+ verbout(R, 'Form Index Error')
+ ErrorLogger(url, 'Form Index Error.')
+ continue # Making sure program won't end here (dirty fix :( )
+ verbout(GR, 'Preparing form inputs...')
+ contents2, genpoc = form.prepareFormInputs(form2) # prepare for form 3 as user3
+ r3 = Post(url, action, contents2) # make request as user3 with user3's form
+ if (POST_BASED) and ((not query) or (txor)):
try:
if m['name']:
- query, token = Entropy(result, url, m['action'], m['name'])
+ PostBased(url, r1.text, r2.text, r3.text, m['action'], result, genpoc, m.prettify(), m['name'])
except KeyError:
- query, token = Entropy(result, url, m['action'])
- # Go for token parameter tamper checks.
- if (query and token):
- Tamper(url, action, result, r2, query, token)
- o2 = resp2.open(url).read() # make request as user2
+ PostBased(url, r1.text, r2.text, r3.text, m['action'], result, genpoc, m.prettify())
+ else:
+ print(color.GREEN+' [+] The form was requested with a Anti-CSRF token.')
+ print(color.GREEN+' [+] Endpoint '+color.BG+' NOT VULNERABLE '+color.END+color.GREEN+' to POST-Based CSRF Attacks!')
+ NovulLogger(url, 'Not vulnerable to POST-Based CSRF Attacks.')
+ except HTTPError as msg: # if runtime exception...
+ verbout(R, 'Exception : '+msg.__str__()) # again exception :(
+ ErrorLogger(url, msg)
+ actionDone.append(action) # add the stuff done
+ i+=1 # Increase user iteration
+ else:
+ # Implementing the 2nd mode [CRAWLING AND SCANNING].
+ verbout(GR, "Initializing crawling and scanning...")
+ crawler = Crawler.Handler(init1, resp1) # Init to the Crawler handler
+ while crawler.noinit(): # Until 0 urls left
+ url = next(crawler) # Go for next!
+ print(C+'Testing :> '+color.CYAN+url) # Display what url its crawling
+ try:
+ soup = crawler.process(fld) # Start the parser
+ if not soup:
+ continue # Making sure not to end the program yet...
+ i = 0 # Set count = 0 (user number 0, which will be subsequently incremented)
+ if REFERER_ORIGIN_CHECKS:
+ # Referer Based Checks if True...
+ verbout(O, 'Checking endpoint request validation via '+color.GREY+'Referer'+color.END+' Checks...')
+ if Referer(url):
+ ref_detect = 0x01
+ verbout(O, 'Confirming the vulnerability...')
+ # We have finished with Referer Based Checks, lets go for Origin Based Ones...
+ verbout(O, 'Confirming endpoint request validation via '+color.GREY+'Origin'+color.END+' Checks...')
+ if Origin(url):
+ ori_detect = 0x01
+ # Now lets get the forms...
+ verbout(O, 'Retrieving all forms on ' +color.GREY+url+color.END+'...')
+ for m in Debugger.getAllForms(soup): # iterating over all forms extracted
+ FORMS_TESTED.append('(i) '+url+':\n\n'+m.prettify()+'\n')
+ try:
+ if m['action']:
+ pass
+ except KeyError:
+ m['action'] = '/' + url.rsplit('/', 1)[1]
+ ErrorLogger(url, 'No standard "action" attribute.')
+ action = Parser.buildAction(url, m['action']) # get all forms which have 'action' attribute
+ if not action in actionDone and action != '': # if url returned is not a null value nor duplicate...
+ # If form submission is kept to True
+ if FORM_SUBMISSION:
try:
- form2 = Debugger.getAllForms(BeautifulSoup(o2))[i] # user2 gets his form
- except IndexError:
- verbout(R, 'Form Error')
- continue # making sure program won't end here (dirty fix :( )
- verbout(GR, 'Preparing form inputs...')
- contents2 = form.prepareFormInputs(form2) # prepare for form 2 as user2
- r3 = Post(url,action,contents2).text # make request as user3 with user2's form
- if POST_BASED:
+ result, genpoc = form.prepareFormInputs(m) # prepare inputs as user 1
+ r1 = Post(url, action, result) # make request with token values generated as user1
+ result, genpoc = form.prepareFormInputs(m) # prepare inputs as user 2
+ r2 = Post(url, action, result) # again make request with token values generated as user2
+ if COOKIE_BASED:
+ Cookie(url, r1)
+ # Go for token based entropy checks...
try:
if m['name']:
- PostBased(url, r1, r2, r3, m['action'], result, m['name'])
+ query, token = Entropy(result, url, r1.headers, m.prettify(), m['action'], m['name'])
except KeyError:
- PostBased(url, r1, r2, r3, m['action'], result)
-
- except HTTPError as msg: # if runtime exception...
- verbout(R, 'Exception : '+msg.__str__()) # again exception :(
-
- actionDone.append(action) # add the stuff done
- i+=1 # Increase user iteration
-
- except URLError: # if again...
- verbout(R, 'Exception at : '+url) # again exception -_-
- time.sleep(0.4)
- verbout(O, 'Moving on...')
- continue # make sure it doesn't stop
-
+ query, token = Entropy(result, url, r1.headers, m.prettify(), m['action'])
+ ErrorLogger(url, 'No standard form "name".')
+ # Now its time to detect the encoding type (if any) of the Anti-CSRF token.
+ fnd, detct = Encoding(token)
+ if fnd == 0x01 and detct:
+ VulnLogger(url, 'String encoded token value. Token might be decrypted.', '[i] Encoding: '+detct)
+ else:
+ NovulLogger(url, 'Anti-CSRF token is not a string encoded value.')
+ # Go for token parameter tamper checks.
+ if (query and token):
+ txor = Tamper(url, action, result, r2.text, query, token)
+ o2 = resp2.open(url).read() # make request as user2
+ try:
+ form2 = Debugger.getAllForms(BeautifulSoup(o2))[i] # user2 gets his form
+ except IndexError:
+ verbout(R, 'Form Index Error')
+ ErrorLogger(url, 'Form Index Error.')
+ continue # making sure program won't end here (dirty fix :( )
+ verbout(GR, 'Preparing form inputs...')
+ contents2, genpoc = form.prepareFormInputs(form2) # prepare for form 3 as user3
+ r3 = Post(url, action, contents2) # make request as user3 with user3's form
+ if (POST_BASED) and ((query == '') or (txor == True)):
+ try:
+ if m['name']:
+ PostBased(url, r1.text, r2.text, r3.text, m['action'], result, genpoc, m.prettify(), m['name'])
+ except KeyError:
+ PostBased(url, r1.text, r2.text, r3.text, m['action'], result, genpoc, m.prettify())
+ else:
+ print(color.GREEN+' [+] The form was requested with a Anti-CSRF token.')
+ print(color.GREEN+' [+] Endpoint '+color.BG+' NOT VULNERABLE '+color.END+color.GREEN+' to P0ST-Based CSRF Attacks!')
+ NovulLogger(url, 'Not vulnerable to POST-Based CSRF Attacks.')
+ except HTTPError as msg: # if runtime exception...
+ verbout(color.RED, ' [-] Exception : '+color.END+msg.__str__()) # again exception :(
+ ErrorLogger(url, msg)
+ actionDone.append(action) # add the stuff done
+ i+=1 # Increase user iteration
+ except URLError as e: # if again...
+ verbout(R, 'Exception at : '+url) # again exception -_-
+ time.sleep(0.4)
+ verbout(O, 'Moving on...')
+ ErrorLogger(url, e)
+ continue # make sure it doesn't stop at exceptions
+ # This error usually happens when some sites are protected by some load balancer
+ # example Cloudflare. These domains return a 403 forbidden response in various
+ # contexts. For example when making reverse DNS queries.
+ except HTTPError as e:
+ if str(e.code) == '403':
+ verbout(R, 'HTTP Authentication Error!')
+ verbout(R, 'Error Code : ' +O+ str(e.code))
+ ErrorLogger(url, e)
+ quit()
+ GetLogger() # The scanning has finished, so now we can log out all the links ;)
print('\n'+G+"Scan completed!"+'\n')
Analysis() # For Post Scan Analysis
-
- # This error usually happens when some sites are protected by some load balancer
- # example Cloudflare. These domains return a 403 forbidden response in various
- # contexts. For example when making reverse DNS queries.
- except HTTPError as e:
- if str(e.code) == '403':
- verbout(R, 'HTTP Authentication Error!')
- verbout(R, 'Error Code : ' +O+ str(e.code))
- quit()
-
- except KeyboardInterrupt: # incase user wants to exit ;-; (while crawling)
+ except KeyboardInterrupt as e: # Incase user wants to exit :') (while crawling)
verbout(R, 'User Interrupt!')
time.sleep(1.5)
Analysis() # For Post scan Analysis
print(R+'Aborted!') # say goodbye
+ ErrorLogger('KeyBoard Interrupt', 'Aborted')
+ GetLogger() # The scanning has interrupted, so now we can log out all the links ;)
quit()
+# except Exception as e:
+ # verbout(R, e.__str__())
+ # ErrorLogger(url, e)
diff --git a/core/options.py b/core/options.py
index d02d253..0ec3f2a 100644
--- a/core/options.py
+++ b/core/options.py
@@ -5,15 +5,15 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
# Importing stuff
-import argparse, sys
-import urllib.parse
+import argparse, sys, tld
+import urllib.parse, os
from files import config
-from core.colors import R
+from core.colors import R, G
from core.updater import updater
# Processing command line arguments
@@ -32,17 +32,22 @@
optional.add_argument('-o', '--output', help='Output directory where files to be stored. Default is the`files` folder where all files generated will be stored.', dest='output')
optional.add_argument('-d', '--delay', help='Time delay between requests in seconds. Default is zero.', dest='delay', type=float)
optional.add_argument('-q', '--quiet', help='Set the DEBUG mode to quiet. Report only when vulnerabilities are found. Minimal output will be printed on screen. ', dest='quiet', action='store_true')
+optional.add_argument('-v', '--verbose', help='Increase the verbosity of the output (e.g., -vv is more than -v). ', dest='verbose', action='store_true')
# Other Options
# optional.add_argument('-h', '--help', help='Show this help message and exit', dest='disp', default=argparse.SUPPRESS, action='store_true')
optional.add_argument('--user-agent', help='Custom user-agent to be used. Only one user-agent can be specified.', dest='user_agent', type=str)
-optional.add_argument('--headers', help='Comma separated list of custom headers you\'d want to use. For example: ``--headers Accept=text/php, DNT=1``.', dest='headers', type=str)
+optional.add_argument('--headers', help='Comma separated list of custom headers you\'d want to use. For example: ``--headers "Accept=text/php, X-Requested-With=Dumb"``.', dest='headers', type=str)
optional.add_argument('--exclude', help='Comma separated list of paths or directories to be excluded which are not in scope. These paths/dirs won\'t be scanned. For example: `--exclude somepage/, sensitive-dir/, pleasedontscan/`', dest='exclude', type=str)
-optional.add_argument('--timeout', help='HTTP request timeout value in seconds. The entered value must be in floating point decimal. Example: ``--timeout 10.0``', dest='timeout', type=float)
+optional.add_argument('--timeout', help='HTTP request timeout value in seconds. The entered value may be either in floating point decimal or an integer. Example: ``--timeout 10.0``', dest='timeout', type=(float or int))
optional.add_argument('--max-chars', help='Maximum allowed character length for the custom token value to be generated. For example: `--max-chars 5`. Default value is 6.', dest='maxchars', type=int)
-optional.add_argument('--skip-analysis', help='Skip the Post-Scan Analysis of Tokens which were gathered during requests', dest='skipal', action='store_true')
+optional.add_argument('--crawl', help="Crawl the whole site and simultaneously test all discovered endpoints for CSRF.", dest='crawl', action='store_true')
+optional.add_argument('--no-analysis', help='Skip the Post-Scan Analysis of Tokens which were gathered during requests', dest='skipal', action='store_true')
+optional.add_argument('--malicious', help='Generate a malicious CSRF Form which can be used in real-world exploits.', dest='malicious', action='store_true')
+optional.add_argument('--skip-poc', help='Skip the PoC Form Generation of POST-Based Cross Site Request Forgeries.', dest='skippoc', action='store_true')
+optional.add_argument('--display', help='Print out response headers of requests while making requests.', dest='disphead', action='store_true')
optional.add_argument('--update', help='Update XSRFProbe to latest version on GitHub via git.', dest='update', action='store_true')
-optional.add_argument('--random-agent', help='Use random user-agents for making requests', dest='randagent', action='store_true')
+optional.add_argument('--random-agent', help='Use random user-agents for making requests.', dest='randagent', action='store_true')
optional.add_argument('--version', help='Display the version of XSRFProbe and exit.', dest='version', action='store_true')
args = parser.parse_args()
@@ -75,6 +80,14 @@
if args.skipal:
config.SCAN_ANALYSIS = False
+# Option to skip poc generation
+if args.skippoc:
+ config.POC_GENERATION = False
+
+# Option to generate malicious form
+if args.malicious:
+ config.GEN_MALICIOUS = True
+
# Updating main root url
if not args.version and not args.update:
if args.url: # and not args.help:
@@ -83,7 +96,13 @@
else:
config.SITE_URL = 'http://'+args.url
else:
- print(R+'You must supply a url.')
+ print(R+'You must supply a url/endpoint.')
+
+# Crawl the site if --crawl supplied.
+if args.crawl:
+ config.CRAWL_SITE = True
+ # Turning off the display header feature due to too much log generation.
+ config.DISPLAY_HEADERS = False
if args.cookie:
# Assigning Cookie
@@ -96,6 +115,10 @@
# security mechanisms (or worse, perhaps block your ip?)
config.USER_AGENT_RANDOM = False
+# Set the headers displayer to 1 (actively display headers)
+if args.disphead:
+ config.DISPLAY_HEADERS = True
+
# Timeout value
if args.timeout:
config.TIMEOUT_VALUE = args.timeout
@@ -110,7 +133,7 @@
#
#config.HEADER_VALUES = {}
for m in args.headers.split(','):
- config.HEADER_VALUES[m.split('=')[0]] = m.split('=')[1]
+ config.HEADER_VALUES[m.split('=')[0]] = m.split('=')[1] # nice hack ;)
if args.exclude:
exc = args.exclude
@@ -122,15 +145,24 @@
if args.randagent:
# If random-agent argument supplied...
config.USER_AGENT_RANDOM = True
- # Turn off a single User-Agent mechanism
+ # Turn off a single User-Agent mechanism...
config.USER_AGENT = ''
if config.SITE_URL:
if args.output:
# If output directory is mentioned...
- config.OUTPUT_DIR = args.output + config.SITE_URL.split('//')[1]
+ try:
+ if not os.path.exists(args.output+tld.get_fld(config.SITE_URL)):
+ os.makedirs(args.output+tld.get_fld(config.SITE_URL))
+ except FileExistsError:
+ pass
+ config.OUTPUT_DIR = args.output+tld.get_fld(config.SITE_URL) + '/'
else:
- config.OUTPUT_DIR = 'files' + config.SITE_URL.split('//')[1]
+ try:
+ os.makedirs('output/'+tld.get_fld(config.SITE_URL))
+ except FileExistsError:
+ pass
+ config.OUTPUT_DIR = 'output/'+tld.get_fld(config.SITE_URL) + '/'
if args.quiet:
config.DEBUG = False
diff --git a/core/prettify.py b/core/prettify.py
new file mode 100644
index 0000000..a4339de
--- /dev/null
+++ b/core/prettify.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+#-:-:-:-:-:-:-::-:-:#
+# XSRF Probe #
+#-:-:-:-:-:-:-::-:-:#
+
+# Author: 0xInfection
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
+
+import re
+from core.colors import *
+
+def formPrettify(response):
+ '''
+ The main aim for this is to beautify the forms
+ that will be displayed on the terminal.
+ '''
+ highlighted = []
+ response = response.splitlines()
+ for newLine in response:
+ line = newLine
+ # Find starting tags
+ pattern = re.findall(r"""(<+\w+>)""", line)
+ for grp in pattern:
+ starttag = ''.join(grp)
+ if starttag:
+ line = line.replace(starttag, color.BLUE + starttag + color.END)
+ # Find attributes
+ pattern = re.findall(r'''(\s\w+=)''', line)
+ for grp in pattern:
+ stu = ''.join(grp)
+ if stu:
+ line = line.replace(stu, color.CYAN + stu + color.END)
+ # Find ending tags
+ pattern = re.findall(r'''()''', line)
+ for grp in pattern:
+ endtag = ''.join(grp)
+ if endtag:
+ line = line.replace(endtag, color.CYAN + endtag + color.END)
+ if line != newLine:
+ highlighted.append(line)
+ else:
+ highlighted.append(color.GREY+newLine)
+ for h in highlighted:
+ print(' '+h)
+
+def indentPrettify(soup, indent=2):
+ # where desired_indent is number of spaces as an int()
+ pretty_soup = str()
+ previous_indent = 0
+ # iterate over each line of a prettified soup
+ for line in soup.prettify().split("\n"):
+ # returns the index for the opening html tag '<'
+ current_indent = str(line).find("<")
+ # which is also represents the number of spaces in the lines indentation
+ if current_indent == -1 or current_indent > previous_indent + 2:
+ current_indent = previous_indent + 1
+ # str.find() will equal -1 when no '<' is found. This means the line is some kind
+ # of text or script instead of an HTML element and should be treated as a child
+ # of the previous line. also, current_indent should never be more than previous + 1.
+ previous_indent = current_indent
+ pretty_soup += writeOut(line, current_indent, indent)
+ return pretty_soup
+
+def writeOut(line, current_indent, desired_indent):
+ new_line = ""
+ spaces_to_add = (current_indent * desired_indent) - current_indent
+ if spaces_to_add > 0:
+ for i in range(spaces_to_add):
+ new_line += " "
+ new_line += str(line) + "\n"
+ return new_line
diff --git a/core/randua.py b/core/randua.py
index 591c898..581c881 100644
--- a/core/randua.py
+++ b/core/randua.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -37,7 +37,21 @@
'Mozilla/5.0 (Windows NT 6.1; Win64; x64; Trident/7.0; rv:11.0) like Gecko',
'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)',
'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
- 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)'
+ 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)',
+ # Opera
+ 'Opera/9.80 (X11; Linux x86_64; U; bg) Presto/2.8.131 Version/11.10',
+ 'Opera/9.80 (Windows NT 6.0; U; en) Presto/2.8.99 Version/11.10',
+ 'Opera/9.80 (Windows NT 5.1; U; zh-tw) Presto/2.8.131 Version/11.10',
+ 'Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1',
+ 'Opera/9.80 (X11; Linux x86_64; U; Ubuntu/10.10 (maverick); pl) Presto/2.7.62 Version/11.01',
+ 'Opera/9.80 (X11; Linux i686; U; ja) Presto/2.7.62 Version/11.01',
+ 'Opera/9.80 (X11; Linux i686; U; fr) Presto/2.7.62 Version/11.01',
+ 'Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01',
+ 'Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.7.62 Version/11.01',
+ 'Opera/9.80 (Windows NT 6.1; U; sv) Presto/2.7.62 Version/11.01',
+ 'Opera/9.80 (Windows NT 6.1; U; en-US) Presto/2.7.62 Version/11.01',
+ 'Opera/9.80 (Windows NT 6.1; U; cs) Presto/2.7.62 Version/11.01',
+ 'Opera/9.80 (Windows NT 6.0; U; pl) Presto/2.7.62 Version/11.01'
]
def RandomAgent():
diff --git a/core/request.py b/core/request.py
index 7e6e3bf..7500dfb 100644
--- a/core/request.py
+++ b/core/request.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -13,10 +13,10 @@
from core.colors import *
from files.config import *
from core.verbout import verbout
-from core.logger import pheaders
from core.randua import RandomAgent
from urllib.parse import urljoin
-from files.discovered import FILES_EXEC # import ends
+from files.discovered import FILES_EXEC
+from core.logger import pheaders, ErrorLogger # import ends
headers = HEADER_VALUES # set the headers
@@ -26,7 +26,7 @@
headers['Cookie'] = cookie
# Set User-Agent
-if USER_AGENT_RANDOM:
+if USER_AGENT_RANDOM or not USER_AGENT:
headers['User-Agent'] = RandomAgent()
else:
headers['User-Agent'] = USER_AGENT
@@ -38,22 +38,29 @@ def Post(url, action, data):
'''
time.sleep(DELAY_VALUE) # If delay param has been supplied
verbout(GR, 'Processing the '+color.GREY+'POST'+color.END+' Request...')
- main_url = urljoin(url, action) # encode stuff to make callable
+ main_url = urljoin(url, action) # join url and action
try:
# Make the POST Request.
response = requests.post(main_url, headers=headers, data=data, timeout=TIMEOUT_VALUE)
if DISPLAY_HEADERS:
pheaders(response.headers)
return response # read data content
- except requests.exceptions: # if error
- verbout(R, "HTTP Error : "+action)
- return
- except ValueError: # again if valuerror
- verbout(R, "Value Error : "+action)
- return
+ except requests.exceptions.HTTPError as e: # if error
+ verbout(R, "HTTP Error : "+main_url)
+ ErrorLogger(main_url, e.__str__())
+ return None
+ except requests.exceptions.ConnectionError as e:
+ verbout(R, 'Connection Aborted : '+main_url)
+ ErrorLogger(main_url, e.__str__())
+ return None
+ except ValueError as e: # again if valuerror
+ verbout(R, "Value Error : "+main_url)
+ ErrorLogger(main_url, e.__str__())
+ return None
except Exception as e:
verbout(R, "Exception Caught: "+e.__str__())
- return '' # if at all nothing happens :(
+ ErrorLogger(main_url, e.__str__())
+ return None # if at all nothing happens :(
def Get(url, headers=headers):
'''
@@ -65,17 +72,30 @@ def Get(url, headers=headers):
# Making sure the url is not a file
if url.split('.')[-1].lower() in (FILE_EXTENSIONS or EXECUTABLES):
FILES_EXEC.append(url)
- print(G+'Found File: '+color.BLUE+url)
- return
+ verbout(G, 'Found File: '+color.BLUE+url)
+ return None
try:
verbout(GR, 'Processing the '+color.GREY+'GET'+color.END+' Request...')
- req = requests.get(url, headers=headers, timeout=TIMEOUT_VALUE, stream=False, verify=False)
+ req = requests.get(url, headers=headers, timeout=TIMEOUT_VALUE, stream=False)
# Displaying headers if DISPLAY_HEADERS is 'True'
if DISPLAY_HEADERS:
pheaders(req.headers)
# Return the object
return req
except requests.exceptions.MissingSchema as e:
- print(R+'Exception at: '+color.GREY+url)
- print(R+'Error: Invalid URL Format')
- return
+ verbout(R, 'Exception at: '+color.GREY+url)
+ verbout(R, 'Error: Invalid URL Format')
+ ErrorLogger(url, e.__str__())
+ return None
+ except requests.exceptions.HTTPError as e: # if error
+ verbout(R, "HTTP Error : "+main_url)
+ ErrorLogger(main_url, e.__str__())
+ return None
+ except requests.exceptions.ConnectionError as e:
+ verbout(R, 'Connection Aborted : '+main_url)
+ ErrorLogger(main_url, e.__str__())
+ return None
+ except Exception as e:
+ verbout(R, "Exception Caught: "+e.__str__())
+ ErrorLogger(main_url, e.__str__())
+ return None # if at all nothing happens :(
diff --git a/core/updater.py b/core/updater.py
index f41d02a..f54e09b 100644
--- a/core/updater.py
+++ b/core/updater.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -14,6 +14,9 @@
from requests import get
def updater():
+ '''
+ Function to update XSRFProbe seamlessly.
+ '''
print(GR+'Checking for updates...')
vno = get('https://github.com/raw/0xInfection/XSRFProbe/master/files/VersionNum').text
print(GR+'Version on GitHub: '+color.CYAN+vno.strip())
diff --git a/core/utils.py b/core/utils.py
index 1b27e93..5f49e4c 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -1,13 +1,13 @@
#!/usr/bin/env python3
-#-*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
#-:-:-:-:-:-:-:-:-:#
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-#Author: @_tID
-#This module requires XSRFProbe
-#https://github.com/0xInfection/XSRFProbe
+# Author: 0xInfection
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
from difflib import SequenceMatcher
@@ -18,7 +18,7 @@ def sameSequence(str1,str2):
'''
# Initialize SequenceMatcher object with
# Input string
- seqMatch = SequenceMatcher(None,str1,str2)
+ seqMatch = SequenceMatcher(None, str1, str2)
# Find match of longest sub-string
# Output will be like Match(a=0, b=0, size=5)
@@ -60,10 +60,6 @@ def byteString(s, encoding='utf8'):
s = str(s)
return s
-# Iterative Python program to check if a string is subsequence of another string
-
-# Returns true if str1 is a subsequence of str2
-# m is length of str1, n is length of str2
def subSequence(str1,str2):
'''
Returns whether 'str1' and 'str2' are subsequence
diff --git a/core/verbout.py b/core/verbout.py
index 4d7487d..57fb174 100644
--- a/core/verbout.py
+++ b/core/verbout.py
@@ -5,14 +5,14 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
from core.colors import *
from files.config import DEBUG as verbose
-def verbout(stat,content_info):
+def verbout(stat, content_info):
'''
This module is for giving a verbose
output.
@@ -22,3 +22,7 @@ def verbout(stat,content_info):
if verbose:
# Concatenate the stat type and string value and print out
print(stat+content_info)
+
+#def verbo_sity(*verb_args):
+# if verb_args[0] > (3 - args.verbose):
+# print verb_args[1]
diff --git a/files/VersionNum b/files/VersionNum
index 46b105a..437382d 100644
--- a/files/VersionNum
+++ b/files/VersionNum
@@ -1 +1 @@
-v2.0.0
+v2 (stable)
diff --git a/files/__init__.py b/files/__init__.py
index 793ff4b..310ac79 100644
--- a/files/__init__.py
+++ b/files/__init__.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
#This module requires XSRFProbe
#https://github.com/0xInfection/XSRFProbe
diff --git a/files/config.py b/files/config.py
index c62c43a..039b9b7 100644
--- a/files/config.py
+++ b/files/config.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -14,14 +14,22 @@
# Lets assign some global variables...
global SITE_URL, DEBUG, USER_AGENT, USER_AGENT_RANDOM, COOKIE_BASED, COOKIE_VALUE
global HEADER_VALUES, TIMEOUT_VALUE, REFERER_ORIGIN_CHECKS, REFERER_URL, POST_BASED
+global DISPLAY_HEADERS, EXECUTABLES, FILE_EXTENSIONS, POC_GENERATION, OUTPUT_DIR
+global CRAWL_SITE, TOKEN_CHECKS, DELAY_VALUE, SCAN_ANALYSIS, EXCLUDE_DIRS, GEN_MALICIOUS
# Site Url to be scanned (Required)
SITE_URL = ''
+# Switch for whether to crawl the site or not
+CRAWL_SITE = False
+
# Print out verbose (turn it off for only brief outputs).
# Turning off is Highly Discouraged, since you will miss what the tool is doing.
DEBUG = True
+# Debug level of the output (beta test feature)
+DEBUG_LEVEL = 3
+
# User-Agent to be used (If COOKIE_VALUE is not supplied)
USER_AGENT_RANDOM = False
@@ -126,6 +134,14 @@
# results in not analysing the tokens gathered.
SCAN_ANALYSIS = True
+# Option to skip PoC Form Generation of POST_BASED Request Forgeries.
+# The form will not be generated.
+POC_GENERATION = True
+
+# Option whether or not to generate a malicious CSRF form with all
+# hidden fields.
+GEN_MALICIOUS = False
+
# A list of file extensions that might be come across while scanning
# and crawling
FILE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'pdf', 'js', 'css', 'ico', 'bmp', 'svg', 'json', 'xml', 'xls', 'csv', 'docx',]
diff --git a/files/dcodelist.py b/files/dcodelist.py
index 4448ca6..9c4498b 100644
--- a/files/dcodelist.py
+++ b/files/dcodelist.py
@@ -5,13 +5,82 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
# This file contains various regex expressions for detecting the
# encoding type of strings.
+# Token hash encoding detection regex database
+HASH_DB = (
+ ("Blowfish (Eggdrop)", r"^\+[a-zA-Z0-9\/\.]{12}$"),
+ ("Blowfish (OpenBSD)", r"^\$2a\$[0-9]{0,2}?\$[a-zA-Z0-9\/\.]{53}$"),
+ ("Blowfish Crypt", r"^\$2[axy]{0,1}\$[a-zA-Z0-9./]{8}\$[a-zA-Z0-9./]{1,}$"),
+ ("DES (Unix)", r"^.{0,2}[a-zA-Z0-9\/\.]{11}$"),
+ ("MD5 (Unix)", r"^\$1\$.{0,8}\$[a-zA-Z0-9\/\.]{22}$"),
+ ("MD5 (APR)", r"^\$apr1\$.{0,8}\$[a-zA-Z0-9\/\.]{22}$"),
+ ("MD5 (MyBB)", r"^[a-fA-F0-9]{32}:[a-z0-9]{8}$"),
+ ("MD5 (ZipMonster)", r"^[a-fA-F0-9]{32}$"),
+ ("MD5 crypt", r"^\$1\$[a-zA-Z0-9./]{8}\$[a-zA-Z0-9./]{1,}$"),
+ ("MD5 apache crypt", r"^\$apr1\$[a-zA-Z0-9./]{8}\$[a-zA-Z0-9./]{1,}$"),
+ ("MD5 (Joomla)", r"^[a-fA-F0-9]{32}:[a-zA-Z0-9]{16,32}$"),
+ ("MD5 (Wordpress)", r"^\$P\$[a-zA-Z0-9\/\.]{31}$"),
+ ("MD5 (phpBB3)", r"^\$H\$[a-zA-Z0-9\/\.]{31}$"),
+ ("MD5 (Cisco PIX)", r"^[a-zA-Z0-9\/\.]{16}$"),
+ ("MD5 (osCommerce)", r"^[a-fA-F0-9]{32}:[a-zA-Z0-9]{2}$"),
+ ("MD5 (Palshop)", r"^[a-fA-F0-9]{51}$"),
+ ("MD5 (IP.Board)", r"^[a-fA-F0-9]{32}:.{5}$"),
+ ("MD5 (Chap)", r"^[a-fA-F0-9]{32}:[0-9]{32}:[a-fA-F0-9]{2}$"),
+ ("Juniper Netscreen/SSG (ScreenOS)", r"^[a-zA-Z0-9]{30}:[a-zA-Z0-9]{4,}$"),
+ ("Fortigate (FortiOS)", r"^[a-fA-F0-9]{47}$"),
+ ("Minecraft (Authme)", r"^\$sha\$[a-zA-Z0-9]{0,16}\$[a-fA-F0-9]{64}$"),
+ ("Lotus Domino", r"^\(?[a-zA-Z0-9\+\/]{20}\)?$"),
+ ("Lineage II C4", r"^0x[a-fA-F0-9]{32}$"),
+ ("CRC-96 (ZIP)", r"^[a-fA-F0-9]{24}$"),
+ ("NT crypt", r"^\$3\$[a-zA-Z0-9./]{8}\$[a-zA-Z0-9./]{1,}$"),
+ ("Skein-1024", r"^[a-fA-F0-9]{256}$"),
+ ("RIPEMD-320", r"^[A-Fa-f0-9]{80}$"),
+ ("EPi hash", r"^0x[A-F0-9]{60}$"),
+ ("EPiServer 6.x < v4", r"^\$episerver\$\*0\*[a-zA-Z0-9]{22}==\*[a-zA-Z0-9\+]{27}$"),
+ ("EPiServer 6.x >= v4", r"^\$episerver\$\*1\*[a-zA-Z0-9]{22}==\*[a-zA-Z0-9]{43}$"),
+ ("Cisco IOS SHA256", r"^[a-zA-Z0-9]{43}$"),
+ ("SHA-1 (Django)", r"^sha1\$.{0,32}\$[a-fA-F0-9]{40}$"),
+ ("SHA-1 crypt", r"^\$4\$[a-zA-Z0-9./]{8}\$[a-zA-Z0-9./]{1,}$"),
+ ("SHA-1 (Hex)", r"^[a-fA-F0-9]{40}$"),
+ ("SHA-1 (LDAP) Base64", r"^\{SHA\}[a-zA-Z0-9+/]{27}=$"),
+ ("SHA-1 (LDAP) Base64 + salt", r"^\{SSHA\}[a-zA-Z0-9+/]{28,}[=]{0,3}$"),
+ ("SHA-512 (Drupal)", r"^\$S\$[a-zA-Z0-9\/\.]{52}$"),
+ ("SHA-512 crypt", r"^\$6\$[a-zA-Z0-9./]{8}\$[a-zA-Z0-9./]{1,}$"),
+ ("SHA-256 (Django)", r"^sha256\$.{0,32}\$[a-fA-F0-9]{64}$"),
+ ("SHA-256 crypt", r"^\$5\$[a-zA-Z0-9./]{8}\$[a-zA-Z0-9./]{1,}$"),
+ ("SHA-384 (Django)", r"^sha384\$.{0,32}\$[a-fA-F0-9]{96}$"),
+ ("SHA-256 (Unix)", r"^\$5\$.{0,22}\$[a-zA-Z0-9\/\.]{43,69}$"),
+ ("SHA-512 (Unix)", r"^\$6\$.{0,22}\$[a-zA-Z0-9\/\.]{86}$"),
+ ("SHA-384", r"^[a-fA-F0-9]{96}$"),
+ ("SHA-512", r"^[a-fA-F0-9]{128}$"),
+ ("SSHA-1", r"^({SSHA})?[a-zA-Z0-9\+\/]{32,38}?(==)?$"),
+ ("SSHA-1 (Base64)", r"^\{SSHA\}[a-zA-Z0-9]{32,38}?(==)?$"),
+ ("SSHA-512 (Base64)", r"^\{SSHA512\}[a-zA-Z0-9+]{96}$"),
+ ("Oracle 11g", r"^S:[A-Z0-9]{60}$"),
+ ("SMF >= v1.1", r"^[a-fA-F0-9]{40}:[0-9]{8}&"),
+ ("MySQL 5.x", r"^\*[a-f0-9]{40}$"),
+ ("MySQL 3.x", r"^[a-fA-F0-9]{16}$"),
+ ("OSX v10.7", r"^[a-fA-F0-9]{136}$"),
+ ("OSX v10.8", r"^\$ml\$[a-fA-F0-9$]{199}$"),
+ ("SAM (LM_Hash:NT_Hash)", r"^[a-fA-F0-9]{32}:[a-fA-F0-9]{32}$"),
+ ("MSSQL (2000)", r"^0x0100[a-f0-9]{0,8}?[a-f0-9]{80}$"), (
+ "MSSQL (2005)", r"^0x0100[a-f0-9]{0,8}?[a-f0-9]{40}$"),
+ ("MSSQL (2012)", r"^0x02[a-f0-9]{0,10}?[a-f0-9]{128}$"),
+ ("TIGER-160 (HMAC)", r"^[a-f0-9]{40}$"),
+ ("SHA-256", r"^[a-fA-F0-9]{64}$"),
+ ("SHA-1(Oracle)", r"^[a-fA-F0-9]{48}$"),
+ ("SHA-224", r"^[a-fA-F0-9]{56}$"),
+ ("Adler32", r"^[a-f0-9]{8}$"),
+ ("CRC-16-CCITT", r"^[a-fA-F0-9]{4}$"),
+ ("NTLM", r"^[0-9A-Fa-f]{32}$"),
+ )
+
# Get rid of Double ../../
RID_DOUBLE = r'/\.\./'
@@ -35,19 +104,3 @@
# Protocol Types
PROTOCOLS = r'(.*\/)[^\/]*'
-
-# Token encoding detection
-token = {
- # Base64 encoded strings.
- 'BASE64': r'^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$',
- # SHA2 encoded strings/hashes.
- 'SHA2' : r'^([a-f0-9]{64})$',
- # SHA1 encoded strings/hashes.
- 'SHA1' : r'^([a-f0-9]{40})$',
- # MD5 encoded strings/hashes.
- 'MD5' : r'^([a-f0-9]{32})$',
- # Hex Strings.
- 'HEX' : r'^(0x|0X)?[a-fA-F0-9]+$',
- # FromChar Strings
- 'FROMCHAR': r'\d*, \d*,'
- }
diff --git a/files/discovered.py b/files/discovered.py
index 5debcda..63bf204 100644
--- a/files/discovered.py
+++ b/files/discovered.py
@@ -5,13 +5,19 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
# INFO: This file is for storing the various important parts of
# requests discovered during making of various requests.
+# Vulnerabilities which were noticed
+VULN_LIST = []
+
+# Strengths or positive sides of the application
+STRENGTH_LIST = []
+
# This is for storing the various tokens which got discovered
# during making the requests. This will be used for various
# analysis of token generation prototypes and logic used in
@@ -21,5 +27,11 @@
# List of all Urls that we found
INTERNAL_URLS = []
-# Files discovered during crawling
+# Files/executables discovered during crawling
FILES_EXEC = []
+
+# Forms that were tested
+FORMS_TESTED = []
+
+# Errors that were encountered
+SCAN_ERRORS = []
diff --git a/files/paramlist.py b/files/paramlist.py
index ad86cb8..0f6ef2d 100644
--- a/files/paramlist.py
+++ b/files/paramlist.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -18,36 +18,40 @@
# These are a list of known common tokens parameters
'CSRFName', # OWASP CSRF_Guard
'CSRFToken', # OWASP CSRF_Guard
+ 'csrf_token', # PHP NoCSRF Class
'anticsrf', # AntiCsrfParam.java
- '__RequestVerificationToken', # AntiCsrfParam.java
+ '__RequestVerificationToken', # ASP.NET TokenParam
+ 'VerificationToken', # AntiCSRFParam.java
+ 'form_build-id', # Drupal CMS AntiCSRF
+ 'nonce', # WordPress Nonce
'authenticity_token', # Ruby on Rails
'csrf_param', # Ruby on Rails
- 'TransientKey', # VanillaForums
+ 'TransientKey', # VanillaForums Param
+ 'csrf', # PHP CSRFProtect
+ 'AntiCSURF', # Anti CSURF (PHP)
'YII_CSRF_TOKEN', # http://www.yiiframework.com/
'yii_anticsrf' # http://www.yiiframework.com/
'[_token]', # Symfony 2.x
'_csrf_token', # Symfony 1.4
'csrfmiddlewaretoken', # Django 1.5
- 'ccm_token', # Concrete 5
+ 'ccm_token', # Concrete 5 CMS
'XOOPS_TOKEN_REQUEST', # Xoops CMS
+ '_csrf', # Express JS Default Anti-CSRF
# These are some other various token names I have seen in
# various websites.
#
# TODO: Add more similar csrf token parameters
'token',
- 'csrf',
'authenticity',
'auth_token',
'auth',
'anti_csrf',
- 'auth_value',
+ 'debug_token',
'csrf_value',
'_debugval',
'csrf_token',
- 'VerificationToken',
'__authvalue',
- 'authenticity_value',
'__token',
'__auth',
'secret',
@@ -66,6 +70,19 @@
'vtoken'
)
+COMMON_CSRF_HEADERS = (
+ # These are a list of HTTP Headers often found in requests
+ # of web applications using various frameworks.
+ 'CSRF-Token', # Express JS CSURF Middleware
+ 'XSRF-Token', # Node JS/ Express JS
+ 'X-CSRF-Token', # Ruby on Rails
+ 'X-XSRF-Token', # Express JS CSURF Middleware
+ # Some other probabilties
+ 'X-CSRF-Header',
+ 'X-XSRF-Header',
+ 'X-CSRF-Protection'
+ )
+
# TODO: Add and replace with more valid and arguable exclusion lists
EXCLUSIONS_LIST = (
'logout',
@@ -76,3 +93,27 @@
'osCsid',
'action=logout',
)
+
+# List of common errors shown when token is tampered.
+TOKEN_ERRORS = (
+ 'the required form field',
+ 'token could not be decrypted',
+ 'invalid token',
+ 'wrong',
+ 'error',
+ 'not valid',
+ 'please check your request',
+ 'your browser did something unexpected',
+ 'clearing your cookies',
+ 'tampered token',
+ 'null',
+ 'unacceptable',
+ 'false',
+ 'void',
+ 'incorrect',
+ 'inoperative',
+ 'faulty',
+ 'absurd',
+ 'inconsistent',
+ 'not acceptable',
+ )
diff --git a/modules/Analysis.py b/modules/Analysis.py
index 8d372e0..ed11dce 100644
--- a/modules/Analysis.py
+++ b/modules/Analysis.py
@@ -5,16 +5,18 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
import stringdist
import itertools, time
from core.colors import *
+from .Entropy import calcEntropy
from core.verbout import verbout
from core.utils import sameSequence, byteString
from files.discovered import REQUEST_TOKENS
+from core.logger import VulnLogger, NovulLogger
def Analysis():
'''
@@ -25,9 +27,9 @@ def Analysis():
ctr = 0 # Counter variable set to 0
# Checking if the no of tokens is greater than 1
if len(REQUEST_TOKENS) > 1:
- print(color.RED+'\n +--------------+')
- print(color.RED+' | Analysis |')
- print(color.RED+' +--------------+\n')
+ verbout(color.RED, '\n +--------------+')
+ verbout(color.RED, ' | Analysis |')
+ verbout(color.RED, ' +--------------+\n')
print(GR+'Proceeding for post-scan analysis of tokens gathered...')
verbout(G, 'A total of %s tokens was discovered during the scan' % (len(REQUEST_TOKENS)))
# The idea behind this is to generate all possible combinations (not
@@ -36,8 +38,10 @@ def Analysis():
for tokenx1, tokenx2 in itertools.combinations(REQUEST_TOKENS, 2):
try:
verbout(GR, 'Analysing 2 Anti-CSRF Tokens from gathered requests...')
- verbout(C, 'First Token: '+color.ORANGE+str(tokenx1))
- verbout(C, 'Second Token: '+color.ORANGE+str(tokenx2))
+ verbout(color.CYAN, ' [+] First Token: '+color.BLUE+tokenx1)
+ verbout(color.ORANGE, ' [+] Shannon Entropy: '+color.GREEN+'%s' % (calcEntropy(tokenx1)))
+ verbout(color.CYAN, ' [+] Second Token: '+color.BLUE+tokenx2)
+ verbout(color.ORANGE, ' [+] Shannon Entropy: '+color.GREEN+'%s' % (calcEntropy(tokenx2)))
# Calculating the edit distance via Damerau Levenshtein algorithm
m = stringdist.rdlevenshtein(tokenx1, tokenx2)
verbout(color.CYAN, ' [+] Edit Distance Calculated: '+color.GREY+str(m)+'%')
@@ -45,7 +49,7 @@ def Analysis():
n = stringdist.rdlevenshtein_norm(tokenx1, tokenx2)
verbout(color.CYAN, ' [+] Alignment Ratio Calculated: '+color.GREY+str(n))
# If both tokens are same, then
- if tokenx1 == tokenx2:
+ if len(tokenx1) == len(tokenx2):
verbout(C, 'Token length calculated is same: '+color.ORANGE+'Each %s bytes' % len(byteString(tokenx1)))
else:
verbout(C, 'Token length calculated is different: '+color.ORANGE+'By %s bytes' % (len(byteString(tokenx1)) - len(byteString(tokenx2))))
@@ -54,41 +58,44 @@ def Analysis():
# is composed of two parts, one of them remains static while the other one dynamic.
#
# For example, if the Anti CSRF Tokens “837456mzy29jkd911139” for one request, the
- # other time “837456mzy29jkd337221”, “837456mzy29jkd” part of the token remains same
+ # other is “837456mzy29jkd337221”, “837456mzy29jkd” part of the token remains same
# in both requests.
#
# The main idea behind this is to detect the static and dynamic part via DL Algorithm
# as discussed above by calculating edit distance.
+ p = sameSequence(tokenx1, tokenx2)
+ tokenx01 = tokenx1.replace(p,'')
+ tokenx02 = tokenx2.replace(p,'')
if n == 0.5 or m == len(tokenx1)/2:
verbout(GR, 'The tokens are composed of 2 parts (one static and other dynamic)... ')
- p = sameSequence(tokenx1, tokenx2)
- verbout(C, 'Static Part : '+color.GREY+p+color.END+' | Length: '+str(len(p)))
- verbout(O, 'Dynamic Part of Token 0x1: '+color.GREY+tokenx1.replace(p,'')+color.END+' | Length: '+str(len(tokenx1.replace(p,''))))
- verbout(O, 'Dynamic Part of Token 0x2: '+color.GREY+tokenx2.replace(p,'')+color.END+' | Length: '+str(len(tokenx2.replace(p,''))))
+ verbout(C, 'Static Part : '+color.GREY+p+color.END+' | Length: '+color.CYAN+str(len(p)))
+ verbout(O, 'Dynamic Part of Token 0x1: '+color.GREY+tokenx01+color.END+' | Length: '+color.CYAN+str(len(tokenx01)))
+ verbout(O, 'Dynamic Part of Token 0x2: '+color.GREY+tokenx02+color.END+' | Length: '+color.CYAN+str(len(tokenx02)))
if len(len(tokenx1)/2) <= 6:
verbout(color.RED,' [-] Post-Analysis reveals that token might be '+color.BR+' VULNERABLE '+color.END+'!')
- print(color.GREEN+ ' [+] Possible CSRF Vulnerability Detected!')
+ print(color.RED+ ' [+] Possible CSRF Vulnerability Detected!')
print(color.ORANGE+' [!] Vulnerability Type: '+color.BR+' Weak Dynamic Part of Tokens '+color.END)
- print(color.GREY+' [+] Tokens can easily be '+color.RED+' Forged by Bruteforcing/Guessing '+color.END+'!')
+ print(color.GREY+' [+] Tokens can easily be '+color.RED+'Forged by Bruteforcing/Guessing'+color.END+'!\n')
+ VulnLogger('Analysis', 'Tokens can easily be Forged by Bruteforcing/Guessing.', '[i] Token 1: '+tokenx1+'\n[i] Token 2: '+tokenx2)
elif n < 0.5 or m < len(tokenx1)/2:
verbout(R, 'Token distance calculated is '+color.RED+'less than 0.5!')
- p = sameSequence(tokenx1, tokenx2)
- verbout(C, 'Static Part : '+color.GREY+p+color.END+' | Length: '+str(len(p)))
- verbout(O, 'Dynamic Part of Token 0x1: '+color.GREY+tokenx1.replace(p,'')+color.END+' | Length: '+str(len(tokenx1.replace(p,''))))
- verbout(O, 'Dynamic Part of Token 0x2: '+color.GREY+tokenx2.replace(p,'')+color.END+' | Length: '+str(len(tokenx2.replace(p,''))))
+ verbout(C, 'Static Part : '+color.GREY+p+color.END+' | Length: '+color.CYAN+str(len(p)))
+ verbout(O, 'Dynamic Part of Token 0x1: '+color.GREY+tokenx01+color.END+' | Length: '+color.CYAN+str(len(tokenx01)))
+ verbout(O, 'Dynamic Part of Token 0x2: '+color.GREY+tokenx02+color.END+' | Length: '+color.CYAN+str(len(tokenx02)))
verbout(color.RED,' [-] Post-Analysis reveals that token might be '+color.BR+' VULNERABLE '+color.END+'!')
print(color.GREEN+ ' [+] Possible CSRF Vulnerability Detected!')
print(color.ORANGE+' [!] Vulnerability Type: '+color.BR+' Weak Dynamic Part of Tokens '+color.END)
- print(color.GREY+' [+] Tokens can easily be '+color.RED+' Forged by Bruteforcing/Guessing '+color.END+'!')
+ print(color.GREY+' [+] Tokens can easily be '+color.RED+'Forged by Bruteforcing/Guessing'+color.END+'!\n')
+ VulnLogger('Analysis', 'Tokens can easily be Forged by Bruteforcing/Guessing.', '[i] Token 1: '+tokenx1+'\n[i] Token 2: '+tokenx2)
else:
verbout(R, 'Token distance calculated is '+color.GREEN+'greater than 0.5!')
- p = sameSequence(tokenx1, tokenx2)
- verbout(C, 'Static Part : '+color.GREY+p+color.END+' | Length: '+str(len(p)))
- verbout(O, 'Dynamic Part of Token 0x1: '+color.GREY+tokenx1.replace(p,'')+color.END+' | Length: '+str(len(tokenx1.replace(p,''))))
- verbout(O, 'Dynamic Part of Token 0x2: '+color.GREY+tokenx2.replace(p,'')+color.END+' | Length: '+str(len(tokenx2.replace(p,''))))
- verbout(color.RED,' [-] Post-Analysis reveals that token might be '+color.BG+' NOT VULNERABLE '+color.END+'!')
+ verbout(C, 'Static Part : '+color.GREY+p+color.END+' | Length: '+color.CYAN+str(len(p)))
+ verbout(O, 'Dynamic Part of Token 0x1: '+color.GREY+tokenx01+color.END+' | Length: '+color.CYAN+str(len(tokenx01)))
+ verbout(O, 'Dynamic Part of Token 0x2: '+color.GREY+tokenx02+color.END+' | Length: '+color.CYAN+str(len(tokenx02)))
+ verbout(color.GREEN,' [+] Post-Analysis reveals that tokens are '+color.BG+' NOT VULNERABLE '+color.END+'!')
print(color.ORANGE+' [!] Vulnerability Mitigation: '+color.BG+' Strong Dynamic Part of Tokens '+color.END)
- print(color.GREY+' [+] Tokens '+color.GREEN+' Cannot be Forged by Bruteforcing/Guessing '+color.END+'!')
+ print(color.GREY+' [+] Tokens '+color.GREEN+'Cannot be Forged by Bruteforcing/Guessing'+color.END+'!\n')
+ NovulLogger('Analysis', 'Tokens cannot be Forged by Bruteforcing/Guessing.')
time.sleep(1)
except KeyboardInterrupt:
continue
diff --git a/modules/Checkpost.py b/modules/Checkpost.py
index 76898ac..c561f36 100644
--- a/modules/Checkpost.py
+++ b/modules/Checkpost.py
@@ -5,7 +5,7 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -15,42 +15,73 @@
from core.colors import *
from core.verbout import verbout
from urllib.parse import urlencode
+from core.logger import VulnLogger
+from files.config import POC_GENERATION, GEN_MALICIOUS
+from modules.Generator import GenNormalPoC, GenMalicious
-def PostBased(url, r1, r2, r3, m_action, result, m_name=''):
- checkdiffx1 = difflib.ndiff(r1.splitlines(1),r2.splitlines(1)) # check the diff noted
- checkdiffx2 = difflib.ndiff(r1.splitlines(1),r3.splitlines(1)) # check the diff noted
+def PostBased(url, r1, r2, r3, m_action, result, genpoc, form, m_name=''):
+ '''
+ This method is for detecting POST-Based Request Forgeries
+ on basis of fuzzy string matching and comparison
+ based on Ratcliff-Obershelp Algorithm.
+ '''
+ verbout(color.RED, '\n +------------------------------+')
+ verbout(color.RED, ' | POST-Based Forgery Check |')
+ verbout(color.RED, ' +------------------------------+\n')
+ verbout(O, 'Matching response query differences...')
+ checkdiffx1 = difflib.ndiff(r1.splitlines(1), r2.splitlines(1)) # check the diff noted
+ checkdiffx2 = difflib.ndiff(r1.splitlines(1), r3.splitlines(1)) # check the diff noted
result12 = [] # an init
+ verbout(O, 'Matching results...')
for n in checkdiffx1:
- if re.match('\+|-',n): # get regex matching stuff
+ if re.match('\+|-', n): # get regex matching stuff only +/-
result12.append(n) # append to existing list
result13 = [] # an init
for n in checkdiffx2:
- if re.match('\+|-',n): # get regex matching stuff
+ if re.match('\+|-', n): # get regex matching stuff
result13.append(n) # append to existing list
- # This logic is based purely on the assumption on the difference of requests and
- # response body.
+ # Make sure m_action has a / before it. (legitimate action).
+ if not m_action.startswith('/'):
+ m_action = '/' + m_action
+
+ # This logic is based purely on the assumption on the difference of various requests
+ # and response body.
# If the number of differences of result12 are less than the number of differences
# than result13 then we have the vulnerability. (very basic check)
#
- # NOTE: The alogrithm has lots of scopes of improvements
- if len(result12)<=len(result13):
+ # NOTE: The algorithm has lots of scopes of improvement...
+ if len(result12) <= len(result13):
print(color.GREEN+ ' [+] CSRF Vulnerability Detected : '+color.ORANGE+url+'!')
print(color.ORANGE+' [!] Vulnerability Type: '+color.BR+' POST-Based Request Forgery '+color.END)
+ VulnLogger(url, 'POST-Based Request Forgery on Forms.', '[i] Form: '+form.__str__()+'\n[i] POST Query: '+result.__str__()+'\n')
time.sleep(0.3)
- verbout(O,'PoC of response and request...')
+ verbout(O, 'PoC of response and request...')
if m_name:
- print(color.RED+'\n +---------+')
- print(color.RED+' | PoC |')
- print(color.RED+' +---------+\n')
+ print(color.RED+'\n +-----------------+')
+ print(color.RED+' | Request PoC |')
+ print(color.RED+' +-----------------+\n')
print(color.BLUE+' [+] URL : ' +color.CYAN+url) # url part
print(color.CYAN+' [+] Name : ' +color.ORANGE+m_name) # name
- print(color.GREEN+' [+] Action : ' +color.END+m_action) # action
+ if m_action.count('/') > 1:
+ print(color.GREEN+' [+] Action : ' +color.END+'/'+m_action.rsplit('/', 1)[1]) # action
+ else:
+ print(color.GREEN+' [+] Action : ' +color.END+m_action) # action
else: # if value m['name'] not there :(
- print(color.RED+'\n +---------+')
- print(color.RED+' | PoC |')
- print(color.RED+' +---------+\n')
+ print(color.RED+'\n +-----------------+')
+ print(color.RED+' | Request PoC |')
+ print(color.RED+' +-----------------+\n')
print(color.BLUE+' [+] URL : ' +color.CYAN+url) # the url
- print(color.GREEN+' [+] Action : ' +color.END+ m_action) # action
-
- print(color.ORANGE+' [+] Query : '+color.GREY+ urlencode(result).strip())
- print('') # print out the params + url
+ if m_action.count('/') > 1:
+ print(color.GREEN+' [+] Action : ' +color.END+'/'+m_action.rsplit('/', 1)[1]) # action
+ else:
+ print(color.GREEN+' [+] Action : ' +color.END+m_action) # action
+ print(color.ORANGE+' [+] POST Query : '+color.GREY+ urlencode(result).strip())
+ # If option --skip-poc hasn't been supplied...
+ if POC_GENERATION:
+ # If --malicious has been supplied
+ if GEN_MALICIOUS:
+ # Generates a malicious CSRF form
+ GenMalicious(url, genpoc.__str__())
+ else:
+ # Generates a normal PoC
+ GenNormalPoC(url, genpoc.__str__())
diff --git a/modules/Cookie.py b/modules/Cookie.py
index 0ef4c6d..9aac5a3 100644
--- a/modules/Cookie.py
+++ b/modules/Cookie.py
@@ -5,7 +5,7 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -17,32 +17,40 @@
from core.request import Get
from core.randua import RandomAgent
from .Persistence import Persistence
+from core.logger import VulnLogger, NovulLogger
from urllib.parse import urlencode, unquote, urlsplit
resps = []
-def Cookie(url):
+def Cookie(url, request):
'''
This module is for checking the varied HTTP Cookies
and the related security on them to
prevent CSRF attacks.
'''
- print(color.GREY+' [+] Proceeding for cookie based checks...')
+ verbout(GR, 'Proceeding for cookie based checks...')
SameSite(url)
- Persistence(url)
+ Persistence(url, request)
def SameSite(url):
'''
This function parses and verifies the cookies with
SameSite Flags.
'''
- foundx1, foundx2, foundx3 = 0x00, 0x00, 0x00
+ verbout(color.RED, '\n +------------------------------------+')
+ verbout(color.RED, ' | Cross Origin Cookie Validation |')
+ verbout(color.RED, ' +------------------------------------+\n')
+ # Some Flags we'd need later...
+ foundx1 = 0x00
+ foundx2 = 0x00
+ foundx3 = 0x00
# Step 1: First we check that if the server returns any
# SameSite flag on Cookies with the same Referer as the netloc
verbout(color.GREY,' [+] Lets examine how server reacts to same referer...')
gen_headers = HEADER_VALUES
gen_headers['User-Agent'] = USER_AGENT or RandomAgent()
verbout(GR,'Setting Referer header same as host...')
+ # Setting the netloc as the referer for the first check.
gen_headers['Referer'] = urlsplit(url).netloc
if COOKIE_VALUE:
for cook in COOKIE_VALUE:
@@ -75,7 +83,8 @@ def SameSite(url):
verbout(color.GREY,' [+] Lets examine how server reacts to a fake external referer...')
gen_headers = HEADER_VALUES
gen_headers['User-Agent'] = USER_AGENT or RandomAgent() # Setting user-agents
- gen_headers['Referer'] = REFERER_URL # Assigning a fake referer
+ # Assigning a fake referer for the second check, but no cookie.
+ gen_headers['Referer'] = REFERER_URL
getreq = Get(url, headers=gen_headers)
head = getreq.headers # Getting headers from requests
for h in head:
@@ -108,6 +117,7 @@ def SameSite(url):
verbout(color.GREY,' [+] Lets examine how server reacts to valid cookie from a different referer...')
gen_headers = HEADER_VALUES
gen_headers['User-Agent'] = USER_AGENT or RandomAgent()
+ # Assigning a fake referer for third request, this time with cookie ;)
gen_headers['Referer'] = REFERER_URL
if COOKIE_VALUE:
for cook in COOKIE_VALUE:
@@ -123,8 +133,8 @@ def SameSite(url):
m = cookieval.split(';')
verbout(GR,'Examining Cookie...')
for q in m:
- if search('SameSite', q, I):
- verbout(G,'SameSite Flag '+color.ORANGE+' detected on cookie!')
+ if search('samesite', q.lower(), I):
+ verbout(G,'SameSite Flag '+color.ORANGE+' detected on cookie on Cross Origin Request!')
foundx3 = 0x01
q = q.split('=')[1].strip()
verbout(C, 'Cookie: '+color.ORANGE+q)
@@ -133,16 +143,25 @@ def SameSite(url):
foundx3 = 0x02
if foundx1 == 0x01:
- verbout(R,'Endpoint '+color.ORANGE+'SameSite Flag Cookie Validation'+color.END+' Present!')
+ verbout(R,'Endpoint '+color.ORANGE+'SameSite Flag Cookie Validation'+color.END+' is Present!')
if (foundx1 == 0x01 and foundx3 == 0x00) and (foundx2 == 0x00 or foundx2 == 0x01):
- print(color.GREEN+' [+] Endpoint '+color.BG+' NOT VULNERABLE '+color.END+color.GREEN+' to ANY type of CSRF attacks!')
+ print(color.GREEN+' [+] Endpoint '+color.BG+' NOT VULNERABLE to ANY type of CSRF attacks! '+color.END)
print(color.GREEN+' [+] Protection Method Detected : '+color.BG+' SameSite Flag on Cookies '+color.END)
+ NovulLogger(url, 'SameSite Flag set on Cookies on Cross-Origin Requests.')
+ # If a SameSite flag is set on cookies, then the application is totally fool-proof
+ # against CSRF attacks unless there is some XSS stuff on it. So for now the job of
+ # this application is done. We need to confirm before we quit.
+ oq = input(color.BLUE+' [+] Continue scanning? (y/N) :> ')
+ if oq.lower().startswith('n'):
+ sys.exit('\n'+R+'Shutting down XSRFProbe...\n')
elif foundx1 == 0x02 and foundx2 == 0x02 and foundx3 == 0x02:
print(color.GREEN+' [+] Endpoint '+color.BG+' NOT VULNERABLE '+color.END+color.GREEN+' to CSRF attacks!')
print(color.GREEN+' [+] Type: '+color.BG+' No Cookie Set while Cross Origin Requests '+color.END)
+ NovulLogger(url, 'No cookie set on Cross-Origin Requests.')
else:
- verbout(R,'Endpoint '+color.ORANGE+'SameSite Flag Cookie Validation'+color.END+' Not Present!')
+ verbout(R,'Endpoint '+color.ORANGE+'Cross Origin Cookie Validation'+color.END+' Not Present!')
verbout(R,'Heuristic(s) reveal endpoint might be '+color.BY+' VULNERABLE '+color.END+' to CSRFs...')
print(color.GREEN+ ' [+] Possible CSRF Vulnerability Detected : '+color.ORANGE+url+'!')
- print(color.ORANGE+' [!] Possible Vulnerability Type: '+color.BY+' No SameSite Flag on Cookies '+color.END)
+ print(color.ORANGE+' [!] Possible Vulnerability Type: '+color.BY+' No Cross Origin Cookie Validation Presence '+color.END)
+ VulnLogger(url, 'No Cookie Validation on Cross-Origin Requests.', '[i] Headers: '+str(head))
diff --git a/modules/Crawler.py b/modules/Crawler.py
index 534af57..882f928 100644
--- a/modules/Crawler.py
+++ b/modules/Crawler.py
@@ -5,7 +5,7 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-#Author: @_tID
+#Author: 0xInfection
#This module requires XSRFProbe
#https://github.com/0xInfection/XSRFProbe
@@ -18,6 +18,7 @@
from bs4 import BeautifulSoup
from core.request import Get
from core.verbout import verbout
+from core.logger import ErrorLogger
from files.discovered import INTERNAL_URLS
class Handler(): # Main Crawler Handler
@@ -50,7 +51,7 @@ def noinit(self):
return True # +1
return False # -1
- def addToVisit(self,Parser):
+ def addToVisit(self, Parser):
self.toVisit.append(Parser) # Add what we have got
def process(self, root):
@@ -69,35 +70,36 @@ def process(self, root):
self.toVisit.remove(url)
except (urllib.error.HTTPError, urllib.error.URLError) as msg: # Incase there isan exception connecting to Url
- verbout(R,'HTTP Request Error: '+msg.__str__())
+ verbout(R, 'HTTP Request Error: '+msg.__str__())
+ ErrorLogger(url, msg.__str__())
if url in self.toVisit:
self.toVisit.remove(url) # Remove non-existent / errored urls
- return
+ return None
# Making sure the content type is in HTML format, so that BeautifulSoup
# can parse it...
if not query or not re.search('html', query.headers['Content-Type']):
- return
+ return None
# Just in case there is a redirection, we are supposed to follow it :D
- verbout(GR,'Making request to new location...')
+ verbout(GR, 'Making request to new location...')
if hasattr(query.headers, 'Location'):
url = query.headers['Location']
verbout(O,'Reading response...')
response = query.content # Read the response contents
try:
- verbout(O,'Trying to parse response...')
+ verbout(O, 'Trying to parse response...')
soup = BeautifulSoup(response) # Parser init
except HTMLParser.HTMLParseError:
- verbout(R,'BeautifulSoup Error: '+url)
+ verbout(R, 'BeautifulSoup Error: '+url)
self.visited.append(url)
if url in self.toVisit:
self.toVisit.remove(url)
- return
+ return None
- for m in soup.findAll('a',href=True): # find out all href^?://*
+ for m in soup.findAll('a', href=True): # find out all href^?://*
app = ''
# Making sure that href is not a function or doesn't begin with http://
if not re.match(r'javascript:', m['href']) or re.match('http://', m['href']):
@@ -106,17 +108,17 @@ def process(self, root):
# If we get a valid link
if app!='' and re.search(root, app):
# Getting rid of Urls starting with '../../../..'
- while re.search(RID_DOUBLE,app):
+ while re.search(RID_DOUBLE, app):
p = re.compile(RID_COMPILE)
- app = p.sub('/',app)
+ app = p.sub('/', app)
# Getting rid of Urls starting with './'
p = re.compile(RID_SINGLE)
- app = p.sub('',app)
+ app = p.sub('', app)
# Add new link to the queue only if its pattern has not been added yet
uriPattern=removeIDs(app) # remove IDs
if self.notExist(uriPattern) and app != url:
- verbout(G,'Added :> ' +color.BLUE+ app) # display what we have got!
+ verbout(G, 'Added :> ' +color.BLUE+ app) # display what we have got!
self.toVisit.append(app) # add up urls to visit
self.uriPatterns.append(uriPattern)
diff --git a/modules/Debugger.py b/modules/Debugger.py
index 444f7f6..2927acc 100644
--- a/modules/Debugger.py
+++ b/modules/Debugger.py
@@ -5,7 +5,7 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -20,76 +20,117 @@ class Form_Debugger():
def prepareFormInputs(self, form):
'''
- This method parses specific form types and generates tokens based
+ This method parses form types and generates strings based
on their input types.
'''
verbout(O,'Crafting inputs as form type...')
- input = {}
+ cr_input = {}
+ totcr = []
- verbout(O,'Processing '+color.BOLD+'
max_length:
- print(color.GREEN+' [+] CSRF Token Length greater than 256 bytes. '+color.ORANGE+'Token value cannot be guessed/bruteforced...')
- print(color.ORANGE+' [+] Endpoint likely '+color.BG+' NOT VULNERABLE '+color.END+color.ORANGE+' to CSRF Attacks...')
- print(color.ORANGE+' [!] CSRF Mitigation Method: '+color.BG+' Long Anti-CSRF Tokens '+color.END)
-
- # Calculate entropy
- verbout(O, 'Proceeding to calculate '+color.GREY+'Shannon Entropy'+color.END+' of Token audited...')
- entropy = calcEntropy(value)
- verbout(GR, 'Calculating Entropy...')
- verbout(color.BLUE, ' [+] Entropy Calculated: '+color.CYAN+str(entropy))
- if entropy >= min_entropy:
- verbout(color.GREEN,' [+] Anti-CSRF Token Entropy Calculated is '+color.BY+'GREATER than 2.4'+color.END+'... ')
- print(color.ORANGE+' [+] Endpoint '+color.BG+' NOT VULNERABLE '+color.END+color.ORANGE+' to CSRF Attacks...')
- print(color.ORANGE+' [!] CSRF Mitigation Method: '+color.BG+' High Entropy Anti-CSRF Tokens '+color.END)
- else:
- verbout(color.RED,' [-] Anti-CSRF Token Entropy Calculated is '+color.BY+'LESS than 2.4'+color.END+'... ')
- print(color.ORANGE+' [-] Endpoint likely '+color.BR+' VULNERABLE '+color.END+color.ORANGE+' to CSRF Attacks inspite of CSRF Tokens...')
- print(color.ORANGE+' [!] Vulnerability Type: '+color.BR+' Low Entropy Anti-CSRF Tokens '+color.END)
+ verbout(color.RED, '\n +------------------------------+')
+ verbout(color.RED, ' | Token Strength Detection |')
+ verbout(color.RED, ' +------------------------------+\n')
+ for para in REQUEST_TOKENS:
+ # Coverting the token to a raw string, cause some special
+ # chars might fu*k with the Shannon Entropy operation.
+ value = r'%s' % para
+ verbout(color.CYAN, ' [!] Testing Anti-CSRF Token: '+color.ORANGE+'%s' % (value))
+ # Check length
+ if len(value) <= min_length:
+ print(color.RED+' [-] CSRF Token Length less than 5 bytes. '+color.ORANGE+'Token value can be guessed/bruteforced...')
+ print(color.ORANGE+' [-] Endpoint likely '+color.BR+' VULNERABLE '+color.END+color.ORANGE+' to CSRF Attacks...')
+ print(color.RED+' [!] Vulnerability Type: '+color.BR+' Very Short/No Anti-CSRF Tokens '+color.END)
+ VulnLogger(url, 'Very Short Anti-CSRF Tokens.', 'Token: '+value)
+ if len(value) >= max_length:
+ print(color.ORANGE+' [+] CSRF Token Length greater than '+color.CYAN+'256 bytes. '+color.GREEN+'Token value cannot be guessed/bruteforced...')
+ print(color.GREEN+' [+] Endpoint likely '+color.BG+' NOT VULNERABLE '+color.END+color.GREEN+' to CSRF Attacks...')
+ print(color.GREEN+' [!] CSRF Mitigation Method: '+color.BG+' Long Anti-CSRF Tokens '+color.END)
+ NovulLogger(url, 'Long Anti-CSRF tokens with Good Strength.')
+ found = 0x01
+ # Checking entropy
+ verbout(O, 'Proceeding to calculate '+color.GREY+'Shannon Entropy'+color.END+' of Token audited...')
+ entropy = calcEntropy(value)
+ verbout(GR, 'Calculating Entropy...')
+ verbout(color.BLUE, ' [+] Entropy Calculated: '+color.CYAN+str(entropy))
+ if entropy >= min_entropy:
+ verbout(color.ORANGE,' [+] Anti-CSRF Token Entropy Calculated is '+color.BY+' GREATER than 2.4 '+color.END+'... ')
+ print(color.GREEN+' [+] Endpoint '+color.BG+' PROBABLY NOT VULNERABLE '+color.END+color.GREEN+' to CSRF Attacks...')
+ print(color.GREEN+' [!] CSRF Mitigation Method: '+color.BG+' High Entropy Anti-CSRF Tokens '+color.END)
+ NovulLogger(url, 'High Entropy Anti-CSRF Tokens.')
+ found = 0x01
+ else:
+ verbout(color.RED,' [-] Anti-CSRF Token Entropy Calculated is '+color.BY+' LESS than 2.4 '+color.END+'... ')
+ print(color.RED+' [-] Endpoint likely '+color.BR+' VULNERABLE '+color.END+color.RED+' to CSRF Attacks inspite of CSRF Tokens...')
+ print(color.RED+' [!] Vulnerability Type: '+color.BR+' Low Entropy Anti-CSRF Tokens '+color.END)
+ VulnLogger(url, 'Low Entropy Anti-CSRF Tokens.', 'Token: '+value)
+ if found == 0x00:
if m_name:
print(color.RED+'\n +---------+')
print(color.RED+' | PoC |')
@@ -92,17 +107,17 @@ def Entropy(req, url, m_action, m_name=''):
# Print out the params
print(color.ORANGE+' [+] Query : '+color.GREY+urllib.parse.urlencode(result))
print('')
- return _q, para # Return the query paramter and anti-csrf token
+ return (_q, para) # Return the query paramter and anti-csrf token
def calcEntropy(data):
"""
This function is used to calculate
- Entropy.
+ Shannon Entropy.
"""
if not data:
return 0
- entropy = 0 # init
+ entropy = 0 # init
for x in range(256):
p_x = float(data.count(chr(x)))/len(data)
diff --git a/modules/Generator.py b/modules/Generator.py
new file mode 100644
index 0000000..9cb3b8c
--- /dev/null
+++ b/modules/Generator.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+#-:-:-:-:-:-:-::-:-:#
+# XSRF Probe #
+#-:-:-:-:-:-:-::-:-:#
+
+# Author: 0xInfection
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
+
+from core.colors import *
+from ast import literal_eval
+from bs4 import BeautifulSoup
+from yattag import Doc, indent
+from core.verbout import verbout
+from files.config import OUTPUT_DIR
+from core.prettify import formPrettify
+from core.prettify import indentPrettify
+
+doc, tag, text = Doc().tagtext()
+
+def GenNormalPoC(action, fields, method='POST', encoding_type='application/x-www-form-urlencoded'):
+ """
+ Generate a normal CSRF PoC using basic form data
+ """
+ print(GR, 'Generating normal PoC Form...' )
+ verbout(color.RED, '\n +---------------------+')
+ verbout(color.RED, ' | Normal Form PoC |')
+ verbout(color.RED, ' +---------------------+\n'+color.CYAN)
+ # Main starting which we will use to generate form.
+ with tag('html'):
+ with tag('title'):
+ text('CSRF PoC')
+ with tag('body'):
+ with tag('h2'):
+ text('Your CSRF PoC')
+ # Try not messing with this part. (1)
+ with tag('form', id='xsrfprobe_csrfpoc', action=action, enctype=encoding_type, method="POST"):
+ for field in literal_eval(fields):
+ with tag('label'):
+ text(field['label'].title())
+ doc.input(name=field['name'], type=field['type'], value=field['value'])
+ # Adding the Submit Button
+ doc.stag('input', value='Submit', type='submit')
+ doc.stag('br')
+ # Brand tag :p ...I guess...
+ with tag('small'):
+ text('(i) This form was generated by ')
+ with tag('a', href='https://github.com/0xinfection/xsrfprobe'):
+ text('XSRFProbe')
+ text('.')
+ content = BeautifulSoup(doc.getvalue(), 'html.parser')
+ formPrettify(indentPrettify(content))
+ print('')
+ # Write out the file af...
+ fi = open(OUTPUT_DIR+action.split('//')[1].replace('/','-')+'-csrf-poc.html', 'w+', encoding='utf8')
+ fi.write(content.prettify())
+ fi.close()
+ print(G+'PoC successfully saved under '+color.ORANGE+OUTPUT_DIR+action.split('//')[1].replace('/','-')+'-csrf-poc.html')
+
+def GenMalicious(action, fields, method='POST', encoding_type='application/x-www-form-urlencoded'):
+ """
+ Generate a malicious CSRF PoC using basic form data
+ """
+ print(GR, 'Generating malicious PoC Form...' )
+ verbout(color.RED, '\n +------------------------+')
+ verbout(color.RED, ' | Malicious Form PoC |')
+ verbout(color.RED, ' +------------------------+\n'+color.CYAN)
+ # Main starting which we will use to generate form.
+ with tag('html'):
+ with tag('title'):
+ text('CSRF PoC')
+ with tag('body'):
+ with tag('script'):
+ doc.asis('alert("You have been pwned!!!")')
+ # Try not messing with this part. (1)
+ with tag('form', id='xsrfprobe_csrfpoc', action=action, enctype=encoding_type, method="POST"):
+ for field in literal_eval(fields):
+ if not field['value']:
+ val = input(C+'Enter value for form field '+color.GREEN+field['name'].title()+' :> '+color.CYAN)
+ doc.input(name=field['name'], type='hidden', value=val)
+ # The idea behind this is to generate PoC forms not requiring any
+ # user interaction. As soon as the page loads, the form with submit automatically.
+ with tag('script'):
+ # Try not messing with this part. (2)
+ doc.asis('document.getElementById("xsrfprobe_csrfpoc").submit();')
+ # Brand tag :p ...I guess...
+ doc.asis('')
+ content = BeautifulSoup(doc.getvalue(), 'html.parser')
+ formPrettify(indentPrettify(content))
+ print('')
+ # Write out the file af...
+ fi = open(OUTPUT_DIR+action.split('//')[1].replace('/','-')+'-malicious-poc.html', 'w+', encoding='utf8')
+ fi.write(content.prettify())
+ fi.close()
+ print(G+'PoC successfully saved under '+color.ORANGE+OUTPUT_DIR+action.split('//')[1].replace('/','-')+'-malicious-poc.html')
diff --git a/modules/Origin.py b/modules/Origin.py
index 456f56e..b814599 100644
--- a/modules/Origin.py
+++ b/modules/Origin.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -15,13 +15,16 @@
from core.verbout import verbout
from core.request import Get
from core.randua import RandomAgent
+from core.logger import VulnLogger, NovulLogger
def Origin(url):
"""
Check if the remote web application verifies the Origin before
processing the HTTP request.
"""
-
+ verbout(color.RED, '\n +-------------------------------------+')
+ verbout(color.RED, ' | Origin Based Request Validation |')
+ verbout(color.RED, ' +-------------------------------------+\n')
# Make the request normally and get content
verbout(O,'Making request on normal basis...')
req0x01 = Get(url)
@@ -38,7 +41,7 @@ def Origin(url):
gen_headers['Cookie'] = cookie
# Make the request with different Origin header and get the content
- verbout(O,'Making request with tampered headers...')
+ verbout(O,'Making request with '+color.CYAN+'Tampered Origin Header'+color.END+'...')
req0x02 = Get(url, headers=gen_headers)
# Comparing the length of the requests' responses. If both content
@@ -57,11 +60,13 @@ def Origin(url):
if len(req0x01.content) != len(req0x02.content):
verbout(color.GREEN,' [+] Endoint '+color.ORANGE+'Origin Validation'+color.GREEN+' Present!')
print(color.GREEN+' [-] Heuristics reveal endpoint might be '+color.BG+' NOT VULNERABLE '+color.END+'...')
- print(color.ORANGE+' [+] Mitigation Method: '+color.BG+' Origin Based Request Validation '+color.END)
+ print(color.ORANGE+' [+] Mitigation Method: '+color.BG+' Origin Based Request Validation '+color.END+'\n')
+ NovulLogger(url, 'Presence of Origin Header based request Validation.')
return True
else:
- verbout(R,'Endpoint '+color.ORANGE+'Origin Validation Not Present'+color.END+'!')
+ verbout(R,'Endpoint '+color.RED+'Origin Validation Not Present'+color.END+'!')
verbout(R,'Heuristics reveal endpoint might be '+color.BY+' VULNERABLE '+color.END+' to Origin Based CSRFs...')
print(color.CYAN+ ' [+] Possible CSRF Vulnerability Detected : '+color.GREY+url+'!')
- print(color.ORANGE+' [!] Possible Vulnerability Type: '+color.BY+' Origin Based Request Forgery '+color.END)
+ print(color.ORANGE+' [!] Possible Vulnerability Type: '+color.BY+' No Origin Based Request Validation '+color.END+'\n')
+ VulnLogger(url, 'No Origin Header based request validation presence.', '[i] Response Headers: '+str(req0x02.headers))
return False
diff --git a/modules/Parser.py b/modules/Parser.py
index db7ac36..8262828 100644
--- a/modules/Parser.py
+++ b/modules/Parser.py
@@ -5,7 +5,7 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-#Author: @_tID
+#Author: 0xInfection
#This module requires XSRFProbe
#https://github.com/0xInfection/XSRFProbe
@@ -61,6 +61,6 @@ def buildAction(url, action):
on Current Location and Destination.
'''
verbout(O,'Parsing URL parameters...')
- if action and not re.match('#', action): # make sure it is not a fragment (eg. http://site.tld/index.php#search)
+ if action and not action.startswith('#'): # make sure it is not a fragment (eg. http://site.tld/index.php#search)
return buildUrl(url, action) # get the url and reutrn it!
return url # return the url itself if buildAction didn't identify the action
diff --git a/modules/Persistence.py b/modules/Persistence.py
index eea8b98..a1acd3d 100644
--- a/modules/Persistence.py
+++ b/modules/Persistence.py
@@ -5,7 +5,7 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -16,42 +16,113 @@
from core.verbout import verbout
from core.request import Get
from core.randua import RandomAgent
+from datetime import datetime
from core.utils import checkDuplicates
+from core.logger import VulnLogger, NovulLogger
from urllib.parse import urlencode, unquote, urlsplit
-
+# Response storing list init
resps = []
-def Persistence(url):
+def Persistence(url, postq):
+ '''
+ The main idea behind this is to check for Cookie
+ Persistence.
+ '''
+ verbout(color.RED, '\n +-----------------------------------+')
+ verbout(color.RED, ' | Cookie Persistence Validation |')
+ verbout(color.RED, ' +-----------------------------------+\n')
+ # Checking if user has supplied a value.
+ verbout(GR,'Proceeding to test for '+color.GREY+'Cookie Persistence'+color.END+'...')
+ time.sleep(0.7)
+ found = 0x00
+ # Now let the real test begin...
+ #
+ # [Step 1]: Lets examine now whether cookies set by server are persistent or not.
+ # For this we'll have to parse the cookies set by the server and check for the
+ # time when the cookie expires. Lets do it!
+ #
+ # First its time for GET type requests. Lets prepare our request.
+ cookies = []
+ verbout(C, 'Proceeding to test cookie persistence via '+color.CYAN+'Prepared GET Requests'+color.END+'...')
+ gen_headers = HEADER_VALUES
+ gen_headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36'
if COOKIE_VALUE:
- verbout(GR,'Proceeding to test for '+color.GREY+'Cookie Persistence'+color.END+'...')
- time.sleep(0.7)
+ for cookie in COOKIE_VALUE:
+ gen_headers['Cookie'] = cookie
+ verbout(GR,'Making the request...')
+ req = Get(url, headers=gen_headers)
+ if req.cookies:
+ for cook in req.cookies:
+ if cook.expires:
+ print(color.GREEN+' [+] Persistent Cookies found in Response Headers!')
+ print(color.GREY+' [+] Cookie: '+color.CYAN+cook.__str__())
+ # cookie.expires returns a timestamp value. I didn't know it. :( Spent over 2+ hours scratching my head
+ # over this, until I stumbled upon a stackoverflow answer comment. So to decode this, we'd need to
+ # convert it a human readable format.
+ print(color.GREEN+' [+] Cookie Expiry Period: '+color.ORANGE+datetime.fromtimestamp(cook.expires).__str__())
+ found = 0x01
+ VulnLogger(url, 'Persistent Session Cookies Found.', '[i] Cookie: '+req.headers.get('Set-Cookie'))
+ else:
+ NovulLogger(url, 'No Persistent Session Cookies.')
+ if found == 0x00:
+ verbout(R, 'No persistent session cookies identified on GET Type Requests!')
+ verbout(C, 'Proceeding to test cookie persistence on '+color.CYAN+'POST Requests'+color.END+'...')
+ # Now its time for POST Based requests.
+ #
+ # NOTE: As a standard method, every web application should supply a cookie upon a POST query.
+ # It might or might not be in case of GET requests.
+ if postq.cookies:
+ for cookie in postq.cookies:
+ if cookie.expires:
+ print(color.GREEN+' [+] Persistent Cookies found in Response Headers!')
+ print(color.GREY+' [+] Cookie: '+color.CYAN+cookie.__str__())
+ # So to decode this, we'd need to convert it a human readable format.
+ print(color.GREEN+' [+] Cookie Expiry Period: '+color.ORANGE+datetime.fromtimestamp(cookie.expires).__str__())
+ found = 0x01
+ VulnLogger(url, 'Persistent Session Cookies Found.', '[i] Cookie: '+req.headers.get('Set-Cookie'))
+ print(color.ORANGE+' [!] Probable Insecure Practice: '+color.BR+' Persistent Session Cookies '+color.END)
+ else:
+ NovulLogger(url, 'No Persistent Cookies.')
+ if found == 0x00:
+ verbout(R, 'No persistent session cookies identified upon POST Requests!')
+ print(color.GREEN+' [+] Endpoint might be '+color.BG+' NOT VULNERABLE '+color.END+color.GREEN+' to CSRF attacks!')
+ print(color.GREEN+' [+] Detected : '+color.BG+' No Persistent Cookies '+color.END)
+
+ # [Step 2]: The idea here is to try to identify cookie persistence on basis of observing
+ # variations in cases of using different user-agents. For this test we have chosen 5 different
+ # well used and common user-agents (as below) and then we observe the variation of set-cookie
+ # header under different conditions.
+ #
+ # We'll test this method only when we haven't identified requests based on previous algo.
+ if found != 0x01:
+ verbout(C, 'Proceeding to test cookie persistence via '+color.CYAN+'User-Agent Alteration'+color.END+'...')
user_agents = {
'Chrome on Windows 8.1' : 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36',
'Safari on iOS' : 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B466 Safari/600.1.4',
'IE6 on Windows XP' : 'Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)',
'Opera on Windows 10' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991',
'Chrome on Android' : 'Mozilla/5.0 (Linux; U; Android 2.3.1; en-us; MID Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1'
- }
+ }
verbout(GR,'Setting custom generic headers...')
gen_headers = HEADER_VALUES
for name, agent in user_agents.items():
verbout(C,'Using User-Agent : '+color.CYAN+name)
verbout(GR,'Value : '+color.ORANGE+agent)
gen_headers['User-Agent'] = agent
- for cookie in COOKIE_VALUE:
- gen_headers['Cookie'] = cookie
- verbout(GR,'Making the request...')
+ if COOKIE_VALUE:
+ for cookie in COOKIE_VALUE:
+ gen_headers['Cookie'] = cookie
req = Get(url, headers=gen_headers)
resps.append(req.headers.get('Set-Cookie'))
if checkDuplicates(resps):
- verbout(G,'Set-Cookie header does not change with varied User-Agents...')
- verbout(R,'Possible persistent session cookies found...')
+ verbout(G, 'Set-Cookie header does not change with varied User-Agents...')
+ verbout(color.GREEN, ' [+] Possible persistent session cookies found...')
print(color.RED+ ' [+] Possible CSRF Vulnerability Detected : '+color.ORANGE+url+'!')
- print(color.ORANGE+' [!] Possible Vulnerability Type: '+color.BR+' Persistent Session Cookies '+color.END)
+ print(color.ORANGE+' [!] Probable Insecure Practice: '+color.BR+' Persistent Session Cookies '+color.END)
+ VulnLogger(url, 'Persistent Session Cookies Found.', '[i] Cookie: '+req.headers.get('Set-Cookie'))
else:
verbout(G,'Set-Cookie header changes with varied User-Agents...')
verbout(R,'No possible persistent session cookies found...')
- print(color.GREEN+' [+] Endpoint might be '+color.BG+' NOT VULNERABLE '+color.END+color.GREEN+' to CSRF attacks!')
- print(color.GREEN+' [+] Protection Method Detected : '+color.BG+' No Persistent Cookies '+color.END)
- else:
- verbout(R,'Skipping persistence checks as no cookie value supplied...')
+ print(color.GREEN+' [+] Endpoint '+color.BG+' NOT VULNERABLE '+color.END+color.GREEN+' to CSRF attacks!')
+ print(color.GREEN+' [+] Application Practice Method Detected : '+color.BG+' No Persistent Cookies '+color.END)
+ NovulLogger(url, 'No Persistent Cookies.')
diff --git a/modules/Referer.py b/modules/Referer.py
index faa5a90..0c1a86a 100644
--- a/modules/Referer.py
+++ b/modules/Referer.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -14,13 +14,16 @@
from files.config import *
from core.verbout import verbout
from core.request import Get
+from core.logger import VulnLogger, NovulLogger
def Referer(url):
"""
Check if the remote web application verifies the Referer before
processing the HTTP request.
"""
-
+ verbout(color.RED, '\n +--------------------------------------+')
+ verbout(color.RED, ' | Referer Based Request Validation |')
+ verbout(color.RED, ' +--------------------------------------+\n')
# Make the request normally and get content
verbout(O,'Making request on normal basis...')
req0x01 = Get(url)
@@ -31,7 +34,6 @@ def Referer(url):
# Set a fake Referer along with UA (pretending to be a
# legitimate request from a browser)
- verbout(GR,'Setting generic headers...')
gen_headers['Referer'] = REFERER_URL
# We put the cookie in request, if cookie supplied :D
@@ -40,7 +42,7 @@ def Referer(url):
gen_headers['Cookie'] = cookie
# Make the request with different referer header and get the content
- verbout(O,'Making request with tampered headers...')
+ verbout(O,'Making request with '+color.CYAN+'Tampered Referer Header'+color.END+'...')
req0x02 = Get(url, headers=gen_headers)
# Comparing the length of the requests' responses. If both content
@@ -60,10 +62,12 @@ def Referer(url):
print(color.GREEN+' [+] Endoint '+color.ORANGE+'Referer Validation'+color.GREEN+' Present!')
print(color.GREEN+' [-] Heuristics reveal endpoint might be '+color.BG+' NOT VULNERABLE '+color.END+'...')
print(color.ORANGE+' [+] Mitigation Method: '+color.BG+' Referer Based Request Validation '+color.END)
+ NovulLogger(url, 'Presence of Referer Header based Request Validation.')
return True
else:
- verbout(R,'Endpoint '+color.ORANGE+'Referer Validation Not Present'+color.END+'!')
+ verbout(R,'Endpoint '+color.RED+'Referer Validation Not Present'+color.END+'!')
verbout(R,'Heuristics reveal endpoint might be '+color.BY+' VULNERABLE '+color.END+' to Origin Based CSRFs...')
print(color.CYAN+ ' [+] Possible CSRF Vulnerability Detected : '+color.GREY+url+'!')
- print(color.ORANGE+' [+] Possible Vulnerability Type: '+color.BY+' Referer Based Request Forgery '+color.END)
+ print(color.ORANGE+' [+] Possible Vulnerability Type: '+color.BY+' No Referer Based Request Validation '+color.END)
+ VulnLogger(url, 'No Referer Header based Request Validation presence.', '[i] Response Headers: '+str(req0x02.headers))
return False
diff --git a/modules/Tamper.py b/modules/Tamper.py
index 74a4f16..82f8104 100644
--- a/modules/Tamper.py
+++ b/modules/Tamper.py
@@ -5,7 +5,7 @@
# XSRFProbe #
#-:-:-:-:-:-:-:-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
@@ -15,7 +15,9 @@
from files.config import *
from core.verbout import verbout
from core.utils import replaceStrIndex
+from files.paramlist import TOKEN_ERRORS
from urllib.parse import urlencode, quote
+from core.logger import VulnLogger, NovulLogger
def Tamper(url, action, req, body, query, para):
'''
@@ -23,6 +25,9 @@ def Tamper(url, action, req, body, query, para):
found and check the content length for related
vulnerabilities.
'''
+ verbout(color.RED, '\n +---------------------------------------+')
+ verbout(color.RED, ' | Anti-CSRF Token Tamper Validation |')
+ verbout(color.RED, ' +---------------------------------------+\n')
# Null char flags (hex)
flagx1 = 0x00
flagx2 = 0x00
@@ -32,12 +37,13 @@ def Tamper(url, action, req, body, query, para):
if para == '':
return True
# Coverting the token to a raw string, cause some special
- # chars might fu*k with the Shannon Entropy operation.
+ # chars might fu*k with the operation.
value = r'%s' % para
+ copy = req
# Alright lets start...
- # [Step 1]: First we take the token and then replace a char
- # at a specific position and test the response body.
+ # [Step 1]: First we take the token and then replace a particular character
+ # at a specific position (here at 4th position) and test the response body.
#
# Required check for checking if string at that position isn't the
# same char we are going to replace with.
@@ -60,12 +66,16 @@ def Tamper(url, action, req, body, query, para):
# request, then we have the vulnerability.
#
# NOTE: This algorithm has lots of room for improvement.
- if str(resp.status_code).startswith('50'):
- verbout(color.RED,' [+] Token tamper from request causes a 50x Internal Error!')
- if (str(resp.status_code).startswith('2') and str(resp.status_code).startswith('3')) and (len(body) == len(resp.text)):
+ if ((str(resp.status_code).startswith('2') and not any(search(s, resp.text, I) for s in TOKEN_ERRORS))
+ or (len(body) == len(resp.text))):
+ verbout(color.RED,' [-] Anti-CSRF Token tamper by index replacement returns valid response!')
flagx1 = 0x01
+ VulnLogger(url, 'Anti-CSRF Token tamper by index replacement returns valid response.', '[i] POST Query: '+req.__str__())
+ else:
+ verbout(color.RED,' [+] Token tamper in request does not return valid response!')
+ NovulLogger(url, 'Anti-CSRF Token tamper by index replacement does not return valid response.')
- # [Step 2]: Second we take the token and then remove a char
+ # [Step 2]: Second we take the token and then remove a character
# at a specific position and test the response body.
verbout(GR, 'Tampering Token by '+color.GREY+'index removal'+color.END+'...')
tampvalx2 = replaceStrIndex(value, 3)
@@ -80,36 +90,49 @@ def Tamper(url, action, req, body, query, para):
# (Accepted) or a 30x (Redirection), then we know it worked.
#
# NOTE: This algorithm has lots of room for improvement.
- if str(resp.status_code).startswith('50'):
- verbout(color.RED,' [+] Token tamper from request causes a 50x Internal Error!')
- if (str(resp.status_code).startswith('2') and str(resp.status_code).startswith('3')) and (len(body) == len(resp.text)):
+ if ((str(resp.status_code).startswith('2') and not any(search(s, resp.text, I) for s in TOKEN_ERRORS))
+ or (len(body) == len(resp.text))):
+ verbout(color.RED,' [-] Anti-CSRF Token tamper by index removal returns valid response!')
flagx2 = 0x01
+ VulnLogger(url, 'Anti-CSRF Token tamper by index removal returns valid response.', '[i] POST Query: '+req.__str__())
+ else:
+ verbout(color.RED,' [+] Token tamper in request does not return valid response!')
+ NovulLogger(url, 'Anti-CSRF Token tamper by index removal does not return valid response.')
# [Step 3]: Third we take the token and then remove the whole
# anticsrf token and test the response body.
verbout(GR, 'Tampering Token by '+color.GREY+'Token removal'+color.END+'...')
+ # Removing the anti-csrf token from request
del req[query]
- verbout(G, 'Removed token from request!')
+ verbout(color.GREY, ' [+] Removed token parameter from request!')
# Lets build up the request...
resp = Post(url, action, req)
# If there is a 40x (Not Found) or a 50x (Internal Error) error,
- # we assume that the tamper did not work :( But if there is a 20x
+ # we assume that the tamper did not work :(. But if there is a 20x
# (Accepted) or a 30x (Redirection), then we know it worked.
#
# NOTE: This algorithm has lots of room for improvement.
- if str(resp.status_code).startswith('50'):
- verbout(color.RED,' [+] Token removal from request causes a 50x Internal Error!')
- if (str(resp.status_code).startswith('2') and str(resp.status_code).startswith('3')) and (len(body) == len(resp.text)):
+ if ((str(resp.status_code).startswith('2') and not any(search(s, resp.text, I) for s in TOKEN_ERRORS))
+ or (len(body) == len(resp.text))):
+ verbout(color.RED,' [-] Anti-CSRF Token removal returns valid response!')
flagx3 = 0x01
+ VulnLogger(url, 'Anti-CSRF Token removal returns valid response.', '[i] POST Query: '+req.__str__())
+ else:
+ verbout(color.RED,' [+] Token tamper in request does not return valid response!')
+ NovulLogger(url, 'Anti-CSRF Token removal does not return valid response.')
# If any of the forgeries worked...
- if flagx1 == 0x01 or flagx2 == 0x01 or flagx3 == 0x01:
- verbout(color.GREEN,' [+] The tampered token value works!')
- verbout(color.GREEN,' [-] The Tampered Anti-CSRF Token requested does NOT return a 40x or 50x response! ')
- print(color.ORANGE+' [-] Endpoint '+color.BR+' CONFIRMED VULNERABLE '+color.END+color.ORANGE+' to Request Forgery Attacks...')
- print(color.ORANGE+' [!] Vulnerability Type: '+color.BG+' Non-Unique Anti-CSRF Tokens in Requests '+color.END)
+ if (flagx1 or flagx2 or flagx3) == 0x01:
+ verbout(color.RED,' [+] The tampered token value works! Endpoint '+color.BR+' VULNERABLE to Replay Attacks '+color.END+'!')
+ verbout(color.ORANGE,' [-] The Tampered Anti-CSRF Token requested does NOT return a 40x or 50x response! ')
+ print(color.RED+' [-] Endpoint '+color.BR+' CONFIRMED VULNERABLE '+color.END+color.RED+' to Request Forgery Attacks...')
+ print(color.ORANGE+' [!] Vulnerability Type: '+color.BR+' Non-Unique Anti-CSRF Tokens in Requests '+color.END+'\n')
+ VulnLogger(url, 'Anti-CSRF Tokens are not Unique. Token Reuse detected.', '[i] Request: '+str(copy))
+ return True
else:
print(color.RED+' [-] The Tampered Anti-CSRF Token requested returns a 40x or 50x response... ')
- print(color.ORANGE+' [-] Endpoint '+color.BG+' NOT VULNERABLE '+color.END+color.ORANGE+' to CSRF Attacks...')
- print(color.ORANGE+' [!] CSRF Mitigation Method: '+color.BG+' Unique Anti-CSRF Tokens '+color.END)
+ print(color.GREEN+' [-] Endpoint '+color.BG+' NOT VULNERABLE '+color.END+color.ORANGE+' to CSRF Attacks...')
+ print(color.ORANGE+' [!] CSRF Mitigation Method: '+color.BG+' Unique Anti-CSRF Tokens '+color.END+'\n')
+ NovulLogger(url, 'Unique Anti-CSRF Tokens. No token reuse.')
+ return False
diff --git a/modules/Token.py b/modules/Token.py
index 3e25d90..5f4c344 100644
--- a/modules/Token.py
+++ b/modules/Token.py
@@ -5,28 +5,32 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
+from files import config
from re import search, I
from time import sleep
-from files.config import *
from core.colors import *
from core.verbout import verbout
from files.discovered import REQUEST_TOKENS
from urllib.parse import urlencode, unquote
-from files.paramlist import COMMON_CSRF_NAMES
+from files.paramlist import COMMON_CSRF_NAMES, COMMON_CSRF_HEADERS
-def Token(req):
+def Token(req, headers):
'''
This method checks for whether Anti-CSRF Tokens are
present in the request.
'''
+ verbout(color.RED, '\n +---------------------------+')
+ verbout(color.RED, ' | Anti-CSRF Token Check |')
+ verbout(color.RED, ' +---------------------------+\n')
param = '' # Initializing param
query = ''
+ found = False
# First lets have a look at config.py and see if its set
- if TOKEN_CHECKS:
+ if config.TOKEN_CHECKS:
verbout(O,'Parsing request for detecting anti-csrf tokens...')
try:
# Lets check for the request values. But before that lets encode and unquote the request :D
@@ -34,19 +38,32 @@ def Token(req):
for c in con:
for name in COMMON_CSRF_NAMES: # Iterate over the list
qu = c.split('=')
- if name.lower() in qu[0].lower(): # Search if the token is there in request...
- verbout(color.GREEN,' [+] The form was requested with a '+color.ORANGE+'Anti-CSRF Token'+color.GREEN+'...')
- verbout(color.GREY,' [+] Token Parameter: '+color.CYAN+qu[0]+'='+qu[1]+' ...')
+ # Search if the token is there in request...
+ if name.lower() in qu[0].lower():
+ verbout(color.GREEN, ' [+] The form was requested with an '+color.BG+' Anti-CSRF Token '+color.END+color.GREEN+'!')
+ verbout(color.GREY, ' [+] Token Parameter: '+color.CYAN+qu[0]+'='+color.ORANGE+qu[1])
query, param = qu[0], qu[1]
- REQUEST_TOKENS.append(param) # We are appending the token to a variable for further analysis
+ # We are appending the token to a variable for further analysis
+ REQUEST_TOKENS.append(param)
+ found = True
break # Break execution if a Anti-CSRF token is found
-
+ # If we haven't found the Anti-CSRF token in query, we'll search for it in headers :)
+ if not found:
+ for key, value in headers.items():
+ for name in COMMON_CSRF_HEADERS: # Iterate over the list
+ # Search if the token is there in request...
+ if name.lower() in key.lower():
+ verbout(color.GREEN, ' [+] The form was requested with an '+color.BG+' Anti-CSRF Token Header '+color.END+color.GREEN+'!')
+ verbout(color.GREY, ' [+] Token Parameter: '+color.CYAN+qu[0]+'='+color.ORANGE+qu[1])
+ query, param = key, value
+ # We are appending the token to a variable for further analysis
+ REQUEST_TOKENS.append(param)
+ break # Break execution if a Anti-CSRF token is found
except Exception as e:
- verbout(R,'Request Parsing Execption!')
- verbout(R,'Error: '+e.__str__())
-
+ verbout(R, 'Request Parsing Exception!')
+ verbout(R, 'Error: '+e.__str__())
if param:
- return query, param
- verbout(color.RED,' [-] The form was requested '+color.BR+' Without an Anti-CSRF Token '+color.END+color.RED+'...')
+ return (query, param)
+ verbout(color.ORANGE,' [-] The form was requested '+color.RED+' Without an Anti-CSRF Token '+color.END+color.ORANGE+'...')
print(color.RED+' [-] Endpoint seems '+color.BR+' VULNERABLE '+color.END+color.RED+' to '+color.BR+' POST-Based Request Forgery '+color.END)
- return '', ''
+ return (None, None)
diff --git a/modules/__init__.py b/modules/__init__.py
index 793ff4b..310ac79 100644
--- a/modules/__init__.py
+++ b/modules/__init__.py
@@ -5,7 +5,7 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
#This module requires XSRFProbe
#https://github.com/0xInfection/XSRFProbe
diff --git a/output/example.com/README b/output/example.com/README
new file mode 100644
index 0000000..b0833df
--- /dev/null
+++ b/output/example.com/README
@@ -0,0 +1,3 @@
+This is just a sample folder where all stuffs and output will be stored.
+
+DO NOT DELETE THIS FOLDER!!!
diff --git a/output/example.com/errored.log b/output/example.com/errored.log
new file mode 100644
index 0000000..3afa5f6
--- /dev/null
+++ b/output/example.com/errored.log
@@ -0,0 +1,2 @@
+(i) http://example.com/csrf -> No standard form "action".
+
diff --git a/output/example.com/example.com-csrf-poc.html b/output/example.com/example.com-csrf-poc.html
new file mode 100644
index 0000000..0ffbfac
--- /dev/null
+++ b/output/example.com/example.com-csrf-poc.html
@@ -0,0 +1,25 @@
+
+
+ CSRF PoC
+
+
+
+ Your CSRF PoC
+
+
+
+
+ (i) This form was generated by
+
+ XSRFProbe
+
+ .
+
+
+
diff --git a/output/example.com/forms-tested.log b/output/example.com/forms-tested.log
new file mode 100644
index 0000000..d774dd2
--- /dev/null
+++ b/output/example.com/forms-tested.log
@@ -0,0 +1,7 @@
+(i) http://example.com/csrf:
+
+
+
diff --git a/output/example.com/strengths.log b/output/example.com/strengths.log
new file mode 100644
index 0000000..1418084
--- /dev/null
+++ b/output/example.com/strengths.log
@@ -0,0 +1,2 @@
+[+] http://example.com/csrf -> No Persistent Session Cookies.
+[+] http://example.com/csrf -> Anti-CSRF token is not a string encoded value.
diff --git a/output/example.com/vulnerabilities.log b/output/example.com/vulnerabilities.log
new file mode 100644
index 0000000..ade88b3
--- /dev/null
+++ b/output/example.com/vulnerabilities.log
@@ -0,0 +1,34 @@
+[!] http://example.com/csrf -> No Referer Header based Request Validation presence.
+
+[i] Response Headers: {'Date': 'Fri, 28 Dec 2018 07:07:43 GMT', 'Set-Cookie': 'TEST_SESSIONID=jhf8avl5; path=/, NB_SRVID=srv140700; path=/', 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'Cache-Control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'Pragma': 'no-cache', 'Vary': 'Accept-Encoding', 'Content-Encoding': 'gzip', 'Content-Length': '663', 'Connection': 'close', 'Content-Type': 'text/html'}
+
+
+[!] http://example.com/csrf -> No Origin Header based request validation presence.
+
+[i] Response Headers: {'Date': 'Fri, 28 Dec 2018 07:07:45 GMT', 'Set-Cookie': 'TEST_SESSIONID=1cgnr23oqil463c6; path=/, NB_SRVID=srv140717; path=/', 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'Cache-Control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'Pragma': 'no-cache', 'Vary': 'Accept-Encoding', 'Content-Encoding': 'gzip', 'Content-Length': '663', 'Connection': 'close', 'Content-Type': 'text/html'}
+
+
+[!] http://example.com/csrf -> No Cookie Validation on Cross-Origin Requests.
+
+[i] Headers: {'Date': 'Fri, 28 Dec 2018 07:07:50 GMT','Set-Cookie': 'TEST_SESSIONID=3f9054b0g2; path=/, NB_SRVID=srv140717; path=/', 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'Cache-Control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'Pragma': 'no-cache', 'Vary': 'Accept-Encoding', 'Content-Encoding': 'gzip', 'Content-Length': '663', 'Connection': 'close', 'Content-Type': 'text/html'}
+
+
+[!] http://example.com/csrf -> Form Requested Without Anti-CSRF Token.
+
+[i] Form Requested:
+
+[i] Request Query: {'csrf_it': 'PBgCWG'}
+
+
+[!] http://example.com/csrf -> POST-Based Request Forgery on Forms.
+
+[i] Form:
+
+[i] POST Query: {'csrf_it': 'PBgCWG'}
+
diff --git a/requirements.txt b/requirements.txt
index 4592b91..73bc67f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,6 @@
requests
bs4
-lxml
request
stringdist
+tld
+yattag
diff --git a/xsrfprobe.py b/xsrfprobe.py
index 939e5c5..092d4a1 100644
--- a/xsrfprobe.py
+++ b/xsrfprobe.py
@@ -5,9 +5,9 @@
# XSRF Probe #
#-:-:-:-:-:-:-::-:-:#
-# Author: @_tID
+# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-from core import main # import all stuff
-main.Engine() # the true start of the program ;)
+from core import main # import stuff
+main.Engine() # start the Scanner Engine ;)