You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/deps/libinjection/make_parens.py

394 lines
11 KiB

#!/usr/bin/env python
# pylint: disable=C0103,R0911,R0912,R0915
# disable short-variable-names, too many branches, returns, statements
"""
fingerprint fuzzer and generator
Given a fingerprint, this generates other similar fingerprints
that are functionally equivalent for SQLi detection
"""
import sys
class PermuteFingerprints(object):
""" class to mutate / fuzz fingerprints to find new SQLi """
def __init__(self):
""" initialization """
self.fingerprints = set()
self.blacklist = set([
'E1n', 'sns', '1&n', 's1s', '1n1', '1o1', '1os', 'sn1',
'sonc', 'so1', 'n&n', 'son', 'nov', 'n&s', 'E1s', 'nos',
'nkn&n', '1sn', 'n&nkn', 's1n', 'n&nEn', 's&sn', '1os1o',
'sU', 'nU', 'n,(n)', 'n&n&n', 'Enkn', 'nk1;',
'1os1o', '1n1;', 's*1s', '1s1', 'nknEn', 'n&sn',
'so1', 'nkn;', 'n&n;', 'von', 'n&nc', 'sonkn',
'n)o1', 'Enn;', 'nBn', 'Ennc', 'n&En', 'nEnEn', 'Esn',
'n1s', 'n(1)s', 'En1', 'En(1)', 'n(1)n', 'n1v',
'n(1)1', 'n&EUE', 'n&EkU', 's&EUE', 's&EkU', 'v&EUE', 'v&EkU',
'n&nTn', 'nA', 'nos;n', 'UEn', 'so1no', '1)on', '1k(1)',
's)on', '1;TnE', 's&1s', 'n)c'
])
self.whitelist = set([
'T(vv)', 'Tnvos', 'Tnv;', '1UEnn', '1;Tvk'
])
def aslist(self):
"""
return the fingerprints as a sorted list
"""
return sorted(list(self.fingerprints))
def insert(self, fingerprint):
"""
insert a new fingerprint, with possible variations
"""
if len(fingerprint) > 5:
fingerprint = fingerprint[0:5]
if self.validate(fingerprint):
self.fingerprints.add(fingerprint)
def validate(self, s):
"""
detemines if a fingerprint could be used a SQLi
"""
if len(s) == 0:
return False
if s in self.whitelist:
return True
if s in self.blacklist:
return False
# SQL Types are rarely used
if 't' in s and 'f(t' not in s and 'At' not in s:
return False
if 'v,s' in s:
return False
if 's,v' in s:
return False
if 'v,v' in s:
return False
if 'v,1' in s:
return False
if 'v,n' in s:
return False
if 'n,v' in s:
return False
if '1,v' in s:
return False
if 'Eo(' in s:
return False
if '(o(' in s:
return False
if '(o1' in s:
return False
if '(on' in s:
return False
if '(os' in s:
return False
if '(of' in s:
return False
if '(ov' in s:
return False
if 'oso' in s:
return False
if 'o1o' in s:
return False
if 'ono' in s:
return False
# only 1 special case for this
# 1;foo:goto foo
# 1;n:k
# the 'foo' can only be a 'n' type
if ':' in s and not 'n:' in s:
return False
if '11' in s:
return False
if '))' in s:
return False
if '((' in s:
return False
if 'v1' in s:
return False
if 'nv' in s and ';T' not in s:
return False
if 'nn' in s and ';T' not in s:
return False
# select @version foo is legit
# but unlikely anywhere else
if 'vn' in s and 'Evn' not in s:
return False
if 'oE' in s:
return False
if 'A1' in s:
return False
if 'An' in s:
return False
if 'A(1' in s:
return False
if 'vov' in s:
return False
if 'vo1' in s:
return False
if 'von' in s:
return False
if 'ns' in s:
if 'U' in s:
return True
if 'T' in s:
return True
return False
if 'sn' in s:
# that is... Tsn is ok
if s.find('T') != -1 and s.find('T') < s.find('sn'):
return True
return False
# select foo (as) bar is only nn type i know
if 'nn' in s and 'Enn' not in s and ';T' not in s:
return False
if ',o' in s:
return False
if 'kk' in s and 'Tkk' not in s:
return False
if 'ss' in s:
return False
if 'ff' in s:
return False
if '1no' in s:
return False
if 'kno' in s:
return False
if 'nEk' in s:
return False
if 'n(n' in s:
return False
if '1so' in s:
return False
if '1s1' in s:
return False
if 'noo' in s:
return False
if 'ooo' in s:
return False
if 'vvv' in s:
return False
if '1vn' in s:
return False
if '1n1' in s:
return False
if '&1n' in s:
return False
if '&1v' in s:
return False
if '&1s' in s:
return False
if 'nnk' in s:
return False
# folded away
if s.startswith('('):
return False
if '&o' in s:
return False
if '1,1' in s:
return False
if '1,s' in s:
return False
if '1,n' in s:
return False
if 's,1' in s:
return False
if 's,s' in s:
return False
if 's,n' in s:
return False
if 'n,1' in s:
return False
if 'n,s' in s:
return False
if 'n,n' in s:
return False
if '1o1' in s:
return False
if '1on' in s:
return False
if 'no1' in s:
return False
if 'non' in s:
return False
if '1(v' in s:
return False
if '1(n' in s:
return False
if '1(s' in s:
return False
if '1(1' in s:
return False
if 's(s' in s:
return False
if 's(n' in s:
return False
if 's(1' in s:
return False
if 's(v' in s:
return False
if 'v(s' in s:
return False
if 'v(n' in s:
return False
if 'v(1' in s:
return False
if 'v(v' in s:
return False
if s.startswith('vs'):
return False
if ')(' in s:
return False
# need to investigate T(vv) to see
# if it's correct
if 'vv' in s and s != 'T(vv)':
return False
# unlikely to be sqli but case FP
if s in ('so1n)', 'sonoE'):
return False
return True
def permute(self, fp):
"""
generate alternative (possiblely invalid) fingerprints
"""
self.insert(fp)
# do this for safety
if len(fp) > 1 and len(fp) < 5 and fp[-1] != ';' and fp[-1] != 'c':
self.insert(fp + ";")
self.insert(fp + ";c")
# do this for safety
if len(fp) > 1 and len(fp) < 5 and fp[-1] != 'c':
self.insert(fp + "c")
for i in range(len(fp)):
if fp[i] == '1':
self.insert(fp[0:i] + 'n' + fp[i+1:])
self.insert(fp[0:i] + 'v' + fp[i+1:])
self.insert(fp[0:i] + 's' + fp[i+1:])
self.insert(fp[0:i] + 'f(1)' + fp[i+1:])
self.insert(fp[0:i] + 'f()' + fp[i+1:])
self.insert(fp[0:i] + '1os' + fp[i+1:])
self.insert(fp[0:i] + '1ov' + fp[i+1:])
self.insert(fp[0:i] + '1on' + fp[i+1:])
self.insert(fp[0:i] + '(1)' + fp[i+1:])
elif fp[i] == 's':
self.insert(fp[0:i] + 'v' + fp[i+1:])
self.insert(fp[0:i] + '1' + fp[i+1:])
self.insert(fp[0:i] + 'f(1)' + fp[i+1:])
self.insert(fp[0:i] + 'f()' + fp[i+1:])
self.insert(fp[0:i] + 'so1' + fp[i+1:])
self.insert(fp[0:i] + 'sov' + fp[i+1:])
self.insert(fp[0:i] + 'son' + fp[i+1:])
self.insert(fp[0:i] + '(s)' + fp[i+1:])
elif fp[i] == 'v':
self.insert(fp[0:i] + 's' + fp[i+1:])
self.insert(fp[0:i] + '1' + fp[i+1:])
self.insert(fp[0:i] + 'f(1)' + fp[i+1:])
self.insert(fp[0:i] + 'f()' + fp[i+1:])
self.insert(fp[0:i] + 'vo1' + fp[i+1:])
self.insert(fp[0:i] + 'vos' + fp[i+1:])
self.insert(fp[0:i] + 'von' + fp[i+1:])
self.insert(fp[0:i] + '(v)' + fp[i+1:])
elif fp[i] == 'E':
# Select top, select distinct, case when
self.insert(fp[0:i] + 'Ek' + fp[i+1:])
elif fp[i] == ')':
self.insert(fp[0:i] + '))' + fp[i+1:])
self.insert(fp[0:i] + ')))' + fp[i+1:])
self.insert(fp[0:i] + '))))' + fp[i+1:])
if ';E' in fp:
self.insert(fp.replace(';E', ';T'))
if fp.startswith('T'):
self.insert('1;' + fp)
self.insert('1);' + fp)
if 'At' in fp:
self.insert(fp.replace('At', 'As'))
if '(' in fp:
done = False
parts = []
for char in fp:
if char == '(' and done is False:
parts.append(char)
done = True
parts.append(char)
newline = ''.join(parts)
self.insert(newline)
done = False
parts = []
for char in fp:
if char == '(':
if done is True:
parts.append(char)
else:
done = True
parts.append(char)
newline = ''.join(parts)
self.insert(newline)
done = False
parts = []
for char in fp:
if char == '(':
parts.append(char)
parts.append(char)
newline = ''.join(parts)
self.insert(newline)
def main():
""" main entrance """
mutator = PermuteFingerprints()
for line in sys.stdin:
mutator.permute(line.strip())
for fingerprint in mutator.aslist():
print fingerprint
if __name__ == '__main__':
main()