alpha release

This commit is contained in:
Somdev Sangwan
2018-12-30 03:07:15 +05:30
committed by GitHub
parent 991bdf841d
commit 7b14cac963
13 changed files with 1617 additions and 0 deletions

264
bolt.py Normal file
View File

@@ -0,0 +1,264 @@
import argparse
import concurrent.futures
import json
import random
import re
import statistics
from collections import Counter
from fuzzywuzzy import fuzz, process
import core.config
from core.config import token
from core.datanize import datanize
from core.prompt import prompt
from core.photon import photon
from core.tweaker import tweaker
from core.evaluate import evaluate
from core.ranger import ranger
from core.zetanize import zetanize
from core.requester import requester
from core.utils import extractHeaders, entropy, isProtected
from core.colors import green, yellow, end, run, good, info, bad, white
parser = argparse.ArgumentParser()
parser.add_argument('-u', help='target url', dest='target')
parser.add_argument('-t', help='number of threads', dest='threads', type=int)
parser.add_argument('-l', help='levels to crawl', dest='level', type=int)
parser.add_argument('--delay', help='delay between requests', dest='delay', type=int)
parser.add_argument('--timeout', help='http request timeout', dest='timeout', type=int)
parser.add_argument('--headers', help='http headers', dest='headers', action='store_true')
args = parser.parse_args()
def banner():
print ('''%s
%sBOLT%s
%s''' % (yellow, white, yellow, end))
if not args.target:
banner()
print('\n' + parser.format_help().lower())
quit()
if args.headers:
headers = extractHeaders(prompt())
else:
headers = core.config.headers
banner()
target = args.target
delay = args.delay or 0
level = args.level or 2
timeout = args.timeout or 20
threadCount = args.threads or 2
allTokens = []
weakTokens = []
tokenDatabase = []
insecureForms = []
print ('%s Phase: Crawling %s[%s1/5%s]%s' % (run, green, end, green, end))
dataset = photon(target, headers, level, threadCount)
allForms = dataset[0]
print ('\r%s Crawled %i URL(s) and found %i form(s).%-10s' % (info, dataset[1], len(allForms), ' '))
print ('%s Phase: Evaluating %s[%s2/5%s]%s' % (run, green, end, green, end))
evaluate(allForms, weakTokens, tokenDatabase, allTokens, insecureForms)
if weakTokens:
print ('%s Weak token(s) found' % good)
for weakToken in weakTokens:
url = list(weakToken.keys())[0]
token = list(weakToken.values())[0]
print ('%s %s %s' % (info, url, token))
if insecureForms:
print ('%s Insecure form(s) found' % good)
for insecureForm in insecureForms:
url = list(insecureForm.keys())[0]
action = list(insecureForm.values())[0]['action']
form = action.replace(target, '')
if form:
print ('%s %s %s[%s%s%s]%s' % (bad, url, green, end, form, green, end))
with open('./db/hashes.json') as f:
hashPatterns = json.load(f)
aToken = allTokens[0]
matches = []
for element in hashPatterns:
pattern = element['regex']
if re.match(pattern, aToken):
for name in element['matches']:
matches.append(name)
if matches:
print ('%s Token matches the pattern of following hash type(s):' % info)
for name in matches:
print (' %s>%s %s' % (yellow, end, name))
print ('%s Phase: Comparing %s[%s3/5%s]%s' % (run, green, end, green, end))
uniqueTokens = set(allTokens)
if len(uniqueTokens) < len(allTokens):
print ('%s Potential Replay Attack condition found' % good)
print ('%s Verifying and looking for the cause' % run)
replay = False
for url, token in tokenDatabase:
for url2, token2 in tokenDatabase:
if token == token2 and url != url2:
print ('%s The same token was used on %s%s%s and %s%s%s' % (good, green, url, end, green, url2, end))
replay = True
if not replay:
print ('%s Further investigation shows that it was a false positive.')
def fuzzy(tokens):
averages = []
for token in tokens:
sameTokenRemoved = False
result = process.extract(token, tokens, scorer=fuzz.partial_ratio)
scores = []
for each in result:
score = each[1]
if score == 100 and not sameTokenRemoved:
sameTokenRemoved = True
continue
scores.append(score)
average = statistics.mean(scores)
averages.append(average)
return statistics.mean(averages)
try:
similarity = fuzzy(allTokens)
print ('%s Tokens are %s%i%%%s similar to each other on an average' % (info, green, similarity, end))
except statistics.StatisticsError:
print ('%s No CSRF protection to test' % bad)
quit()
simTokens = []
print ('%s Phase: Observing %s[%s4/5%s]%s' % (run, green, end, green, end))
print ('%s 100 simultaneous requests are being made, please wait.' % info)
def extractForms(url):
response = requester(url, {}, headers, True, 0).text
forms = zetanize(url, response)
for each in forms.values():
localTokens = set()
inputs = each['inputs']
for inp in inputs:
value = inp['value']
if value and match(r'^[\w\-_]+$', value):
if entropy(value) > 10:
simTokens.append(value)
while True:
sample = random.choice(tokenDatabase)
goodToken = list(sample.values())[0]
if len(goodToken) > 0:
goodCandidate = list(sample.keys())[0]
break
threadpool = concurrent.futures.ThreadPoolExecutor(max_workers=30)
futures = (threadpool.submit(extractForms, goodCandidate) for goodCandidate in [goodCandidate] * 30)
for i in concurrent.futures.as_completed(futures):
pass
if simTokens:
if len(set(simTokens)) < len(simTokens):
print ('%s Same tokens were issued for simultaneous requests.' % good)
else:
print (simTokens)
else:
print ('%s Different tokens were issued for simultaneous requests.' % info)
print ('%s Phase: Testing %s[%s5/5%s]%s' % (good, green, end, green, end))
parsed = ''
print ('%s Finding a suitable form for further testing. It may take a while.' % run)
for url, forms in allForms[0].items():
found = False
parsed = datanize(forms, tolerate=True)
if parsed:
found = True
break
if found:
break
if not parsed:
candidate = list(random.choice(tokenDatabase).keys())[0]
parsed = datanize(candidate, headers, tolerate=True)
print (parsed)
origGET = parsed[0]
origUrl = parsed[1]
origData = parsed[2]
print ('%s Making a request with CSRF token for comparison.' % run)
response = requester(origUrl, origData, headers, origGET, 0)
originalCode = response.status_code
originalLength = len(response.text)
print ('%s Status Code: %s' % (info, originalCode))
print ('%s Content Length: %i' % (info, originalLength))
print ('%s Checking if the resonse is dynamic.' % run)
response = requester(origUrl, origData, headers, origGET, 0)
secondLength = len(response.text)
if originalLength != secondLength:
print ('%s Response is dynamic.' % info)
tolerableDifference = abs(originalLength - secondLength)
else:
print ('%s Response isn\'t dynamic.' % info)
tolerableDifference = 0
print ('%s Emulating a mobile browser' % run)
print ('%s Making a request with mobile browser' % run)
headers['User-Agent'] = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows CE; PPC; 240x320)'
response = requester(origUrl, {}, headers, True, 0).text
parsed = zetanize(origUrl, response)
if isProtected(parsed):
print ('%s CSRF protection is enabled for mobile browsers as well.' % bad)
else:
print ('%s CSRF protection isn\'t enabled for mobile browsers.' % good)
print ('%s Making a request without CSRF token parameter.' % run)
data = tweaker(origData, 'remove')
response = requester(origUrl, data, headers, origGET, 0)
if response.status_code == originalCode:
if str(originalCode)[0] in ['4', '5']:
print ('%s It didn\'t work' % bad)
else:
difference = abs(originalLength - len(response.text))
if difference <= tolerableDifference:
print ('%s It worked!' % good)
else:
print ('%s It didn\'t work' % bad)
print ('%s Making a request without CSRF token parameter value.' % run)
data = tweaker(origData, 'clear')
response = requester(origUrl, data, headers, origGET, 0)
if response.status_code == originalCode:
if str(originalCode)[0] in ['4', '5']:
print ('%s It didn\'t work' % bad)
else:
difference = abs(originalLength - len(response.text))
if difference <= tolerableDifference:
print ('%s It worked!' % good)
else:
print ('%s It didn\'t work' % bad)
seeds = ranger(allTokens)
print ('%s Generating a fake token.' % run)
data = tweaker(origData, 'generate', seeds=seeds)
print ('%s Making a request with the self generated token.' % run)
response = requester(origUrl, data, headers, origGET, 0)
if response.status_code == originalCode:
if str(originalCode)[0] in ['4', '5']:
print ('%s It didn\'t work' % bad)
else:
difference = abs(originalLength - len(response.text))
if difference <= tolerableDifference:
print ('%s It worked!' % good)
else:
print ('%s It didn\'t work' % bad)
print ('%s Making requests with various tweaks to the token. It may take a while.' % run)
# data = datanize(goodCandidate, headers)[1]
# data = tweaker(data, 'remove')
# response = requester(origUrl, data, headers, origGET, 0)

