diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..a404981
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,5 @@
+bs4
+requests
+stringdist
+tld
+yattag
diff --git a/setup.py b/setup.py
index 43bfce3..f64ab0e 100644
--- a/setup.py
+++ b/setup.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -12,40 +12,36 @@
import io
from setuptools import setup, find_packages
from os import path
+
this_directory = path.abspath(path.dirname(__file__))
-with io.open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
+with io.open(path.join(this_directory, "README.md"), encoding="utf-8") as f:
desc = f.read()
setup(
- name='xsrfprobe',
- version=__import__('xsrfprobe').__version__,
- description='The Prime Cross Site Request Forgery (CSRF) Audit & Exploitation Toolkit',
+ name="xsrfprobe",
+ version=__import__("xsrfprobe").__version__,
+ description="The Prime Cross Site Request Forgery (CSRF) Audit & Exploitation Toolkit",
long_description=desc,
- long_description_content_type='text/markdown',
- author='Pinaki Mondal',
- author_email='theinfecteddrake@gmail.com',
- license='GPLv3',
- url='https://github.com/0xInfection/XSRFProbe',
- download_url='https://github.com/0xInfection/XSRFProbe/archive/v%s.zip' % __import__('xsrfprobe').__version__,
+ long_description_content_type="text/markdown",
+ author="Pinaki Mondal",
+ author_email="theinfecteddrake@gmail.com",
+ license="GPLv3",
+ url="https://github.com/0xInfection/XSRFProbe",
+ download_url="https://github.com/0xInfection/XSRFProbe/archive/v%s.zip"
+ % __import__("xsrfprobe").__version__,
packages=find_packages(),
- scripts=['xsrfprobe/bin/xsrfprobe'],
- install_requires=[
- 'requests',
- 'bs4',
- 'stringdist',
- 'tld',
- 'yattag'
- ],
+ scripts=["xsrfprobe/bin/xsrfprobe"],
+ install_requires=["requests", "bs4", "stringdist", "tld", "yattag"],
classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Intended Audience :: System Administrators',
- 'Intended Audience :: Developers',
- 'Intended Audience :: Information Technology',
- 'Operating System :: OS Independent',
- 'Topic :: Internet',
- 'Topic :: Security',
- 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
- 'Programming Language :: Python :: 3',
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: System Administrators",
+ "Intended Audience :: Developers",
+ "Intended Audience :: Information Technology",
+ "Operating System :: OS Independent",
+ "Topic :: Internet",
+ "Topic :: Security",
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+ "Programming Language :: Python :: 3",
],
- keywords=['csrf', 'xsrf', 'appsec', 'vulnerability scanner', 'webapps', 'hacking'],
+ keywords=["csrf", "xsrf", "appsec", "vulnerability scanner", "webapps", "hacking"],
)
diff --git a/xsrfprobe/__init__.py b/xsrfprobe/__init__.py
index 2dc3802..18f4355 100644
--- a/xsrfprobe/__init__.py
+++ b/xsrfprobe/__init__.py
@@ -1,13 +1,13 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-__version__ = '2.3.1'
-__license__ = 'GNU General Public License v3 (GPLv3)'
+__version__ = "4.4.29"
+__license__ = "GNU General Public License v3 (GPLv3)"
diff --git a/xsrfprobe/bin/xsrfprobe b/xsrfprobe/bin/xsrfprobe
old mode 100644
new mode 100755
index fdb603f..3fa41c4
--- a/xsrfprobe/bin/xsrfprobe
+++ b/xsrfprobe/bin/xsrfprobe
@@ -9,7 +9,15 @@
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
+import os
+import sys
+
+# Allow loading of the module from the path where we are when you
+# didn't install it using pip
+SCRIPT_DIR = os.path.realpath(os.path.dirname("."))
+sys.path.append(SCRIPT_DIR)
+
from xsrfprobe import xsrfprobe
if __name__ == '__main__':
- xsrfprobe.startEngine()
\ No newline at end of file
+ xsrfprobe.startEngine()
diff --git a/xsrfprobe/core/__init__.py b/xsrfprobe/core/__init__.py
index 34048e2..0645695 100644
--- a/xsrfprobe/core/__init__.py
+++ b/xsrfprobe/core/__init__.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
-#This module requires XSRFProbe
-#https://github.com/0xInfection/XSRFProbe
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
diff --git a/xsrfprobe/core/banner.py b/xsrfprobe/core/banner.py
index 202501e..911d6b6 100644
--- a/xsrfprobe/core/banner.py
+++ b/xsrfprobe/core/banner.py
@@ -1,48 +1,221 @@
#!/usr/bin/env python3
-#coding: utf-8
+# coding: utf-8
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
-#Author: 0xInfection
-#This module requires XSRF-Probe
-#https://github.com/0xInfection/XSRF-Probe
+# Author: 0xInfection
+# This module requires XSRF-Probe
+# https://github.com/0xInfection/XSRF-Probe
# Just for some fancy benner to appear at beginning
import time
from xsrfprobe import __version__
-from xsrfprobe.core.colors import *
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+SLEEP_TIME = 0
+
def banner():
+ """Display the program banner"""
+ print("\n\n")
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.ORANGE
+ + " _____ _____ _____ _____ _____ "
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.RED
+ + " __"
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "__ "
+ + colors.ORANGE
+ + " |_ "
+ + colors.RED
+ + "__"
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "___ "
+ + colors.ORANGE
+ + " |_ "
+ + colors.RED
+ + "__"
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "___ "
+ + colors.ORANGE
+ + "|_ "
+ + colors.RED
+ + "_"
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "____ "
+ + colors.ORANGE
+ + "|_"
+ + colors.RED
+ + " _"
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "____ "
+ + colors.ORANGE
+ + "|_ "
+ + colors.RED
+ + " _____ _____ ______ ______ "
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.RED
+ + " \ ` / "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "| ___| "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "| _ _| "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "| ___| "
+ + colors.ORANGE
+ + "| "
+ + colors.RED
+ + "| _ | "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "| _ ,' / \| _ )| ___| "
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.RED
+ + " > < "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + " `-.`-. "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "| \ "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "| ___| "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + " | __| "
+ + colors.ORANGE
+ + "|"
+ + colors.RED
+ + "| \ | - || |_ { | ___| "
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.RED
+ + " /__/__\ "
+ + colors.ORANGE
+ + "_|"
+ + colors.RED
+ + "|______| "
+ + colors.ORANGE
+ + "_|"
+ + colors.RED
+ + "|__|\__\ "
+ + colors.ORANGE
+ + " _|"
+ + colors.RED
+ + "|___| "
+ + colors.ORANGE
+ + " _|"
+ + colors.RED
+ + " |___| "
+ + colors.ORANGE
+ + " _|"
+ + colors.RED
+ + "|__|\__\\\_____/|______)|______| "
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.ORANGE
+ + " |_____| |_____| |_____| |_____| |_____| \n\n"
+ )
+ time.sleep(SLEEP_TIME)
+
- print('\n\n')
- time.sleep(0.05)
- print(color.ORANGE+' _____ _____ _____ _____ _____ ')
- time.sleep(0.05)
- print(color.RED+' __'+color.ORANGE+'|'+color.RED+'__ '+color.ORANGE+' |_ '+color.RED+'__'+color.ORANGE+'|'+color.RED+'___ '+color.ORANGE+' |_ '+color.RED+'__'+color.ORANGE+'|'+color.RED+'___ '+color.ORANGE+'|_ '+color.RED+'_'+color.ORANGE+'|'+color.RED+'____ '+color.ORANGE+'|_'+color.RED+' _'+color.ORANGE+'|'+color.RED+'____ '+color.ORANGE+'|_ '+color.RED+' _____ _____ ______ ______ ')
- time.sleep(0.05)
- print(color.RED+" \ ` / "+color.ORANGE+'|'+color.RED+'| ___| '+color.ORANGE+'|'+color.RED+'| _ _| '+color.ORANGE+'|'+color.RED+'| ___| '+color.ORANGE+'| '+color.RED+'| _ | '+color.ORANGE+"|"+color.RED+"| _ ,' / \| _ )| ___| ")
- time.sleep(0.05)
- print(color.RED+' > < '+color.ORANGE+'|'+color.RED+' `-.`-. '+color.ORANGE+'|'+color.RED+'| \ '+color.ORANGE+'|'+color.RED+'| ___| '+color.ORANGE+'|'+color.RED+' | __| '+color.ORANGE+'|'+color.RED+'| \ | - || |_ { | ___| ')
- time.sleep(0.05)
- print(color.RED+' /__/__\ '+color.ORANGE+'_|'+color.RED+'|______| '+color.ORANGE+'_|'+color.RED+'|__|\__\ '+color.ORANGE+' _|'+color.RED+'|___| '+color.ORANGE+' _|'+color.RED+' |___| '+color.ORANGE+' _|'+color.RED+'|__|\__\\\_____/|______)|______| ')
- time.sleep(0.05)
- print(color.ORANGE+' |_____| |_____| |_____| |_____| |_____| \n\n')
- time.sleep(0.05)
-
-def banabout(): # some fancy banner stuff :p
-
- print(color.BLUE+' [---] '+color.GREY+'XSRFProbe,'+color.RED+' A'+color.ORANGE+' Cross Site Request Forgery '+color.RED+'Audit Toolkit '+color.BLUE+'[---]')
- time.sleep(0.05)
- print(color.BLUE+' [---] [---]')
- time.sleep(0.05)
- print(color.BLUE+' [---] '+color.PURPLE+' '+color.GREEN+'~ Author : '+color.CYAN+'Pinaki Mondal ~ '+color.BLUE+' [---]')
- time.sleep(0.05)
- print(color.BLUE+' [---] '+color.CYAN+' ~ github.com / '+color.GREY+'0xInfection ~ '+color.BLUE+' [---]')
- time.sleep(0.05)
- print(color.BLUE+' [---] [---]')
- time.sleep(0.05)
- print(color.BLUE+' [---] '+color.ORANGE+' ~ Version '+color.RED+__version__+color.ORANGE+' ~ '+color.BLUE+' [---]\n')
- time.sleep(0.05)
+def banabout(): # some fancy banner stuff :p
+ print(
+ colors.BLUE
+ + " [---] "
+ + colors.GREY
+ + "XSRFProbe,"
+ + colors.RED
+ + " A"
+ + colors.ORANGE
+ + " Cross Site Request Forgery "
+ + colors.RED
+ + "Audit Toolkit "
+ + colors.BLUE
+ + "[---]"
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.BLUE
+ + " [---] [---]"
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.BLUE
+ + " [---] "
+ + colors.PURPLE
+ + " "
+ + colors.GREEN
+ + "~ Author : "
+ + colors.CYAN
+ + "Pinaki Mondal ~ "
+ + colors.BLUE
+ + " [---]"
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.BLUE
+ + " [---] "
+ + colors.CYAN
+ + " ~ github.com / "
+ + colors.GREY
+ + "0xInfection ~ "
+ + colors.BLUE
+ + " [---]"
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.BLUE
+ + " [---] [---]"
+ )
+ time.sleep(SLEEP_TIME)
+ print(
+ colors.BLUE
+ + " [---] "
+ + colors.ORANGE
+ + " ~ Version "
+ + colors.RED
+ + __version__
+ + colors.ORANGE
+ + " ~ "
+ + colors.BLUE
+ + " [---]\n"
+ )
+ time.sleep(SLEEP_TIME)
diff --git a/xsrfprobe/core/colors.py b/xsrfprobe/core/colors.py
index b82c3ea..0afb4ab 100644
--- a/xsrfprobe/core/colors.py
+++ b/xsrfprobe/core/colors.py
@@ -1,44 +1,53 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
import os
-if os.name != 'nt' and os.name != 'mac':
- class color:
- END = '\033[0m' # normal
- BOLD = '\033[1m' # bold
- RED = '\033[1;91m' # red
- GREEN = '\033[1;92m' # green
- ORANGE = '\033[1;93m' # orange
- BLUE = '\033[1;94m' # blue
- PURPLE = '\033[1;95m' # purple
- UNDERLINE = '\033[4m' # underline
- CYAN = '\033[1;96m' # cyan
- GREY = '\033[1;97m' # gray
- BR = '\033[1;97;41m' # background red
- BG = '\033[1;97;42m' # background green
- BY = '\033[1;97;43m' # background yellow
-
- O = '\033[1m \033[93m[!]\033[0m ' # information
- R = '\033[1m \033[91m[-]\033[0m ' # something's not right
- GR = '\033[1m \033[97m[*]\033[0m ' # processing
- G = '\033[1m \033[92m[+]\033[0m ' # yay!
- C = '\033[1m \033[96m[+]\033[0m ' # crawling...
-
-else:
- class color:
- # no escape sequences
- END=BOLD=RED=GREEN=ORANGE=BLUE=PURPLE=UNDERLINE=CYAN=GREY=BR=BG=BY=''
+
+
+class color:
+ END = (
+ BOLD
+ ) = (
+ RED
+ ) = GREEN = ORANGE = BLUE = PURPLE = UNDERLINE = CYAN = GREY = BR = BG = BY = ""
+
# no color values
- O = ' [!] '
- R = ' [-] '
- GR = ' [*] '
- G = ' [+] '
- C = ' [+] '
+ O = " [!] "
+ R = " [-] "
+ GR = " [*] "
+ G = " [+] "
+ C = " [+] "
+
+ def __init__(self) -> None:
+ # Put it here so that it is called after options is called
+ from xsrfprobe.files.config import NO_COLORS
+
+ if os.name == "nt" or os.name == "mac" or NO_COLORS:
+ return
+
+ self.END = "\033[0m" # normal
+ self.BOLD = "\033[1m" # bold
+ self.RED = "\033[1;91m" # red
+ self.GREEN = "\033[1;92m" # green
+ self.ORANGE = "\033[1;93m" # orange
+ self.BLUE = "\033[1;94m" # blue
+ self.PURPLE = "\033[1;95m" # purple
+ self.UNDERLINE = "\033[4m" # underline
+ self.CYAN = "\033[1;96m" # cyan
+ self.GREY = "\033[1;97m" # gray
+ self.BR = "\033[1;97;41m" # background red
+ self.BG = "\033[1;97;42m" # background green
+ self.BY = "\033[1;97;43m" # background yellow
+ self.O = "\033[1m \033[93m[!]\033[0m " # information
+ self.R = "\033[1m \033[91m[-]\033[0m " # something's not right
+ self.GR = "\033[1m \033[97m[*]\033[0m " # processing
+ self.G = "\033[1m \033[92m[+]\033[0m " # yay!
+ self.C = "\033[1m \033[96m[+]\033[0m " # crawling...
diff --git a/xsrfprobe/core/forms.py b/xsrfprobe/core/forms.py
index 57ed6b4..ac1d553 100644
--- a/xsrfprobe/core/forms.py
+++ b/xsrfprobe/core/forms.py
@@ -1,60 +1,64 @@
#!/usr/bin/env python3
# coding: utf-8
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-def testFormx1(): # an example xsrfprobe-test-form to make sure the stuff works properly ;)
+def testFormx1(): # an example xsrfprobe-test-form to make sure the stuff works properly ;)
test_form_0x01 = """
-
"""
+
+"""
return test_form_0x01
-def testFormx2(): # an example of a xsrfprobe-test-form (used drupal)
+def testFormx2(): # an example of a xsrfprobe-test-form (used drupal)
test_form_0x02 = """
-
+"""
return test_form_0x02
diff --git a/xsrfprobe/core/inputin.py b/xsrfprobe/core/inputin.py
index 88b46fd..c57e33f 100644
--- a/xsrfprobe/core/inputin.py
+++ b/xsrfprobe/core/inputin.py
@@ -1,63 +1,84 @@
#!/usr/bin/env python3
-#coding: utf-8
+# coding: utf-8
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
-#Author: 0xInfection (@_tID)
-#This module requires XSRFProbe
-#https://github.com/0xInfection/XSRFProbe
+# Author: 0xInfection (@_tID)
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
-import requests, re
from urllib.parse import urlparse
-from xsrfprobe.core.colors import *
+import re
+
+import requests
+
+from xsrfprobe.files.config import TIMEOUT_VALUE
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
from xsrfprobe.core.verbout import verbout
from xsrfprobe.files.dcodelist import IP
from xsrfprobe.core.logger import ErrorLogger
-from xsrfprobe.files.config import (
- SITE_URL,
- CRAWL_SITE,
- VERIFY_CERT
-)
+from xsrfprobe.files.config import SITE_URL, CRAWL_SITE, VERIFY_CERT
+
def inputin():
- '''
+ """
This module actually parses the url passed by the user.
- '''
+ """
+ web = ""
if SITE_URL:
web = SITE_URL # If already assigned
- if not web.endswith('/'):
- web = web + '/'
- if 'http' not in web: # add protocol to site
- web = 'http://' + web
+
+ if not web.endswith("/"):
+ web = web + "/"
+
+ if "http" not in web: # add protocol to site
+ web = "http://" + web
+
try:
web0 = urlparse(web).netloc
except Exception:
web0 = re.search(IP, web).group(0)
+
try:
- print(O+'Testing site '+color.CYAN+web0+color.END+' status...')
- requests.get(web) # test whether site is up or not
- print(color.GREEN+' [+] Site seems to be up!'+color.END)
+ print(
+ colors.O + "Testing site " + colors.CYAN + web0 + colors.END + " status..."
+ )
+ requests.get(web, timeout=TIMEOUT_VALUE) # test whether site is up or not
+ print(colors.GREEN + " [+] Site seems to be up!" + colors.END)
except requests.exceptions.RequestException: # if site is down
- print(R+'Site seems to be down...')
+ print(colors.R + "Site seems to be down...")
quit()
+
# We'll test for endpoint only when the --crawl isn't supplied.
if not CRAWL_SITE:
try:
- print(O+'Testing '+color.CYAN+web.split('//')[1].split('/', 1)[1]+color.END+' endpoint status...')
- requests.get(web, verify=VERIFY_CERT)
- print(color.GREEN+' [+] Endpoint seems to be up!'+color.END)
+ end_point = web.split("//")[1].split("/", 1)[1]
+ if end_point == "":
+ end_point = "/"
+
+ print(
+ f"{colors.O}Testing {colors.CYAN}{end_point}{colors.END} endpoint status..."
+ )
+ requests.get(web, timeout=TIMEOUT_VALUE, verify=VERIFY_CERT)
+ print(f" {colors.GREEN}[+] Endpoint seems to be up!{colors.END}")
except requests.exceptions.RequestException as e:
- verbout(R, 'Endpoint error: '+web.split('//')[1].split('/', 1)[1])
+ verbout(colors.R, "Endpoint error: " + end_point)
ErrorLogger(web0, e.__str__())
quit()
except Exception as e:
- verbout(R, "Exception Caught: "+e.__str__())
+ verbout(colors.R, "Exception Caught: " + e.__str__())
ErrorLogger(web0, e.__str__())
quit()
- if not web0.endswith('/'):
- web0 = web0 + '/'
- if web.split('//')[1] == web0:
- return web, ''
+
+ if not web0.endswith("/"):
+ web0 = web0 + "/"
+
+ if web.split("//")[1] == web0:
+ return web, ""
+
return (web, web0)
diff --git a/xsrfprobe/core/logger.py b/xsrfprobe/core/logger.py
index ad33bda..a480054 100644
--- a/xsrfprobe/core/logger.py
+++ b/xsrfprobe/core/logger.py
@@ -1,81 +1,130 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-import os
-from xsrfprobe.core.colors import *
-from xsrfprobe.files.config import *
+import json
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+from xsrfprobe.files.config import OUTPUT_DIR, JSON_OUTPUT
from xsrfprobe.core.verbout import verbout
-from xsrfprobe.files.discovered import INTERNAL_URLS, FILES_EXEC, SCAN_ERRORS
-from xsrfprobe.files.discovered import VULN_LIST, FORMS_TESTED, REQUEST_TOKENS, STRENGTH_LIST
+from xsrfprobe.files.discovered import (
+ INTERNAL_URLS,
+ FILES_EXEC,
+ SCAN_ERRORS,
+ 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 + '.log'
- with open(output_file, 'w+', encoding='utf8') as f:
- if type(content) is tuple or type(content) is list:
+ """
+ output_file = f"{OUTPUT_DIR}{filename}.log"
+ with open(output_file, "w+", encoding="utf8") as f:
+ if isinstance(content, tuple) or isinstance(content, list):
for m in content: # if it is list or tuple, it is iterable
- f.write(m+'\n')
+ f.write(m + "\n")
else:
f.write(content) # else we write out as it is... ;)
- f.write('\n')
+ f.write("\n")
+
def preqheaders(tup):
- '''
+ """
This module prints out the headers as received in the
requests normally.
- '''
- verbout(GR, 'Receiving headers...\n')
- verbout(color.GREY,' '+color.UNDERLINE+'REQUEST HEADERS'+color.END+color.GREY+':'+'\n')
+ """
+ verbout(colors.GR, "Receiving headers...\n")
+ verbout(
+ colors.GREY,
+ f" {colors.UNDERLINE}REQUEST HEADERS{colors.END}{colors.GREY}:\n",
+ )
for key, val in tup.items():
- verbout(' ',color.CYAN+key+': '+color.ORANGE+val)
- verbout('','')
+ verbout(" ", f"{colors.CYAN}{key}: {colors.ORANGE}{val}")
+
+ verbout("", "")
+
def presheaders(tup):
- '''
+ """
This module prints out the headers as received in the
requests normally.
- '''
- verbout(GR, 'Receiving headers...\n')
- verbout(color.GREY,' '+color.UNDERLINE+'RESPONSE HEADERS'+color.END+color.GREY+':'+'\n')
+ """
+ verbout(colors.GR, "Receiving headers...\n")
+ verbout(
+ colors.GREY,
+ f" {colors.UNDERLINE}RESPONSE HEADERS{colors.END}{colors.GREY}:\n",
+ )
for key, val in tup.items():
- verbout(' ',color.CYAN+key+': '+color.ORANGE+val)
- verbout('','')
+ verbout(" ", f"{colors.CYAN}{key}: {colors.ORANGE}{val}")
+
+ verbout("", "")
+
def GetLogger():
+ """Write out the results"""
if INTERNAL_URLS:
- logger('internal-links', INTERNAL_URLS)
+ logger("internal-links", INTERNAL_URLS)
+
if SCAN_ERRORS:
- logger('errored', SCAN_ERRORS)
+ logger("errored", SCAN_ERRORS)
+
if FILES_EXEC:
- logger('files-found', FILES_EXEC)
+ logger("files-found", FILES_EXEC)
+
if REQUEST_TOKENS:
- logger('anti-csrf-tokens', REQUEST_TOKENS)
+ logger("anti-csrf-tokens", REQUEST_TOKENS)
+
if FORMS_TESTED:
- logger('forms-tested', FORMS_TESTED)
+ logger("forms-tested", FORMS_TESTED)
+
if VULN_LIST:
- logger('vulnerabilities', VULN_LIST)
+ logger("vulnerabilities", VULN_LIST)
+
if STRENGTH_LIST:
- logger('strengths', STRENGTH_LIST)
+ logger("strengths", STRENGTH_LIST)
+
+ if JSON_OUTPUT:
+ results = {
+ "internal-links": INTERNAL_URLS,
+ "errors": SCAN_ERRORS,
+ "files-found": FILES_EXEC,
+ "anti-csrf-tokens": REQUEST_TOKENS,
+ "forms-tested": FORMS_TESTED,
+ "vulnerabilities": VULN_LIST,
+ "strengths": STRENGTH_LIST,
+ }
+
+ with open(
+ f"{OUTPUT_DIR}results.json", mode="w", encoding="latin1"
+ ) as file_handle:
+ json.dump(results, fp=file_handle)
+
def ErrorLogger(url, error):
- con = '(i) '+url+' -> '+error.__str__()
+ con = f"(i) {url} -> {error}"
SCAN_ERRORS.append(con)
-def VulnLogger(url, vuln, content=''):
- tent = '[!] '+url+' -> '+vuln+'\n\n'+str(content)+'\n\n'
+
+def VulnLogger(url, vuln, content=""):
+ tent = f"[!] {url} -> {vuln}\n\n{content}\n\n"
VULN_LIST.append(tent)
+
def NovulLogger(url, strength):
- tent = '[+] '+url+' -> '+strength
+ tent = f"[+] {url} -> {strength}"
STRENGTH_LIST.append(tent)
diff --git a/xsrfprobe/core/main.py b/xsrfprobe/core/main.py
index 259e257..482f48f 100644
--- a/xsrfprobe/core/main.py
+++ b/xsrfprobe/core/main.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -11,28 +11,44 @@
# Standard Package imports
import os
-import re
+import sys
import ssl
import time
import warnings
-import difflib
import http.cookiejar
from bs4 import BeautifulSoup
+
+# This needs to be first, so that the options are loaded, as well as things like
+# colors are disabled
+import xsrfprobe.core.options
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+
try:
from urllib.parse import urlencode
from urllib.error import HTTPError, URLError
from urllib.request import build_opener, HTTPCookieProcessor
except ImportError: # Throws exception in Case of Python2
- 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.")
+ print(
+ f"{colors.RED} [-] {colors.ORANGE}XSRFProbe{colors.END} isn't compatible with Python 2.x versions.\n"
+ f"{colors.RED} [-] {colors.END}Use Python 3.x to run {colors.ORANGE}XSRFProbe."
+ )
quit()
+
try:
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.')
+ print(
+ " [-] Required dependencies are not installed.\n"
+ " [-] Run {colors.ORANGE}pip3 install -r requirements.txt{colors.END} to fix it."
+ )
# Imports from core
-from xsrfprobe.core.options import *
-from xsrfprobe.core.colors import *
+from xsrfprobe.files.discovered import FORMS_TESTED
+
from xsrfprobe.core.inputin import inputin
from xsrfprobe.core.request import Get, Post
from xsrfprobe.core.verbout import verbout
@@ -43,8 +59,16 @@
from xsrfprobe.core.logger import VulnLogger, NovulLogger
# Imports from files
-from xsrfprobe.files.config import *
-from xsrfprobe.files.discovered import FORMS_TESTED
+from xsrfprobe.files.config import (
+ VERIFY_CERT,
+ COOKIE_VALUE,
+ HEADER_VALUES,
+ CRAWL_SITE,
+ REFERER_ORIGIN_CHECKS,
+ FORM_SUBMISSION,
+ COOKIE_BASED,
+ POST_BASED,
+)
# Imports from modules
from xsrfprobe.modules import Debugger
@@ -58,14 +82,15 @@
from xsrfprobe.modules.Encoding import Encoding
from xsrfprobe.modules.Analysis import Analysis
from xsrfprobe.modules.Checkpost import PostBased
+
# Import Ends
# First rule, remove the warnings!
-warnings.filterwarnings('ignore')
+warnings.filterwarnings("ignore")
-def Engine(): # lets begin it!
- os.system('clear') # Clear shit from terminal :p
+def Engine(): # lets begin it!
+ os.system("clear") # Clear terminal :p
banner() # Print the banner
banabout() # The second banner
web, fld = inputin() # Take the input
@@ -75,22 +100,24 @@ def Engine(): # lets begin it!
Cookie0 = http.cookiejar.CookieJar() # First as User1
Cookie1 = http.cookiejar.CookieJar() # Then as User2
if not VERIFY_CERT:
- context=ssl._create_unverified_context()
+ context = ssl._create_unverified_context()
sslHandler = urllib.request.HTTPSHandler(context=context)
resp1 = build_opener(HTTPCookieProcessor(Cookie0), sslHandler)
resp2 = build_opener(HTTPCookieProcessor(Cookie1), sslHandler)
else:
resp1 = build_opener(HTTPCookieProcessor(Cookie0))
- resp2 = build_opener(HTTPCookieProcessor(Cookie1))
+ resp2 = build_opener(HTTPCookieProcessor(Cookie1))
actionDone = [] # init to the done stuff
- csrf = '' # no token initialise / invalid token
+ csrf = "" # no token initialise / invalid token
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
+ 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
- hdrs = [('Cookie', ','.join(cookie for cookie in COOKIE_VALUE))]
+ hdrs = [("Cookie", ",".join(cookie for cookie in COOKIE_VALUE))]
[hdrs.append((k, v)) for k, v in HEADER_VALUES.items()]
resp1.addheaders = resp2.addheaders = hdrs
resp1.open(init1) # Makes request as User2
@@ -105,36 +132,59 @@ def Engine(): # lets begin it!
url = web
try:
response = Get(url).text
- verbout(O, 'Trying to parse response...')
+ verbout(colors.O, "Trying to parse response...")
soup = BeautifulSoup(response) # Parser init
except AttributeError:
- verbout(R, 'No response received, site probably down: '+url)
- i = 0 # Init user number
+ verbout(colors.R, "No response received, site probably down: " + 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...')
+ verbout(
+ colors.O,
+ "Checking endpoint request validation via "
+ + colors.GREY
+ + "Referer"
+ + colors.END
+ + " Checks...",
+ )
+
if Referer(url):
ref_detect = 0x01
- verbout(O, 'Confirming the vulnerability...')
+
+ verbout(colors.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...')
+ verbout(
+ colors.O,
+ "Confirming endpoint request validation via "
+ f"{colors.GREY}Origin{colors.END} Checks...",
+ )
+
if Origin(url):
ori_detect = 0x01
+
# Now lets get the forms...
- verbout(O, 'Retrieving all forms on ' +color.GREY+url+color.END+'...')
+ verbout(
+ colors.O,
+ f"Retrieving all forms on {colors.GREY}{url}{colors.END}...",
+ )
+
for m in Debugger.getAllForms(soup): # iterating over all forms extracted
- verbout(O,'Testing form:\n'+color.CYAN)
+ verbout(colors.O, "Testing form:\n" + colors.CYAN)
formPrettify(m.prettify())
- verbout('', '')
- FORMS_TESTED.append('(i) '+url+':\n\n'+m.prettify()+'\n')
+ verbout("", "")
+ FORMS_TESTED.append("(i) " + url + ":\n\n" + m.prettify() + "\n")
try:
- if m['action']:
+ if m["action"]:
pass
except KeyError:
- m['action'] = '/' + url.rsplit('/', 1)[1]
+ 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...
+ 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:
@@ -144,166 +194,357 @@ def Engine(): # lets begin it!
# 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
+ 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'])
+ 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'])
+ 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)
+ 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.')
+ 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)
+ if query and token:
+ txor = Tamper(
+ url, action, result, r2.text, query, token
+ )
+
o2 = Get(url).text # make request as user2
try:
- form2 = Debugger.getAllForms(BeautifulSoup(o2))[i] # user2 gets his form
+ form2 = Debugger.getAllForms(BeautifulSoup(o2))[
+ i
+ ] # user2 gets his form
except IndexError:
- verbout(R, 'Form Index Error')
- ErrorLogger(url, 'Form Index Error.')
+ verbout(colors.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
+
+ verbout(colors.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']:
- PostBased(url, r1.text, r2.text, r3.text, action, result, genpoc, m.prettify(), m['name'])
+ if m["name"]:
+ PostBased(
+ url,
+ r1.text,
+ r2.text,
+ r3.text,
+ action,
+ result,
+ genpoc,
+ m.prettify(),
+ m["name"],
+ )
except KeyError:
- PostBased(url, r1.text, r2.text, r3.text, action, result, genpoc, m.prettify())
+ PostBased(
+ url,
+ r1.text,
+ r2.text,
+ r3.text,
+ 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.')
+ print(
+ f"{colors.GREEN} [+] The form was requested "
+ "with a Anti-CSRF token."
+ )
+ print(
+ f"{colors.GREEN} [+] Endpoint {colors.BG} "
+ f"NOT VULNERABLE {colors.END}{colors.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 :(
+ verbout(
+ colors.R, "Exception : " + msg.__str__()
+ ) # again exception :(
ErrorLogger(url, msg)
actionDone.append(action) # add the stuff done
- i+=1 # Increase user iteration
+ i += 1 # Increase user iteration
else:
# Implementing the 2nd mode [CRAWLING AND SCANNING].
- verbout(GR, "Initializing crawling and scanning...")
+ verbout(colors.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
+ print(
+ f"{colors.C}Testing :> {colors.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...
+ 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...')
+ verbout(
+ colors.O,
+ "Checking endpoint request validation via "
+ + colors.GREY
+ + "Referer"
+ + colors.END
+ + " Checks...",
+ )
+
if Referer(url):
ref_detect = 0x01
- verbout(O, 'Confirming the vulnerability...')
+
+ verbout(colors.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...')
+ verbout(
+ colors.O,
+ "Confirming endpoint request validation via "
+ f"{colors.GREY}Origin{colors.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')
+ verbout(
+ colors.O,
+ f"Retrieving all forms on {colors.GREY}{url}{colors.END}...",
+ )
+
+ for m in Debugger.getAllForms(
+ soup
+ ): # iterating over all forms extracted
+ FORMS_TESTED.append(f"(i) {url}:\n\n{m.prettify()}\n")
+
try:
- if m['action']:
+ if m["action"]:
pass
except KeyError:
- m['action'] = '/' + url.rsplit('/', 1)[1]
+ 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...
+
+ action = Parser.buildAction(
+ url, m["action"]
+ ) # get all forms which have 'action' attribute
+ if (
+ action not 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:
- 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
+ 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']:
- query, token = Entropy(result, url, r1.headers, m.prettify(), m['action'], m['name'])
+ 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'])
+ 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)
+ 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.')
+ 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)
+ if query and token:
+ txor = Tamper(
+ url, action, result, r2.text, query, token
+ )
+
o2 = Get(url).text # make request as user2
try:
- form2 = Debugger.getAllForms(BeautifulSoup(o2))[i] # user2 gets his form
+ form2 = Debugger.getAllForms(BeautifulSoup(o2))[
+ i
+ ] # user2 gets his form
except IndexError:
- verbout(R, 'Form Index Error')
- ErrorLogger(url, 'Form Index Error.')
+ verbout(colors.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)):
+
+ verbout(colors.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):
try:
- if m['name']:
- PostBased(url, r1.text, r2.text, r3.text, m['action'], result, genpoc, m.prettify(), m['name'])
+ 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())
+ 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.')
+ print(
+ f"{colors.GREEN} [+] The form was requested with a Anti-CSRF token."
+ )
+ print(
+ colors.GREEN
+ + " [+] Endpoint "
+ + colors.BG
+ + " NOT VULNERABLE "
+ + colors.END
+ + colors.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 :(
+ verbout(
+ colors.RED,
+ " [-] Exception : "
+ + colors.END
+ + msg.__str__(),
+ ) # again exception :(
ErrorLogger(url, msg)
+
actionDone.append(action) # add the stuff done
- i+=1 # Increase user iteration
+ i += 1 # Increase user iteration
+
# 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))
+ if str(e.code) == "403":
+ verbout(colors.R, "HTTP Authentication Error!")
+ verbout(colors.R, "Error Code : " + colors.O + str(e.code))
ErrorLogger(url, e)
quit()
except URLError as e: # if again...
- verbout(R, 'Exception at : '+url) # again exception -_-
+ verbout(colors.R, "Exception at : " + url) # again exception -_-
time.sleep(0.4)
- verbout(O, 'Moving on...')
+ verbout(colors.O, "Moving on...")
ErrorLogger(url, e)
continue # make sure it doesn't stop at exceptions
+
GetLogger() # The scanning has finished, so now we can log out all the links ;)
- print('\n'+G+"Scan completed!"+'\n')
+ print(f"\n{colors.G}Scan done\n")
Analysis() # For Post Scan Analysis
except KeyboardInterrupt as e: # Incase user wants to exit :') (while crawling)
- verbout(R, 'User Interrupt!')
+ verbout(colors.R, "User Interrupt!")
time.sleep(1.5)
Analysis() # For Post scan Analysis
- print(R+'Aborted!') # say goodbye
- ErrorLogger('KeyBoard Interrupt', 'Aborted')
+ print(colors.R + "Aborted!") # say goodbye
+ ErrorLogger("KeyBoard Interrupt", "Aborted")
GetLogger() # The scanning has interrupted, so now we can log out all the links ;)
sys.exit(1)
except Exception as e:
- print('\n'+R+'Encountered an error. \n')
- print(R+'Please view the error log files to view what went wrong.')
- verbout(R, e.__str__())
+ print("\n" + colors.R + "Encountered an error. \n")
+ print(colors.R + "Please view the error log files to view what went wrong.")
+ verbout(colors.R, e.__str__())
ErrorLogger(url, e)
GetLogger()
diff --git a/xsrfprobe/core/options.py b/xsrfprobe/core/options.py
index a7accd4..8795e77 100644
--- a/xsrfprobe/core/options.py
+++ b/xsrfprobe/core/options.py
@@ -1,62 +1,193 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
# Importing stuff
-import argparse, sys, tld
-import urllib.parse, os, re
+import argparse
+import sys
+import urllib.parse
+import os
+import re
+
+import tld
+
from xsrfprobe.files import config
-from xsrfprobe.core.colors import R, G
from xsrfprobe.core.updater import updater
from xsrfprobe.files.dcodelist import IP
from xsrfprobe import __version__, __license__
-print('''
- \033[1;91mXSRFProbe\033[0m, \033[1;97mA \033[1;93mCross Site Request Forgery \033[1;97mAudit Toolkit\033[0m
-''')
# Processing command line arguments
parser = argparse.ArgumentParser(usage="xsrfprobe -u ")
parser._action_groups.pop()
-# A simple hack to have required argumentsa and optional arguments separately
-required = parser.add_argument_group('Required Arguments')
-optional = parser.add_argument_group('Optional Arguments')
+# A simple hack to have required arguments and optional arguments separately
+required = parser.add_argument_group("Required Arguments")
+optional = parser.add_argument_group("Optional Arguments")
# Required Options
-required.add_argument('-u', '--url', help='Main URL to test', dest='url')
+required.add_argument("-u", "--url", help="Main URL to test", dest="url")
# Optional Arguments (main stuff and necessary)
-optional.add_argument('-c', '--cookie', help='Cookie value to be requested with each successive request. If there are multiple cookies, separate them with commas. For example: `-c PHPSESSID=i837c5n83u4, _gid=jdhfbuysf`.', dest='cookie')
-optional.add_argument('-o', '--output', help='Output directory where files to be stored. Default is the output/ 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('-H', '--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('-v', '--verbose', help='Increase the verbosity of the output (e.g., -vv is more than -v). ', dest='verbose', action='store_true')
-optional.add_argument('-t', '--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('-E', '--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(
+ "-c",
+ "--cookie",
+ help="Cookie value to be requested with each successive request. If there are multiple cookies, separate them with commas. For example: `-c PHPSESSID=i837c5n83u4, _gid=jdhfbuysf`.",
+ dest="cookie",
+)
+optional.add_argument(
+ "-o",
+ "--output",
+ help="Output directory where files to be stored. Default is the output/ 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(
+ "-H",
+ "--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(
+ "-v",
+ "--verbose",
+ help="Increase the verbosity of the output (e.g., -vv is more than -v). ",
+ dest="verbose",
+ action="store_true",
+)
+optional.add_argument(
+ "-t",
+ "--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(
+ "-E",
+ "--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,
+)
# 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('--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('--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('--no-verify', help='Do not verify SSL certificates with requests.', dest='no_verify', 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('--version', help='Display the version of XSRFProbe and exit.', dest='version', 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(
+ "--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(
+ "--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(
+ "--no-verify",
+ help="Do not verify SSL certificates with requests.",
+ dest="no_verify",
+ 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(
+ "--no-colors",
+ help="Disable colors.",
+ dest="nocolors",
+ 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(
+ "--version",
+ help="Display the version of XSRFProbe and exit.",
+ dest="version",
+ action="store_true",
+)
+optional.add_argument(
+ "--json",
+ help="Output the results into a JSON file.",
+ dest="json",
+ action="store_true",
+)
args = parser.parse_args()
+# Hide colors if user doesn't want it
+if args.nocolors:
+ config.NO_COLORS = True
+
+# Support hiding the colors "early"
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+print(
+ f"""
+ {colors.RED}XSRFProbe{colors.END}, {colors.GREY}A {colors.ORANGE}Cross Site Request Forgery """
+ f"""{colors.GREY}Audit Toolkit{colors.END}
+"""
+)
+
if not len(sys.argv) > 1:
parser.print_help()
quit()
@@ -68,8 +199,14 @@
# Print out XSRFProbe version
if args.version:
- print('\033[1;96m [+] \033[1;91mXSRFProbe Version\033[0m : v'+__version__)
- print('\033[1;96m [+] \033[1;91mXSRFProbe License\033[0m : '+__license__+'\n')
+ print(
+ f"{colors.CYAN} [+] {colors.RED}XSRFProbe Version{colors.END} : v" + __version__
+ )
+ print(
+ f"{colors.CYAN} [+] {colors.RED}XSRFProbe License{colors.END} : "
+ + __license__
+ + "\n"
+ )
quit()
# Now lets update some global config variables
@@ -94,13 +231,13 @@
# Updating main root url
if not args.version and not args.update:
- if args.url: # and not args.help:
- if 'http' in args.url:
+ if args.url: # and not args.help:
+ if "http" in args.url:
config.SITE_URL = args.url
else:
- config.SITE_URL = 'http://'+args.url
+ config.SITE_URL = "http://" + args.url
else:
- print(R+'You must supply a url/endpoint.')
+ print(colors.R + "You must supply a url/endpoint.")
# Crawl the site if --crawl supplied.
if args.crawl:
@@ -110,7 +247,7 @@
if args.cookie:
# Assigning Cookie
- for cook in args.cookie.split(','):
+ for cook in args.cookie.split(","):
config.COOKIE_VALUE.append(cook)
# This is necessary when a cookie value is supplied
# Since if the user-agent used to make the request changes
@@ -135,17 +272,19 @@
# NOTE: As a default idea, when the user supplies custom headers, we
# simply add the custom headers to a list of existing headers in
# files/config.py.
- # Uncomment the following lines to just reinitialise the headers everytime
+ # Uncomment the following lines to just reinitialise the headers every time
# they make a request.
#
- #config.HEADER_VALUES = {}
- for m in args.headers.split(','):
- config.HEADER_VALUES[m.split('=')[0].strip()] = m.split('=')[1].strip() # nice hack ;)
+ # config.HEADER_VALUES = {}
+ for m in args.headers.split(","):
+ config.HEADER_VALUES[m.split("=")[0].strip()] = m.split("=")[
+ 1
+ ].strip() # nice hack ;)
if args.exclude:
exc = args.exclude
- #config.EXCLUDE_URLS = [s for s in exc.split(',').strip()]
- m = exc.split(',').strip()
+ # config.EXCLUDE_URLS = [s for s in exc.split(',').strip()]
+ m = exc.split(",").strip()
for s in m:
config.EXCLUDE_DIRS.append(urllib.parse.urljoin(config.SITE_URL, s))
@@ -153,41 +292,49 @@
# If random-agent argument supplied...
config.USER_AGENT_RANDOM = True
# Turn off a single User-Agent mechanism...
- config.USER_AGENT = ''
+ config.USER_AGENT = ""
if config.SITE_URL:
try:
if args.output:
# If output directory is mentioned...
try:
- if not os.path.exists(args.output+tld.get_fld(config.SITE_URL)):
- os.makedirs(args.output+tld.get_fld(config.SITE_URL))
+ if not os.path.exists(f"{args.output}{tld.get_fld(config.SITE_URL)}"):
+ os.makedirs(f"{args.output}{tld.get_fld(config.SITE_URL)}")
except FileExistsError:
pass
- config.OUTPUT_DIR = args.output+tld.get_fld(config.SITE_URL) + '/'
+
+ config.OUTPUT_DIR = f"{args.output}{tld.get_fld(config.SITE_URL)}/"
else:
try:
- os.makedirs('xsrfprobe-output/'+tld.get_fld(config.SITE_URL))
+ os.makedirs(f"xsrfprobe-output/{tld.get_fld(config.SITE_URL)}")
except FileExistsError:
pass
- config.OUTPUT_DIR = 'xsrfprobe-output/'+tld.get_fld(config.SITE_URL) + '/'
+
+ config.OUTPUT_DIR = f"xsrfprobe-output/{tld.get_fld(config.SITE_URL)}/"
+
# When this exception turns out, we know the user has supplied a IP not domain
except tld.exceptions.TldDomainNotFound:
direc = re.search(IP, config.SITE_URL).group(0)
if args.output:
# If output directory is mentioned...
try:
- if not os.path.exists(args.output+direc):
- os.makedirs(args.output+direc)
+ if not os.path.exists(f"{args.output}{direc}"):
+ os.makedirs(f"{args.output}{direc}")
except FileExistsError:
pass
- config.OUTPUT_DIR = args.output+direc + '/'
+
+ config.OUTPUT_DIR = f"{args.output}{direc}/"
else:
try:
- os.makedirs('xsrfprobe-output/'+direc)
+ os.makedirs(f"xsrfprobe-output/{direc}")
except FileExistsError:
pass
- config.OUTPUT_DIR = 'xsrfprobe-output/'+direc + '/'
+
+ config.OUTPUT_DIR = f"xsrfprobe-output/{direc}/"
if args.quiet:
config.DEBUG = False
+
+if args.json:
+ config.JSON_OUTPUT = True
diff --git a/xsrfprobe/core/prettify.py b/xsrfprobe/core/prettify.py
index 12b6843..0ff509c 100644
--- a/xsrfprobe/core/prettify.py
+++ b/xsrfprobe/core/prettify.py
@@ -1,22 +1,26 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
import re
-from xsrfprobe.core.colors import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
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:
@@ -24,51 +28,53 @@ def formPrettify(response):
# Find starting tags
pattern = re.findall(r"""(<+\w+>)""", line)
for grp in pattern:
- starttag = ''.join(grp)
+ starttag = "".join(grp)
if starttag:
- line = line.replace(starttag, color.BLUE + starttag + color.END)
+ line = line.replace(starttag, colors.BLUE + starttag + colors.END)
# Find attributes
- pattern = re.findall(r'''(\s\w+=)''', line)
+ pattern = re.findall(r"""(\s\w+=)""", line)
for grp in pattern:
- stu = ''.join(grp)
+ stu = "".join(grp)
if stu:
- line = line.replace(stu, color.CYAN + stu + color.END)
+ line = line.replace(stu, colors.CYAN + stu + colors.END)
# Find ending tags
- pattern = re.findall(r'''()''', line)
+ pattern = re.findall(r"""()""", line)
for grp in pattern:
- endtag = ''.join(grp)
+ endtag = "".join(grp)
if endtag:
- line = line.replace(endtag, color.CYAN + endtag + color.END)
+ line = line.replace(endtag, colors.CYAN + endtag + colors.END)
if line != newLine:
highlighted.append(line)
else:
- highlighted.append(color.GREY+newLine)
+ highlighted.append(colors.GREY + newLine)
for h in highlighted:
- print(' '+h)
+ 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
+ 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
+ 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/xsrfprobe/core/randua.py b/xsrfprobe/core/randua.py
index 581c881..b7c1c81 100644
--- a/xsrfprobe/core/randua.py
+++ b/xsrfprobe/core/randua.py
@@ -1,58 +1,58 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
import random
-import datetime
user_agent_list = [
# Chrome
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
- 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
+ "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
# Firefox
- 'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)',
- 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
- 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)',
- 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko',
- 'Mozilla/5.0 (Windows NT 6.2; WOW64; Trident/7.0; rv:11.0) like Gecko',
- 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
- 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)',
- 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko',
- 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)',
- '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 9.0; Windows NT 6.1)",
+ "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
+ "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)",
+ "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko",
+ "Mozilla/5.0 (Windows NT 6.2; WOW64; Trident/7.0; rv:11.0) like Gecko",
+ "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko",
+ "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)",
+ "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko",
+ "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)",
+ "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)",
# 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'
+ "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():
return random.choice(user_agent_list)
diff --git a/xsrfprobe/core/request.py b/xsrfprobe/core/request.py
index afd8749..62283c4 100644
--- a/xsrfprobe/core/request.py
+++ b/xsrfprobe/core/request.py
@@ -1,18 +1,35 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-import requests, time
+import time
from urllib.parse import urljoin
-from xsrfprobe.core.colors import *
-from xsrfprobe.files.config import *
+
+import requests
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+from xsrfprobe.files.config import (
+ HEADER_VALUES,
+ COOKIE_VALUE,
+ USER_AGENT_RANDOM,
+ USER_AGENT,
+ DELAY_VALUE,
+ DISPLAY_HEADERS,
+ TIMEOUT_VALUE,
+ VERIFY_CERT,
+ FILE_EXTENSIONS,
+ EXECUTABLES,
+)
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.randua import RandomAgent
from xsrfprobe.files.discovered import FILES_EXEC
@@ -22,99 +39,131 @@
# Set Cookie
if COOKIE_VALUE:
- headers['Cookie'] = ','.join(cookie for cookie in COOKIE_VALUE)
+ headers["Cookie"] = ",".join(cookie for cookie in COOKIE_VALUE)
# Set User-Agent
if USER_AGENT_RANDOM:
- headers['User-Agent'] = RandomAgent()
+ headers["User-Agent"] = RandomAgent()
if USER_AGENT:
- headers['User-Agent'] = USER_AGENT
+ headers["User-Agent"] = USER_AGENT
+
def Post(url, action, data):
- '''
+ """
The main use of this function is as a
Form Requester [POST].
- '''
+ """
global headers, TIMEOUT_VALUE, VERIFY_CERT
time.sleep(DELAY_VALUE) # If delay param has been supplied
- verbout(GR, 'Preparing the request...')
+ verbout(colors.GR, "Preparing the request...")
+
if DISPLAY_HEADERS:
preqheaders(headers)
- verbout(GR, 'Processing the '+color.GREY+'POST'+color.END+' Request...')
+
+ verbout(colors.GR, f"Processing the {colors.GREY}POST{colors.END} Request...")
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, verify=VERIFY_CERT)
+ response = requests.post(
+ main_url,
+ headers=headers,
+ data=data,
+ timeout=TIMEOUT_VALUE,
+ verify=VERIFY_CERT,
+ )
if DISPLAY_HEADERS:
presheaders(response.headers)
return response # read data content
except requests.exceptions.HTTPError as e: # if error
- verbout(R, "HTTP Error : "+main_url)
+ verbout(colors.R, "HTTP Error : " + main_url)
ErrorLogger(main_url, e.__str__())
return None
except requests.exceptions.ConnectionError as e:
- verbout(R, 'Connection Aborted : '+main_url)
+ verbout(colors.R, "Connection Aborted : " + main_url)
ErrorLogger(main_url, e.__str__())
return None
except requests.exceptions.ReadTimeout as e:
- verbout(R, 'Exception at: '+color.GREY+url)
- verbout(R, 'Error: Read Timeout. Consider increasing the timeout value via --timeout.')
+ verbout(colors.R, "Exception at: " + colors.GREY + url)
+ verbout(
+ colors.R,
+ "Error: Read Timeout. Consider increasing the timeout value via --timeout.",
+ )
ErrorLogger(url, e.__str__())
return None
except ValueError as e: # again if valuerror
- verbout(R, "Value Error : "+main_url)
+ verbout(colors.R, "Value Error : " + main_url)
ErrorLogger(main_url, e.__str__())
return None
except Exception as e:
- verbout(R, "Exception Caught: "+e.__str__())
+ verbout(colors.R, "Exception Caught: " + e.__str__())
ErrorLogger(main_url, e.__str__())
return None # if at all nothing happens :(
+
def Get(url, headers=headers):
- '''
+ """
The main use of this function is as a
Url Requester [GET].
- '''
+ """
global TIMEOUT_VALUE, VERIFY_CERT
# We do not verify the request while GET requests
time.sleep(DELAY_VALUE) # We make requests after the time delay
# Making sure the url is not a file
- if url.split('.')[-1].lower() in (FILE_EXTENSIONS or EXECUTABLES):
+ if url.split(".")[-1].lower() in (FILE_EXTENSIONS or EXECUTABLES):
FILES_EXEC.append(url)
- verbout(G, 'Found File: '+color.BLUE+url)
+ verbout(colors.G, "Found File: " + colors.BLUE + url)
return None
try:
- verbout(GR, 'Preparing the request...')
+ verbout(colors.GR, "Preparing the request...")
+
if DISPLAY_HEADERS:
preqheaders(headers)
- verbout(GR, 'Processing the '+color.GREY+'GET'+color.END+' Request...')
- req = requests.get(url, headers=headers, timeout=TIMEOUT_VALUE,
- stream=False, verify=VERIFY_CERT)
+
+ verbout(
+ colors.GR,
+ f"Processing the {colors.GREY}GET{colors.END} Request...",
+ )
+
+ req = requests.get(
+ url,
+ headers=headers,
+ timeout=TIMEOUT_VALUE,
+ stream=False,
+ verify=VERIFY_CERT,
+ )
+
+ if req is None:
+ verbout(colors.RED, f" [!] Failed to get a response from: {url}")
+ return None
+
# Displaying headers if DISPLAY_HEADERS is 'True'
if DISPLAY_HEADERS:
presheaders(req.headers)
+
# Return the object
return req
except requests.exceptions.MissingSchema as e:
- verbout(R, 'Exception at: '+color.GREY+url)
- verbout(R, 'Error: Invalid URL Format')
+ verbout(colors.R, "Exception at: " + colors.GREY + url)
+ verbout(colors.R, "Error: Invalid URL Format")
ErrorLogger(url, e.__str__())
return None
except requests.exceptions.ReadTimeout as e:
- verbout(R, 'Exception at: '+color.GREY+url)
- verbout(R, 'Error: Read Timeout. Consider increasing the timeout value via --timeout.')
+ verbout(colors.R, "Exception at: " + colors.GREY + url)
+ verbout(
+ colors.R,
+ "Error: Read Timeout. Consider increasing the timeout value via --timeout.",
+ )
ErrorLogger(url, e.__str__())
return None
except requests.exceptions.HTTPError as e: # if error
- verbout(R, "HTTP Error Encountered : "+url)
+ verbout(colors.R, "HTTP Error Encountered : " + url)
ErrorLogger(url, e.__str__())
return None
except requests.exceptions.ConnectionError as e:
- verbout(R, 'Connection Aborted : '+url)
+ verbout(colors.R, "Connection Aborted : " + url)
ErrorLogger(url, e.__str__())
return None
except Exception as e:
- verbout(R, "Exception Caught: "+e.__str__())
+ verbout(colors.R, "Exception Caught: " + e.__str__())
ErrorLogger(url, e.__str__())
return None
diff --git a/xsrfprobe/core/updater.py b/xsrfprobe/core/updater.py
index c8679cd..0bf1ada 100644
--- a/xsrfprobe/core/updater.py
+++ b/xsrfprobe/core/updater.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -12,27 +12,41 @@
import os
from requests import get
from xsrfprobe import __version__
-from xsrfprobe.core.colors import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
def updater():
- '''
+ """
Function to update XSRFProbe seamlessly.
- '''
- print(GR+'Checking for updates...')
- vno = get('https://raw.githubusercontent.com/0xInfection/XSRFProbe/master/xsrfprobe/files/VersionNum').text
- print(GR+'Version on GitHub: '+color.CYAN+vno.strip())
- print(GR+'Version You Have : '+color.CYAN+__version__)
+ """
+ print(colors.GR + "Checking for updates...")
+ vno = get(
+ "https://raw.githubusercontent.com/0xInfection/XSRFProbe/master/xsrfprobe/files/VersionNum",
+ timeout=1,
+ ).text
+
+ print(colors.GR + "Version on GitHub: " + colors.CYAN + vno.strip())
+ print(colors.GR + "Version You Have : " + colors.CYAN + __version__)
if vno != __version__:
- print(G+'A new version of XSRFProbe is available!')
- current_path = os.getcwd().split('/') # if you know it, you know it
- folder = current_path[-1] # current directory name
- path = '/'.join(current_path) # current directory path
- choice = input(O+'Would you like to update? [Y/n] :> ').lower()
- if choice != 'n':
- print(GR+'Updating XSRFProbe...')
- os.system('git clone --quiet https://github.com/0xInfection/XSRFProbe %s' % (folder))
- os.system('cp -r %s/%s/* %s && rm -r %s/%s/ 2>/dev/null' % (path, folder, path, path, folder))
- print(G+'Update successful!')
+ print(colors.G + "A new version of XSRFProbe is available!")
+ current_path = os.getcwd().split("/") # if you know it, you know it
+ folder = current_path[-1] # current directory name
+ path = "/".join(current_path) # current directory path
+ choice = input(colors.O + "Would you like to update? [Y/n] :> ").lower()
+ if choice != "n":
+ print(colors.GR + "Updating XSRFProbe...")
+ os.system(
+ "git clone --quiet https://github.com/0xInfection/XSRFProbe %s"
+ % (folder)
+ )
+ os.system(
+ "cp -r %s/%s/* %s && rm -r %s/%s/ 2>/dev/null"
+ % (path, folder, path, path, folder)
+ )
+ print(colors.G + "Update successful!")
else:
- print(G+'XSRFProbe is up to date!')
+ print(colors.G + "XSRFProbe is up to date!")
quit()
diff --git a/xsrfprobe/core/utils.py b/xsrfprobe/core/utils.py
index 5f49e4c..2adf7e8 100644
--- a/xsrfprobe/core/utils.py
+++ b/xsrfprobe/core/utils.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -11,11 +11,12 @@
from difflib import SequenceMatcher
-def sameSequence(str1,str2):
- '''
+
+def sameSequence(str1, str2):
+ """
This function is intended to find same sequence
between str1 and str2.
- '''
+ """
# Initialize SequenceMatcher object with
# Input string
seqMatch = SequenceMatcher(None, str1, str2)
@@ -25,23 +26,25 @@ def sameSequence(str1,str2):
match = seqMatch.find_longest_match(0, len(str1), 0, len(str2))
# Print longest substring
- if (match.size!=0):
- return (str1[match.a: match.a + match.size])
+ if match.size != 0:
+ return str1[match.a : match.a + match.size]
else:
- return ''
+ return ""
+
-def replaceStrIndex(text, index=0, replacement=''):
- '''
+def replaceStrIndex(text, index=0, replacement=""):
+ """
This method returns a tampered string by
replacement
- '''
- return '%s%s%s' % (text[:index], replacement, text[index+1:])
+ """
+ return "%s%s%s" % (text[:index], replacement, text[index + 1 :])
+
def checkDuplicates(iterable):
- '''
+ """
This function works as a byte sequence checker for
tuples passed onto this function.
- '''
+ """
seen = set()
for x in iterable:
if x in seen:
@@ -49,7 +52,8 @@ def checkDuplicates(iterable):
seen.add(x)
return False
-def byteString(s, encoding='utf8'):
+
+def byteString(s, encoding="utf8"):
"""
Return a byte-string version of 's',
Encoded as utf-8.
@@ -60,13 +64,14 @@ def byteString(s, encoding='utf8'):
s = str(s)
return s
-def subSequence(str1,str2):
- '''
+
+def subSequence(str1, str2):
+ """
Returns whether 'str1' and 'str2' are subsequence
of one another.
- '''
- j = 0 # Index of str1
- i = 0 # Index of str2
+ """
+ j = 0 # Index of str1
+ i = 0 # Index of str2
# Traverse both str1 and str2
# Compare current character of str2 with
@@ -74,10 +79,10 @@ def subSequence(str1,str2):
# If matched, then move ahead in str1
m = len(str1)
n = len(str2)
- while j (3 - args.verbose):
-# print verb_args[1]
+ print(stat + content_info)
diff --git a/xsrfprobe/files/VersionNum b/xsrfprobe/files/VersionNum
index a625450..a9fa699 100644
--- a/xsrfprobe/files/VersionNum
+++ b/xsrfprobe/files/VersionNum
@@ -1 +1 @@
-2.3.1
\ No newline at end of file
+4.4.29
\ No newline at end of file
diff --git a/xsrfprobe/files/__init__.py b/xsrfprobe/files/__init__.py
index d79d32c..7dc4748 100644
--- a/xsrfprobe/files/__init__.py
+++ b/xsrfprobe/files/__init__.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
-#This module requires XSRFProbe
-#https://github.com/0xInfection/XSRFProbe
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
diff --git a/xsrfprobe/files/config.py b/xsrfprobe/files/config.py
index 89de426..00cfa37 100644
--- a/xsrfprobe/files/config.py
+++ b/xsrfprobe/files/config.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -16,9 +16,10 @@
global HEADER_VALUES, TIMEOUT_VALUE, REFERER_ORIGIN_CHECKS, REFERER_URL, POST_BASED
global DISPLAY_HEADERS, EXECUTABLES, FILE_EXTENSIONS, POC_GENERATION, OUTPUT_DIR, VERIFY_CERT
global CRAWL_SITE, TOKEN_CHECKS, DELAY_VALUE, SCAN_ANALYSIS, EXCLUDE_DIRS, GEN_MALICIOUS
+global NO_COLORS
# Site Url to be scanned (Required)
-SITE_URL = ''
+SITE_URL = ""
# Switch for whether to crawl the site or not
CRAWL_SITE = False
@@ -35,12 +36,12 @@
# User-Agent to be used (If COOKIE_VALUE supplied).
#
-# This is standard User-Agent emulating Chrome 68 on Windows 10
+# This is standard User-Agent emulating Chrome 68 on Windows 10
#
# NOTE: This is a precaution in case the cookie value is supplied,
# if the user-agent gets changed from time to time, the remote
# application might trigger up some protection agents
-USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
+USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36"
# Cookie value to be sent alongwith the requests. This option is particularly
# needed for a wholesome check on CSRFs. Since for a basic successful CSRF attack
@@ -60,20 +61,20 @@
# Header values to be used (Modify it as per your need)
HEADER_VALUES = {
- 'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
- 'Accept-Language' : 'en-US,en;q=0.9',
- 'Accept-Encoding' : 'gzip, deflate, br',
- 'Sec-Fetch-Mode' : 'navigate',
- 'DNT' : '1', # Do Not Track Request Header :D
- 'Connection' : 'close'
- }
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
+ "Accept-Language": "en-US,en;q=0.9",
+ "Accept-Encoding": "gzip, deflate, br",
+ "Sec-Fetch-Mode": "navigate",
+ "DNT": "1", # Do Not Track Request Header :D
+ "Connection": "close",
+}
# Email value to be supplied when parsing/filling forms
# You can modify it as per your need :)
-EMAIL_VALUE = 'csrf.testing@xsrfprobe.tld'
+EMAIL_VALUE = "csrf.testing@xsrfprobe.tld"
# Plaintext value to be supplied when parsing/filling forms
-TEXT_VALUE = 'csrftesting'
+TEXT_VALUE = "csrftesting"
# Request Timeout (Keep the max. timeout value to 10s)
TIMEOUT_VALUE = 7
@@ -117,11 +118,11 @@
# Referer Url (Change It Accordingly)
# eg. Use one of your Subdomains (Same Origin Policy))
-REFERER_URL = 'http://not-a-valid-referer.xsrfprobe-csrftesting.0xinfection.xyz'
+REFERER_URL = "http://not-a-valid-referer.xsrfprobe-csrftesting.0xinfection.xyz"
# Origin Url (Change It Accordingly)
# eg. Use one of your Subdomains (Same Origin Policy))
-ORIGIN_URL = 'http://not-a-valid-origin.xsrfprobe-csrftesting.0xinfection.xyz'
+ORIGIN_URL = "http://not-a-valid-origin.xsrfprobe-csrftesting.0xinfection.xyz"
# The length of the custom token to be generated for params
#
@@ -138,13 +139,19 @@
# Output directory where everything (including logs) are to
# be stored
-OUTPUT_DIR = ''
+OUTPUT_DIR = ""
+
+# Allow JSON output
+JSON_OUTPUT = False
# This option is for displaying the headers received as response.
# Turn this off if you don't want to see the headers on the
# terminal, or if it feels irritating.
DISPLAY_HEADERS = False
+# No colors option
+NO_COLORS = False
+
# Option for controlling post-scan analysis. Turning it off
# results in not analysing the tokens gathered.
SCAN_ANALYSIS = True
@@ -159,6 +166,39 @@
# 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',]
+FILE_EXTENSIONS = [
+ "png",
+ "jpg",
+ "jpeg",
+ "pdf",
+ "js",
+ "css",
+ "ico",
+ "bmp",
+ "svg",
+ "json",
+ "xml",
+ "xls",
+ "csv",
+ "docx",
+]
# These are a list of executable files that are found on the web
-EXECUTABLES = ['deb', 'bat', 'exe', 'msu', 'msi', 'apk', 'bin', 'csh', 'inf', 'ini', 'msc', 'osx' ,'out', 'vbe', 'ws', 'msp', 'jse']
+EXECUTABLES = [
+ "deb",
+ "bat",
+ "exe",
+ "msu",
+ "msi",
+ "apk",
+ "bin",
+ "csh",
+ "inf",
+ "ini",
+ "msc",
+ "osx",
+ "out",
+ "vbe",
+ "ws",
+ "msp",
+ "jse",
+]
diff --git a/xsrfprobe/files/dcodelist.py b/xsrfprobe/files/dcodelist.py
index 14efd1c..1d5ab27 100644
--- a/xsrfprobe/files/dcodelist.py
+++ b/xsrfprobe/files/dcodelist.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -14,100 +14,100 @@
# Token hash encoding detection db, thanks to a book, Python for Penetation Testers, LOL!
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}$"),
- ("oRACLE 11g/12c", r"^(S:)?[a-f0-9]{40}(:)?[a-f0-9]{20}$"),
- ("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}$"),
- ("SipHash", r"^[a-f0-9]{16}:2:4:[a-f0-9]{32}$"),
- ("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}$"),
- ("Cisco Type 7", r"^[a-f0-9]{4,}$"),
- ("Snefru-256", r"^(\\$snefru\\$)?[a-f0-9]{64}$"),
- ("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}$"),
- )
+ ("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}$"),
+ ("oRACLE 11g/12c", r"^(S:)?[a-f0-9]{40}(:)?[a-f0-9]{20}$"),
+ ("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}$"),
+ ("SipHash", r"^[a-f0-9]{16}:2:4:[a-f0-9]{32}$"),
+ ("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}$"),
+ ("Cisco Type 7", r"^[a-f0-9]{4,}$"),
+ ("Snefru-256", r"^(\\$snefru\\$)?[a-f0-9]{64}$"),
+ ("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}$"),
+)
# IP Regex
-IP = r'((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
+IP = r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
# Get rid of Double ../../
-RID_DOUBLE = r'/\.\./'
+RID_DOUBLE = r"/\.\./"
# Get rid of ./'s
-RID_SINGLE = r'\./'
+RID_SINGLE = r"\./"
# Complier based regex
-RID_COMPILE = r'/[^/]*/../'
+RID_COMPILE = r"/[^/]*/../"
# Number based.
-NUM_SUB = r'=[0-9]+'
+NUM_SUB = r"=[0-9]+"
# Number based compile.
-NUM_COM = r'(title=)[^&]*'
+NUM_COM = r"(title=)[^&]*"
# Binary strings.
-BINARY = r'^[01]+$'
+BINARY = r"^[01]+$"
# Decimal Strings.
-DEC = r'.*;+'
+DEC = r".*;+"
# Protocol Types
-PROTOCOLS = r'(.*\/)[^\/]*'
\ No newline at end of file
+PROTOCOLS = r"(.*\/)[^\/]*"
diff --git a/xsrfprobe/files/discovered.py b/xsrfprobe/files/discovered.py
index 63bf204..ff99e8c 100644
--- a/xsrfprobe/files/discovered.py
+++ b/xsrfprobe/files/discovered.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
diff --git a/xsrfprobe/files/paramlist.py b/xsrfprobe/files/paramlist.py
index a8d3b76..e374e03 100644
--- a/xsrfprobe/files/paramlist.py
+++ b/xsrfprobe/files/paramlist.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -15,85 +15,83 @@
#
# Feel free to add more of your tokens if you have. ;)
COMMON_CSRF_NAMES = (
- # 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', # 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 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 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',
- 'auth',
- 'hash',
- 'secret',
- 'verify',
- )
+ # 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", # 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 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 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",
+ "auth",
+ "hash",
+ "secret",
+ "verify",
+)
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'
- )
+ # 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',
- 'action=out',
- 'action=logoff',
- 'action=delete',
- 'UserLogout',
- 'osCsid',
- 'action=logout',
- )
+ "logout",
+ "action=out",
+ "action=logoff",
+ "action=delete",
+ "UserLogout",
+ "osCsid",
+ "action=logout",
+)
# List of common errors shown when token is tampered.
TOKEN_ERRORS = (
- 'the required form field',
- 'token could not',
- 'invalid token',
- 'wrong',
- 'error',
- 'not valid',
- 'please check your request',
- 'your browser did something unexpected',
- 'csrf'
- 'clearing your cookies',
- 'tampered token',
- 'null',
- 'unacceptable',
- 'false',
- 'void',
- 'incorrect',
- 'inoperative',
- 'faulty',
- 'absurd',
- 'inconsistent',
- 'not acceptable',
- )
+ "the required form field",
+ "token could not",
+ "invalid token",
+ "wrong",
+ "error",
+ "not valid",
+ "please check your request",
+ "your browser did something unexpected",
+ "csrf" "clearing your cookies",
+ "tampered token",
+ "null",
+ "unacceptable",
+ "false",
+ "void",
+ "incorrect",
+ "inoperative",
+ "faulty",
+ "absurd",
+ "inconsistent",
+ "not acceptable",
+)
diff --git a/xsrfprobe/modules/Analysis.py b/xsrfprobe/modules/Analysis.py
index 5ad57cb..1e03a79 100644
--- a/xsrfprobe/modules/Analysis.py
+++ b/xsrfprobe/modules/Analysis.py
@@ -1,58 +1,97 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
import stringdist
-import itertools, time
-from xsrfprobe.core.colors import *
+import itertools
+import time
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
from xsrfprobe.modules.Entropy import calcEntropy
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.utils import sameSequence, byteString
from xsrfprobe.files.discovered import REQUEST_TOKENS
from xsrfprobe.core.logger import VulnLogger, NovulLogger
+
def Analysis():
- '''
+ """
The main idea behind this is to observe and analyse
the patterns in which the CSRF tokens
are generated by server.
- '''
+ """
ctr = 0 # Counter variable set to 0
# Checking if the no of tokens is greater than 1
if len(REQUEST_TOKENS) > 1:
- 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)))
+ verbout(colors.RED, "\n +--------------+")
+ verbout(colors.RED, " | Analysis |")
+ verbout(colors.RED, " +--------------+\n")
+ print(colors.GR + "Proceeding for post-scan analysis of tokens gathered...")
+ verbout(
+ colors.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
# considering permutations) from the given list of discovered tokens
# and generate anti-CSRF token generation pattern.
for tokenx1, tokenx2 in itertools.combinations(REQUEST_TOKENS, 2):
try:
- verbout(GR, 'Analysing 2 Anti-CSRF Tokens from gathered requests...')
- 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)))
+ verbout(
+ colors.GR, "Analysing 2 Anti-CSRF Tokens from gathered requests..."
+ )
+ verbout(colors.CYAN, " [+] First Token: " + colors.BLUE + tokenx1)
+ verbout(
+ colors.ORANGE,
+ " [+] Shannon Entropy: "
+ + colors.GREEN
+ + "%s" % (calcEntropy(tokenx1)),
+ )
+ verbout(colors.CYAN, " [+] Second Token: " + colors.BLUE + tokenx2)
+ verbout(
+ colors.ORANGE,
+ " [+] Shannon Entropy: "
+ + colors.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)+'%')
+ verbout(
+ colors.CYAN,
+ " [+] Edit Distance Calculated: " + colors.GREY + str(m) + "%",
+ )
# Now its time to detect the alignment ratio
n = stringdist.rdlevenshtein_norm(tokenx1, tokenx2)
- verbout(color.CYAN, ' [+] Alignment Ratio Calculated: '+color.GREY+str(n))
+ verbout(
+ colors.CYAN,
+ " [+] Alignment Ratio Calculated: " + colors.GREY + str(n),
+ )
# If both tokens are same, then
if len(tokenx1) == len(tokenx2):
- verbout(C, 'Token length calculated is same: '+color.ORANGE+'Each %s bytes' % len(byteString(tokenx1)))
+ verbout(
+ colors.C,
+ "Token length calculated is same: "
+ + colors.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))))
+ verbout(
+ colors.C,
+ "Token length calculated is different: "
+ + colors.ORANGE
+ + "By %s bytes"
+ % (len(byteString(tokenx1)) - len(byteString(tokenx2))),
+ )
time.sleep(0.5)
# In my experience with web security assessments, often the Anti-CSRF token
# is composed of two parts, one of them remains static while the other one dynamic.
@@ -64,40 +103,190 @@ def Analysis():
# 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)... ')
- 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.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+'!\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!')
- 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+'!\n')
- VulnLogger('Analysis', 'Tokens can easily be Forged by Bruteforcing/Guessing.', '[i] Token 1: '+tokenx1+'\n[i] Token 2: '+tokenx2)
+ tokenx01 = tokenx1.replace(p, "")
+ tokenx02 = tokenx2.replace(p, "")
+ if n == 0.5 or m == len(tokenx1) / 2:
+ verbout(
+ colors.GR,
+ "The tokens are composed of 2 parts (one static and other dynamic)... ",
+ )
+ verbout(
+ colors.C,
+ "Static Part : "
+ + colors.GREY
+ + p
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(p)),
+ )
+ verbout(
+ colors.O,
+ "Dynamic Part of Token 0x1: "
+ + colors.GREY
+ + tokenx01
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(tokenx01)),
+ )
+ verbout(
+ colors.O,
+ "Dynamic Part of Token 0x2: "
+ + colors.GREY
+ + tokenx02
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(tokenx02)),
+ )
+ if len(len(tokenx1) / 2) <= 6:
+ verbout(
+ colors.RED,
+ " [-] Post-Analysis reveals that token might be "
+ f"{colors.BR} VULNERABLE {colors.END}",
+ )
+ print(f"{colors.RED} [+] Possible CSRF Vulnerability Detected!")
+ print(
+ f"{colors.ORANGE} [!] Vulnerability Type: "
+ f"{colors.BR} Weak Dynamic Part of Tokens {colors.END}"
+ )
+ print(
+ f"{colors.GREY} [+] Tokens can easily be "
+ f"{colors.RED}Forged by Bruteforcing/Guessing{colors.END}!"
+ )
+ 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(
+ colors.R,
+ "Token distance calculated is " + colors.RED + "less than 0.5!",
+ )
+ verbout(
+ colors.C,
+ "Static Part : "
+ + colors.GREY
+ + p
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(p)),
+ )
+ verbout(
+ colors.O,
+ "Dynamic Part of Token 0x1: "
+ + colors.GREY
+ + tokenx01
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(tokenx01)),
+ )
+ verbout(
+ colors.O,
+ "Dynamic Part of Token 0x2: "
+ + colors.GREY
+ + tokenx02
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(tokenx02)),
+ )
+ verbout(
+ colors.RED,
+ " [-] Post-Analysis reveals that token might be "
+ + colors.BR
+ + " VULNERABLE "
+ + colors.END,
+ )
+ print(colors.GREEN + " [+] Possible CSRF Vulnerability Detected!")
+ print(
+ colors.ORANGE
+ + " [!] Vulnerability Type: "
+ + colors.BR
+ + " Weak Dynamic Part of Tokens "
+ + colors.END
+ )
+ print(
+ colors.GREY
+ + " [+] Tokens can easily be "
+ + colors.RED
+ + "Forged by Bruteforcing/Guessing"
+ + colors.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!')
- 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+'!\n')
- NovulLogger('Analysis', 'Tokens cannot be Forged by Bruteforcing/Guessing.')
+ verbout(
+ colors.R,
+ "Token distance calculated is "
+ + colors.GREEN
+ + "greater than 0.5!",
+ )
+ verbout(
+ colors.C,
+ "Static Part : "
+ + colors.GREY
+ + p
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(p)),
+ )
+ verbout(
+ colors.O,
+ "Dynamic Part of Token 0x1: "
+ + colors.GREY
+ + tokenx01
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(tokenx01)),
+ )
+ verbout(
+ colors.O,
+ "Dynamic Part of Token 0x2: "
+ + colors.GREY
+ + tokenx02
+ + colors.END
+ + " | Length: "
+ + colors.CYAN
+ + str(len(tokenx02)),
+ )
+ verbout(
+ colors.GREEN,
+ " [+] Post-Analysis reveals that tokens are "
+ + colors.BG
+ + " NOT VULNERABLE "
+ + colors.END,
+ )
+ print(
+ colors.ORANGE
+ + " [!] Vulnerability Mitigation: "
+ + colors.BG
+ + " Strong Dynamic Part of Tokens "
+ + colors.END
+ )
+ print(
+ colors.GREY
+ + " [+] Tokens "
+ + colors.GREEN
+ + "Cannot be Forged by Bruteforcing/Guessing"
+ + colors.END
+ + "!\n"
+ )
+ NovulLogger(
+ "Analysis", "Tokens cannot be Forged by Bruteforcing/Guessing."
+ )
time.sleep(1)
except KeyboardInterrupt:
- ctr+=1
+ ctr += 1
continue
- print(C+'Post-Scan Analysis Completed!')
+ print(colors.C + "Post-Scan Analysis Completed!")
diff --git a/xsrfprobe/modules/Checkpost.py b/xsrfprobe/modules/Checkpost.py
index 1e4ac4f..49264d2 100644
--- a/xsrfprobe/modules/Checkpost.py
+++ b/xsrfprobe/modules/Checkpost.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -13,32 +13,43 @@
import time
import difflib
from urllib.parse import urlencode
-from xsrfprobe.core.colors import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.logger import VulnLogger
from xsrfprobe.files.config import POC_GENERATION, GEN_MALICIOUS
from xsrfprobe.modules.Generator import GenNormalPoC, GenMalicious
-def PostBased(url, r1, r2, r3, m_action, result, genpoc, form, m_name=''):
- '''
+
+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
+ """
+ verbout(colors.RED, "\n +------------------------------+")
+ verbout(colors.RED, " | POST-Based Forgery Check |")
+ verbout(colors.RED, " +------------------------------+\n")
+ verbout(colors.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...')
+ verbout(colors.O, "Matching results...")
+
for n in checkdiffx1:
- if re.match('\+|-', n): # get regex matching stuff only +/-
+ if re.match(r"\+|-", 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(r"\+|-", n): # get regex matching stuff
result13.append(n) # append to existing list
# This logic is based purely on the assumption on the difference of various requests
@@ -48,31 +59,56 @@ def PostBased(url, r1, r2, r3, m_action, result, genpoc, form, m_name=''):
#
# 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')
+ print(f"{colors.GREEN} [+] CSRF Vulnerability Detected : {colors.ORANGE}{url}!")
+ print(
+ f"{colors.ORANGE} [!] Vulnerability Type: {colors.BR} POST-Based Request Forgery {colors.END}"
+ )
+ VulnLogger(
+ url,
+ "POST-Based Request Forgery on Forms.",
+ f"[i] Form: {form}\n" f"[i] POST Query: {result}\n",
+ )
time.sleep(0.3)
- verbout(O, 'PoC of response and request...')
+ verbout(colors.O, "PoC of response and request...")
+
if m_name:
- 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
- if m_action.count('/') > 1:
- print(color.GREEN+' [+] Action : ' +color.END+'/'+m_action.rsplit('/', 1)[1]) # action
+ print(f"{colors.RED}\n +-----------------+")
+ print(f"{colors.RED} | Request PoC |")
+ print(f"{colors.RED} +-----------------+\n")
+ print(f"{colors.BLUE} [+] URL : {colors.CYAN}{url}") # url part
+ print(f"{colors.CYAN} [+] Name : {colors.ORANGE}{m_name}") # name
+
+ if m_action.count("/") > 1:
+ print(
+ colors.GREEN
+ + " [+] Action : "
+ + colors.END
+ + "/"
+ + m_action.rsplit("/", 1)[1]
+ ) # action
else:
- print(color.GREEN+' [+] Action : ' +color.END+m_action) # action
+ print(colors.GREEN + " [+] Action : " + colors.END + m_action) # action
else: # if value m['name'] not there :(
- print(color.RED+'\n +-----------------+')
- print(color.RED+' | Request PoC |')
- print(color.RED+' +-----------------+\n')
- print(color.BLUE+' [+] URL : ' +color.CYAN+url) # the url
- if m_action.count('/') > 1:
- print(color.GREEN+' [+] Action : ' +color.END+'/'+m_action.rsplit('/', 1)[1]) # action
+ print(f"{colors.RED} \n +-----------------+")
+ print(f"{colors.RED} | Request PoC |")
+ print(f"{colors.RED} +-----------------+\n")
+ print(colors.BLUE + " [+] URL : " + colors.CYAN + url) # the url
+ if m_action.count("/") > 1:
+ print(
+ colors.GREEN
+ + " [+] Action : "
+ + colors.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())
+ print(colors.GREEN + " [+] Action : " + colors.END + m_action) # action
+ print(
+ colors.ORANGE
+ + " [+] POST Query : "
+ + colors.GREY
+ + urlencode(result).strip()
+ )
# If option --skip-poc hasn't been supplied...
if POC_GENERATION:
# If --malicious has been supplied
diff --git a/xsrfprobe/modules/Cookie.py b/xsrfprobe/modules/Cookie.py
index 0cd4cb1..853b5a7 100644
--- a/xsrfprobe/modules/Cookie.py
+++ b/xsrfprobe/modules/Cookie.py
@@ -1,106 +1,139 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-import time, os, sys
+import sys
from re import search, I
-from xsrfprobe.core.colors import *
-from xsrfprobe.files.config import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+from xsrfprobe.files.config import HEADER_VALUES, USER_AGENT, COOKIE_VALUE, REFERER_URL
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.request import Get
from xsrfprobe.core.randua import RandomAgent
from xsrfprobe.modules.Persistence import Persistence
from xsrfprobe.core.logger import VulnLogger, NovulLogger
-from urllib.parse import urlencode, unquote, urlsplit
+from urllib.parse import urlsplit
resps = []
+
def SameSite(url):
- '''
+ """
This function parses and verifies the cookies with
SameSite Flags.
- '''
- verbout(color.RED, '\n +------------------------------------+')
- verbout(color.RED, ' | Cross Origin Cookie Validation |')
- verbout(color.RED, ' +------------------------------------+\n')
+ """
+ verbout(colors.RED, "\n +------------------------------------+")
+ verbout(colors.RED, " | Cross Origin Cookie Validation |")
+ verbout(colors.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...')
+ verbout(colors.GREY, " [+] Lets examine how server reacts to same referer...")
gen_headers = HEADER_VALUES
- gen_headers['User-Agent'] = USER_AGENT if USER_AGENT else RandomAgent()
- verbout(GR,'Setting Referer header same as host...')
+ gen_headers["User-Agent"] = USER_AGENT if USER_AGENT else RandomAgent()
+ verbout(colors.GR, "Setting Referer header same as host...")
# Setting the netloc as the referer for the first check.
- gen_headers['Referer'] = urlsplit(url).netloc
+ gen_headers["Referer"] = urlsplit(url).netloc
if COOKIE_VALUE:
- gen_headers['Cookie'] = ','.join(cookie for cookie in COOKIE_VALUE)
+ gen_headers["Cookie"] = ",".join(cookie for cookie in COOKIE_VALUE)
getreq = Get(url, headers=gen_headers) # Making the request
- map(HEADER_VALUES.pop, ['Referer', 'Cookie'])
+ map(HEADER_VALUES.pop, ["Referer", "Cookie"])
head = getreq.headers
for h in head:
- #if search('cookie', h, I) or search('set-cookie', h, I):
- if 'Cookie'.lower() in h.lower():
- verbout(G,'Found cookie header value...')
+ # if search('cookie', h, I) or search('set-cookie', h, I):
+ if "Cookie".lower() in h.lower():
+ verbout(colors.G, "Found cookie header value...")
cookieval = head[h]
- verbout(color.ORANGE,' [+] Cookie Received: '+color.CYAN+str(cookieval))
- m = cookieval.split(';')
- verbout(GR,'Examining Cookie...')
+ verbout(
+ colors.ORANGE, " [+] Cookie Received: " + colors.CYAN + str(cookieval)
+ )
+ m = cookieval.split(";")
+ verbout(colors.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, I):
+ verbout(
+ colors.G,
+ "SameSite Flag " + colors.ORANGE + " detected on cookie!",
+ )
foundx1 = 0x01
- q = q.split('=')[1].strip()
- verbout(C, 'Cookie: '+color.ORANGE+q)
+ q = q.split("=")[1].strip()
+ verbout(colors.C, "Cookie: " + colors.ORANGE + q)
break
else:
foundx3 = 0x02
if foundx1 == 0x01:
- verbout(R,' [+] Endpoint '+color.ORANGE+'SameSite Flag Cookie Validation'+color.END+' Present!')
+ verbout(
+ colors.R,
+ " [+] Endpoint "
+ + colors.ORANGE
+ + "SameSite Flag Cookie Validation"
+ + colors.END
+ + " Present!",
+ )
# Step 2: Now we check security mechanisms when the Referer is
# different, i.e. request originates from a different url other
# than the host. (This time without the Cookie assigned)
- verbout(color.GREY,' [+] Lets examine how server reacts to a fake external referer...')
+ verbout(
+ colors.GREY, " [+] Lets examine how server reacts to a fake external referer..."
+ )
gen_headers = HEADER_VALUES
- gen_headers['User-Agent'] = USER_AGENT if USER_AGENT else RandomAgent() # Setting user-agents
+ gen_headers["User-Agent"] = (
+ USER_AGENT if USER_AGENT else RandomAgent()
+ ) # Setting user-agents
# Assigning a fake referer for the second check, but no cookie.
- gen_headers['Referer'] = REFERER_URL
- gen_headers.pop('Cookie', None)
+ gen_headers["Referer"] = REFERER_URL
+ gen_headers.pop("Cookie", None)
getreq = Get(url, headers=gen_headers)
- HEADER_VALUES.pop('Referer', None)
+ HEADER_VALUES.pop("Referer", None)
head = getreq.headers # Getting headers from requests
for h in head:
# If search('cookie', h, I) or search('set-cookie', h, I):
- if 'Cookie'.lower() in h.lower():
- verbout(G,'Found cookie header value...')
+ if "Cookie".lower() in h.lower():
+ verbout(colors.G, "Found cookie header value...")
cookieval = head[h]
- verbout(color.ORANGE,' [+] Cookie Received: '+color.CYAN+str(cookieval))
- m = cookieval.split(';')
- verbout(GR,'Examining Cookie...')
+ verbout(
+ colors.ORANGE, " [+] Cookie Received: " + colors.CYAN + str(cookieval)
+ )
+ m = cookieval.split(";")
+ verbout(colors.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, I):
+ verbout(
+ colors.G,
+ "SameSite Flag " + colors.ORANGE + " detected on cookie!",
+ )
foundx2 = 0x01
- q = q.split('=')[1].strip()
- verbout(C, 'Cookie: '+color.ORANGE+q)
+ q = q.split("=")[1].strip()
+ verbout(colors.C, "Cookie: " + colors.ORANGE + q)
break
else:
foundx3 = 0x02
if foundx1 == 0x01:
- verbout(R,' [+] Endpoint '+color.ORANGE+'SameSite Flag Cookie Validation'+color.END+' Present!')
+ verbout(
+ colors.R,
+ " [+] Endpoint "
+ + colors.ORANGE
+ + "SameSite Flag Cookie Validation"
+ + colors.END
+ + " Present!",
+ )
# Step 3: And finally comes the most important step. Lets see how
# the site reacts to a valid cookie (ofc supplied by the user) coming
@@ -108,65 +141,138 @@ def SameSite(url):
# This is the most crucial part of the detection.
#
# TODO: Improve the logic in detection.
- verbout(color.GREY,' [+] Lets examine how server reacts to valid cookie from a different referer...')
+ verbout(
+ colors.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()
+ gen_headers["User-Agent"] = USER_AGENT or RandomAgent()
# Assigning a fake referer for third request, this time with cookie ;)
- gen_headers['Referer'] = REFERER_URL
+ gen_headers["Referer"] = REFERER_URL
if COOKIE_VALUE:
- gen_headers['Cookie'] = ','.join(cookie for cookie in COOKIE_VALUE)
+ gen_headers["Cookie"] = ",".join(cookie for cookie in COOKIE_VALUE)
getreq = Get(url, headers=gen_headers)
- HEADER_VALUES.pop('Referer', None)
+ HEADER_VALUES.pop("Referer", None)
head = getreq.headers
for h in head:
# if search('cookie', h, I) or search('set-cookie', h, I):
- if 'Cookie'.lower() in h.lower():
- verbout(G,'Found cookie header value...')
+ if "Cookie".lower() in h.lower():
+ verbout(colors.G, "Found cookie header value...")
cookieval = head[h]
- verbout(color.ORANGE,' [+] Cookie Received: '+color.CYAN+str(cookieval))
- m = cookieval.split(';')
- verbout(GR,'Examining Cookie...')
+ verbout(
+ colors.ORANGE, " [+] Cookie Received: " + colors.CYAN + str(cookieval)
+ )
+ m = cookieval.split(";")
+ verbout(colors.GR, "Examining Cookie...")
for q in m:
- if search('samesite', q.lower(), I):
- verbout(G,'SameSite Flag '+color.ORANGE+' detected on cookie on Cross Origin Request!')
+ if search("samesite", q.lower(), I):
+ verbout(
+ colors.G,
+ "SameSite Flag "
+ + colors.ORANGE
+ + " detected on cookie on Cross Origin Request!",
+ )
foundx3 = 0x01
- q = q.split('=')[1].strip()
- verbout(C, 'Cookie: '+color.ORANGE+q)
+ q = q.split("=")[1].strip()
+ verbout(colors.C, "Cookie: " + colors.ORANGE + q)
break
else:
foundx3 = 0x02
if foundx1 == 0x01:
- verbout(R,'Endpoint '+color.ORANGE+'SameSite Flag Cookie Validation'+color.END+' is Present!')
+ verbout(
+ colors.R,
+ "Endpoint "
+ + colors.ORANGE
+ + "SameSite Flag Cookie Validation"
+ + colors.END
+ + " is Present!",
+ )
if (foundx1 == 0x01 and foundx3 == 0x00) and (foundx2 == 0x00 or foundx2 == 0x01):
- 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.')
+ print(
+ colors.GREEN
+ + " [+] Endpoint "
+ + colors.BG
+ + " NOT VULNERABLE to ANY type of CSRF attacks! "
+ + colors.END
+ )
+ print(
+ colors.GREEN
+ + " [+] Protection Method Detected : "
+ + colors.BG
+ + " SameSite Flag on Cookies "
+ + colors.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')
+ oq = input(colors.BLUE + " [+] Continue scanning? (y/N) :> ")
+ if oq.lower().startswith("n"):
+ sys.exit("\n" + colors.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.')
+ print(
+ colors.GREEN
+ + " [+] Endpoint "
+ + colors.BG
+ + " NOT VULNERABLE "
+ + colors.END
+ + colors.GREEN
+ + " to CSRF attacks!"
+ )
+ print(
+ colors.GREEN
+ + " [+] Type: "
+ + colors.BG
+ + " No Cookie Set while Cross Origin Requests "
+ + colors.END
+ )
+ NovulLogger(url, "No cookie set on Cross-Origin Requests.")
else:
- 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.CYAN+ ' [+] Possible CSRF Vulnerability Detected : '+color.GREY+url+'!')
- 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))
+ verbout(
+ colors.R,
+ "Endpoint "
+ + colors.ORANGE
+ + "Cross Origin Cookie Validation"
+ + colors.END
+ + " Not Present!",
+ )
+ verbout(
+ colors.R,
+ "Heuristic(s) reveal endpoint might be "
+ + colors.BY
+ + " VULNERABLE "
+ + colors.END
+ + " to CSRFs...",
+ )
+ print(
+ colors.CYAN
+ + " [+] Possible CSRF Vulnerability Detected : "
+ + colors.GREY
+ + url
+ )
+ print(
+ colors.ORANGE
+ + " [!] Possible Vulnerability Type: "
+ + colors.BY
+ + " No Cross Origin Cookie Validation Presence "
+ + colors.END
+ )
+ VulnLogger(
+ url,
+ "No Cookie Validation on Cross-Origin Requests.",
+ "[i] Headers: " + str(head),
+ )
+
def Cookie(url, request):
- '''
+ """
This module is for checking the varied HTTP Cookies
and the related security on them to
prevent CSRF attacks.
- '''
- verbout(GR, 'Proceeding for cookie based checks...')
+ """
+ verbout(colors.GR, "Proceeding for cookie based checks...")
SameSite(url)
- Persistence(url, request)
\ No newline at end of file
+ Persistence(url, request)
diff --git a/xsrfprobe/modules/Crawler.py b/xsrfprobe/modules/Crawler.py
index 857f032..7db548f 100644
--- a/xsrfprobe/modules/Crawler.py
+++ b/xsrfprobe/modules/Crawler.py
@@ -1,37 +1,51 @@
#!/usr/bin/env python3
-#-*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
-#Author: 0xInfection
-#This module requires XSRFProbe
-#https://github.com/0xInfection/XSRFProbe
+# Author: 0xInfection
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
-import re, sys
+import re
import urllib.error
+import urllib.parse
from bs4 import BeautifulSoup
+
from xsrfprobe.modules import Parser
-from xsrfprobe.core.colors import *
-from xsrfprobe.files.config import *
-from xsrfprobe.files.dcodelist import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+from xsrfprobe.files.config import EXCLUDE_DIRS
+from xsrfprobe.files.dcodelist import (
+ RID_DOUBLE,
+ RID_COMPILE,
+ RID_SINGLE,
+ NUM_COM,
+ NUM_SUB,
+)
from xsrfprobe.core.request import Get
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.logger import ErrorLogger
from xsrfprobe.files.discovered import INTERNAL_URLS
-class Handler(): # Main Crawler Handler
- '''
+
+class Handler: # Main Crawler Handler
+ """
This is a crawler that is used to fetch all the Urls
associated to the HTML page, and susequently
crawl them and build checks for CSRFs.
- '''
+ """
+
def __init__(self, start, opener):
self.visited = [] # Visited stuff
self.toVisit = [] # To visit
self.uriPatterns = [] # Patterns to follow
- self.currentURI = '' # What is it now?
+ self.currentURI = "" # What is it now?
self.opener = opener # Init build_opener
self.toVisit.append(start) # Lets add up urls
@@ -63,14 +77,19 @@ def process(self, root):
url = self.currentURI # Main Url (Current)
try:
query = Get(url) # Open it (to check if it exists)
- if query != None and not str(query.status_code).startswith('40'): # Avoiding 40x errors
+ if query != None and not str(query.status_code).startswith(
+ "40"
+ ): # Avoiding 40x errors
INTERNAL_URLS.append(url) # We append it to the list of valid urls
else:
if url in self.toVisit:
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__())
+ except (
+ urllib.error.HTTPError,
+ urllib.error.URLError,
+ ) as msg: # Incase there is an exception connecting to Url
+ verbout(colors.R, "HTTP Request Error: " + msg.__str__())
ErrorLogger(url, msg.__str__())
if url in self.toVisit:
self.toVisit.remove(url) # Remove non-existent / errored urls
@@ -78,47 +97,82 @@ def process(self, root):
# 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']):
+ if not query or not re.search("html", query.headers["Content-Type"]):
return None
# Just in case there is a redirection, we are supposed to follow it :D
- verbout(GR, 'Making request to new location...')
- if hasattr(query.headers, 'Location'):
- url = query.headers['Location']
- verbout(O,'Reading response...')
+ verbout(colors.GR, "Making request to new location...")
+
+ if hasattr(query.headers, "Location"):
+ url = query.headers["Location"]
+
+ verbout(colors.O, "Reading response...")
response = query.content # Read the response contents
try:
- verbout(O, 'Trying to parse response...')
+ verbout(colors.O, "Trying to parse crawler response...")
soup = BeautifulSoup(response) # Parser init
+ verbout(colors.O, "Done parsing crawler response...")
- except HTMLParser.HTMLParseError:
- verbout(R, 'BeautifulSoup Error: '+url)
+ except Exception:
+ verbout(colors.R, f"BeautifulSoup Error: {url}")
self.visited.append(url)
if url in self.toVisit:
self.toVisit.remove(url)
return None
- for m in soup.findAll('a', href=True): # find out all href^?://*
- app = ''
+ verbout(colors.O, "Processing 'a' elements...")
+ 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']):
- app = Parser.buildUrl(url, m['href'])
+ if not re.match(r"javascript:", m["href"]) or re.match(
+ r"http(s?)://", m["href"]
+ ):
+ app = Parser.buildUrl(url, m["href"])
# If we get a valid link
- if app!='' and re.search(root, app):
+ if app != "" and re.search(root, app):
# Getting rid of Urls starting with '../../../..'
- while re.search(RID_DOUBLE, app):
- p = re.compile(RID_COMPILE)
- app = p.sub('/', app)
- # Getting rid of Urls starting with './'
+ res = urllib.parse.urlparse(app)
+ path = res.path
+
+ if "../" in path: # lets remove it
+ # Remove starting ""/../"
+ while path.startswith("/../"):
+ path = path[len("/..") :]
+
+ endless_loop = 0
+ while re.search(pattern=RID_DOUBLE, string=path):
+ endless_loop += 1
+
+ # Prevent an endless loop here
+ if endless_loop > 100:
+ verbout(
+ colors.R,
+ f"URL is causing an endless loop: {app}, "
+ "resetting path to: /",
+ )
+ path = "/"
+ break
+
+ p = re.compile(RID_COMPILE)
+ path = p.sub(repl="/", string=path)
+
+ # Getting rid of URLs starting with './'
p = re.compile(RID_SINGLE)
- app = p.sub('', app)
+ path = p.sub("", path)
+
+ app = f"{res.scheme}://{res.hostname}"
+ if res.port:
+ app += f":{res.port}"
+ app += path
# Add new link to the queue only if its pattern has not been added yet
- uriPattern=removeIDs(app) # remove IDs
+ uriPattern = removeIDs(app) # remove IDs
if self.notExist(uriPattern) and app != url:
- verbout(G, 'Added :> ' +color.BLUE+ app) # display what we have got!
+ verbout(
+ colors.G, f"Added :> {colors.BLUE}{app}"
+ ) # display what we have got!
self.toVisit.append(app) # add up urls to visit
self.uriPatterns.append(uriPattern)
@@ -129,7 +183,7 @@ def getUriPatterns(self): # get uri patterns
return self.uriPatterns
def notExist(self, test): # 404 stuffs
- if (test not in self.uriPatterns): # if non-existent
+ if test not in self.uriPatterns: # if non-existent
return 1
return 0 # else existent
@@ -139,13 +193,14 @@ def addUriPatterns(self, Parser): # append patterns to follow
def addVisited(self, Parser): # visited stuffs added
self.visited.append(Parser)
+
def removeIDs(Parser):
- '''
+ """
This function removes the Numbers from the Urls
which are built.
- '''
+ """
p = re.compile(NUM_SUB)
- Parser = p.sub('=', Parser)
+ Parser = p.sub("=", Parser)
p = re.compile(NUM_COM)
- Parser = p.sub('\\1', Parser)
+ Parser = p.sub("\\1", Parser)
return Parser
diff --git a/xsrfprobe/modules/Debugger.py b/xsrfprobe/modules/Debugger.py
index 220d4cd..190292f 100644
--- a/xsrfprobe/modules/Debugger.py
+++ b/xsrfprobe/modules/Debugger.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -11,141 +11,209 @@
import re
import string
-from xsrfprobe.core.colors import *
from random import Random
-from xsrfprobe.files.config import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+from xsrfprobe.files.config import EMAIL_VALUE, TEXT_VALUE, TOKEN_GENERATION_LENGTH
from xsrfprobe.core.verbout import verbout
-class Form_Debugger():
+class Form_Debugger:
def prepareFormInputs(self, form):
- '''
+ """
This method parses form types and generates strings based
on their input types.
- '''
- verbout(O,'Crafting inputs as form type...')
+ """
+ verbout(colors.O, "Crafting inputs as form type...")
cr_input = {}
totcr = []
- verbout(GR, 'Processing '+color.BOLD+'= 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.')
+ print(
+ colors.ORANGE
+ + " [+] CSRF Token Length greater than "
+ + colors.CYAN
+ + "256 bytes. "
+ + colors.GREEN
+ + "Token value cannot be guessed/bruteforced..."
+ )
+ print(
+ colors.GREEN
+ + " [+] Endpoint likely "
+ + colors.BG
+ + " NOT VULNERABLE "
+ + colors.END
+ + colors.GREEN
+ + " to CSRF Attacks..."
+ )
+ print(
+ colors.GREEN
+ + " [!] CSRF Mitigation Method: "
+ + colors.BG
+ + " Long Anti-CSRF Tokens "
+ + colors.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...')
+ verbout(
+ colors.O,
+ "Proceeding to calculate "
+ + colors.GREY
+ + "Shannon Entropy"
+ + colors.END
+ + " of Token audited...",
+ )
+
entropy = calcEntropy(value)
- verbout(GR, 'Calculating Entropy...')
- verbout(color.BLUE, ' [+] Entropy Calculated: '+color.CYAN+str(entropy))
+ verbout(colors.GR, "Calculating Entropy...")
+ verbout(colors.BLUE, " [+] Entropy Calculated: " + colors.CYAN + str(entropy))
+
if entropy >= min_entropy:
- verbout(color.ORANGE,' [+] Anti-CSRF Token Entropy Calculated is '+color.BY+' GREATER than 3.0 '+color.END+'... ')
- print(color.ORANGE+' [+] Endpoint '+color.BY+' PROBABLY NOT VULNERABLE '+color.END+color.ORANGE+' to CSRF Attacks...')
- print(color.ORANGE+' [!] CSRF Mitigation Method: '+color.BY+' High Entropy Anti-CSRF Tokens '+color.END)
- NovulLogger(url, 'High Entropy Anti-CSRF Tokens.')
+ verbout(
+ colors.ORANGE,
+ " [+] Anti-CSRF Token Entropy Calculated is "
+ + colors.BY
+ + " GREATER than 3.0 "
+ + colors.END
+ + "... ",
+ )
+ print(
+ colors.ORANGE
+ + " [+] Endpoint "
+ + colors.BY
+ + " PROBABLY NOT VULNERABLE "
+ + colors.END
+ + colors.ORANGE
+ + " to CSRF Attacks..."
+ )
+ print(
+ colors.ORANGE
+ + " [!] CSRF Mitigation Method: "
+ + colors.BY
+ + " High Entropy Anti-CSRF Tokens "
+ + colors.END
+ )
+ NovulLogger(url, "High Entropy Anti-CSRF Tokens.")
found = 0x01
else:
- verbout(color.RED,' [-] Anti-CSRF Token Entropy Calculated is '+color.BY+' LESS than 3.0 '+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)
+ verbout(
+ colors.RED,
+ " [-] Anti-CSRF Token Entropy Calculated is "
+ + colors.BY
+ + " LESS than 3.0 "
+ + colors.END
+ + "... ",
+ )
+ print(
+ colors.RED
+ + " [-] Endpoint likely "
+ + colors.BR
+ + " VULNERABLE "
+ + colors.END
+ + colors.RED
+ + " to CSRF Attacks inspite of CSRF Tokens..."
+ )
+ print(
+ colors.RED
+ + " [!] Vulnerability Type: "
+ + colors.BR
+ + " Low Entropy Anti-CSRF Tokens "
+ + colors.END
+ )
+ VulnLogger(url, "Low Entropy Anti-CSRF Tokens.", "Token: " + value)
+
if found == 0x00:
if m_name:
- print(color.RED+'\n +---------+')
- print(color.RED+' | PoC |')
- print(color.RED+' +---------+\n')
- print(color.BLUE+' [+] URL : ' +color.CYAN+url)
- print(color.CYAN+' [+] Name : ' +color.ORANGE+m_name)
- print(color.GREEN+' [+] Action : ' +color.ORANGE+m_action)
+ print(colors.RED + "\n +---------+")
+ print(colors.RED + " | PoC |")
+ print(colors.RED + " +---------+\n")
+ print(colors.BLUE + " [+] URL : " + colors.CYAN + url)
+ print(colors.CYAN + " [+] Name : " + colors.ORANGE + m_name)
+ print(colors.GREEN + " [+] Action : " + colors.ORANGE + m_action)
else: # if value m_name not there :(
- print(color.RED+'\n +---------+')
- print(color.RED+' | PoC |')
- print(color.RED+' +---------+\n')
- print(color.BLUE+' [+] URL : ' +color.CYAN+url)
- print(color.GREEN+' [+] Action : ' +color.ORANGE+m_action)
+ print(colors.RED + "\n +---------+")
+ print(colors.RED + " | PoC |")
+ print(colors.RED + " +---------+\n")
+ print(colors.BLUE + " [+] URL : " + colors.CYAN + url)
+ print(colors.GREEN + " [+] Action : " + colors.ORANGE + m_action)
# Print out the params
- print(color.ORANGE+' [+] Query : '+color.GREY+urllib.parse.urlencode(req))
- print('')
+ print(
+ colors.ORANGE + " [+] Query : " + colors.GREY + urllib.parse.urlencode(req)
+ )
+ print("")
+
return (_q, para) # Return the query paramter and anti-csrf token
+
def calcEntropy(data):
"""
This function is used to calculate
@@ -119,8 +219,8 @@ def calcEntropy(data):
entropy = 0 # init
for x in range(256):
- p_x = float(data.count(chr(x)))/len(data)
+ p_x = float(data.count(chr(x))) / len(data)
if p_x > 0:
- entropy += - p_x * log(p_x, 2)
+ entropy += -p_x * log(p_x, 2)
return entropy
diff --git a/xsrfprobe/modules/Generator.py b/xsrfprobe/modules/Generator.py
index b70bf64..ce89aea 100644
--- a/xsrfprobe/modules/Generator.py
+++ b/xsrfprobe/modules/Generator.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
@@ -11,8 +11,12 @@
from ast import literal_eval
from bs4 import BeautifulSoup
-from yattag import Doc, indent
-from xsrfprobe.core.colors import *
+from yattag import Doc
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
from xsrfprobe.core.verbout import verbout
from xsrfprobe.files.config import OUTPUT_DIR
from xsrfprobe.core.prettify import formPrettify
@@ -20,84 +24,129 @@
doc, tag, text = Doc().tagtext()
-def GenNormalPoC(action, fields, method='POST', encoding_type='application/x-www-form-urlencoded'):
+
+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)
+ print(colors.GR + "Generating normal PoC Form...")
+ verbout(colors.RED, "\n +---------------------+")
+ verbout(colors.RED, " | Normal Form PoC |")
+ verbout(colors.RED, " +---------------------+\n" + colors.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')
+ 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"):
+ 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'])
+ 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')
+ doc.stag("input", value="Submit", type="submit")
+ doc.stag("br")
# Brand tag :p ...I guess...
- with tag('small'):
- text('(o) This form was generated by ')
- with tag('a', href='https://github.com/0xInfection/xsrfprobe'):
- text('XSRFProbe')
- text('.')
- content = BeautifulSoup(doc.getvalue(), 'html.parser')
+ with tag("small"):
+ text("(o) 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('')
+ print("")
# Write out the file af...
- if '//' in action:
- splitterfunc = action.split('//', 1)[1].replace('/','-')
- else: splitterfunc = action.replace('/', '-')
- fi = open(OUTPUT_DIR+splitterfunc+'-csrf-poc.html', 'w+', encoding='utf-8')
+ if "//" in action:
+ splitterfunc = action.split("//", 1)[1].replace("/", "-")
+ else:
+ splitterfunc = action.replace("/", "-")
+ fi = open(OUTPUT_DIR + splitterfunc + "-csrf-poc.html", "w+", encoding="utf-8")
fi.write(content.prettify())
fi.close()
- print(G+'PoC successfully saved under '+color.ORANGE+OUTPUT_DIR+splitterfunc+'-csrf-poc.html')
+ print(
+ colors.G
+ + "PoC successfully saved under "
+ + colors.ORANGE
+ + OUTPUT_DIR
+ + splitterfunc
+ + "-csrf-poc.html"
+ )
+
-def GenMalicious(action, fields, method='POST', encoding_type='application/x-www-form-urlencoded'):
+def GenMalicious(
+ action, fields, method="POST", encoding_type="application/x-www-form-urlencoded"
+):
"""
- Generate a malicious CSRF PoC using basic form data
+ 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)
+ print(colors.GR, "Generating malicious PoC Form...")
+ verbout(colors.RED, "\n +------------------------+")
+ verbout(colors.RED, " | Malicious Form PoC |")
+ verbout(colors.RED, " +------------------------+\n" + colors.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'):
+ 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"):
+ 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)
+ if not field["value"]:
+ val = input(
+ colors.C
+ + "Enter value for form field "
+ + colors.GREEN
+ + field["name"].title()
+ + " :> "
+ + colors.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'):
+ 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')
+ doc.asis(
+ ""
+ )
+ content = BeautifulSoup(doc.getvalue(), "html.parser")
formPrettify(indentPrettify(content))
- print('')
+ print("")
# Write out the file af...
- if '//' in action:
- splitterfunc = action.split('//', 1)[1].replace('/','-')
- else: splitterfunc = action.replace('/', '-')
- fi = open(OUTPUT_DIR+splitterfunc+'-csrf-poc.html', 'w+', encoding='utf-8')
+ if "//" in action:
+ splitterfunc = action.split("//", 1)[1].replace("/", "-")
+ else:
+ splitterfunc = action.replace("/", "-")
+ fi = open(OUTPUT_DIR + splitterfunc + "-csrf-poc.html", "w+", encoding="utf-8")
fi.write(content.prettify())
fi.close()
- print(G+'PoC successfully saved under '+color.ORANGE+OUTPUT_DIR+splitterfunc+'-malicious-poc.html')
+ print(
+ colors.G
+ + "PoC successfully saved under "
+ + colors.ORANGE
+ + OUTPUT_DIR
+ + splitterfunc
+ + "-malicious-poc.html"
+ )
diff --git a/xsrfprobe/modules/Origin.py b/xsrfprobe/modules/Origin.py
index 794a57f..0b6736a 100644
--- a/xsrfprobe/modules/Origin.py
+++ b/xsrfprobe/modules/Origin.py
@@ -1,48 +1,54 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-import requests
-from xsrfprobe.core.colors import *
-from xsrfprobe.files.config import *
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+from xsrfprobe.files.config import HEADER_VALUES, ORIGIN_URL, COOKIE_VALUE
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.request import Get
-from xsrfprobe.core.randua import RandomAgent
from xsrfprobe.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')
+ global HEADER_VALUES
+
+ verbout(colors.RED, "\n +-------------------------------------+")
+ verbout(colors.RED, " | Origin Based Request Validation |")
+ verbout(colors.RED, " +-------------------------------------+\n")
# Make the request normally and get content
- verbout(O,'Making request on normal basis...')
+ verbout(colors.O, "Making request on normal basis...")
req0x01 = Get(url)
- global HEADER_VALUES
# Set a fake Origin along with UA (pretending to be a
# legitimate request from a browser)
- verbout(GR,'Setting generic headers...')
+ verbout(colors.GR, "Setting generic headers...")
gen_headers = HEADER_VALUES
- gen_headers['Origin'] = ORIGIN_URL
+ gen_headers["Origin"] = ORIGIN_URL
# We put the cookie in request, if cookie supplied :D
if COOKIE_VALUE:
- gen_headers['Cookie'] = ','.join(cookie for cookie in COOKIE_VALUE)
+ gen_headers["Cookie"] = ",".join(cookie for cookie in COOKIE_VALUE)
# Make the request with different Origin header and get the content
- verbout(O,'Making request with '+color.CYAN+'Tampered Origin Header'+color.END+'...')
+ verbout(
+ colors.O,
+ f"Making request with {colors.CYAN}Tampered Origin Header{colors.END}...",
+ )
req0x02 = Get(url, headers=gen_headers)
- HEADER_VALUES.pop('Origin', None)
+ HEADER_VALUES.pop("Origin", None)
# Comparing the length of the requests' responses. If both content
# lengths are same, then the site actually does not validate Origin
@@ -57,16 +63,49 @@ def Origin(url):
# domain.
#
# TODO: This algorithm has lots of room for improvement
+ if req0x01 is None or req0x02 is None:
+ verbout(
+ colors.RED,
+ " [!] Cannot compare the two requests as at least one of them is None",
+ )
+ return False
+
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+'\n')
- NovulLogger(url, 'Presence of Origin Header based request Validation.')
+ verbout(
+ colors.GREEN,
+ f" [+] Endoint {colors.ORANGE}Origin Validation{colors.GREEN} Present!",
+ )
+ print(
+ f"{colors.GREEN} [-] Heuristics reveal endpoint might be "
+ f"{colors.BG} NOT VULNERABLE {colors.END}..."
+ )
+ print(
+ f"{colors.ORANGE} [+] Mitigation Method: "
+ f"{colors.BG} Origin Based Request Validation {colors.END}"
+ )
+ NovulLogger(url, "Presence of Origin Header based request Validation.")
return True
- else:
- 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+' 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
+
+ verbout(
+ colors.R,
+ "Endpoint " + colors.RED + "Origin Validation Not Present" + colors.END,
+ )
+ verbout(
+ colors.R,
+ "Heuristics reveal endpoint might be "
+ f"{colors.BY} VULNERABLE {colors.END} to Origin Based CSRFs...",
+ )
+ print(
+ f"{colors.CYAN} [+] Possible CSRF Vulnerability Detected : "
+ f"{colors.GREY}{url}"
+ )
+ print(
+ f"{colors.ORANGE} [!] Possible Vulnerability Type: {colors.BY}"
+ f" No Origin Based Request Validation {colors.END}"
+ )
+ VulnLogger(
+ url,
+ "No Origin Header based request validation presence.",
+ "[i] Response Headers: " + str(req0x02.headers),
+ )
+ return False
diff --git a/xsrfprobe/modules/Parser.py b/xsrfprobe/modules/Parser.py
index 817b0d2..666e4f5 100644
--- a/xsrfprobe/modules/Parser.py
+++ b/xsrfprobe/modules/Parser.py
@@ -1,25 +1,29 @@
#!/usr/bin/env python3
-#-*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
-#Author: 0xInfection
-#This module requires XSRFProbe
-#https://github.com/0xInfection/XSRFProbe
+# Author: 0xInfection
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
import re
from urllib.parse import urlsplit
from xsrfprobe.core.verbout import verbout
from xsrfprobe.files.dcodelist import PROTOCOLS
from xsrfprobe.files.paramlist import EXCLUSIONS_LIST
-from xsrfprobe.core.colors import * # import ends
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
def buildUrl(url, href): # receive form input type / url
- '''
+ """
This function is for building a proper URL based on comparison to 'href'.
- '''
+ """
# Making an exclusion list, so as to stop detection of Self-CSRF (Logout-CSRF)
#
# This is yet another step is reducing false positives [[ significantly ]].
@@ -27,39 +31,57 @@ def buildUrl(url, href): # receive form input type / url
# of as low quality CSRF (bugs).
#
# TODO: Add more to EXCLUSIONS_LIST.
- if href == "http://localhost" or any((re.search(s, href, re.IGNORECASE)) for s in EXCLUSIONS_LIST):
+ if href == "http://localhost" or any(
+ (re.search(s, href, re.IGNORECASE)) for s in EXCLUSIONS_LIST
+ ):
return None
url_parts = urlsplit(url) # SplitResult(scheme, netloc, path, query, fragment)
+ port_part = ""
+ if url_parts.port is not None:
+ port_part = f":{url_parts.port}"
+
href_parts = urlsplit(href)
- app = '' # Init to the Url that will be built
+ app = "" # Init to the Url that will be built
# If Url and Destination have the same domain...
if href_parts.netloc == url_parts.netloc:
app = href # Assigning the main netloc
+ return app
+
+ # if netloc of href_parts is empty, but we have a path or query
+ # build it from scratch
+ if href_parts.netloc == "" and (href_parts.path != "" or href_parts.query != ""):
+ domain = url_parts.hostname # Assigning the main domain
+ if href_parts.path.startswith("/"):
+ # If the href starts with a '/', it is an internal Url
+ app = f"{url_parts.scheme}://{domain}{port_part}"
+
+ app += f"{href_parts.path}" # Startpage
+ else:
+ try:
+ app = f"{url_parts.scheme}://{domain}{port_part}"
+
+ app += re.findall(PROTOCOLS, url_parts.path)[0] + href_parts.path
+ # Get real protocol urls
+ except IndexError:
+ app = f"{url_parts.scheme}://{domain}{port_part}{href_parts.path}"
+
+ if href_parts.query: # Checking if any queries were there...
+ app += "?" + href_parts.query # Adding the query parameters to Url
- else: # If the destination Url doesn't have a domain
- if len(href_parts.netloc) == 0 and (len(href_parts.path) != 0 or len(href_parts.query) != 0):
- domain = url_parts.netloc # Assigning the main domain
- if href_parts.path.startswith('/'): # If the href starts with a '/', it is an internal Url
- app = url_parts.scheme + '://' + domain + href_parts.path # Startpage
- else:
- try:
- app = 'http://' + domain + re.findall(PROTOCOLS, url_parts.path)[0] + href_parts.path
- # Get real protocol urls
- except IndexError:
- app = 'http://' + domain + href_parts.path
- if href_parts.query: # Checking if any queries were there...
- app += '?' + href_parts.query # Adding the query paramaters to Url
# Return '' for invalid url, url otherwise
return app
+
def buildAction(url, action):
- '''
+ """
The main function of this is to create an action Url based
on Current Location and Destination.
- '''
- verbout(O,'Parsing URL parameters...')
- if action and not action.startswith('#'): # make sure it is not a fragment (eg. http://site.tld/index.php#search)
+ """
+ verbout(colors.O, "Parsing URL parameters...")
+ 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/xsrfprobe/modules/Persistence.py b/xsrfprobe/modules/Persistence.py
index 8cf168b..3319eda 100644
--- a/xsrfprobe/modules/Persistence.py
+++ b/xsrfprobe/modules/Persistence.py
@@ -1,38 +1,45 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-import time, os, sys
-from re import search, I
+import time
from datetime import datetime
-from xsrfprobe.core.colors import *
-from xsrfprobe.files.config import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+from xsrfprobe.files.config import HEADER_VALUES, COOKIE_VALUE
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.request import Get
-from xsrfprobe.core.randua import RandomAgent
from xsrfprobe.core.utils import checkDuplicates
from xsrfprobe.core.logger import VulnLogger, NovulLogger
-from urllib.parse import urlencode, unquote, urlsplit
+
# Response storing list init
resps = []
+
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')
+ """
+ verbout(colors.RED, "\n +-----------------------------------+")
+ verbout(colors.RED, " | Cookie Persistence Validation |")
+ verbout(colors.RED, " +-----------------------------------+\n")
# Checking if user has supplied a value.
- verbout(GR,'Proceeding to test for '+color.GREY+'Cookie Persistence'+color.END+'...')
+ verbout(
+ colors.GR,
+ f"Proceeding to test for {colors.GREY}Cookie Persistence{colors.END}...",
+ )
+
time.sleep(0.7)
found = 0x00
# Now let the real test begin...
@@ -42,26 +49,48 @@ def Persistence(url, postq):
# 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+'...')
- verbout(GR,'Making the request...')
+ # cookies = []
+ verbout(
+ colors.C,
+ f"Proceeding to test cookie persistence via {colors.CYAN}Prepared GET Requests{colors.END}...",
+ )
+ verbout(colors.GR, "Making the request...")
req = Get(url, headers=HEADER_VALUES)
+
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
+ print(
+ f"{colors.GREEN} [+] Persistent Cookies found in Response Headers!"
+ )
+ print(f"{colors.GREY} [+] Cookie: {colors.CYAN}{cook}")
+ # 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__())
+ print(
+ f"{colors.GREEN} [+] Cookie Expiry Period: "
+ f"{colors.ORANGE}{datetime.fromtimestamp(cook.expires)}"
+ )
found = 0x01
- VulnLogger(url, 'Persistent Session Cookies Found.', '[i] Cookie: '+req.headers.get('Set-Cookie'))
+ VulnLogger(
+ url,
+ "Persistent Session Cookies Found.",
+ "[i] Cookie: " + req.headers.get("Set-Cookie"),
+ )
else:
- NovulLogger(url, 'No Persistent Session Cookies.')
+ 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+'...')
+ verbout(
+ colors.R, "No persistent session cookies identified on GET Type Requests!"
+ )
+
+ verbout(
+ colors.C,
+ f"Proceeding to test cookie persistence on {colors.CYAN}POST Requests{colors.END}...",
+ )
# Now its time for POST Based requests.
#
# NOTE: As a standard method, every web application should supply a cookie upon a POST query.
@@ -69,19 +98,42 @@ def Persistence(url, postq):
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__())
+ print(
+ f"{colors.GREEN} [+] Persistent Cookies found in Response Headers!"
+ )
+ print(f"{colors.GREY} [+] Cookie: {colors.CYAN}{cookie}")
# 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__())
+ print(
+ f"{colors.GREEN} [+] Cookie Expiry Period: {colors.ORANGE}"
+ f"{datetime.fromtimestamp(cookie.expires)}"
+ )
found = 0x01
- VulnLogger(url, 'Persistent Session Cookies Found.', '[i] Cookie: '+req.headers.get('Set-Cookie'))
- print(color.ORANGE+' [!] Probable Insecure Practice: '+color.BY+' Persistent Session Cookies '+color.END)
+ VulnLogger(
+ url,
+ "Persistent Session Cookies Found.",
+ "[i] Cookie: " + req.headers.get("Set-Cookie"),
+ )
+ print(
+ colors.ORANGE
+ + " [!] Probable Insecure Practice: "
+ + colors.BY
+ + " Persistent Session Cookies "
+ + colors.END
+ )
else:
- NovulLogger(url, 'No Persistent Cookies.')
+ NovulLogger(url, "No Persistent Cookies (via POST).")
+
if found == 0x00:
- verbout(R, 'No persistent session cookies identified upon POST Requests!')
- print(color.ORANGE+' [+] Endpoint might be '+color.BY+' NOT VULNERABLE '+color.END+color.ORANGE+' to CSRF attacks!')
- print(color.ORANGE+' [+] Detected : '+color.BY+' No Persistent Cookies '+color.END)
+ verbout(
+ colors.R, "No persistent session cookies identified upon POST Requests!"
+ )
+ print(
+ f"{colors.ORANGE} [+] Endpoint might be {colors.BY} NOT VULNERABLE "
+ f"{colors.END}{colors.ORANGE} to CSRF attacks!"
+ )
+ print(
+ f"{colors.ORANGE} [+] Detected : {colors.BY} No Persistent Cookies {colors.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
@@ -90,40 +142,90 @@ def Persistence(url, postq):
#
# 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+'...')
+ verbout(
+ colors.C,
+ "Proceeding to test cookie persistence via "
+ f"{colors.CYAN}User-Agent Alteration{colors.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...')
+ "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(colors.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
+ verbout(colors.C, f"Using User-Agent : {colors.CYAN}{name}")
+ verbout(colors.GR, f"Value : {colors.ORANGE}{agent}")
+ gen_headers["User-Agent"] = agent
+
if COOKIE_VALUE:
- gen_headers['Cookie'] = ','.join(cookie for cookie in COOKIE_VALUE)
+ gen_headers["Cookie"] = ",".join(cookie for cookie in COOKIE_VALUE)
+
req = Get(url, headers=gen_headers)
+
# We will append this to stuff only when set-cookie is being supplied.
- if req.headers.get('Set-Cookie'):
- resps.append(req.headers.get('Set-Cookie'))
- HEADER_VALUES.pop('User-Agent', None)
- HEADER_VALUES['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36'
+ if req.headers.get("Set-Cookie"):
+ resps.append(req.headers.get("Set-Cookie"))
+
+ HEADER_VALUES.pop("User-Agent", None)
+ HEADER_VALUES[
+ "User-Agent"
+ ] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"
+
if resps:
if checkDuplicates(resps):
- verbout(G, 'Set-Cookie header does not change with varied User-Agents...')
- verbout(color.ORANGE, ' [+] Possible persistent session cookies found...')
- print(color.RED+' [+] Possible CSRF Vulnerability Detected : '+color.ORANGE+url+'!')
- print(color.ORANGE+' [!] Probable Insecure Practice: '+color.BY+' Persistent Session Cookies '+color.END)
- VulnLogger(url, 'Persistent Session Cookies Found.', '[i] Cookie: '+req.headers.get('Set-Cookie'))
+ verbout(
+ colors.G,
+ "Set-Cookie header does not change with different User-Agents...",
+ )
+ verbout(
+ colors.ORANGE, " [+] Possible persistent session cookies found..."
+ )
+ print(
+ colors.RED
+ + " [+] Possible CSRF Vulnerability Detected : "
+ + colors.ORANGE
+ + url
+ )
+ print(
+ colors.ORANGE
+ + " [!] Probable Insecure Practice: "
+ + colors.BY
+ + " Persistent Session Cookies "
+ + colors.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...')
- verbout(color.ORANGE, ' [+] Endpoint '+color.BY+' PROBABLY NOT VULNERABLE '+color.END+color.ORANGE+' to CSRF attacks!')
- verbout(color.ORANGE, ' [+] Application Practice Method Detected : '+color.BY+' No Persistent Cookies '+color.END)
- NovulLogger(url, 'No Persistent Cookies.')
+ verbout(
+ colors.G, "Set-Cookie header changes with different User-Agents..."
+ )
+ verbout(colors.R, "No possible persistent session cookies found...")
+ verbout(
+ colors.ORANGE,
+ " [+] Endpoint "
+ + colors.BY
+ + " PROBABLY NOT VULNERABLE "
+ + colors.END
+ + colors.ORANGE
+ + " to CSRF attacks!",
+ )
+ verbout(
+ colors.ORANGE,
+ " [+] Application Practice Method Detected : "
+ + colors.BY
+ + " No Persistent Cookies "
+ + colors.END,
+ )
+ NovulLogger(url, "No Persistent Cookies (via different User-Agent).")
else:
- verbout(R, 'No cookies are being set on any requests.')
\ No newline at end of file
+ verbout(colors.R, "No cookies are being set on any requests.")
diff --git a/xsrfprobe/modules/Referer.py b/xsrfprobe/modules/Referer.py
index fe3dba9..4cdf26c 100644
--- a/xsrfprobe/modules/Referer.py
+++ b/xsrfprobe/modules/Referer.py
@@ -1,49 +1,62 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-import requests
-from xsrfprobe.core.colors import *
-from xsrfprobe.files.config import *
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
+from xsrfprobe.files.config import HEADER_VALUES, REFERER_URL, COOKIE_VALUE
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.request import Get
from xsrfprobe.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')
+ verbout(colors.RED, "\n +--------------------------------------+")
+ verbout(colors.RED, " | Referer Based Request Validation |")
+ verbout(colors.RED, " +--------------------------------------+\n")
# Make the request normally and get content
- verbout(O,'Making request on normal basis...')
+ verbout(colors.O, "Making request on normal basis...")
req0x01 = Get(url)
# Set normal headers...
- verbout(GR,'Setting generic headers...')
+ verbout(colors.GR, "Setting generic headers...")
gen_headers = HEADER_VALUES
# Set a fake Referer along with UA (pretending to be a
# legitimate request from a browser)
- gen_headers['Referer'] = REFERER_URL
+ gen_headers["Referer"] = REFERER_URL
# We put the cookie in request, if cookie supplied :D
if COOKIE_VALUE:
- gen_headers['Cookie'] = ','.join(cookie for cookie in COOKIE_VALUE)
+ gen_headers["Cookie"] = ",".join(cookie for cookie in COOKIE_VALUE)
# Make the request with different referer header and get the content
- verbout(O,'Making request with '+color.CYAN+'Tampered Referer Header'+color.END+'...')
+ verbout(
+ colors.O,
+ f"Making request with {colors.CYAN}Tampered Referer Header{colors.END}...",
+ )
req0x02 = Get(url, headers=gen_headers)
- HEADER_VALUES.pop('Referer', None)
+ HEADER_VALUES.pop("Referer", None)
+
+ if req0x01 is None or req0x02 is None:
+ verbout(
+ colors.RED,
+ " [!] Cannot compare the two requests as at least one of them is None",
+ )
+ return False
# Comparing the length of the requests' responses. If both content
# lengths are same, then the site actually does not validate referer
@@ -59,15 +72,39 @@ def Referer(url):
#
# TODO: This algorithm has lots of room for improvement.
if len(req0x01.content) != len(req0x02.content):
- 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.')
+ print(
+ f"{colors.GREEN} [+] Endoint {colors.ORANGE}Referer Validation{colors.GREEN} Present!"
+ )
+ print(
+ f"{colors.GREEN} [-] Heuristics reveal endpoint might be "
+ f"{colors.BG} NOT VULNERABLE {colors.END}..."
+ )
+ print(
+ f"{colors.ORANGE} [+] Mitigation Method: {colors.BG} "
+ f"Referer Based Request Validation {colors.END}"
+ )
+ NovulLogger(url, "Presence of Referer Header based Request Validation.")
return True
- else:
- 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+' No Referer Based Request Validation '+color.END)
- VulnLogger(url, 'No Referer Header based Request Validation presence.', '[i] Response Headers: '+str(req0x02.headers))
- return False
+
+ verbout(
+ colors.R,
+ f"Endpoint {colors.RED}Referer Validation Not Present{colors.END}",
+ )
+ verbout(
+ colors.R,
+ f"Heuristics reveal endpoint might be {colors.BY} "
+ f"VULNERABLE {colors.END} to Origin Based CSRFs...",
+ )
+ print(
+ f"{colors.CYAN} [+] Possible CSRF Vulnerability Detected : {colors.GREY}{url}"
+ )
+ print(
+ f"{colors.ORANGE} [+] Possible Vulnerability Type: {colors.BY} "
+ f"No Referer Based Request Validation {colors.END}"
+ )
+ VulnLogger(
+ url,
+ "No Referer Header based Request Validation presence.",
+ f"[i] Response Headers: {req0x02.headers}",
+ )
+ return False
diff --git a/xsrfprobe/modules/Tamper.py b/xsrfprobe/modules/Tamper.py
index e5a8636..220283b 100644
--- a/xsrfprobe/modules/Tamper.py
+++ b/xsrfprobe/modules/Tamper.py
@@ -1,44 +1,47 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# XSRFProbe #
-#-:-:-:-:-:-:-:-:-:#
+# -:-:-:-:-:-:-:-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
from re import search, I
-from urllib.parse import urlencode, quote
-from xsrfprobe.core.colors import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
from xsrfprobe.core.request import Post
-from xsrfprobe.files.config import *
from xsrfprobe.core.verbout import verbout
from xsrfprobe.core.utils import replaceStrIndex
from xsrfprobe.files.paramlist import TOKEN_ERRORS
from xsrfprobe.core.logger import VulnLogger, NovulLogger
+
def Tamper(url, action, req, body, query, para):
- '''
+ """
The main idea behind this is to tamper the Anti-CSRF tokens
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')
+ """
+ verbout(colors.RED, "\n +---------------------------------------+")
+ verbout(colors.RED, " | Anti-CSRF Token Tamper Validation |")
+ verbout(colors.RED, " +---------------------------------------+\n")
# Null char flags (hex)
flagx1, destx1 = 0x00, 0x00
flagx2, destx2 = 0x00, 0x00
flagx3, destx3 = 0x00, 0x00
- verbout(GR, 'Proceeding for CSRF attack via Anti-CSRF token tampering...')
+ verbout(colors.GR, "Proceeding for CSRF attack via Anti-CSRF token tampering...")
# First of all lets get out token from request
- if para == '':
+ if para == "":
return True
# Coverting the token to a raw string, cause some special
# chars might fu*k with the operation.
- value = r'%s' % para
+ value = r"%s" % para
copy = req
# Alright lets start...
@@ -47,13 +50,16 @@ def Tamper(url, action, req, body, query, para):
#
# Required check for checking if string at that position isn't the
# same char we are going to replace with.
- verbout(GR, 'Tampering Token by '+color.GREY+'index replacement'+color.END+'...')
- if value[3] != 'a':
- tampvalx1 = replaceStrIndex(value, 3, 'a')
+ verbout(
+ colors.GR,
+ "Tampering Token by " + colors.GREY + "index replacement" + colors.END + "...",
+ )
+ if value[3] != "a":
+ tampvalx1 = replaceStrIndex(value, 3, "a")
else:
- tampvalx1 = replaceStrIndex(value, 3, 'x')
- verbout(color.BLUE, ' [+] Original Token: '+color.CYAN+value)
- verbout(color.BLUE, ' [+] Tampered Token: '+color.CYAN+tampvalx1)
+ tampvalx1 = replaceStrIndex(value, 3, "x")
+ verbout(colors.BLUE, " [+] Original Token: " + colors.CYAN + value)
+ verbout(colors.BLUE, " [+] Tampered Token: " + colors.CYAN + tampvalx1)
# Lets build up the request...
req[query] = tampvalx1
resp = Post(url, action, req)
@@ -66,23 +72,45 @@ 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('2'): destx1 = 0x01
- if not any(search(s, resp.text, I) for s in TOKEN_ERRORS): destx2 = 0x01
- if len(body) == len(resp.text): destx3 = 0x01
- if ((destx1 == 0x01 and destx2 == 0x01) or (destx3 == 0x01)):
- verbout(color.RED,' [-] Anti-CSRF Token tamper by '+color.GREY+'index replacement'+color.RED+' returns valid response!')
+ if str(resp.status_code).startswith("2"):
+ destx1 = 0x01
+ if not any(search(s, resp.text, I) for s in TOKEN_ERRORS):
+ destx2 = 0x01
+ if len(body) == len(resp.text):
+ destx3 = 0x01
+ if (destx1 == 0x01 and destx2 == 0x01) or (destx3 == 0x01):
+ verbout(
+ colors.RED,
+ " [-] Anti-CSRF Token tamper by "
+ + colors.GREY
+ + "index replacement"
+ + colors.RED
+ + " returns valid response!",
+ )
flagx1 = 0x01
- VulnLogger(url, 'Anti-CSRF Token tamper by index replacement returns valid response.', '[i] POST Query: '+req.__str__())
+ 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.')
+ verbout(
+ colors.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 character
# at a specific position and test the response body.
- verbout(GR, 'Tampering Token by '+color.GREY+'index removal'+color.END+'...')
+ verbout(
+ colors.GR,
+ "Tampering Token by " + colors.GREY + "index removal" + colors.END + "...",
+ )
tampvalx2 = replaceStrIndex(value, 3)
- verbout(color.BLUE, ' [+] Original Token: '+color.CYAN+value)
- verbout(color.BLUE, ' [+] Tampered Token: '+color.CYAN+tampvalx2)
+ verbout(colors.BLUE, " [+] Original Token: " + colors.CYAN + value)
+ verbout(colors.BLUE, " [+] Tampered Token: " + colors.CYAN + tampvalx2)
# Lets build up the request...
req[query] = tampvalx2
resp = Post(url, action, req)
@@ -92,23 +120,45 @@ 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('2'): destx1 = 0x02
- if not any(search(s, resp.text, I) for s in TOKEN_ERRORS): destx2 = 0x02
- if len(body) == len(resp.text): destx3 = 0x02
- if ((destx1 == 0x02 and destx2 == 0x02) or destx3 == 0x02):
- verbout(color.RED,' [-] Anti-CSRF Token tamper by '+color.GREY+'index removal'+color.RED+' returns valid response!')
+ if str(resp.status_code).startswith("2"):
+ destx1 = 0x02
+ if not any(search(s, resp.text, I) for s in TOKEN_ERRORS):
+ destx2 = 0x02
+ if len(body) == len(resp.text):
+ destx3 = 0x02
+ if (destx1 == 0x02 and destx2 == 0x02) or destx3 == 0x02:
+ verbout(
+ colors.RED,
+ " [-] Anti-CSRF Token tamper by "
+ + colors.GREY
+ + "index removal"
+ + colors.RED
+ + " returns valid response!",
+ )
flagx2 = 0x01
- VulnLogger(url, 'Anti-CSRF Token tamper by index removal returns valid response.', '[i] POST Query: '+req.__str__())
+ 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.')
+ verbout(
+ colors.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+'...')
+ verbout(
+ colors.GR,
+ "Tampering Token by " + colors.GREY + "Token removal" + colors.END + "...",
+ )
# Removing the anti-csrf token from request
del req[query]
- verbout(color.GREY, ' [+] Removed token parameter from request!')
+ verbout(colors.GREY, " [+] Removed token parameter from request!")
# Lets build up the request...
resp = Post(url, action, req)
@@ -117,28 +167,94 @@ 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('2'): destx1 = 0x03
- if not any(search(s, resp.text, I) for s in TOKEN_ERRORS): destx2 = 0x03
- if len(body) == len(resp.text): destx3 = 0x03
- if ((destx1 == 0x03 and destx2 == 0x03) or destx3 == 0x03):
- verbout(color.RED,' [-] Anti-CSRF'+color.GREY+' Token removal'+color.RED+' returns valid response!')
+ if str(resp.status_code).startswith("2"):
+ destx1 = 0x03
+ if not any(search(s, resp.text, I) for s in TOKEN_ERRORS):
+ destx2 = 0x03
+ if len(body) == len(resp.text):
+ destx3 = 0x03
+ if (destx1 == 0x03 and destx2 == 0x03) or destx3 == 0x03:
+ verbout(
+ colors.RED,
+ " [-] Anti-CSRF"
+ + colors.GREY
+ + " Token removal"
+ + colors.RED
+ + " returns valid response!",
+ )
flagx3 = 0x01
- VulnLogger(url, 'Anti-CSRF Token removal returns valid response.', '[i] POST Query: '+req.__str__())
+ 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.')
+ verbout(
+ colors.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 and flagx2==0x01) or (flagx1==0x01 and flagx3==0x01) or (flagx2==0x01 and 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))
+ if (
+ (flagx1 == 0x01 and flagx2 == 0x01)
+ or (flagx1 == 0x01 and flagx3 == 0x01)
+ or (flagx2 == 0x01 and flagx3 == 0x01)
+ ):
+ verbout(
+ colors.RED,
+ " [+] The tampered token value works! Endpoint "
+ + colors.BR
+ + " VULNERABLE to Replay Attacks "
+ + colors.END,
+ )
+ verbout(
+ colors.ORANGE,
+ " [-] The Tampered Anti-CSRF Token requested does NOT return a 40x or 50x response! ",
+ )
+ print(
+ colors.RED
+ + " [-] Endpoint "
+ + colors.BR
+ + " CONFIRMED VULNERABLE "
+ + colors.END
+ + colors.RED
+ + " to Request Forgery Attacks..."
+ )
+ print(
+ colors.ORANGE
+ + " [!] Vulnerability Type: "
+ + colors.BR
+ + " Non-Unique Anti-CSRF Tokens in Requests "
+ + colors.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.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.')
+ print(
+ colors.RED
+ + " [-] The Tampered Anti-CSRF Token requested returns a 40x or 50x response... "
+ )
+ print(
+ colors.GREEN
+ + " [-] Endpoint "
+ + colors.BG
+ + " NOT VULNERABLE "
+ + colors.END
+ + colors.ORANGE
+ + " to CSRF Attacks..."
+ )
+ print(
+ colors.ORANGE
+ + " [!] CSRF Mitigation Method: "
+ + colors.BG
+ + " Unique Anti-CSRF Tokens "
+ + colors.END
+ + "\n"
+ )
+ NovulLogger(url, "Unique Anti-CSRF Tokens. No token reuse.")
return False
diff --git a/xsrfprobe/modules/Token.py b/xsrfprobe/modules/Token.py
index d75d83a..e6d1b08 100644
--- a/xsrfprobe/modules/Token.py
+++ b/xsrfprobe/modules/Token.py
@@ -1,47 +1,66 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
-from re import search, I
-from time import sleep
+from re import I
from xsrfprobe.files import config
-from xsrfprobe.core.colors import *
+
+import xsrfprobe.core.colors
+
+colors = xsrfprobe.core.colors.color()
+
from xsrfprobe.core.verbout import verbout
from xsrfprobe.files import discovered
from urllib.parse import urlencode, unquote
from xsrfprobe.files.paramlist import COMMON_CSRF_NAMES, COMMON_CSRF_HEADERS
+
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 = ''
+ """
+ verbout(colors.RED, "\n +---------------------------+")
+ verbout(colors.RED, " | Anti-CSRF Token Check |")
+ verbout(colors.RED, " +---------------------------+\n")
+ param = "" # Initializing param
+ query = ""
found = False
# First lets have a look at config.py and see if its set
if config.TOKEN_CHECKS:
- verbout(O,'Parsing request for detecting anti-csrf tokens...')
+ verbout(colors.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
- con = unquote(urlencode(req)).split('&')
+ con = unquote(urlencode(req)).split("&")
for c in con:
for name in COMMON_CSRF_NAMES: # Iterate over the list
- qu = c.split('=')
+ qu = c.split("=")
# 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])
+ verbout(
+ colors.GREEN,
+ " [+] The form was requested with an "
+ + colors.BG
+ + " Anti-CSRF Token "
+ + colors.END
+ + colors.GREEN,
+ )
+ verbout(
+ colors.GREY,
+ " [+] Token Parameter: "
+ + colors.CYAN
+ + qu[0]
+ + "="
+ + colors.ORANGE
+ + qu[1],
+ )
query, param = qu[0], qu[1]
# We are appending the token to a variable for further analysis
discovered.REQUEST_TOKENS.append(param)
@@ -53,17 +72,51 @@ def Token(req, headers):
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])
+ verbout(
+ colors.GREEN,
+ " [+] The form was requested with an "
+ + colors.BG
+ + " Anti-CSRF Token Header "
+ + colors.END
+ + colors.GREEN,
+ )
+ verbout(
+ colors.GREY,
+ " [+] Token Parameter: "
+ + colors.CYAN
+ + qu[0]
+ + "="
+ + colors.ORANGE
+ + qu[1],
+ )
query, param = key, value
# We are appending the token to a variable for further analysis
discovered.REQUEST_TOKENS.append(param)
break # Break execution if a Anti-CSRF token is found
except Exception as e:
- verbout(R, 'Request Parsing Exception!')
- verbout(R, 'Error: '+e.__str__())
+ verbout(colors.R, "Request Parsing Exception!")
+ verbout(colors.R, "Error: " + e.__str__())
if param:
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)
+ verbout(
+ colors.ORANGE,
+ " [-] The form was requested "
+ + colors.RED
+ + " Without an Anti-CSRF Token "
+ + colors.END
+ + colors.ORANGE
+ + "...",
+ )
+ print(
+ colors.RED
+ + " [-] Endpoint seems "
+ + colors.BR
+ + " VULNERABLE "
+ + colors.END
+ + colors.RED
+ + " to "
+ + colors.BR
+ + " POST-Based Request Forgery "
+ + colors.END
+ )
return (None, None)
diff --git a/xsrfprobe/modules/__init__.py b/xsrfprobe/modules/__init__.py
index 800794b..7dc4748 100644
--- a/xsrfprobe/modules/__init__.py
+++ b/xsrfprobe/modules/__init__.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
-#This module requires XSRFProbe
-#https://github.com/0xInfection/XSRFProbe
\ No newline at end of file
+# This module requires XSRFProbe
+# https://github.com/0xInfection/XSRFProbe
diff --git a/xsrfprobe/xsrfprobe.py b/xsrfprobe/xsrfprobe.py
index 34f0f28..a0a902e 100644
--- a/xsrfprobe/xsrfprobe.py
+++ b/xsrfprobe/xsrfprobe.py
@@ -1,14 +1,16 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# XSRF Probe #
-#-:-:-:-:-:-:-::-:-:#
+# -:-:-:-:-:-:-::-:-:#
# Author: 0xInfection
# This module requires XSRFProbe
# https://github.com/0xInfection/XSRFProbe
+
def startEngine():
from xsrfprobe.core import main # import stuff
+
main.Engine() # start the Scanner Engine ;)