2
0
mirror of https://github.com/lanjelot/patator synced 2024-11-12 01:10:42 +00:00

added the RANGE and PROG keywords (replaces the stdin capability)

This commit is contained in:
lanjelot 2013-06-26 17:52:27 +10:00
parent 2a1b9c5b27
commit 73ff44ac4c

View File

@ -609,7 +609,7 @@ logger.addHandler(handler)
# imports {{{
import re
import os
from sys import stdin, exc_info, exit, version_info, maxint
from sys import exc_info, exit, version_info, maxint
from time import localtime, strftime, sleep, time
from functools import reduce
from threading import Thread, active_count
@ -664,20 +664,19 @@ def which(program):
return None
def create_dir(top_path, from_stdin=False):
def create_dir(top_path):
top_path = os.path.abspath(top_path)
if os.path.isdir(top_path):
files = os.listdir(top_path)
if files:
if not from_stdin:
if raw_input("Directory '%s' is not empty, do you want to wipe it ? [Y/n]: " % top_path) != 'n':
for root, dirs, files in os.walk(top_path):
if dirs:
print("Directory '%s' contains sub-directories, safely aborting..." % root)
exit(0)
for f in files:
os.unlink(os.path.join(root, f))
break
if raw_input("Directory '%s' is not empty, do you want to wipe it ? [Y/n]: " % top_path) != 'n':
for root, dirs, files in os.walk(top_path):
if dirs:
print("Directory '%s' contains sub-directories, safely aborting..." % root)
exit(0)
for f in files:
os.unlink(os.path.join(root, f))
break
else:
os.mkdir(top_path)
return top_path
@ -707,6 +706,135 @@ def md5hex(plain):
def sha1hex(plain):
return hashlib.sha1(plain).hexdigest()
# I rewrote itertools.product to avoid memory over-consumption when using large wordlists
def product(xs, *rest):
if len(rest) == 0:
for x in xs():
yield [x]
else:
for head in xs():
for tail in product(*rest):
yield [head] + tail
def chain(*iterables):
def xs():
for iterable in iterables:
for element in iterable:
yield element
return xs
class FileIter:
def __init__(self, filename):
self.filename = filename
def __iter__(self):
return open(self.filename)
# These are examples. You can easily write your own iterator to fit your needs.
# Or using the PROG keyword, you can call an external program such as:
# - seq(1) from coreutils
# - http://hashcat.net/wiki/doku.php?id=maskprocessor
# - john -stdout -i
# For instance:
# $ ./dummy_test data=PROG0 0='seq 1 80'
# $ ./dummy_test data=PROG0 0='mp64.bin ?l?l?l',$(mp64.bin --combination ?l?l?l)
class RangeIter:
def __init__(self, typ, rng, random=None): #random.Random()):
r = rng.split('-')
if typ == 'hex':
self.fmt = '%x'
self.mn = int(r[0], 16)
self.mx = int(r[1], 16)
elif typ == 'digits':
self.fmt = '%d'
c = rng.count('-')
if c == 1: # 1-50
self.mn = int(r[0])
self.mx = int(r[1])
elif c == 2: # -50-50
self.mn = int(r[1]) * -1
self.mx = int(r[2])
elif c == 3: # -50--25
self.mn = int(r[1]) * -1
self.mx = int(r[3]) * -1
if self.mn > self.mx:
self.mn, self.mx = self.mx, self.mn
else:
raise NotImplementedError("Unsupported range '%s'" % rng)
self.cur = self.mn
self.random = random
def __iter__(self):
return self
def next(self):
if self.random:
val = self.random.randint(self.mn, self.mx)
return self.fmt % val
else:
if self.cur > self.mx:
raise StopIteration
ret = self.fmt % self.cur
self.cur += 1
return ret
def __len__(self):
if self.random:
return maxint
else:
return self.mx - self.mn + 1
def letterrange(first, last, charset):
for k in range(len(last)):
for x in product(*[chain(charset)]*(k+1)):
result = ''.join(x)
if first:
if first != result:
continue
else:
first = None
yield result
if result == last:
return
class LetterRangeIter:
def __init__(self, typ, rng):
self.first, self.last = rng.split('-')
if typ == 'letters':
self.charset = [c for c in string.letters]
elif 'lower' in typ:
self.charset = [c for c in string.lowercase]
elif 'upper' in typ:
self.charset = [c for c in string.uppercase]
else:
raise NotImplementedError("Incorrect type '%s'" % typ)
def __iter__(self):
return letterrange(self.first, self.last, self.charset)
def __len__(self):
def count(f):
total = 0
i = 0
for c in f[::-1]:
z = self.charset.index(c) + 1
total += (26**i)*z
i += 1
return total + 1
return count(self.last) - count(self.first) + 1
# }}}
# Controller {{{
@ -742,6 +870,12 @@ class Controller:
def find_module_keys(self, value):
return map(int, re.findall(r'MOD(\d)', value))
def find_range_keys(self, value):
return map(int, re.findall(r'RANGE(\d)', value))
def find_prog_keys(self, value):
return map(int, re.findall(r'PROG(\d)', value))
def usage_parser(self, name):
from optparse import OptionParser
from optparse import OptionGroup
@ -855,7 +989,6 @@ Please read the README inside for more examples and usage information.
self.actions = {}
self.free_list = []
self.paused = False
self.from_stdin = False
self.start_time = 0
self.total_size = 1
self.quit_now = False
@ -886,9 +1019,6 @@ Please read the README inside for more examples and usage information.
if k.isdigit():
wlists[k] = v
if v == '-':
self.from_stdin = True
else:
if v.startswith('@'):
p = os.path.expanduser(v[1:])
@ -935,7 +1065,19 @@ Please read the README inside for more examples and usage information.
self.iter_keys[i][2].append(k)
else:
self.payload[k] = v
for i in self.find_range_keys(v):
if i not in self.iter_keys:
self.iter_keys[i] = ('RANGE', iter_vals[i], [])
self.iter_keys[i][2].append(k)
else:
for i in self.find_prog_keys(v):
if i not in self.iter_keys:
self.iter_keys[i] = ('PROG', iter_vals[i], [])
self.iter_keys[i][2].append(k)
else:
self.payload[k] = v
logger.debug('iter_keys: %s' % self.iter_keys) # { 0: ('NET', '10.0.0.0/24', ['host']), 1: ('COMBO', 'combos.txt', [(0, 'user'), (1, 'password')]), 2: ('MOD', 'TLD', ['name'])
logger.debug('enc_keys: %s' % self.enc_keys) # [('password', 'ENC', hexlify), ('header', 'B64', b64encode), ...
@ -952,7 +1094,7 @@ Please read the README inside for more examples and usage information.
if opts.auto_log:
self.log_dir = create_time_dir(opts.log_dir or '/tmp/patator', opts.auto_log)
elif opts.log_dir:
self.log_dir = create_dir(opts.log_dir, self.from_stdin)
self.log_dir = create_dir(opts.log_dir)
if self.log_dir:
log_file = os.path.join(self.log_dir, 'RUNTIME.log')
@ -1042,11 +1184,8 @@ Please read the README inside for more examples and usage information.
total_time = time() - self.start_time
speed_avg = done_count / total_time
if self.from_stdin:
if self.quit_now:
self.total_size = -1
else:
self.total_size = done_count+skip_count
if self.total_size >= maxint:
self.total_size = -1
self.show_final()
@ -1083,7 +1222,7 @@ Please read the README inside for more examples and usage information.
# consumers
for num in range(self.num_threads):
pqueue = Queue()
pqueue = Queue(maxsize=1000)
t = Thread(target=self.consume, args=(gqueues[num], pqueue))
t.daemon = True
t.start()
@ -1097,33 +1236,6 @@ Please read the README inside for more examples and usage information.
def produce(self, queues):
if self.from_stdin:
from itertools import product, chain
else:
def product(xs, *rest):
if len(rest) == 0:
for x in xs():
yield [x]
else:
for head in xs():
for tail in product(*rest):
yield [head] + tail
def chain(*iterables):
def xs():
for iterable in iterables:
for element in iterable:
yield element
return xs
class FileIter:
def __init__(self, filename):
self.filename = filename
def __iter__(self):
return open(self.filename)
iterables = []
for _, (t, v, _) in self.iter_keys.items():
@ -1132,14 +1244,9 @@ Please read the README inside for more examples and usage information.
files = []
for fname in v.split(','):
if fname == '-': # stdin
size += maxint
files.append(stdin)
else:
fpath = os.path.expanduser(fname)
size += sum(1 for _ in open(fpath))
files.append(FileIter(fpath))
fpath = os.path.expanduser(fname)
size += sum(1 for _ in open(fpath))
files.append(FileIter(fpath))
iterable = chain(*files)
@ -1152,6 +1259,35 @@ Please read the README inside for more examples and usage information.
elements, size = self.module.available_keys[v]()
iterable = chain(elements)
elif t == 'RANGE':
typ, opt = v.split(':', 1)
logger.debug('typ: %s, opt: %s' % (typ, opt))
if typ in ['hex', 'digits']:
it = RangeIter(typ, opt)
size = len(it)
elif typ in ['letters', 'lower', 'lowercase', 'upper', 'uppercase']:
it = LetterRangeIter(typ, opt)
size = len(it)
else:
raise NotImplementedError("Incorrect range type '%s'" % typ)
iterable = chain(it)
elif t == 'PROG':
m = re.match(r'(.+),(\d+)$', v)
if m:
prog, size = m.groups()
else:
prog, size = v, maxint
logger.debug('prog: %s, size: %s' % (prog, size))
p = subprocess.Popen(prog.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
iterable, size = chain(p.stdout), int(size)
else:
raise NotImplementedError("Incorrect keyword '%s'" % t)
@ -1243,6 +1379,12 @@ Please read the README inside for more examples and usage information.
elif t == 'MOD':
for k in keys:
payload[k] = payload[k].replace('MOD%d' %i, prod[i])
elif t == 'RANGE':
for k in keys:
payload[k] = payload[k].replace('RANGE%d' %i, prod[i])
elif t == 'PROG':
for k in keys:
payload[k] = payload[k].replace('PROG%d' %i, prod[i])
for k, m, e in self.enc_keys:
payload[k] = re.sub(r'{0}(.+?){0}'.format(m), lambda m: e(m.group(1)), payload[k])
@ -1251,7 +1393,7 @@ Please read the README inside for more examples and usage information.
pp_prod = ':'.join(prod)
if self.check_free(payload):
pqueue.put_nowait(('skip', pp_prod, None, 0))
pqueue.put(('skip', pp_prod, None, 0))
continue
try_count = 0
@ -1298,7 +1440,7 @@ Please read the README inside for more examples and usage information.
actions = {'fail': None}
actions.update(self.lookup_actions(resp))
pqueue.put_nowait((actions, pp_prod, resp, time() - start_time))
pqueue.put((actions, pp_prod, resp, time() - start_time))
for name in self.module_actions:
if name in actions:
@ -1319,15 +1461,13 @@ Please read the README inside for more examples and usage information.
def monitor_progress(self):
while active_count() > 1 and not self.quit_now:
self.report_progress()
if not self.from_stdin:
self.monitor_interaction()
self.monitor_interaction()
def report_progress(self):
for i, pq in enumerate(self.thread_report):
p = self.thread_progress[i]
while True:
for _ in range(pq.maxsize):
try:
actions, current, resp, seconds = pq.get_nowait()
@ -1381,7 +1521,7 @@ Please read the README inside for more examples and usage information.
def monitor_interaction(self):
from sys import stdin
i, _, _ = select([stdin], [], [], .1)
if not i: return
command = i[0].readline().strip()
@ -1424,16 +1564,22 @@ Please read the README inside for more examples and usage information.
else: # show progress
total_count = sum(p.done_count+p.skip_count for p in self.thread_progress)
speed_avg = self.num_threads / (sum(sum(p.seconds) / len(p.seconds) for p in self.thread_progress) / self.num_threads)
remain_seconds = (self.total_size - total_count) / speed_avg
etc_time = datetime.now() + timedelta(seconds = remain_seconds)
if self.total_size >= maxint:
etc_time = 'inf'
remain_time = 'inf'
else:
remain_seconds = (self.total_size - total_count) / speed_avg
remain_time = pprint_seconds(remain_seconds, '%02d:%02d:%02d')
etc_seconds = datetime.now() + timedelta(seconds=remain_seconds)
etc_time = etc_seconds.strftime('%H:%M:%S')
logger.info('Progress: {0:>3}% ({1}/{2}) | Speed: {3:.0f} r/s | ETC: {4} ({5} remaining) {6}'.format(
total_count * 100/self.total_size,
total_count,
self.total_size,
speed_avg,
etc_time.strftime('%H:%M:%S'),
pprint_seconds(remain_seconds, '%02d:%02d:%02d'),
etc_time,
remain_time,
self.paused and '| Paused' or ''))
if command == 'f':
@ -3439,6 +3585,62 @@ class Keystore_pass:
# }}}
# TCP Fuzz {{{
class TCP_fuzz:
'''Fuzz TCP services'''
usage_hints = (
'''%prog host=10.0.0.1 data=RANGE0 0=hex:0x00-0xffffff''',
)
available_options = (
('host', 'target host'),
('port', 'target port'),
('timeout', 'seconds to wait for a response [10]'),
)
available_actions = ()
Response = Response_Base
def execute(self, host, port, data='', timeout='2'):
fp = socket.create_connection((host, port), int(timeout))
fp.send(data.decode('hex'))
resp = fp.recv(1024)
fp.close()
code = 0
mesg = resp.encode('hex')
return self.Response(code, mesg)
# }}}
# Dummy Test {{{
class Dummy_test:
'''Testing module'''
usage_hints = (
"""%prog data=RANGE0 0=hex:0x00-0xffff""",
"""%prog data=PROG0 0='seq 0 80'""",
"""%prog data=PROG0 0='mp64.bin -i ?l?l?l',$(mp64.bin --combination -i ?l?l?l)""",
)
available_options = (
('data', 'data to test'),
)
available_actions = ()
Response = Response_Base
def execute(self, data):
code = 0
mesg = data
#sleep(.01)
return self.Response(code, mesg)
# }}}
# modules {{{
modules = [
('ftp_login', (Controller, FTP_login)),
@ -3470,6 +3672,9 @@ modules = [
('unzip_pass', (Controller, Unzip_pass)),
('keystore_pass', (Controller, Keystore_pass)),
('tcp_fuzz', (Controller, TCP_fuzz)),
('dummy_test', (Controller, Dummy_test)),
]
dependencies = {