20
core/colors.py Normal file
View File

@@ -0,0 +1,20 @@
import sys
colors = True # Output should be colored
machine = sys.platform # Detecting the os of current system
if machine.lower().startswith(('os', 'win', 'darwin', 'ios')):
colors = False # Colors shouldn't be displayed in mac & windows
if not colors:
end = red = white = green = yellow = run = bad = good = info = que = ''
else:
white = '\033[97m'
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'

14
core/config.py Normal file
View File

@@ -0,0 +1,14 @@
password = 'xXx!69!xXx'
email = 'testing@gmail.com'
strings = ['red', 'bob', 'admin', 'alex', 'testing', 'test', 'lol', 'yes', 'dragon', 'bad']
commonNames = ['csrf', 'auth', 'token', 'verify', 'hash']
token = r'^[\w\-_+=/]{14,256}$'
headers = { # default headers
'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',
}

39
core/datanize.py Normal file
View File

@@ -0,0 +1,39 @@
import random
import re
from core.config import password, email, token, strings
from core.requester import requester
from core.zetanize import zetanize
def datanize(forms, tolerate=False):
parsedForms = list(forms.values())
for oneForm in parsedForms:
data = {}
login = False
protected = False
action = oneForm['action']
method = oneForm['method']
inputs = oneForm['inputs']
for inp in inputs:
name = inp['name']
kind = inp['type']
value = inp['value']
if re.match(token, value):
protected = True
if kind == 'password':
data[name] = password
login = True
if kind == 'email':
data[name] = email
if kind == 'text':
data[name] = random.choice(strings)
else:
data[name] = value
if method == 'GET':
GET = True
else:
GET = False
if protected:
if not login or tolerate:
return [GET, action, data]
return None

30
core/evaluate.py Normal file
View File

@@ -0,0 +1,30 @@
from re import match
from core.utils import entropy
from core.config import commonNames
def evaluate(dataset, weakTokens, tokenDatabase, allTokens, insecureForms):
done = []
for i in dataset:
for url, page in i.items():
localTokens = set()
for each in page.values():
protected = False
action = each['action']
method = each['method']
inputs = each['inputs']
for inp in inputs:
name = inp['name']
value = inp['value']
if value and match(r'^[\w\-_]+$', value):
if entropy(value) > 10:
localTokens.add(value)
protected = True
break
elif name.lower() in commonNames:
weakTokens.append({url : {name : value}})
if not protected and action not in done:
done.append(done)
insecureForms.append({url : each})
for token in localTokens:
allTokens.append(token)
tokenDatabase.append({url : localTokens})

