diff --git a/attack.py b/attack.py new file mode 100644 index 0000000..fb58602 --- /dev/null +++ b/attack.py @@ -0,0 +1,82 @@ +# dead simple dictionary attack for fixed-length passwords. +# this is far from optimal. + +from collections import defaultdict +import argparse +import sys +import random + +program = sys.argv[0] +args = sys.argv[1:] + +parser = argparse.ArgumentParser( + description="attack: produce fixed-length strings from dictionaries") + +parser.add_argument( + 'length', type=int, + help="the length of the strings to output") +parser.add_argument( + 'path', metavar='text-file', nargs='+', + help="a dictionary file containing one string per line") +parser.add_argument( + '-n', metavar='limit', type=float, default='inf', + help="output this many strings and exit") +parser.add_argument( + '-s', metavar='seed', type=int, default=None, + help="use as a seed for the random number generator") + +a = parser.parse_args(args) +seed = a.s +limit = a.n +paths = a.path +fixed_length = a.length +del a + +lines = [] +for path in paths: + with open(path, "r") as f: + for line in f: + lines.append(line.strip('\r\n')) + +nlines = defaultdict(lambda: []) +ncount = defaultdict(lambda: 0) +for line in lines: + length = len(line) + if length == 0 or length > fixed_length: + continue + nlines[length].append(line) + ncount[length] += 1 + +del lines + +if seed is not None: + random.seed(seed) + +lengths = list(ncount.keys()) +length_weights = list(ncount.values()) + +i = 0 +while i < limit: + s = '' + new_weights = length_weights.copy() + + while len(s) < fixed_length: + if len(s) > 0: + for j, length in enumerate(lengths): + if len(s) + length > fixed_length: + new_weights[j] = 0 + + if sum(new_weights) == 0: + s = '' + new_weights = length_weights.copy() + continue + + chosen_length = random.choices(lengths, new_weights)[0] + s += random.choice(nlines[chosen_length]) + + try: + print(s) + except OSError: + # pipe closed. + break + i += 1