54
core/photon.py Normal file
View File

@@ -0,0 +1,54 @@
# 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(seedUrl, headers, depth, threadCount):
forms = [] # web forms
processed = set() # urls that have been crawled
storage = set() # urls that belong to the target i.e. in-scope
scheme = urlparse(seedUrl).scheme
host = urlparse(seedUrl).netloc
main_url = scheme + '://' + host
storage.add(seedUrl)
def rec(url):
processed.add(url)
urlPrint = (url + (' ' * 60))[:60]
print ('%s Parsing %-40s' % (run, urlPrint), end='\r')
url = getUrl(url, '', True)
params = getParams(url, '', True)
if '=' in url:
inps = []
for name, value in params.items():
inps.append({'name': name, 'value': value})
forms.append({url : {0: {'action': url, 'method': 'get', 'inputs': inps}}})
response = requester(url, params, headers, True, 0).text
forms.append({url : zetanize(url, response)})
matches = findall(r'<[aA].*href=["\']{0,1}(.*?)["\']', response)
for link in matches: # iterate over the matches
link = link.split('#')[0].lstrip(' ') # remove everything after a "#" to deal with in-page anchors
if link[:4] == 'http':
if link.startswith(main_url):
storage.add(link)
elif link[:2] == '//':
if link.split('/')[2].startswith(host):
storage.add(schema + link)
elif link[:1] == '/':
storage.add(main_url + link)
else:
storage.add(main_url + '/' + link)
for x in range(depth):
urls = storage - processed
threadpool = concurrent.futures.ThreadPoolExecutor(max_workers=10)
futures = (threadpool.submit(rec, url) for url in urls)
for i in concurrent.futures.as_completed(futures):
pass
return [forms, len(processed)]

19
core/prompt.py Normal file
View 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()

10
core/ranger.py Normal file
View File

@@ -0,0 +1,10 @@
def ranger(tokens):
digits = set()
alphabets = set()
for token in tokens:
for char in token:
if char in '0123456789':
digits.add(char)
elif char in 'abcdefghijklmnopqrstuvwxyz':
alphabets.add(char)
return [list(digits), list(alphabets)]

20
core/requester.py Normal file
View File

@@ -0,0 +1,20 @@
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']
if headers:
if 'User-Agent' not in headers:
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

38
core/tweaker.py Normal file
View File

@@ -0,0 +1,38 @@
from core.config import token
import random
import re
def tweaker(data, strategy, index=0, seeds=[None, None]):
newData = {}
if strategy == 'clear':
for name, value in data.items():
if re.match(token, value):
value = ''
newData[name] = value
return newData
elif strategy == 'remove':
for name, value in data.items():
if not re.match(token, value):
newData[name] = value
elif strategy == 'break':
for name, value in data.items():
if re.match(token, value):
value = value[:-index]
newData[name] = value
elif strategy == 'generate':
digits = seeds[0]
alphabets = seeds[1]
for name, value in data.items():
if re.match(token, value):
newToken = ''
for char in list(value):
if char in digits:
newToken += random.choice(digits)
elif char in alphabets:
newToken += random.choice(alphabets)
else:
newToken += char
newData[name] = newToken
else:
newData[name] = value
return newData

62
core/utils.py Normal file
View File

@@ -0,0 +1,62 @@
import re
from core.config import token
def entropy(string):
digits = re.findall(r'\d', string)
lowerAlphas = re.findall(r'[a-z]', string)
upperAlphas = re.findall(r'[A-Z]', string)
entropy = len(set(digits + lowerAlphas + upperAlphas))
if not digits:
entropy = entropy/2
return entropy
def isProtected(parsed):
protected = False
parsedForms = list(parsed.values())
for oneForm in parsedForms:
inputs = oneForm['inputs']
for inp in inputs:
name = inp['name']
kind = inp['type']
value = inp['value']
if re.match(token, value):
protected = True
return protected
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 getUrl(url, data, GET):
if GET:
return url.split('?')[0]
else:
return url
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

48
core/zetanize.py Normal file
View File

@@ -0,0 +1,48 @@
import re
from urllib.parse import urlparse
def zetanize(url, response):
parsedUrl = urlparse(url)
mainUrl = parsedUrl.scheme + '://' + parsedUrl.netloc
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] = {}
action = d(e(page.group(1)))
if not action.startswith('http'):
if action.startswith('/'):
action = mainUrl + action
else:
action = mainUrl + '/' + action
forms[num]['action'] = action.replace('&amp;', '&') 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

999
db/hashes.json Normal file
View File

@@ -0,0 +1,999 @@
[
{
"regex": "^[a-f0-9]{4}$",
"matches": [
"CRC-16",
"CRC-16-CCITT",
"FCS-16"
]
},
{
"regex": "^[a-f0-9]{8}$",
"matches": [
"Adler-32",
"CRC-32B",
"FCS-32",
"GHash-32-3",
"GHash-32-5",
"FNV-132",
"Fletcher-32",
"Joaat",
"ELF-32",
"XOR-32"
]
},
{
"regex": "^[a-f0-9]{6}$",
"matches": [
"CRC-24"
]
},
{
"regex": "^(\\$crc32\\$[a-f0-9]{8}.)?[a-f0-9]{8}$",
"matches": [
"CRC-32"
]
},
{
"regex": "^\\+[a-z0-9\\/.]{12}$",
"matches": [
"Eggdrop IRC Bot"
]
},
{
"regex": "^[a-z0-9\\/.]{13}$",
"matches": [
"DES(Unix)",
"Traditional DES",
"DEScrypt"
]
},
{
"regex": "^[a-f0-9]{16}$",
"matches": [
"MySQL323",
"DES(Oracle)",
"Half MD5",
"Oracle 7-10g",
"FNV-164",
"CRC-64"
]
},
{
"regex": "^[a-z0-9\\/.]{16}$",
"matches": [
"Cisco-PIX(MD5)"
]
},
{
"regex": "^\\([a-z0-9\\/+]{20}\\)$",
"matches": [
"Lotus Notes/Domino 6"
]
},
{
"regex": "^_[a-z0-9\\/.]{19}$",
"matches": [
"BSDi Crypt"
]
},
{
"regex": "^[a-f0-9]{24}$",
"matches": [
"CRC-96(ZIP)"
]
},
{
"regex": "^[a-z0-9\\/.]{24}$",
"matches": [
"Crypt16"
]
},
{
"regex": "^(\\$md2\\$)?[a-f0-9]{32}$",
"matches": [
"MD2"
]
},
{
"regex": "^[a-f0-9]{32}(:.+)?$",
"matches": [
"MD5",
"MD4",
"Double MD5",
"LM",
"RIPEMD-128",
"Haval-128",
"Tiger-128",
"Skein-256(128)",
"Skein-512(128)",
"Lotus Notes/Domino 5",
"Skype",
"ZipMonster",
"PrestaShop",
"md5(md5(md5($pass)))",
"md5(strtoupper(md5($pass)))",
"md5(sha1($pass))",
"md5($pass.$salt)",
"md5($salt.$pass)",
"md5(unicode($pass).$salt)",
"md5($salt.unicode($pass))",
"HMAC-MD5 (key = $pass)",
"HMAC-MD5 (key = $salt)",
"md5(md5($salt).$pass)",
"md5($salt.md5($pass))",
"md5($pass.md5($salt))",
"md5($salt.$pass.$salt)",
"md5(md5($pass).md5($salt))",
"md5($salt.md5($salt.$pass))",
"md5($salt.md5($pass.$salt))",
"md5($username.0.$pass)"
]
},
{
"regex": "^(\\$snefru\\$)?[a-f0-9]{32}$",
"matches": [
"Snefru-128"
]
},
{
"regex": "^(\\$NT\\$)?[a-f0-9]{32}$",
"matches": [
"NTLM"
]
},
{
"regex": "^([^\\\\\\/:*?\"<>|]{1,20}:)?[a-f0-9]{32}(:[^\\\\\\/:*?\"<>|]{1,20})?$",
"matches": [
"Domain Cached Credentials"
]
},
{
"regex": "^([^\\\\\\/:*?\"<>|]{1,20}:)?(\\$DCC2\\$10240#[^\\\\\\/:*?\"<>|]{1,20}#)?[a-f0-9]{32}$",
"matches": [
"Domain Cached Credentials 2"
]
},
{
"regex": "^{SHA}[a-z0-9\\/+]{27}=$",
"matches": [
"SHA-1(Base64)",
"Netscape LDAP SHA"
]
},
{
"regex": "^\\$1\\$[a-z0-9\\/.]{0,8}\\$[a-z0-9\\/.]{22}(:.*)?$",
"matches": [
"MD5 Crypt",
"Cisco-IOS(MD5)",
"FreeBSD MD5"
]
},
{
"regex": "^0x[a-f0-9]{32}$",
"matches": [
"Lineage II C4"
]
},
{
"regex": "^\\$H\\$[a-z0-9\\/.]{31}$",
"matches": [
"phpBB v3.x",
"Wordpress v2.6.0/2.6.1",
"PHPass' Portable Hash"
]
},
{
"regex": "^\\$P\\$[a-z0-9\\/.]{31}$",
"matches": [
"Wordpress \u2265 v2.6.2",
"Joomla \u2265 v2.5.18",
"PHPass' Portable Hash"
]
},
{
"regex": "^[a-f0-9]{32}:[a-z0-9]{2}$",
"matches": [
"osCommerce",
"xt:Commerce"
]
},
{
"regex": "^\\$apr1\\$[a-z0-9\\/.]{0,8}\\$[a-z0-9\\/.]{22}$",
"matches": [
"MD5(APR)",
"Apache MD5",
"md5apr1"
]
},
{
"regex": "^{smd5}[a-z0-9$\\/.]{31}$",
"matches": [
"AIX(smd5)"
]
},
{
"regex": "^[a-f0-9]{32}:[a-f0-9]{32}$",
"matches": [
"WebEdition CMS"
]
},
{
"regex": "^[a-f0-9]{32}:.{5}$",
"matches": [
"IP.Board \u2265 v2+"
]
},
{
"regex": "^[a-f0-9]{32}:.{8}$",
"matches": [
"MyBB \u2265 v1.2+"
]
},
{
"regex": "^[a-z0-9]{34}$",
"matches": [
"CryptoCurrency(Adress)"
]
},
{
"regex": "^[a-f0-9]{40}(:.+)?$",
"matches": [
"SHA-1",
"Double SHA-1",
"RIPEMD-160",
"Haval-160",
"Tiger-160",
"HAS-160",
"LinkedIn",
"Skein-256(160)",
"Skein-512(160)",
"MangosWeb Enhanced CMS",
"sha1(sha1(sha1($pass)))",
"sha1(md5($pass))",
"sha1($pass.$salt)",
"sha1($salt.$pass)",
"sha1(unicode($pass).$salt)",
"sha1($salt.unicode($pass))",
"HMAC-SHA1 (key = $pass)",
"HMAC-SHA1 (key = $salt)",
"sha1($salt.$pass.$salt)"
]
},
{
"regex": "^\\*[a-f0-9]{40}$",
"matches": [
"MySQL5.x",
"MySQL4.1"
]
},
{
"regex": "^[a-z0-9]{43}$",
"matches": [
"Cisco-IOS(SHA-256)"
]
},
{
"regex": "^{SSHA}[a-z0-9\\/+]{38}==$",
"matches": [
"SSHA-1(Base64)",
"Netscape LDAP SSHA",
"nsldaps"
]
},
{
"regex": "^[a-z0-9=]{47}$",
"matches": [
"Fortigate(FortiOS)"
]
},
{
"regex": "^[a-f0-9]{48}$",
"matches": [
"Haval-192",
"Tiger-192",
"SHA-1(Oracle)",
"OSX v10.4",
"OSX v10.5",
"OSX v10.6"
]
},
{
"regex": "^[a-f0-9]{51}$",
"matches": [
"Palshop CMS"
]
},
{
"regex": "^[a-z0-9]{51}$",
"matches": [
"CryptoCurrency(PrivateKey)"
]
},
{
"regex": "^{ssha1}[0-9]{2}\\$[a-z0-9$\\/.]{44}$",
"matches": [
"AIX(ssha1)"
]
},
{
"regex": "^0x0100[a-f0-9]{48}$",
"matches": [
"MSSQL(2005)",
"MSSQL(2008)"
]
},
{
"regex": "^(\\$md5,rounds=[0-9]+\\$|\\$md5\\$rounds=[0-9]+\\$|\\$md5\\$)[a-z0-9\\/.]{0,16}(\\$|\\$\\$)[a-z0-9\\/.]{22}$",
"matches": [
"Sun MD5 Crypt"
]
},
{
"regex": "^[a-f0-9]{56}$",
"matches": [
"SHA-224",
"Haval-224",
"SHA3-224",
"Skein-256(224)",
"Skein-512(224)"
]
},
{
"regex": "^(\\$2[axy]|\\$2)\\$[0-9]{2}\\$[a-z0-9\\/.]{53}$",
"matches": [
"Blowfish(OpenBSD)",
"Woltlab Burning Board 4.x",
"bcrypt"
]
},
{
"regex": "^[a-f0-9]{40}:[a-f0-9]{16}$",
"matches": [
"Android PIN"
]
},
{
"regex": "^(S:)?[a-f0-9]{40}(:)?[a-f0-9]{20}$",
"matches": [
"Oracle 11g/12c"
]
},
{
"regex": "^\\$bcrypt-sha256\\$(2[axy]|2)\\,[0-9]+\\$[a-z0-9\\/.]{22}\\$[a-z0-9\\/.]{31}$",
"matches": [
"bcrypt(SHA-256)"
]
},
{
"regex": "^[a-f0-9]{32}:.{3}$",
"matches": [
"vBulletin < v3.8.5"
]
},
{
"regex": "^[a-f0-9]{32}:.{30}$",
"matches": [
"vBulletin \u2265 v3.8.5"
]
},
{
"regex": "^(\\$snefru\\$)?[a-f0-9]{64}$",
"matches": [
"Snefru-256"
]
},
{
"regex": "^[a-f0-9]{64}(:.+)?$",
"matches": [
"SHA-256",
"RIPEMD-256",
"Haval-256",
"GOST R 34.11-94",
"GOST CryptoPro S-Box",
"SHA3-256",
"Skein-256",
"Skein-512(256)",
"Ventrilo",
"sha256($pass.$salt)",
"sha256($salt.$pass)",
"sha256(unicode($pass).$salt)",
"sha256($salt.unicode($pass))",
"HMAC-SHA256 (key = $pass)",
"HMAC-SHA256 (key = $salt)"
]
},
{
"regex": "^[a-f0-9]{32}:[a-z0-9]{32}$",
"matches": [
"Joomla < v2.5.18"
]
},
{
"regex": "^[a-f-0-9]{32}:[a-f-0-9]{32}$",
"matches": [
"SAM(LM_Hash:NT_Hash)"
]
},
{
"regex": "^(\\$chap\\$0\\*)?[a-f0-9]{32}[\\*:][a-f0-9]{32}(:[0-9]{2})?$",
"matches": [
"MD5(Chap)",
"iSCSI CHAP Authentication"
]
},
{
"regex": "^\\$episerver\\$\\*0\\*[a-z0-9\\/=+]+\\*[a-z0-9\\/=+]{27,28}$",
"matches": [
"EPiServer 6.x < v4"
]
},
{
"regex": "^{ssha256}[0-9]{2}\\$[a-z0-9$\\/.]{60}$",
"matches": [
"AIX(ssha256)"
]
},
{
"regex": "^[a-f0-9]{80}$",
"matches": [
"RIPEMD-320"
]
},
{
"regex": "^\\$episerver\\$\\*1\\*[a-z0-9\\/=+]+\\*[a-z0-9\\/=+]{42,43}$",
"matches": [
"EPiServer 6.x \u2265 v4"
]
},
{
"regex": "^0x0100[a-f0-9]{88}$",
"matches": [
"MSSQL(2000)"
]
},
{
"regex": "^[a-f0-9]{96}$",
"matches": [
"SHA-384",
"SHA3-384",
"Skein-512(384)",
"Skein-1024(384)"
]
},
{
"regex": "^{SSHA512}[a-z0-9\\/+]{96}$",
"matches": [
"SSHA-512(Base64)",
"LDAP(SSHA-512)"
]
},
{
"regex": "^{ssha512}[0-9]{2}\\$[a-z0-9\\/.]{16,48}\\$[a-z0-9\\/.]{86}$",
"matches": [
"AIX(ssha512)"
]
},
{
"regex": "^[a-f0-9]{128}(:.+)?$",
"matches": [
"SHA-512",
"Whirlpool",
"Salsa10",
"Salsa20",
"SHA3-512",
"Skein-512",
"Skein-1024(512)",
"sha512($pass.$salt)",
"sha512($salt.$pass)",
"sha512(unicode($pass).$salt)",
"sha512($salt.unicode($pass))",
"HMAC-SHA512 (key = $pass)",
"HMAC-SHA512 (key = $salt)"
]
},
{
"regex": "^[a-f0-9]{136}$",
"matches": [
"OSX v10.7"
]
},
{
"regex": "^0x0200[a-f0-9]{136}$",
"matches": [
"MSSQL(2012)",
"MSSQL(2014)"
]
},
{
"regex": "^\\$ml\\$[0-9]+\\$[a-f0-9]{64}\\$[a-f0-9]{128}$",
"matches": [
"OSX v10.8",
"OSX v10.9"
]
},
{
"regex": "^[a-f0-9]{256}$",
"matches": [
"Skein-1024"
]
},
{
"regex": "^grub\\.pbkdf2\\.sha512\\.[0-9]+\\.([a-f0-9]{128,2048}\\.|[0-9]+\\.)?[a-f0-9]{128}$",
"matches": [
"GRUB 2"
]
},
{
"regex": "^sha1\\$[a-z0-9]+\\$[a-f0-9]{40}$",
"matches": [
"Django(SHA-1)"
]
},
{
"regex": "^[a-f0-9]{49}$",
"matches": [
"Citrix Netscaler"
]
},
{
"regex": "^\\$S\\$[a-z0-9\\/.]{52}$",
"matches": [
"Drupal > v7.x"
]
},
{
"regex": "^\\$5\\$(rounds=[0-9]+\\$)?[a-z0-9\\/.]{0,16}\\$[a-z0-9\\/.]{43}$",
"matches": [
"SHA-256 Crypt"
]
},
{
"regex": "^0x[a-f0-9]{4}[a-f0-9]{16}[a-f0-9]{64}$",
"matches": [
"Sybase ASE"
]
},
{
"regex": "^\\$6\\$(rounds=[0-9]+\\$)?[a-z0-9\\/.]{0,16}\\$[a-z0-9\\/.]{86}$",
"matches": [
"SHA-512 Crypt"
]
},
{
"regex": "^\\$sha\\$[a-z0-9]{1,16}\\$([a-f0-9]{32}|[a-f0-9]{40}|[a-f0-9]{64}|[a-f0-9]{128}|[a-f0-9]{140})$",
"matches": [
"Minecraft(AuthMe Reloaded)"
]
},
{
"regex": "^sha256\\$[a-z0-9]+\\$[a-f0-9]{64}$",
"matches": [
"Django(SHA-256)"
]
},
{
"regex": "^sha384\\$[a-z0-9]+\\$[a-f0-9]{96}$",
"matches": [
"Django(SHA-384)"
]
},
{
"regex": "^crypt1:[a-z0-9+=]{12}:[a-z0-9+=]{12}$",
"matches": [
"Clavister Secure Gateway"
]
},
{
"regex": "^[a-f0-9]{112}$",
"matches": [
"Cisco VPN Client(PCF-File)"
]
},
{
"regex": "^[a-f0-9]{1329}$",
"matches": [
"Microsoft MSTSC(RDP-File)"
]
},
{
"regex": "^[^\\\\\\/:*?\"<>|]{1,20}[:]{2,3}([^\\\\\\/:*?\"<>|]{1,20})?:[a-f0-9]{48}:[a-f0-9]{48}:[a-f0-9]{16}$",
"matches": [
"NetNTLMv1-VANILLA / NetNTLMv1+ESS"
]
},
{
"regex": "^([^\\\\\\/:*?\"<>|]{1,20}\\\\)?[^\\\\\\/:*?\"<>|]{1,20}[:]{2,3}([^\\\\\\/:*?\"<>|]{1,20}:)?[^\\\\\\/:*?\"<>|]{1,20}:[a-f0-9]{32}:[a-f0-9]+$",
"matches": [
"NetNTLMv2"
]
},
{
"regex": "^\\$(krb5pa|mskrb5)\\$([0-9]{2})?\\$.+\\$[a-f0-9]{1,}$",
"matches": [
"Kerberos 5 AS-REQ Pre-Auth"
]
},
{
"regex": "^\\$scram\\$[0-9]+\\$[a-z0-9\\/.]{16}\\$sha-1=[a-z0-9\\/.]{27},sha-256=[a-z0-9\\/.]{43},sha-512=[a-z0-9\\/.]{86}$",
"matches": [
"SCRAM Hash"
]
},
{
"regex": "^[a-f0-9]{40}:[a-f0-9]{0,32}$",
"matches": [
"Redmine Project Management Web App"
]
},
{
"regex": "^(.+)?\\$[a-f0-9]{16}$",
"matches": [
"SAP CODVN B (BCODE)"
]
},
{
"regex": "^(.+)?\\$[a-f0-9]{40}$",
"matches": [
"SAP CODVN F/G (PASSCODE)"
]
},
{
"regex": "^(.+\\$)?[a-z0-9\\/.+]{30}(:.+)?$",
"matches": [
"Juniper Netscreen/SSG(ScreenOS)"
]
},
{
"regex": "^0x[a-f0-9]{60}\\s0x[a-f0-9]{40}$",
"matches": [
"EPi"
]
},
{
"regex": "^[a-f0-9]{40}:[^*]{1,25}$",
"matches": [
"SMF \u2265 v1.1"
]
},
{
"regex": "^(\\$wbb3\\$\\*1\\*)?[a-f0-9]{40}[:*][a-f0-9]{40}$",
"matches": [
"Woltlab Burning Board 3.x"
]
},
{
"regex": "^[a-f0-9]{130}(:[a-f0-9]{40})?$",
"matches": [
"IPMI2 RAKP HMAC-SHA1"
]
},
{
"regex": "^[a-f0-9]{32}:[0-9]+:[a-z0-9_.+-]+@[a-z0-9-]+\\.[a-z0-9-.]+$",
"matches": [
"Lastpass"
]
},
{
"regex": "^[a-z0-9\\/.]{16}([:$].{1,})?$",
"matches": [
"Cisco-ASA(MD5)"
]
},
{
"regex": "^\\$vnc\\$\\*[a-f0-9]{32}\\*[a-f0-9]{32}$",
"matches": [
"VNC"
]
},
{
"regex": "^[a-z0-9]{32}(:([a-z0-9-]+\\.)?[a-z0-9-.]+\\.[a-z]{2,7}:.+:[0-9]+)?$",
"matches": [
"DNSSEC(NSEC3)"
]
},
{
"regex": "^(user-.+:)?\\$racf\\$\\*.+\\*[a-f0-9]{16}$",
"matches": [
"RACF"
]
},
{
"regex": "^\\$3\\$\\$[a-f0-9]{32}$",
"matches": [
"NTHash(FreeBSD Variant)"
]
},
{
"regex": "^\\$sha1\\$[0-9]+\\$[a-z0-9\\/.]{0,64}\\$[a-z0-9\\/.]{28}$",
"matches": [
"SHA-1 Crypt"
]
},
{
"regex": "^[a-f0-9]{70}$",
"matches": [
"hMailServer"
]
},
{
"regex": "^[:\\$][AB][:\\$]([a-f0-9]{1,8}[:\\$])?[a-f0-9]{32}$",
"matches": [
"MediaWiki"
]
},
{
"regex": "^[a-f0-9]{140}$",
"matches": [
"Minecraft(xAuth)"
]
},
{
"regex": "^\\$pbkdf2(-sha1)?\\$[0-9]+\\$[a-z0-9\\/.]+\\$[a-z0-9\\/.]{27}$",
"matches": [
"PBKDF2-SHA1(Generic)"
]
},
{
"regex": "^\\$pbkdf2-sha256\\$[0-9]+\\$[a-z0-9\\/.]+\\$[a-z0-9\\/.]{43}$",
"matches": [
"PBKDF2-SHA256(Generic)"
]
},
{
"regex": "^\\$pbkdf2-sha512\\$[0-9]+\\$[a-z0-9\\/.]+\\$[a-z0-9\\/.]{86}$",
"matches": [
"PBKDF2-SHA512(Generic)"
]
},
{
"regex": "^\\$p5k2\\$[0-9]+\\$[a-z0-9\\/+=-]+\\$[a-z0-9\\/+-]{27}=$",
"matches": [
"PBKDF2(Cryptacular)"
]
},
{
"regex": "^\\$p5k2\\$[0-9]+\\$[a-z0-9\\/.]+\\$[a-z0-9\\/.]{32}$",
"matches": [
"PBKDF2(Dwayne Litzenberger)"
]
},
{
"regex": "^{FSHP[0123]\\|[0-9]+\\|[0-9]+}[a-z0-9\\/+=]+$",
"matches": [
"Fairly Secure Hashed Password"
]
},
{
"regex": "^\\$PHPS\\$.+\\$[a-f0-9]{32}$",
"matches": [
"PHPS"
]
},
{
"regex": "^[0-9]{4}:[a-f0-9]{16}:[a-f0-9]{2080}$",
"matches": [
"1Password(Agile Keychain)"
]
},
{
"regex": "^[a-f0-9]{64}:[a-f0-9]{32}:[0-9]{5}:[a-f0-9]{608}$",
"matches": [
"1Password(Cloud Keychain)"
]
},
{
"regex": "^[a-f0-9]{256}:[a-f0-9]{256}:[a-f0-9]{16}:[a-f0-9]{16}:[a-f0-9]{320}:[a-f0-9]{16}:[a-f0-9]{40}:[a-f0-9]{40}:[a-f0-9]{32}$",
"matches": [
"IKE-PSK MD5"
]
},
{
"regex": "^[a-f0-9]{256}:[a-f0-9]{256}:[a-f0-9]{16}:[a-f0-9]{16}:[a-f0-9]{320}:[a-f0-9]{16}:[a-f0-9]{40}:[a-f0-9]{40}:[a-f0-9]{40}$",
"matches": [
"IKE-PSK SHA1"
]
},
{
"regex": "^[a-z0-9\\/+]{27}=$",
"matches": [
"PeopleSoft"
]
},
{
"regex": "^crypt\\$[a-f0-9]{5}\\$[a-z0-9\\/.]{13}$",
"matches": [
"Django(DES Crypt Wrapper)"
]
},
{
"regex": "^(\\$django\\$\\*1\\*)?pbkdf2_sha256\\$[0-9]+\\$[a-z0-9]+\\$[a-z0-9\\/+=]{44}$",
"matches": [
"Django(PBKDF2-HMAC-SHA256)"
]
},
{
"regex": "^pbkdf2_sha1\\$[0-9]+\\$[a-z0-9]+\\$[a-z0-9\\/+=]{28}$",
"matches": [
"Django(PBKDF2-HMAC-SHA1)"
]
},
{
"regex": "^bcrypt(\\$2[axy]|\\$2)\\$[0-9]{2}\\$[a-z0-9\\/.]{53}$",
"matches": [
"Django(bcrypt)"
]
},
{
"regex": "^md5\\$[a-f0-9]+\\$[a-f0-9]{32}$",
"matches": [
"Django(MD5)"
]
},
{
"regex": "^\\{PKCS5S2\\}[a-z0-9\\/+]{64}$",
"matches": [
"PBKDF2(Atlassian)"
]
},
{
"regex": "^md5[a-f0-9]{32}$",
"matches": [
"PostgreSQL MD5"
]
},
{
"regex": "^\\([a-z0-9\\/+]{49}\\)$",
"matches": [
"Lotus Notes/Domino 8"
]
},
{
"regex": "^SCRYPT:[0-9]{1,}:[0-9]{1}:[0-9]{1}:[a-z0-9:\\/+=]{1,}$",
"matches": [
"scrypt"
]
},
{
"regex": "^\\$8\\$[a-z0-9\\/.]{14}\\$[a-z0-9\\/.]{43}$",
"matches": [
"Cisco Type 8"
]
},
{
"regex": "^\\$9\\$[a-z0-9\\/.]{14}\\$[a-z0-9\\/.]{43}$",
"matches": [
"Cisco Type 9"
]
},
{
"regex": "^\\$office\\$\\*2007\\*[0-9]{2}\\*[0-9]{3}\\*[0-9]{2}\\*[a-z0-9]{32}\\*[a-z0-9]{32}\\*[a-z0-9]{40}$",
"matches": [
"Microsoft Office 2007"
]
},
{
"regex": "^\\$office\\$\\*2010\\*[0-9]{6}\\*[0-9]{3}\\*[0-9]{2}\\*[a-z0-9]{32}\\*[a-z0-9]{32}\\*[a-z0-9]{64}$",
"matches": [
"Microsoft Office 2010"
]
},
{
"regex": "^\\$office\\$\\*2013\\*[0-9]{6}\\*[0-9]{3}\\*[0-9]{2}\\*[a-z0-9]{32}\\*[a-z0-9]{32}\\*[a-z0-9]{64}$",
"matches": [
"Microsoft Office 2013"
]
},
{
"regex": "^\\$fde\\$[0-9]{2}\\$[a-f0-9]{32}\\$[0-9]{2}\\$[a-f0-9]{32}\\$[a-f0-9]{3072}$",
"matches": [
"Android FDE \u2264 4.3"
]
},
{
"regex": "^\\$oldoffice\\$[01]\\*[a-f0-9]{32}\\*[a-f0-9]{32}\\*[a-f0-9]{32}$",
"matches": [
"Microsoft Office \u2264 2003 (MD5+RC4)",
"Microsoft Office \u2264 2003 (MD5+RC4) collider-mode #1",
"Microsoft Office \u2264 2003 (MD5+RC4) collider-mode #2"
]
},
{
"regex": "^\\$oldoffice\\$[34]\\*[a-f0-9]{32}\\*[a-f0-9]{32}\\*[a-f0-9]{40}$",
"matches": [
"Microsoft Office \u2264 2003 (SHA1+RC4)",
"Microsoft Office \u2264 2003 (SHA1+RC4) collider-mode #1",
"Microsoft Office \u2264 2003 (SHA1+RC4) collider-mode #2"
]
},
{
"regex": "^(\\$radmin2\\$)?[a-f0-9]{32}$",
"matches": [
"RAdmin v2.x"
]
},
{
"regex": "^{x-issha,\\s[0-9]{4}}[a-z0-9\\/+=]+$",
"matches": [
"SAP CODVN H (PWDSALTEDHASH) iSSHA-1"
]
},
{
"regex": "^\\$cram_md5\\$[a-z0-9\\/+=-]+\\$[a-z0-9\\/+=-]{52}$",
"matches": [
"CRAM-MD5"
]
},
{
"regex": "^[a-f0-9]{16}:2:4:[a-f0-9]{32}$",
"matches": [
"SipHash"
]
},
{
"regex": "^[a-f0-9]{4,}$",
"matches": [
"Cisco Type 7"
]
},
{
"regex": "^[a-z0-9\\/.]{13,}$",
"matches": [
"BigCrypt"
]
},
{
"regex": "^(\\$cisco4\\$)?[a-z0-9\\/.]{43}$",
"matches": [
"Cisco Type 4"
]
},
{
"regex": "^bcrypt_sha256\\$\\$(2[axy]|2)\\$[0-9]+\\$[a-z0-9\\/.]{53}$",
"matches": [
"Django(bcrypt-SHA256)"
]
},
{
"regex": "^\\$postgres\\$.[^\\*]+[*:][a-f0-9]{1,32}[*:][a-f0-9]{32}$",
"matches": [
"PostgreSQL Challenge-Response Authentication (MD5)"
]
},
{
"regex": "^\\$siemens-s7\\$[0-9]{1}\\$[a-f0-9]{40}\\$[a-f0-9]{40}$",
"matches": [
"Siemens-S7"
]
},
{
"regex": "^(\\$pst\\$)?[a-f0-9]{8}$",
"matches": [
"Microsoft Outlook PST"
]
},
{
"regex": "^sha256[:$][0-9]+[:$][a-z0-9\\/+]+[:$][a-z0-9\\/+]{32,128}$",
"matches": [
"PBKDF2-HMAC-SHA256(PHP)"
]
},
{
"regex": "^(\\$dahua\\$)?[a-z0-9]{8}$",
"matches": [
"Dahua"
]
},
{
"regex": "^\\$mysqlna\\$[a-f0-9]{40}[:*][a-f0-9]{40}$",
"matches": [
"MySQL Challenge-Response Authentication (SHA1)"
]
},
{
"regex": "^\\$pdf\\$[24]\\*[34]\\*128\\*[0-9-]{1,5}\\*1\\*(16|32)\\*[a-f0-9]{32,64}\\*32\\*[a-f0-9]{64}\\*(8|16|32)\\*[a-f0-9]{16,64}$",
"matches": [
"PDF 1.4 - 1.6 (Acrobat 5 - 8)"
]
}
]