Welcome to Kitty’s documentation!

Contents:

Introduction

Sulley: Boo?
Boo: Kitty!

What is Kitty?

Kitty is an open-source modular and extensible fuzzing framework written in python, inspired by OpenRCE’s Sulley and Michael Eddington’s (and now Deja Vu Security’s) Peach Fuzzer.

Goal

When we started writing Kitty, our goal was to help us fuzz unusual targets — meaning proprietary and esoteric protocols over non-TCP/IP communication channels — without writing everything from scratch each time. A generic and abstract framework that would include the common functionallity of every fuzzing process we could think of, and would allow the user to easily extend and use it to test their specific target.

Features

With this goal in mind, the following features were very important to us:

Modularity:Each part of the fuzzer stands on its own. This means that you can use the same monitoring code for different applications, or the same payload generator (aka Data Model) for testing parsing of the same data that is received over different channels.
Extensibility:If you need to test something “new”, you will not need to change Kitty’s core code. Most, if not all, features can be implemented in the user code. This includes monitoring, controlling and communicating with the fuzzed target.
Rich data modeling:
 The data model core is rich and allows describing advanced data structures, including strings, hashes, lengths, conditions and many more. And, like most of the framework, it is designed to be extended even further as necessary.
Stateful:Support for multi-stage fuzzing tests. Not only you can describe what the payload of an individual message will look like, you can also describe the order of messages, and even perform fuzzing on the sequence’s order.
Client and Server fuzzing:
 You can fuzz both servers and clients, assuming you have a matching stack. Sounds like a big requirement, but it isn’t: it just means that you should have the means to communicate with the target, which you should have in most cases anyway.
Cross platform:Runs on Linux, OS X and Windows. We don’t judge ;-)

What it’s not?

Well, Kitty is not a fuzzer. It also contains no implementation of specific protocol or communication channel. You can write your own fuzzer with it, and you can use Kitty-based code of others, but it’s not an out-of-the-box fuzzer.

A good place to get (and add) implementations of Kitty models is Katnip.

Katnip

Kitty, as a framework, implements the fuzzer main loop, and provides syntax for modeling data and base classes for each of the elements that are used to create a full fuzzing session. However, specific implementations of classes are not part of the Kitty framework. This means that Kitty defines the interface and base class to perform data transactions with a target, but it doesn’t provide implementations for data transmition over HTTP, TCP or UART.

Implementations of all sorts of classes can be found in the complimentary repository Katnip, which has the sole porpuse of providing specific implementaions. As such, Katnip contains different implementations for targets (such as TcpTarget and SslTarget), controllers (such as TelnetController and SshController), monitors and templates. Use them when you write your fuzzer, as they will save you a lot of time, and may serve as a reference for your own implementations.

What’s Next?

  • Go over Kitty’s structure
  • Take a look at, and run, some examples (requires Katnip)
  • Go over the tutorials
  • Go fuzz something new :)

Contribution FAQ

Found a bug?
Open an issue
Have a fix?
Great! please submit a pull request
Implemented an interesting controller/monitor/target?
Please submit a pull request in the Katnip repository
Found an interesting bug using a Kitty-based fuzzer?
We’d love to hear about it! please drop us a line

Framework Structure

This document goes over the main modules that form a fuzzer and explains the relation between them. It also discusses the differences between client and server fuzzing.

Relation Between Modules

Fuzzer  +--- Model *--- Template *--- Field
        |
        +--- Target  +--- Controller
        |            |
        |            *--- Monitor
        |
        +--- Interface

Modules

Data Model

The first part of Kitty is the data model. It defines the structure of the messages that will be sent by the fuzzer. This includes a separation of the message into fields (such as header, length and payload), types of those fields (such as string, checksum and hex-encoded 32bit integer) and the relation between them (such as length, checksum and count). The data model also describes the order in which different messages are chained together to form a fuzzing session. This can be useful when trying to fuzz deeper parts of the system (for example, getting past an authentication stage in order to fuzz the parts of the system only reachable by authenticated users). The data model can also specify that the order in which messages are sent itself be fuzzed.

The data model is constructed using the classes defined in the model source folder.

For more information, visit the data model documentation, reference and tutorials.

Target

Base Classes:
  1. kitty.targets.client.ClientTarget when fuzzing a client application
  2. kitty.targets.server.ServerTarget when fuzzing a server application
API Reference:

kitty.targets package

The target module is in charge of everything that is related to the victim. Its responsibilities are:

  1. When fuzzing a server — initiating the fuzzing session by sending a request to the server, and handling the response, when such response exists.
  2. When fuzzing a client — triggering a fuzzing session by causing the client to initiate a request to the server. The server, with the help of the stack (see the client fuzzing tutorial), will send a fuzzed response to the client. Note that in this case the target itself is not involved the client-server-fuzzer communication!
  3. Managing the monitors and controllers (see below).

The sources for the target classes are located in the targets source folder. There are two target base classes, ClientTarget (in targets/client.py) and ServerTarget (in targets/server.py). These classes define the target APIs and should be subclassed when implementing a new target class.

A class should be written for every new type of target. For example, if you want to test a server application that communicates over a serial UART connection, you will need to create a new class that inherits from ServerTarget and is able to send data over the UART. However, many times it will only require the implementation of the send/receive functions and not much more. Some targets are already available in the Katnip repository, so you don’t need to implement them yourself: for example, TcpTarget may be used to test HTTP servers and SslTarget may be used to test HTTPS servers.

The controller and the monitors (described below) are managed by the target, which queries and uses them as needed.

For each test the target generates a report which contains the reports of the controller and all monitors. This report is passed back to the fuzzer (described below) upon request.

Controller

Base Class:kitty.controllers.base.BaseController
API Reference:kitty.controllers package

The controller is in charge of preparing the victim for the test. It should make sure that the victim is in an appropriate state before the target initiates the transfer session. Sometimes it means doing nothing, other times it means starting or reseting a VM, killing a process or performing a hard reset to the victim hardware.

Since the controller is reponsible for the state of the victim, it is expected to perform basic monitoring as well, and report whether the victim is ready for the next test.

Monitor

Base Class:kitty.monitors.base.BaseMonitor
API Reference:kitty.monitors package

A monitor object monitors the behavior of the victim. It may monitor the network traffic, memory consumption, serial output or anything else.

Since there might be more than a single behavior to monitor, multiple monitors can be used when fuzzing a victim.

Fuzzer

Classes:
  1. kitty.fuzzers.client.ClientFuzzer when fuzzing a client target.
  2. kitty.fuzzers.server.ServerFuzzer when fuzzing a server target.
API Reference:

kitty.fuzzers package

A fuzzer drives the whole fuzzing process. Its job is to obtain mutated payloads from the model, initiate the data transaction, receive the report from the target, and perform further processing, if needed. A fuzzer is the top level entity in our test runner, and should not be subclassed in most cases.

Interface

Base Class:kitty.interfaces.base.BaseInterface
API Reference:kitty.interfaces package

Interface is a user interface, which allows the user to monitor and check the fuzzer as it goes. The web interface should suffice in most cases.

Tutorials

Here you can find some tutorials to help you get started with Kitty.

Server vs. Client Fuzzing

Kitty is a framework for fuzzing various kinds of entities. It can fuzz servers — by acting like a web client to initiate requests to the victim server, or by acting like a USB host to send commands to the victim USB device; and it can also fuzz clients, by responding to requests made by them — acting as the web server to respond to requests from the victim browser, or as the USB device responding to commands from the victim USB host.

In Kitty’s terminology, the client is the side initiating the data exchange, and the server is the side reacting to it. By this defintion, a TCP server would be considered a server, as it reacts to requests initiated by the fuzzer; but so would a PDF viewer, if it is started with a given file provided by the fuzzer. Similarly, an IRC client or a web browser would be considered clients, as they initiate requests to the fuzzer and then process its responses; but so would a USB-Host stack, which initiates the data exchange with a fuzzer disguised as the USB device. And in fact, the same entity could be a client in some contexts, and a server in others: a web browser would be considered a client in the context of conversations made to the fuzzer-server, as seen above; but would be considerd a server in the context of being run as a process by the fuzzer, with a command-line argument pointing it at a given (presumably-fuzzed) URL.

There is a big difference between server fuzzing and client fuzzing, and the need to support both was a main driver for the creation of Kitty. This section will list the conceptual, flow and implementation differences between those two modes.

Conceptual Differences

Session initiation:
 

Since the nature of a server is to handle and respond to requests, it is rather simple to fuzz it on our terms: we need to make sure it is up and ready to accept requests, and then start sending those requests to it. This means that the fuzzer is active, it initiates the data exchange and decides what data the server will handle, assuming that the server starts in the same state for each data exchange session.

On the other hand, when fuzzing a client, the mutated data is the response, not the request. The client is the one to initiate the data exchange, the fuzzer acts as a server, and so it is passive and cannot control the timing or even the occurence of the fuzzing session. In order to take control back, the fuzzer needs some way to cause the client to start the data exchange.

Communication Stack:
 

When a server is tested, it is the fuzzer, as we just saw, that initiates the communication. This means that the fuzzer can choose at exactly what layer to begin the communication. So, for example, when testing a TCP-based protocol, the TCP stack may be used, and when testing an HTTP-based protocol, an HTTP stack may be used. Moreover, there usually are readily-available Python or C APIs for initiating the communication starting at any of these layers.

When testing a client, on the other hand, the fuzzer is only responding to requests from the client. As such, the fuzzer cannot easily choose at which layer to handle the communication — it must handle the request at whichever layer it was received. Thus, fuzzing a specific layer will very likely require hooking into the stack implementation in order to inject the mutated responses.

Flow Differences

The flow differences are derived from the conceptual differences, the flow for each scenario is described below.

[ TBD - flow charts ]

Implementation Differences

The implementation differences are derived from the flow differences and are listed in the table below.

Component Server Mode Client Mode
Stack Unmodified in most cases Hooked in most cases, runs as a separate process
Target Responsible for sending and receiving data from victim, uses the stack Responsible for triggering the data exchange in the victim (using the controller), data is handled directly by the stack
Controller Brings the victim to initial state, monitors victim status Same as in server mode, but additionally responsible for triggering the data exchange
Fuzzer Actively polls the data model for more data and triggers the data exhange Waits in a separate process for requests from the modified stack over IPC, provides stack with mutated data when needed

Server Fuzzing Tutorial

This tutorial will guide you through the steps that are taken to build a fuzzer for your target, we will build such a fuzzer for a tiny HTTP server. It will be a minimal implementation, just to show the basics.

  • First, we need to define our data model to let Kitty know how our protocol looks like.
  • Then, we need to define how will we communicate with our target.
  • After that, we need to find a way to control our target and how to monitor it (TBD).
  • And finally, we need to connect all those pieces together.

Data Model

We start with a simple example, of fuzzing a simple HTTP GET request. For simplicity, we will not look at the spec to see the format of each message.

A simple “GET” request may look like this:

GET /index.html HTTP/1.1

There are some obvious fields in this request:

  1. Method - a string with the value “GET”
  2. Path - a string with the value “/index.html”
  3. Protocol - a string with the value “HTTP/1.1”

However, there are some other things in this message, which we ignored:

  • (1.a) The space between Method and Path
  • (2.a) The space between Path and Protocol
    1. The double “new lines” (“”) at the end of the request

Those are the delimiters, and we should not forget them.

Here’s the translation of this structure to a Kitty model:

Data model, version 1

from kitty.model import *

http_get_v1 = Template(name='HTTP_GET_V1', fields=[
    String('GET', name='method'),           # 1. Method - a string with the value "GET"
    Delimiter(' ', name='space1'),          # 1.a The space between Method and Path
    String('/index.html', name='path'),     # 2. Path - a string with the value "/index.html"
    Delimiter(' ', name='space2'),          # 2.a. The space between Path and Protocol
    String('HTTP/1.1', name='protocol'),    # 3. Protocol - a string with the value "HTTP/1.1"
    Delimiter('\r\n\r\n', name='eom'),      # 4. The double "new lines" ("\r\n\r\n") at the end of the http request
])

We used three new objects here, all declared in kitty/model/__init__.py:

  1. Template, which is the top most container of the low level data model, it encloses a full message. It received all of its enclosed fields as an array.
  2. String('GET', name='method') creates a new String object with the default value ‘GET’, and names it ‘method’.
  3. Delimiter(' ', name='space1') creates a new Delimiter object with the default value ‘ ‘, and names it ‘space1’.

Based on this model, Kitty will generate various mutations of the template, each mutation is constructed from a mutation of one of the fields, and the default values of the rest of them. When a field has no more mutations, it will return it to its default value, and move to the next field.

Even in this simple example, we refine our model even more. We can see that the Protocol field can be divided even more. We can split it to the following fields:

  • (3.a) Protocol Name - a string with the value “HTTP”
  • (3.b) The ‘/’ after “HTTP”
  • (3.c) Major Version - a number with the value 1
  • (3.d) The ‘.’ between 1 and 1
  • (3.e) Minor Version - a number with the value 1

Now we can replace the protocol string field with 5 fields.

Data model, version 2

from kitty.model import *

http_get_v2 = Template(name='HTTP_GET_V2', fields=[
    String('GET', name='method'),               # 1. Method - a string with the value "GET"
    Delimiter(' ', name='space1'),              # 1.a The space between Method and Path
    String('/index.html', name='path'),         # 2. Path - a string with the value "/index.html"
    Delimiter(' ', name='space2'),              # 2.a. The space between Path and Protocol
    String('HTTP', name='protocol name'),       # 3.a Protocol Name - a string with the value "HTTP"
    Delimiter('/', name='fws1'),                # 3.b The '/' after "HTTP"
    Dword(1, name='major version',              # 3.c Major Version - a number with the value 1
          encoder=ENC_INT_DEC),                 # encode the major version as decimal number
    Delimiter('.', name='dot1'),                # 3.d The '.' between 1 and 1
    Dword(1, name='minor version',              # 3.e Minor Version - a number with the value 1
          encoder=ENC_INT_DEC),                 # encode the minor version as decimal number
    Delimiter('\r\n\r\n', name='eom')           # 4. The double "new lines" ("\r\n\r\n") at the end of the request
])

We just met two new objects:

  1. Dword(1, name='major version') create a 32-bit integer field with default value 1 and name it ‘major version’
  2. ENC_INT_DEC is an encoder that encodes this int as a decimal number. An encoder only affects the representation of the number, not its data nor its mutations

Dword is part of a family of fields (Byte, Word and Qword) that provides convenient initialization to the basic field on BitField.

The last example shows how we can treat a payload in different ways, and how it affects our data model. It is not always good to give too much details in the model. Sometimes too much details will make the fuzzer miss some weird cases, because it will always be “almost correct” and most of the times it will cause the fuzzing session to be very long. There is a balance that should be reached, and each implementor should find his own (this is a spiritual guide as well).

HTTP_GET_V2 is pretty detailed data model, but while all parts of the template that we want Kitty to send should be represented by fields, there are fields that we don’t want Kitty to mutate. For Instance, the two new lines at the end of the request signals the server that the message has ended, and if they are not sent, the request will probably not be processed at all. Or, if we know there is a “GET” handler function in the target, we might want to always have “GET ” at the start of our template.

The next example achieves both goals, but in two different ways:

Data model, version 3

from kitty.model import *

http_get_v3 = Template(name='HTTP_GET_V3', fields=[
    String('GET', name='method', fuzzable=False),   # 1. Method - a string with the value "GET"
    Delimiter(' ', name='space1', fuzzable=False),  # 1.a The space between Method and Path
    String('/index.html', name='path'),             # 2. Path - a string with the value "/index.html"
    Delimiter(' ', name='space2'),                  # 2.a. The space between Path and Protocol
    String('HTTP', name='protocol name'),           # 3.a Protocol Name - a string with the value "HTTP"
    Delimiter('/', name='fws1'),                    # 3.b The '/' after "HTTP"
    Dword(1, name='major version',                  # 3.c Major Version - a number with the value 1
          encoder=ENC_INT_DEC),                     # encode the major version as decimal number
    Delimiter('.', name='dot1'),                    # 3.d The '.' between 1 and 1
    Dword(1, name='minor version',                  # 3.e Minor Version - a number with the value 1
          encoder=ENC_INT_DEC),                     # encode the minor version as decimal number
    Static('\r\n\r\n', name='eom')                  # 4. The double "new lines" ("\r\n\r\n") at the end of the request
])

The first method we used is setting the fuzzable parameter of a field to False, as we did for the first two fields, this method lets us preserve the structure of the model, and change it easily when we do want to mutate those fields:

String('GET', name='method', fuzzable=False),   # 1. Method - a string with the value "GET"
Delimiter(' ', name='space1', fuzzable=False),  # 1.a The space between Method and Path

The second method is by using a Static object, which is immutable, as we did with the last field, this method improves the readability if we have a long chunk of data in our template that will never change:

# 4. The double "new lines" ("\r\n\r\n") at the end of the request
Static('\r\n\r\n', name='eom')

Target

Now that we have a data model, we need to somehow pass it to our target. Since we are fuzzing an HTTP server implementation, we need to send our requests over TCP. There is already a target class to take care of TCP communication with the server - kitty.targets.tcp.TcpTarget, but we will build it here again, step by step, to learn from it.

When fuzzing a server, our target should inherit from ServerTarget. Except of two methods - _send_to_target and _receive_from_target, each method that you override should call its super.

Each fuzzing session goes through the following stages:

1. set up the environment
2. for each mutation:
    1. preform pre-test actions
    2. do transmition
    3. cleanup after the test
    4. provide a test report
3. tear down the environment

Each of those steps is reflected in the ServerTarget API:

Step Corresponding API
set up the environment setup()
perform pre-test actions pre_test(test_num)
do transmission transmit(payload) (calls _send_to_target(payload) and _receive_from_target())
cleanup after test post_test(test_num)
provide a test report get_report()
tear down the environment teardown()

Now let’s implement those methods (the part we need):

class definition and constructor
'''
TcpTarget is an implementation of a TCP target
'''
import socket
from kitty.targets.server import ServerTarget


class TcpTarget(ServerTarget):
    '''
    TcpTarget is implementation of a TCP target for the ServerFuzzer
    '''

    def __init__(self, name, host, port, timeout=None, logger=None):
        '''
        :param name: name of the object
        :param host: hostname of the target (the TCP server)
        :param port: port of the target
        :param timeout: socket timeout (default: None)
        :param logger: logger for this object (default: None)
        '''
        ## Call ServerTarget constructor
        super(TcpTarget, self).__init__(name, logger)
        ## hostname of the target (the TCP server)
        self.host = host
        ## port of the target
        self.port = port
        if (host is None) or (port is None):
            raise ValueError('host and port may not be None')
        ## socket timeout (default: None)
        self.timeout = timeout
        ## the TCP socket
        self.socket = None

We create a socket at the beginning of each test, and close it at the end

pre_test and post_test
def pre_test(self, test_num):
    '''
    prepare to the test, create a socket
    '''
    ## call the super (report preparation etc.)
    super(TcpTarget, self).pre_test(test_num)
    ## only create a socket if we don't have one
    if self.socket is None:
        sock = self._get_socket()
        ## set the timeout
        if self.timeout is not None:
            sock.settimeout(self.timeout)
        ## connect to socket
        sock.connect((self.host, self.port))
        ## our TCP socket
        self.socket = sock

def _get_socket(self):
    '''get a socket object'''
    ## Create a TCP socket
    return socket.socket(socket.AF_INET, socket.SOCK_STREAM)

def post_test(self, test_num):
    '''
    Called after a test is completed, perform cleanup etc.
    '''
    ## Call super, as it prepares the report
    super(TcpTarget, self).post_test(test_num)
    ## close socket
    if self.socket is not None:
        self.socket.close()
        ## set socket to none
        self.socket = None

Notice that we called the super in each overriden method. This is important, as the super class perform many tasks that are not target-specific.

The next step is to implement the sending and receiving. It’s pretty straight forward, we call socket’s send and receive methods. We don’t call super in those methods, as the super is not implemented.

send_to_target + receive_from_target
def _send_to_target(self, data):
    self.socket.send(data)

def _receive_from_target(self):
    return self.socket.recv(10000)

That’s it. We have a target that is able to perform TCP transmissions.

As the final stage of each test is providing a report, you can add fields to the report in your target at each of the methods above.

A basic fuzzer can already created with what we’ve seen so far. Dummy controller can supply supply the requirement of the base target class, and we don’t have to use any monitor at all, but if we want to be able to not only crash the client, but to be able to detect the crash and restart it once it crashes, we need to implement a Controller

Controller

As described in the overview, and in the Controller Documentation, the controller makes sure that our victim is ready to be fuzzed, and if it can’t it reports failure.

In our example, we have an HTTP server that we want to fuzz, for simplicity, we will run the server locally. We will do it by implementing LocalProcessController a class that inherits from kitty.controllers.base.BaseController.

The controller is controller by the Target and it follows pretty much the same stages as the target (excluding the transmission) Each fuzzing session goes through the following stages:

1. set up the environment
2. for each mutation:
    1. preform pre-test actions
    2. cleanup after the test
    3. provide a test report
3. tear down the environment

Each of those steps is reflected in the ServerTarget API:

Step Corresponding API Controllers role
set up the environment setup() preparations
perform pre-test actions pre_test(test_number) prepare the victim to the test (make sure its up)
cleanup after test post_test() check the status of the victim, shut it down if needed
provide a test report get_report() provide a report
tear down the environment teardown() perform a cleanup
class definition and constructor
from kitty.controllers.base import BaseController

class LocalProcessController(BaseController):
    '''
    LocalProcessController a process that was opened using subprocess.Popen.
    The process will be created for each test and killed at the end of the test
    '''

    def __init__(self, name, process_path, process_args, logger=None):
        '''
        :param name: name of the object
        :param process_path: path to the target executable
        :param process_args: arguments to pass to the process
        :param logger: logger for this object (default: None)
        '''
        super(LocalProcessController, self).__init__(name, logger)
        assert(process_path)
        assert(os.path.exists(process_path))
        self._process_path = process_path
        self._process_name = os.path.basename(process_path)
        self._process_args = process_args
        self._process = None

Our controller has nothing to do at the setup stage, so we don’t override this method

Before a test starts, we need to make sure that the victim is up

pre_test
def pre_test(self, test_number):
    '''start the victim'''
    ## call the super
    super(LocalProcessController, self).pre_test(test_num)
    ## stop the process if it still runs for some reason
    if self._process:
        self._stop_process()
    cmd = [self._process_path] + self._process_args
    ## start the process
    self._process = Popen(cmd, stdout=PIPE, stderr=PIPE)
    ## add process information to the report
    self.report.add('process_name', self._process_name)
    self.report.add('process_path', self._process_path)
    self.report.add('process_args', self._process_args)
    self.report.add('process_id', self._process.pid)

When the test is over, we want to store the output of the process, as well as its exit code (if crashed):

post_test
def post_test(self):
    '''Called when test is done'''
    self._stop_process()
    ## Make sure process started by us
    assert(self._process)
    ## add process information to the report
    self.report.add('stdout', self._process.stdout.read())
    self.report.add('stderr', self._process.stderr.read())
    self.logger.debug('return code: %d', self._process.returncode)
    self.report.add('return_code', self._process.returncode)
    ## if the process crashed, we will have a different return code
    if self._process.returncode != 0:
        self.report.failed('return code is not zero: %s' % self._process.returncode)
    self._process = None
    ## call the super
    super(LocalProcessController, self).post_test()

When all fuzzing is over, we perform the teardown:

teardown
def teardown(self):
    '''
    Called at the end of the fuzzing session, override with victim teardown
    '''
    self._stop_process()
    self._process = None
    super(LocalProcessController, self).teardown()

Finally, here is the implementation of the _stop_process method

_stop_process
def _stop_process(self):
    if self._is_victim_alive():
        self._process.terminate()
        time.sleep(0.5)
        if self._is_victim_alive():
            self._process.kill()
            time.sleep(0.5)
            if self._is_victim_alive():
                raise Exception('Failed to kill client process')

def _is_victim_alive(self):
    return self._process and (self._process.poll() is None)

Client Fuzzing Tutorial

One of the advanteges of kitty is its ability to fuzz client targets. Client targets are targets that we cannot fuzz by sending malformed requests, but by sending malformed responses.

As explained in the Server vs. Client Fuzzing, this is a big difference, for two main reasons. The first reason is that unlike server fuzzing, the communication is started by the target, and not by the fuzzer. The second reason is that in order to fuzz a client we usually need to hook some functions in the server stack.

First we will explain how to fuzz a client target with Kitty. After that, we will explain how to separate the code so the fuzzer will run in a separate process than the stack.

How Does It Work

Since in our case the communication is triggered by the target and handled by the stack, the fuzzer is passive. It triggers the client to start the communication from its own context (thread / process) and then does nothing until it is called by the stack to provide a response to the target request.

The stack needs to get a mutation of a response from the fuzzer. The fuzzer exposes the get_mutation(name, data) method to the stack to provide those mutations. The responsibility of the stack is to call get_mutation in request handlers. If get_mutation returns None, the stack handles the request aproprietly, otherwise, it returns the result of get_mutation to the target.

Here’s an example of (pseudo) client hook:

class StackImplementation:
    # ...
    def build_get_response(self, request_id):
        resp = self.fuzzer.get_mutation(stage='get_response', data={ 'request_id' : request_id })
        if resp:
            return resp
        # build valid response
        resp = ...
        return resp

self.fuzzer in the example above is the instance of ClientFuzzer that we passed to the stack. We call get_mutation with two arguments. The first, get_response is the name of the name of the scheme (request) that is used for this request, that we create in our data model. In get_mutation the fuzzer checks if it currently fuzzing this scheme, and if so, it will return a mutated response, otherwise it will return None. The second argument is a dictionary of data that should be inserted into the DynamicFields in the scheme, it is usually a data that is transaction dependant and is not known when building the scheme, for example, transaction or request id, such as in the example above.

Building the Fuzzer

We will list the different parts of the client fuzzer, in the last section we will give a simple example of such a fuzzer.

Target

The target for client fuzzing inherits from kitty.target.client.ClientTarget unlike in server fuzzing, it’s major work is managing the controller and monitors, so you can often just instantiate kitty.target.client.ClientTarget directly.

Controller

The controller of the client target inherits from kitty.controllers.client.ClientController. The most important method in it is trigger. This method triggers the client to start the communication with the server stack. Since this method differ from target to target, it is not implemented in ClientController and must be implemented in a new class.

The other methods are inherited from kitty.controllers.base.BaseController and may or may not be implemented in the new class, based on your needs.

Monitor

The monitors of the client target inherit from kitty.monitors.base.BaseMonitor there is nothing special in client monitors.

User Interface

The user interface of the client fuzzer inherit from kitty.interfaces.base.BaseInterface there is nothing special about client fuzzer user interface.

Fuzzer

When fuzzing a client, you should create a kitty.fuzzers.client.ClientFuzzer object, pass it to the stack, and then start it.

Fuzzer Building Example
fuzz_special_stack.py
import struct
from kitty.targets import ClientTarget
from kitty.controllers import ClientController
from kitty.interfaces import WebInterface
from kitty.fuzzers import ClientFuzzer
from kitty.model import GraphModel
from kitty.model import Template, Dynamic, String


################# Modified Stack #################
class MySpecialStack(object):
    # We only show the relevant methods
    def __init__(self):
        self.fuzzer = None
        self.names = {1: 'Lumpy', 2: 'Cuddles', 3: 'Flaky', 4: 'Petunya'}

    def set_fuzzer(self, fuzzer):
        self.fuzzer = fuzzer

    def handle_GetName(self, name_id):
        resp = self.fuzzer.get_mutation(stage='GetName response', data={'name_id': struct.pack('I', name_id)})
        if resp:
            return resp
        name = '' if name_id not in self.names else self.names[name_id]
        return struct.pack('I', name_id) + name

################# Data Model #################

get_name_response_template = Template(
    name='GetName response',
    fields=[
        Dynamic(key='name_id', default_value='\x00', name='name id'),
        String(value='admin', nane='name')
    ]
)


################# Controller Implementation #################
class MyClientController(ClientController):
    def __init__(self):
        super(MyClientController, self).__init__('MyClientController')

    def trigger(self):
        # trigger transaction start at the client
        pass


################# Actual fuzzer code #################
target = ClientTarget('Example Target')

controller = MyClientController()
target.set_controller(controller)

model = GraphModel()
model.connect(get_name_response_template)
fuzzer = ClientFuzzer()
fuzzer.set_model(model)
fuzzer.set_target(target)
fuzzer.set_interface(WebInterface())

my_stack = MySpecialStack()
my_stack.set_fuzzer(fuzzer)
fuzzer.start()
my_stack.start()

Remote Fuzzer

The are two big problems with the client fuzzer that we’ve shown in the previous section. The first problem is that it ties us to python2 implementations of the stack. This means that even if you have a stack that you can modify, if it’s not written in python2 you will need to perform major changes to your code, or not use it at all. The second problem is that even when using python2, different threading models and signal handling may cause big issues with kitty, as it uses python threads and uses signal handlers.

To overcome those issue, we have created the kitty.remote package. It allows you to separate the stack process from the fuzzer process.

Currently, we only support python2 and python3, using the same python modules (with six) support for other languages will be provided in the future.

The idea is pretty simple - on the stack side, we only add RpcClient. No data models, monitors, target or anything like that. On the fuzzer side, we create the fuzzer as before, with all its classes, and than wrap it with a RpcServer, which waits for requests from the agent.

The next example shows how we convert the previous example to use the remote package.

Python2/3 Remote Fuzzer
my_stack.py (python3)
from kitty.remote import RpcClient

################# Modified Stack #################
class MySpecialStack(object):
    # We only show the relevant methods
    def __init__(self):
        self.fuzzer = None
        self.names = {1: 'Lumpy', 2: 'Cuddles', 3: 'Flaky', 4: 'Petunya'}

    def set_fuzzer(self, fuzzer):
        self.fuzzer = fuzzer

    def handle_GetName(self, name_id):
        resp = self.fuzzer.get_mutation(stage='GetName response', data={'name_id': struct.pack('I', name_id)})
        if resp:
            return resp
        name = '' if name_id not in self.names else self.names[name_id]
        return struct.pack('I', name_id) + name

fuzzer = RpcClient(host='127.0.0.1', port=26010)

my_stack = MySpecialStack()
my_stack.set_fuzzer(fuzzer)

fuzzer.start()
my_stack.start()
my_stack_fuzzer.py (python2)
from kitty.targets import ClientTarget
from kitty.controllers import ClientController
from kitty.interfaces import WebInterface
from kitty.fuzzers import ClientFuzzer
from kitty.model import GraphModel
from kitty.model import Template, Dynamic, String
from kitty.remote import RpcServer


################# Data Model #################
get_name_response_template = Template(
    name='GetName response',
    fields=[
        Dynamic(key='name_id', default_value='\x00', name='name id'),
        String(value='admin', nane='name')
    ]
)


################# Controller Implementation #################
class MyClientController(ClientController):
    def __init__(self):
        super(MyClientController, self).__init__('MyClientController')

    def trigger(self):
        # trigger transaction start at the client
        pass

################# Actual fuzzer code #################
target = ClientTarget('Example Target')

controller = MyClientController()
target.set_controller(controller)

model = GraphModel()
model.connect(get_name_response_template)
fuzzer = ClientFuzzer()
fuzzer.set_model(model)
fuzzer.set_target(target)
fuzzer.set_interface(WebInterface())

remote = RpcServer(host='127.0.0.1', port=26010, impl=fuzzer)
remote.start()

Session Data in Server Fuzzing

Overview

There are cases where not all of the data is available when writing the data model. For example, when you have a multi-message session with the target, and you need to use a session ID that was returned in the first message from the target.

Kitty provides means to handle these cases. However, since this problem is a little complex, Kitty’s solution is composed of 3 parts.

  1. Special fields in the template to indicate that a data is session-specific (done by you)
  2. Callback between messages in the session, providing a hook to parse responses and handle them (done by you)
  3. Call to Target API to get the session specific data before rendering each template (done by the fuzzer)

We’ll go over the usage of this feature using an example protocol.

Example Protocol

Fuzzer (client)                               Target (server)
    ||---------------(open_session)----------------->||
    ||<---------------(session_id)-------------------||
    ||-------(do_something + session_id)------------>||
    ||<----------------(ok)--------------------------||

The Templates

We need to implement two templates:

  1. open_session

    Used to request the server to open a session. The server responds with a session id, which should be used in subsequent calls.

    open_session = Template(name='open_session', fields=[
        String(name='username', value='user'),
        Static(': '),
        String(name='password', value='pass'),
    ])
    
  2. do_something

    Tells the server to do something, providing the session ID to the open session. Since we don’t know the session id at the time we write the tempalte, we define it as a Dynamic field, and assigning a key to it, this key will be used later on.

    do_something = Template(name='do_something', fields=[
        Static('session id: '),
        # this is the intersting field
        Dynamic(key='session_id', default_value=''),
        Static('\naction: '),
        String(name='the action', value='cry me a river')
    ])
    

Great. So at this point we have two templates, and the second one knows that it might be changed (partially) by external entities at runtime. Time to create a connection between the two messages.

The Callback

What do we actually want to do? We want to parse the resposnse to the open_session request, extract the session_id from it and provide the session_id to the do_something template.

We’ll do that by using GraphModel connect() method’s third arguemnt - the callback.

g = GraphModel('session-graph')
g.connect(open_session)
g.connect(open_session, do_something, new_session_callback)

new_session_callback will be called when we get a response to the open_session and before we render and send do_something

And here’s the implementation of new_session_callback:

def new_session_callback(fuzzer, edge, resp):
    '''
    :param fuzzer: the fuzzer object
    :param edge: the edge in the graph we currently at.
                 edge.src is the open_session template
                 edge.dst is the do_something template
    :param resp: the response from the target
    '''
    # let's say that we have a reference to the target object,
    # or this might be a target's method
    target.session_data['session_id'] = extract_session_from_response(resp)

Once we set the session_id value in the session_data dictionary, we are good to go. From this point, the fuzzer will take the session_data dictionary from the target and set it in the template. Note that you can have multiple key/value pairs in the dictionary, and that you don’t have to have key/value pairs for all dynamic fields. Only those who exist in the dictionary will be set in the dynamic fields. Also, you can have multiple dynamic fields with the same key.

Writing a ServerTarget Class

Role

A Target object has two roles in a fuzzing session:

  • Handle the actual communication with the target (victim).
  • Manage and query the Controller and Monitors.

This means that the Target object, in general, doesn’t monitor the behavior of the target, nor does it start / stop the target. It only sends the payloads to the target, and – if needed – waits for response from the target.

This Tutorial

In this tutorial, we will implement a ServerTarget class. This target will allow the fuzzer to send and receive data over a serial (UART) data channel.

Hopefully, by the end of the tutorial you will feel comfortable writing your own controller.

Let’s Begin

Target Flow

The behavior of our target will look like that:

  1. Open and configure serial connection
  2. For each test
    1. For each payload
      1. Send payloads
      2. Receive response
  3. Close serial connection

Overall, not too complicated.

Functions

Kitty’s Target API provides hooks for performing actions in different points in time. We will write implementation for each parts of the flow above.

Part 1 - Open and configure serial connection

There are two functions we need to implement in this part. __init__ - to initialize the Target object, and setup - which will be called when starting the session.

import serial
from kitty.targets import ServerTarget

class SerialTarget(ServerTarget):

    def __init__(self, dev_name, baudrate=115200, name='SerialTarget', logger=None, expect_response=False):
        super(SerialTarget, self).__init__(name, logger, expect_response)
        self.dev_name = dev_name
        self.baudrate = baudrate
        self.serial = None
        # we set this timeout to simplify the reads
        self.timeout = 2

Since each class in the hierarchy of Target perform some important initialization in setup, it is important to call the super function.

def setup(self):
    if self.serial:
        self.serial.close()
    self.serial = serial.Serial(self.dev_name, self.baudrate)
    self.serial.timeout = self.timeout
    super(SerialTarget, self).setup()
Part 2.1.1 - Send payload

Each test might have multiple payloads that should be sent in sequence, the function _send_to_target is expected to send the payload to the target, but not to perform the mutations on it. The payloads that are passed to this function are already mutated (if needed).

Note

We don’t handle exceptions here (and in the next function). The function transmit, which is implemented in the parent class, handles the exceptions.

def _send_to_target(self, payload):
    self.serial.write(payload)
Part 2.1.2 - Receive response

This function responsible for reading the target’s response. As in _send_to_target, we usually don’t handle exceptions in this function, but leave transmit to handle it.

def _receive_from_target(self):
    return self.serial.read(1000)
Part 3 - Close serial connection

When we end the fuzzing session (i.e. all the tests), we need to perform a cleanup. The cleanup function called teardown. As with setup, the super function of teardown should be called as well.

def teardown(self):
    if self.serial:
        self.serial.close()
        self.serial = None
    super(SerialTarget, self).teardown()
New connection in each test

Sometimes, you don’t want to use the same connection in different tests.

This can be acheived by implementing pre_test to create the connection, and post_test to close it, instead of implmenting the setup and teardown functions.

Note

As with setup and teardown, you should call the super functions from your implementations.

def pre_test(self, test_num):
    if self.serial:
        self.serial.close()
    self.serial = serial.Serial(self.dev_name, self.baudrate)
    self.serial.timeout = self.timeout
    super(SerialTarget, self).pre_test(test_num)
def post_test(self, test_num):
    if self.serial:
        self.serial.close()
        self.serial = None
    super(SerialTarget, self).post_test(test_num)

The Data Model

Contents:

Data Model Overview

As described in the overview, Kitty is by default a generation-based fuzzer. This means that a model should be created to represent the data exchanged in the fuzzed protocol.

On the high level, we describe and fuzz different sequences of messages, this allows building fuzzing scenarios that are valid until a specific stage but also to fuzz the order of messages as well.

Low Level Model

At the low level, there is a signle payload, constructed from multiple fields. Those fields can be encoded, dependant on other fields, be renderened conditionaly and combined with other fields into larger logical units. The top unit of the logical units is Template, and Template is the only low-level interface to the high-level of the data model.

A full documentation of the low level data model can be found in the API reference.

A full list of the fields in the Kitty data model syntax can be found in the Data Model Syntax.

High Level Model

The high level model describe how a sequence of messages (Templates) looks like to the fuzzer. There are two main models for this part, GraphModel and StagedSequenceModel. An additional model - RandomSequenceModel is a naive case of StagedSequenceModel and is more convenient, when the order of messages really doesn’t matter.

During the fuzzing session, the fuzzer queries the data model for a sequence to transmit. The data model chooses the next sequence according to its strategy and perform internal mutations if needed.

Data Model Syntax

A big part of generation based fuzzing is the data modeling. It provides guidance to the fuzzer on how the data is supposed to be structured. This allows the fuzzer to perform a more precise and faster fuzzing, and to keep the state of the payload as similar as possible to a valid while performing mutations on very specific fields.

This chapter is composed of three parts. We start by listing the available fields, these fields compose kitty’s syntax. After that, we list the available encoders. The last part of this chapter provides examples on how to use the fields, containers and encoders that are listed in the first two part to compose a full template.

Fields

A basic overview of the data model can be found in the Data Model Overview, and you can also take a look at the API reference.

There are a few types of fields in Kitty’s data model, and all of them are derived from BaseField.

The field type defines the type of value that will be rendered by this field, as well of the type of mutations that will be performed. The type of field instructs the fuzzer what data to render in the payload, but not how to render it. For that, we have encoders (see Encoders).

example:

If we want to fuzz an integer, we will use a field like UInt16, which is used to represent an unsigned integer number with values between 0 and 65535 (2 ** 16 - 1).

This number may be rendered as Little endian binary or a string of ASCII values, or something else.

The use of UInt16 provides the type of the value, but an encoder will encode it in the approriate “encoding” during its generation.

Atomic Fields

The atomic fields are the very basic building blocks of a data model. Each of this fields is self-contained and discrete.

The following atomic fields are available in Kitty (some of them are aliases for other fields):

Calculated (dependant) Fields

Calculated fields are fields that their value is calculated from properties that are outside of their scope by default, for example, the length or the checksum of another fields.

These fields give Kitty’s syntax much of its power. It allows the user to get into deeper layer of the parsing.

These fields can be fuzzed as well, but when they are not fuzzed, they will be calculated each time, to ensure that they render into a valid value.

The following calculated fields are available in Kitty (again, some of them are aliases for other fields):

Containers

Containers, as is pretty obvious from their name, contain other fields. There is no limit on how many fields a container can hold, nor on the nesting level of containers.

Containers can be used to perform encoding on multiple fields at once, or to use some property of multiple rendered fields to perform some calculation. But the most important property of containers, IMHO, is the logical separation and grouping of units in the model.

In general, containers treat the contained field in the order they were added and in most cases will mutate each of the contained fields when in turn. However, in addition to the mutations of the internal fields, some container may add some mutations that are a property of the container itself (although they depend on the value of the contained fields)

The following containers are available in Kitty:

Mutation Based Fields

While Kitty uses a data model, and expects the user to be familiar with the structure of the payload, in some cases the user might not have this information, only a sample payload.

In this cases, a mutation fuzzing might come in handy, and the Mutation based fields can be used to perform a tests on the target without an accurate data model.

some of the fields are wrappers around other fields, specifically, MutableField is a combination of all other mutation based fields.

The following mutation-based fields are available in Kitty:

Encoders

The encoders receive data from the field they are assigned to, and return a sequence of bits, which is the rendered data.

There are 4 types of encoders:

  • Encoders of strings (python2.7’s str), receiving a string and returning Bits.
  • Encoders of Bits, receiving Bits object and returning Bits.
  • Encoders of numbers, receiving int value, length in bits and sign and returning Bits.
  • Encoders of floating point numbers, receiving float and returning Bits.

Encoders are objects, but since most common encoders are stateless, and don’t change state, the same instance can be used in multple fields, kitty provides many default instances of encoders that can be used directly.

These instances are listed in the API reference.

Using the syntax

In this part, we’ll take a look at a few examples on how to use Kitty’s data modeling syntax to model some data or protocol.

It’s important to note that Kitty has no capability of modeling the data automatically. This is really out-of-scope, although, if you developed a tool that can infer the structure of a message from some (or many) samples, or if you know of such a tool, we’d really like to know about that.

Anyway... Kitty’s data model syntax is inspired by the syntax of Construct. In the sense that the entire model of a payload (e.g. template) can be constructed in a single – nested – constructur. Using the standard alignment of python, this results in a very readable data model, although it is 100% python code.

Unlike Construct, the resulted data model is not designed to pacrse payloads or be modified from outside (although it is possible to a certain level), instead, it is designed to generate mutations internally, (when mutate() is called) and provide the resulted payloads (when render() is called).

Example 1 - Sized String

The following template describes an ascii string (not null terminated), prepended by a 16bit int that holds its size (in bytes):

sized_string = Template(name='sized string', fields=[
    SizeInBytes(name='size', sized_field='the string', length=16),
    String(name='the string', value='')
])
Example 2 - Count Elements in a List

The following template describe a List of 32bit little endian encoded integers, preprended by the number of element in the list (field of 16bit, little endian encoded).

counted_list = Template(name='counted list', fields=[
    ElementCount(name='element count', depends_on='the list', length=16, encoder=ENC_INT_LE),
    List(name='the list', fields=[
        LE32(name='element 1', value=0x00010203),
        LE32(name='element 2', value=0x0a050607),
        LE32(name='element 3', value=0x08090a0b),
        LE32(name='element 4', value=0x0c0d0e0f),
        LE32(name='element 5', value=0x10111213),
        LE32(name='element 6', value=0x1a151617),
        LE32(name='element 7', value=0x18191a1b),
        LE32(name='element 8', value=0x1c1d1e1f),
    ])
])

Here’s the default rendered payload (hex encoded), see how it matchs the description

0800030201000706050a0b0a09080f0e0d0c131211101716151a1b1a19181f1e1d1c

0800 -> element count: 8 (16 bits, little endian encoded)
03020100 -> element 1: 0x00010203 (32 bits, little endian encoded)
0706050a -> element 2: 0x0a050607 (32 bits, little endian encoded)
0b0a0908 -> element 3: 0x08090a0b (32 bits, little endian encoded)
0f0e0d0c -> element 4: 0x0c0d0e0f (32 bits, little endian encoded)
13121110 -> element 5: 0x10111213 (32 bits, little endian encoded)
1716151a -> element 6: 0x1a151617 (32 bits, little endian encoded)
1b1a1918 -> element 7: 0x18191a1b (32 bits, little endian encoded)
1f1e1d1c -> element 8: 0x1c1d1e1f (32 bits, little endian encoded)
Example 3 - Base64 Encoded Container

As mentioned, a container may be encoded as a whole, this comes quite handy in HTTP authorization, where the clients sends the username and password as: base64encode(USERNAME:PASSWORD). Of course, encoding each string (USERNAME, : and PASSWORD) separately will result in a different base64 string then if they were encoded together.

http_user_pass = Container(
    name='userpass header',
    fields=[
        String(name='username', value='some user'),
        String(name='delimiter', value=':'),
        String(name='password', value='some password')
    ],
    encoder=ENC_BITS_BASE64_NO_NL
)

The default result of such a container is

'c29tZSB1c2VyOnNvbWUgcGFzc3dvcmQ='

Which is the base64 encoded version of

'some user:some password'

Writing Encoders

A Little Bit About Encoders

Each field in the data model has two main properties:

  1. The value of the field - for example, a number may have the value 1, 2 or -1532 and so on.
  2. The representation of the field - for example, a number with the value 123 may be represented as decimal (123), hexadecimal (7B) or a 32 bit little endian ('\x7b\x00\x00\x00') and so on.

The value of the field is decided by the field itself, using the default value or one of the field’s mutations. The representation of the field is decided by it’s encoder.

Kitty has a few encoder classes, used to encode integer values, floats, strings and Bits objects. You can see a list of available encoder classes in the Data Model Syntax documentation.

Kitty also provides many encoder objects that may be used directly in your model, such as ENC_INT_BE to encode an integer as a big endian, or ENC_STR_BASE64 to encoder a string in base64. Since most encoders have no state, the same object may be used by many fields without issue, thus the use of a global encoder object makes sense.

However, Kitty doesn’t have any possible encoding, and so you might be required to implement your own encoder from time to time.

Implementing Your Own Encoder

Example 1 - Aligned String

Let’s say that you have a String field that must be 4 bytes aligned, and you decide that it makes no sense to have mutations of this field that are not aligned.

Since the alignment is only related to the representation of the string, you should probably just encode it as a 4 byte aligned string, meaning, implement an encoder that encodes the string with padding when needed.

There are several ways to implement such an encoder. Here are two of them.

Straight Forward Implementation

We’ll start with a simple one, which might be just enough in some cases, and will make it more robust. Since we encode a string, we need to inherit from StrEncoder and override its encode() method. This method receive a string as argument, and returns an encoded string as a Bits object.

class AlignedStrEncoder(StrEncoder):

    def encode(self, value):
        pad_len = (4 - len(value) % 4) % 4
        new_value = value + '\x00' * pad_len
        return Bits(bytes=new_value)

Then, we can instantiate it:

ENC_STR_ALIGN = AlignedStrEncoder()

And use it multiple times:

Template(name='two aligned strings', fields=[
    String('foo', encoder=ENC_STR_ALIGN),
    String('bar', encoder=ENC_STR_ALIGN),
])
Using StrFuncEncoder

You can also implement it by passing the encoding function to the constructor of StrFuncEncoder

def align_string(value):
    pad_len = (4 - len(value) % 4) % 4
    new_value = value + '\x00' * pad_len
    return Bits(bytes=new_value)

ENC_STR_ALIGN = StrFuncEncoder(align_string)
Generic Implementation

We might want to create a generic aligned string encoder class and pass the alignment size as a parameter. In this case, we need to override the __init__ function:

class AlignedStrEncoder(StrEncoder):

    def __init__(self, pad_size, pad_char='\x00'):
        self._pad_size = pad_size
        self._pad_char = pad_char

    def encode(self, value):
        pad_len = (self._pad_size - len(value) % self._pad_size) % self._pad_size
        new_value = value + self._pad_char * pad_len
        return Bits(bytes=new_value)

Then, we can instantiate it with different values:

ENC_STR_ALIGN4 = AlignedStrEncoder(4)
ENC_STR_ALIGN8 = AlignedStrEncoder(8)
Example 2 - Reversed Bits

The encode() method returns a Bits object (from the bitstring package). The main difference between the big encoder types is the (type of) value that their encode() method accepts. In the first example, the it accepted a string, in the case of BitsEncoder, it accepts a Bits object.

The encoder below encodes the bits in a reversed order, e.g. if it receives the bits 10101100 it will return 00110101.

There two main ways to implement such an encoder.

Using BitsFuncEncoder

As with StrFuncEncoder in the previous example, BitsFuncEncoder allows you to just pass an encode() function to the constructor, so you don’t need to create a new class and implement its encode method. This comes handy from time to time.

def reverse_bits(value):
    return value[::-1]

ENC_BITS_REVERSED = BitsFuncEncoder(reverse_bits)
Subclassing BitsEncoder

However, you may subclass BitsEncoder directly.

class ReversedBitsEncoder(BitsEncoder):

    def encoder(value):
        return value[::-1]

And instantiate it

ENC_BITS_REVERSED = ReversedBitsEncoder()

kitty.model package

This package contains the entire reference for data and sequence models used by Kitty.

Subpackages

kitty.model.high_level package

This package contains the high level data model, which represents sequences and transition between messages.

Submodules
kitty.model.high_level.base module
class kitty.model.high_level.base.BaseModel(name='BaseModel')[source]

Bases: kitty.core.kitty_object.KittyObject

This class defines the API that is required to be implemented by any top- level model

Note

This class should not be instantiated directly.

__init__(name='BaseModel')[source]
Parameters:name – name of the model (default: BaseModel)
current_index()[source]
Returns:current mutation index
get_model_info()[source]
Return type:dict
Returns:model information
get_sequence()[source]
Return type:[Connection]
Returns:Sequence of current case
get_sequence_str()[source]
Returns:string representation of the sequence
get_stages()[source]
Returns:dictionary of information regarding the stages in the fuzzing session

Note

structure: { current: [‘stage1’, ‘stage2’, ‘stage3’], ‘stages’: {‘source1’: [‘dest1’, ‘dest2’], ‘source2’: [‘dest1’, ‘dest3’]}}

get_template_info()[source]
Returns:dictionary of information regarding the current template

Note

by default, we return None, as the current template might not be interesting. Take a look at GraphModel for actual implementation.

get_test_info()[source]
Return type:dict
Returns:test information
hash()[source]
Returns:a hash of the model object (used for notifying change in the model)
last_index()[source]
Returns:the last valid mutation index
mutate()[source]

Mutate to next state

Returns:True if mutated, False if not
num_mutations()[source]
Returns:number of mutations in the model
set_notification_handler(handler)[source]
Parameters:handler – handler for notifications from the data model

Note

The handler should support the following APIs:

  • handle_stage_changed(data_model)
skip(count)[source]
Parameters:count – number of cases to skip
Returns:number of cases skipped
class kitty.model.high_level.base.Connection(src, dst, callback)[source]

Bases: object

A connection between to messages,

__init__(src, dst, callback)[source]
Parameters:
  • src – the source node
  • dst – the destination node
  • callback – the function to call after sending src and before sending dst
kitty.model.high_level.graph module

Model with a graph structure, all paths in the graph will be fuzzed. The last node in each path will be mutated until exhaustion.

class kitty.model.high_level.graph.GraphModel(name='GraphModel')[source]

Bases: kitty.model.high_level.base.BaseModel

The GraphModel is built of a simple digraph, where the nodes are templates, and on each edge there’s a callback function. It will provide sequences of edges from this graph, always starting from the root (dummy) node, where the last template in the sequence (the destination of the last edge) is mutated. As such the main target of the GraphModel is to fuzz the handling of the fields in a message.

Assuming we have the templates A, B, C and D and we want to fuzz all templates, but we know that in order to make an impact in fuzzing template D, we first need to send A, then B or C, and only then D, e.g. we have the following template graph:

  /==> B
 /      \
A        +==> D
 \      /
  \==> C

Which translate to the following sequences (* - mutated template):

A*
A -> B*
A -> B -> D*
A -> C*
A -> C -> D*

Such a model will be written in Kitty like this:

Example:
model = GraphModel('MyGraphModel')
model.connect(A)
model.connect(A, B)
model.connect(B, D)
model.connect(A, C)
model.connect(C, D)

Note

Make sure there are no loops in your model!!

The callback argument of connect allows monitoring and maintainance during a fuzzing test.

Example:
def log_if_empty_response(fuzzer, edge, response):
    if not response:
        logger.error('Got an empty response for request %s' % edge.src.get_name())

model.connect(A, B, log_if_empty_response)
__init__(name='GraphModel')[source]
Parameters:name – name for this model
check_loops_in_grpah(current=None, visited=[])[source]
Parameters:
  • current – current node to check if visited
  • visited – list of visited fields
Raise:

KittyException if loop found

connect(src, dst=None, callback=None)[source]
Parameters:
  • src – source node, if dst is None it will be destination node and the root (dummy) node will be source
  • dst – destination node (default: None)
  • callback (func(fuzzer, edge, response) -> None) – a function to be called after the response for src received and before dst is sent
get_model_info()[source]
get_stages()[source]
Returns:dictionary of information regarding the stages in the fuzzing session

Note

structure: { current: [‘stage1’, ‘stage2’, ‘stage3’], ‘stages’: {‘source1’: [‘dest1’, ‘dest2’], ‘source2’: [‘dest1’, ‘dest3’]}}

get_template_info()[source]
Returns:dictionary of information regarding the current template
get_test_info()[source]
hash()[source]
skip(count)[source]
kitty.model.high_level.random_sequence module
class kitty.model.high_level.random_sequence.RandomSequenceModel(name='RandomSequenceModel', seed=None, callback_generator=None, num_mutations=1000, max_sequence=10)[source]

Bases: kitty.model.high_level.staged_sequence.StagedSequenceModel

This class provides random sequences of templates based on the selection strategy. It is like StagedSequenceModel with a signle stage.

__init__(name='RandomSequenceModel', seed=None, callback_generator=None, num_mutations=1000, max_sequence=10)[source]
Parameters:
  • name – name of the model object (default: ‘RandomSequenceModel’)
  • seed (int) – RNG seed (default: None)
  • callback_generator (func(from_template, to_template) -> func(fuzzer, edge, response) -> None) – a function that returns callback functions (default: None)
  • num_mutations – number of mutations to perform (defualt: 1000)
  • max_sequence – maximum sequence length (default: 10)
add_template(template)[source]

Add template that might be used in generated sequences

Parameters:template – template to add to the model
kitty.model.high_level.staged_sequence module

Generate random sequences of messages for a session. Currently, perform no special operation for mutations on the nodes.

class kitty.model.high_level.staged_sequence.Stage(name, selection_strategy='1', seed=None)[source]

Bases: kitty.core.kitty_object.KittyObject

Stage supports 4 different sequence length strategy. For all of them, the order of templates will be random. The available strategies are:

  • for random length - ‘random’
  • for exact length - ‘12’
  • for length in range - ‘1-3’
  • for all - ‘all’
__init__(name, selection_strategy='1', seed=None)[source]
Parameters:
  • name – name of the Stage
  • selection_strategy – strategy for selecting amount of template in each mutation
  • seed – RNG seed (default: None)
add_template(template)[source]
Parameters:template – template to add to the stage
get_sequence_templates()[source]
Returns:templates of current sequence mutation
get_templates()[source]
Returns:list of the stage’s templates
hash()[source]
mutate()[source]
class kitty.model.high_level.staged_sequence.StagedSequenceModel(name='StagedSequenceModel', callback_generator=None, num_mutations=1000)[source]

Bases: kitty.model.high_level.base.BaseModel

The StagedSequenceModel provides sequences that are constructed from multiple stages of sequences. Each such stage provides its part of the sequence based on its strategy. The main goal of the Staged sequence mode is to find flaws in the state handling of a stateful target.

Example:

Assuming we have the templates [CreateA .. CreateZ, UseA .. UseZ and DeleteA .. DeleteZ]. A valid sequence is CreateA -> UseA -> DeleteA, so we want to test cases like CreateA -> UseB -> UseA -> DeleteC, or CreateA -> DeleteA -> UseA. And let’s say the common thing to all sequences is that we always want to start by sending one or more of the Create messages.

We can do that with StagedSequenceModel:

stage1 = Stage('create', selection_strategy='1-5')
stage1.add_Template(CreateA)
# ...
stage1.add_Template(CreateZ)
stage2 = Stage('use and delete', selection_strategy='3-20')
stage2.add_Template(UseA)
stage2.add_Template(DeleteA)
# ...
stage2.add_Template(UseZ)
stage2.add_Template(DeleteZ)

model = StagedSequenceModel('use and delete mess', num_mutations=10000)
model.add_stage(stage1)
model.add_stage(stage2)

Each time it is asked, it will provide a sequence that starts with 1-5 random “Create” templates, followed by 3-20 random Use and Delete messages. None of those templates will be mutated, as we try to fuzz the sequence itself, not the message structure.

Since we don’t know the what will be the order of templates in the sequences, we can’t just provide a callback as we do in GraphModel. The solution in our case is to provide the model with a callback generator, which receives the from_template and to_template and returns a callback function as described in GraphModel in runtime.

__init__(name='StagedSequenceModel', callback_generator=None, num_mutations=1000)[source]
Parameters:
  • name – name of the model object (default: ‘StagedSequenceModel’)
  • callback_generator (func(from_template, to_template) -> func(fuzzer, edge, response) -> None) – a function that returns callback functions
  • num_mutations – number of mutations to perform
add_stage(stage)[source]

add a stage. the order of stage is preserved

Parameters:stage (Stage) – the stage to add
get_model_info()[source]
Returns:dictionary of information about this model
get_test_info()[source]
Returns:dictionary of information about the current test
hash()[source]
kitty.model.low_level package

This package contains the low level data model, which represents the structure of specific messages in the fuzzed protocol.

Submodules
kitty.model.low_level.aliases module

Functions that provide simpler field creation API and name aliasing for convenience

Integer Aliases:
 

Since integers the are binary encoded are used in many places, there are many aliases for them. Some are similar to common type conventions, others are simply meant to make the template code smaller. Below is a (hopefully) full list of aliases. However, the safest place to check, as always, is the source.

Bytes Signed Endianess Aliases
1 No   U8, UInt8, BE8, LE8, Byte
2 No big, but can be changed U16, UInt16, Word
2 big BE16, WordBE
2 little LE16, WordLE
2 Yes big, but can be changed S16, SInt16
4 No big, but can be changed U32, UInt32, Dword
4 big BE32, DwordBE
4 little LE32, DwordLE
4 Yes big, but can be changed S32, SInt32
8 No big, but can be changed U64, UInt64, Qword
8 big BE64, QwordBE
8 little LE64, QwordLE
8 Yes big, but can be changed S64, SInt64
Hash Aliases:

There are multiple functions that return frequently used Hash instances:

  • Md5
  • Sha1
  • Sha224
  • Sha256
  • Sha384
  • Sha512.

Their prototype is the same as Hash, excluding the algorithm argument.

Compare Aliases:
 

The Compare aliases create a Compare object with a specific comp_type. See table below:

comp_type Aliases
‘==’ Equal
‘!=’ NotEqual
‘>’ Greater
‘>=’ GreaterEqual, AtLeast
‘<’ Lesser
‘<=’ LesserEqual, AtMost
‘&’ BitMaskSet
‘&=0’ BitMaskNotSet
kitty.model.low_level.aliases.AtLeast(field, comp_value)

Condition applies if the value of the field is greater than or equals to the comp_value

Return type:Compare
kitty.model.low_level.aliases.AtMost(field, comp_value)

Condition applies if the value of the field is lesser than or equals to the comp_value

Return type:Compare
kitty.model.low_level.aliases.BE16(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)[source]

16-bit field, Big endian encoded

kitty.model.low_level.aliases.BE32(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)[source]

32-bit field, Big endian encoded

kitty.model.low_level.aliases.BE64(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)[source]

64-bit field, Big endian encoded

kitty.model.low_level.aliases.BE8(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)[source]

8-bit field, Big endian encoded

kitty.model.low_level.aliases.BitMaskNotSet(field, comp_value)[source]

Condition applies if the given bitmask is equal to 0 in the value of the field

Return type:Compare
kitty.model.low_level.aliases.BitMaskSet(field, comp_value)[source]

Condition applies if the given bitmask is set in the value of the field

Return type:Compare
kitty.model.low_level.aliases.Byte(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Unsigned 8-bit field

kitty.model.low_level.aliases.Dword(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Unsigned 32-bit field

kitty.model.low_level.aliases.DwordBE(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)

32-bit field, Big endian encoded

kitty.model.low_level.aliases.DwordLE(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)

32-bit field, Little endian encoded

kitty.model.low_level.aliases.Equal(field, comp_value)[source]

Condition applies if the value of the field is equal to the comp_value

Return type:Compare
kitty.model.low_level.aliases.Greater(field, comp_value)[source]

Condition applies if the value of the field is greater than the comp_value

Return type:Compare
kitty.model.low_level.aliases.GreaterEqual(field, comp_value)[source]

Condition applies if the value of the field is greater than or equals to the comp_value

Return type:Compare
kitty.model.low_level.aliases.LE16(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)[source]

16-bit field, Little endian encoded

kitty.model.low_level.aliases.LE32(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)[source]

32-bit field, Little endian encoded

kitty.model.low_level.aliases.LE64(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)[source]

64-bit field, Little endian encoded

kitty.model.low_level.aliases.LE8(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)[source]

8-bit field, Little endian encoded

kitty.model.low_level.aliases.Lesser(field, comp_value)[source]

Condition applies if the value of the field is lesser than the comp_value

Return type:Compare
kitty.model.low_level.aliases.LesserEqual(field, comp_value)[source]

Condition applies if the value of the field is lesser than or equals to the comp_value

Return type:Compare
kitty.model.low_level.aliases.Md5(depends_on, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Return type:Hash
Returns:MD5 hash field
kitty.model.low_level.aliases.NotEqual(field, comp_value)[source]

Condition applies if the value of the field is not equal to the comp_value

Return type:Compare
kitty.model.low_level.aliases.Qword(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Unsigned 64-bit field

kitty.model.low_level.aliases.QwordBE(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)

64-bit field, Big endian encoded

kitty.model.low_level.aliases.QwordLE(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)

64-bit field, Little endian encoded

kitty.model.low_level.aliases.S16(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Signed 16-bit field

kitty.model.low_level.aliases.S32(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Signed 32-bit field

kitty.model.low_level.aliases.S64(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Signed 64-bit field

kitty.model.low_level.aliases.S8(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Signed 8-bit field

kitty.model.low_level.aliases.SInt16(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Signed 16-bit field

kitty.model.low_level.aliases.SInt32(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Signed 32-bit field

kitty.model.low_level.aliases.SInt64(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Signed 64-bit field

kitty.model.low_level.aliases.SInt8(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Signed 8-bit field

kitty.model.low_level.aliases.Sha1(depends_on, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Return type:Hash
Returns:SHA1 hash field
kitty.model.low_level.aliases.Sha224(depends_on, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Return type:Hash
Returns:SHA224 hash field
kitty.model.low_level.aliases.Sha256(depends_on, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Return type:Hash
Returns:SHA256 hash field
kitty.model.low_level.aliases.Sha384(depends_on, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Return type:Hash
Returns:SHA384 hash field
kitty.model.low_level.aliases.Sha512(depends_on, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Return type:Hash
Returns:SHA512 hash field
kitty.model.low_level.aliases.SizeInBytes(sized_field, length, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]
Return type:Size
Returns:Size field, which holds the size in bytes of the sized field
kitty.model.low_level.aliases.U16(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Unsigned 16-bit field

kitty.model.low_level.aliases.U32(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Unsigned 32-bit field

kitty.model.low_level.aliases.U64(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Unsigned 64-bit field

kitty.model.low_level.aliases.U8(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Unsigned 8-bit field

kitty.model.low_level.aliases.UInt16(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Unsigned 16-bit field

kitty.model.low_level.aliases.UInt32(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Unsigned 32-bit field

kitty.model.low_level.aliases.UInt64(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Unsigned 64-bit field

kitty.model.low_level.aliases.UInt8(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Unsigned 8-bit field

kitty.model.low_level.aliases.Word(value, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)

Unsigned 16-bit field

kitty.model.low_level.aliases.WordBE(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)

16-bit field, Big endian encoded

kitty.model.low_level.aliases.WordLE(value, min_value=None, max_value=None, fuzzable=True, name=None, full_range=False)

16-bit field, Little endian encoded

kitty.model.low_level.calculated module

Fields that are dependant on other fields - Size, Checksum etc.

class kitty.model.low_level.calculated.AbsoluteOffset(target_field, length, correction=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.calculated.Offset

An absolute offset of a field from the beginning of the payload

__init__(target_field, length, correction=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • target_field – (name of) field to calculate offset to
  • length – length of the AbsoluteOffset field (in bits)
  • correction – correction function, or value for the index, (default: divide by 8 (bytes))
  • encoder (BitFieldEncoder) – encoder for the field (default: ENC_INT_DEFAULT)
  • fuzzable – is container fuzzable
  • name – (unique) name of the container (default: None)
Example:
Container(fields=[
    String(name='A', value='base string'),
    String(name='B', value='bar'),
    String(name='C', value='target string'),
    AbsoluteOffset(
        target_field='C',
        length=32,
    )
])
class kitty.model.low_level.calculated.Calculated(depends_on, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

A base type for fields that are calculated based on other fields

FIELD_PROP_BASED = 'field property'
LENGTH_BASED = 'length'
VALUE_BASED = 'value'
__init__(depends_on, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • depends_on – (name of) field we depend on
  • encoder (BitsEncoder) – encoder for the field
  • fuzzable – is container fuzzable
  • name – (unique) name of the container
hash()[source]
Return type:int
Returns:hash of the field
is_default()[source]

Checks if the field is in its default form

Returns:True if field is in default form
render(ctx=None)[source]

Render the current value into a bitstring.Bits object

Return type:bitstring.Bits
Returns:the rendered field
class kitty.model.low_level.calculated.CalculatedBits(depends_on, func, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.calculated.Calculated

field that depends on the rendered value of a field, and rendered into Bits() object

__init__(depends_on, func, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • depends_on – (name of) field we depend on
  • func – function for processing of the dependant data. func(Bits)->Bits
  • encoder (BitsEncoder) – encoder for the field
  • fuzzable – is container fuzzable
  • name – (unique) name of the container
class kitty.model.low_level.calculated.CalculatedInt(depends_on, bit_field, calc_func, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.Calculated

field that depends on the rendered value of another field and is rendered to (int, length, signed) tuple

__init__(depends_on, bit_field, calc_func, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=False, name=None)[source]
Parameters:
  • depends_on – (name of) field we depend on
  • bit_field – a BitField to be used for holding the value
  • calc_func (func(bits) -> int) – function to calculate the value of the field
  • encoder (BitsEncoder) – encoder for the field (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable
  • name – (unique) name of the container
reset()[source]
scan_for_field(field_name)[source]

If the field name is the internal field - return it

class kitty.model.low_level.calculated.CalculatedStr(depends_on, func, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.Calculated

field that depends on the rendered value of a byte-aligned field and rendered to a byte aligned Bits() object

__init__(depends_on, func, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Parameters:
  • depends_on – (name of) field we depend on
  • func – function for processing of the dependant data. func(str)->str
  • encoder (StrEncoder) – encoder for the field (default: ENC_STR_DEFAULT)
  • fuzzable – is container fuzzable
  • name – (unique) name of the container
class kitty.model.low_level.calculated.Checksum(depends_on, length, algorithm='crc32', encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.CalculatedInt

Checksum of another container.

__init__(depends_on, length, algorithm='crc32', encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]
Parameters:
  • depends_on – (name of) field to be checksummed
  • length – length of the checksum field (in bits)
  • algorithm – checksum algorithm name (from Checksum._algos) or a function to calculate the value of the field. func(Bits) -> int
  • encoder (BitFieldEncoder) – encoder for the field (default: ENC_INT_DEFAULT)
  • fuzzable – is field fuzzable (default: False)
  • name – (unique) name of the field (default: None)
Example:
Container(name='checksummed chunk', fields=[
    RandomBytes(name='chunk', value='1234', min_length=0, max_length=75),
    Checksum(name='CRC', depends_on='chunk', length=32)
])
class kitty.model.low_level.calculated.Clone(depends_on, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.CalculatedBits

rendered the same as the field it depends on

__init__(depends_on, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=False, name=None)[source]
Parameters:
  • depends_on – (name of) field we depend on
  • encoder (BitsEncoder) – encoder for the field (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable
  • name – (unique) name of the container
Example:
Container(name='empty HTML body', fields=[
    Static('<'),
    String(name='opening tag', value='body'),
    Static('>'),
    Static('</'),
    Clone(name='closing tag', depends_on='opening tag'),
    Static('>'),
])
class kitty.model.low_level.calculated.ElementCount(depends_on, length, correction=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.FieldIntProperty

Number of elements inside another field. The value depends on the number of fields in the field it depends on.

Example:
Container(name='list with count', fields=[
    ElementCount(  # will be rendered to '3'
        name='element count',
        depends_on='list of items',
        length=32,
        encoder=ENC_INT_DEC
    ),
    Container(name='list of items', fields=[
        Static('element 1'),
        Static('element 2'),
        Static('element 3'),
    ])
])
class kitty.model.low_level.calculated.FieldIntProperty(depends_on, length, correction=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.CalculatedInt

Calculate an int value based on some field property. The main difference from CalculatedInt is that it provides the field itself to the calculation function, not its rendered value.

__init__(depends_on, length, correction=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]
Parameters:
  • depends_on – (name of) field we depend on
  • length – length of the FieldIntProperty field (in bits)
  • correction – correction function, or value for the index
  • encoder (BitFieldEncoder) – encoder for the field (default: ENC_INT_DEFAULT)
  • fuzzable – is container fuzzable
  • name – (unique) name of the container (default: None)
class kitty.model.low_level.calculated.Hash(depends_on, algorithm, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.CalculatedStr

Hash of a field.

Note

To make it more convenient, there are multiple aliases for various hashes. Take a look at aliases.

__init__(depends_on, algorithm, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Parameters:
  • depends_on – (name of) field to be hashed
  • algorithm – hash algorithm name (from Hash._algos) or a function to calculate the value of the field. func(str) -> str
  • encoder (StrEncoder) – encoder for the field (default: ENC_STR_DEFAULT)
  • fuzzable – is field fuzzable (default: False)
  • name – (unique) name of the field (default: None)
Example:
Container(name='SHA1 hashed string', fields=[
    Meta(String(name='secret', value='s3cr3t')),
    Hash(name='secret_hash', algorithm='sha1', depends_on='secret')
])
class kitty.model.low_level.calculated.IndexOf(depends_on, length, correction=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.FieldIntProperty

Index of a field in its container.

Edge case behavior:

  • If field has no encloser - return 0
  • If field is not rendered - return len(rendered element list) as index/
Example:
Container(name='indexed list', fields=[
    IndexOf(  # will be rendered to '2'
        name='index of second',
        depends_on='second',
        length=32,
        encoder=ENC_INT_DEC
    ),
    Container(name='list of items', fields=[
        Static(name='first', value='A'),
        Static(name='second', value='B'),
        Static(name='third', value='C'),
    ])
])
class kitty.model.low_level.calculated.Offset(base_field, target_field, length, correction=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.calculated.FieldIntProperty

A relative offset of a field from another field

__init__(base_field, target_field, length, correction=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • base_field – (name of) field to calculate offset from
  • target_field – (name of) field to calculate offset to
  • length – length of the Offset field (in bits)
  • correction – correction function, or value for the index, (default: divide by 8 (bytes))
  • encoder (BitFieldEncoder) – encoder for the field (default: ENC_INT_DEFAULT)
  • fuzzable – is container fuzzable
  • name – (unique) name of the container (default: None)
Examples:

Calculate the offset of field C from field B, in bits

Container(fields=[
    String(name='A', value='base string'),
    String(name='B', value='bar'),
    String(name='C', value='target string'),
    Offset(
        base_field='B',
        target_field='C',
        length=32,
        correction=lambda x: x,
    )
])

Calculate the absolute offset of field C from the beginning of the payload (also, see AbsoluteOffset)

Container(fields=[
    String(name='A', value='base string'),
    String(name='B', value='bar'),
    String(name='C', value='target string'),
    Offset(
        base_field=None,
        target_field='C',
        length=32,
    )
])
class kitty.model.low_level.calculated.Size(sized_field, length, calc_func=<function <lambda>>, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.calculated.CalculatedInt

Size of another container. Calculated in each render call

Note

In most cases you can use the function SizeInBytes() instead, which receives the same arguments except of calc_func

__init__(sized_field, length, calc_func=<function <lambda>>, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=False, name=None)[source]
Parameters:
  • sized_field – (name of) field to be sized
  • length – length of the size field (in bits)
  • calc_func – function to calculate the value of the field. func(bits) -> int (default: length in bytes)
  • encoder (BitFieldEncoder) – encoder for the field (default: ENC_INT_DEFAULT)
  • fuzzable – is field fuzzable (default: False)
  • name – (unique) name of the field (default: None)
Examples:

Calculate the size of a field/container in bits

Container(name='sized chunk', fields=[
    RandomBytes(name='chunk', value='1234', min_length=0, max_length=75),
    Size(
        name='size in bits',
        sized_field='chunk',
        length=32,
        calc_func=lambda x: len(x)
    )
])

Calculate the size of a field/container in bytes, and add 5 to the result

Container(name='sized chunk', fields=[
    RandomBytes(name='chunk', value='1234', min_length=0, max_length=75),
    Size(
        name='size in bytes plus 5',
        sized_field='chunk',
        length=32,
        calc_func=lambda x: len(x) / 8 + 5
    )
])
kitty.model.low_level.calculated.num_bits_to_bytes(x)[source]
kitty.model.low_level.condition module

conditon object has one mandatory function - applies, which receives a Container as the single argument.

Conditions are used by the If and IfNot fields to decide wether to render their content or not. In many cases the decision is made based on a value of a specific field, but you can create whatever condition you want. In future versions, they will probably be used to make other decisions, not only basic rendering decisions.

class kitty.model.low_level.condition.Compare(field, comp_type, comp_value)[source]

Bases: kitty.model.low_level.condition.FieldCondition

Condition applies if the comparison between the value of the field and the comp_value evaluates to True

Note

There are some functions for creating specific Compare conditions that you can see below.

__init__(field, comp_type, comp_value)[source]
Parameters:
  • field – (name of) field that should meet the condition
  • comp_type – how to compare the values. One of (‘>’, ‘<’, ‘>=’, ‘<=’, ‘==’, ‘!=’)
  • comp_value – value to compare the field to
Example:

Render the content of the If container only if the value in the field called “name” is not ‘kitty’

Template([
    String('kitty', name='name'),
    If(Compare('name', '!=', 'kitty'), [
        String('current name is not kitty')
    ])
])
hash()[source]
Return type:int
Returns:hash of the condition
class kitty.model.low_level.condition.Condition[source]

Bases: object

applies(container, ctx)[source]

All subclasses must implement the applies(self, container) method

Parameters:
  • container (Container) – the caller
  • ctx – rendering context in which applies was called
Returns:

True if condition applies, False otherwise

copy()[source]
Returns:a copy of the condition
hash()[source]
Return type:int
Returns:hash of the condition
invalidate(container)[source]
Parameters:container – the container that tries to invalidate the condition
class kitty.model.low_level.condition.FieldCondition(field)[source]

Bases: kitty.model.low_level.condition.Condition

Base class for field-based conditions (if field meets condition return true)

__init__(field)[source]
Parameters:field (BaseField or str) – (name of, or) field that should meet the condition
applies(container, ctx)[source]

Subclasses should not override applies, but instead they should override _applies, which has the same syntax as applies. In the _applies method the condition is guaranteed to have a reference to the desired field, as self._field.

Parameters:
  • container (Container) – the caller
  • ctx – rendering context in which applies was called
Returns:

True if condition applies, False otherwise

hash()[source]
invalidate(container)[source]
Parameters:container – the container that tries to invalidate the condition
class kitty.model.low_level.condition.FieldMutating(field)[source]

Bases: kitty.model.low_level.condition.FieldCondition

Condition applies if the field is currently mutating

Example:

Render the content of the If container only if ‘kittyfield’ is currently mutating.

Template([
    String('kitty', name='kittyfield'),
    String('fritty', name='frittyfield'),
    If(FieldMutating('kittyfield'), [
        String('kitty is now mutating')
    ])
])
class kitty.model.low_level.condition.InList(field, value_list)[source]

Bases: kitty.model.low_level.condition.ListCondition

Condition applies if the value of the field appears in the value list

Example:

Render the content of the If container only if the current rendered value of the ‘numbers’ field is ‘1’, ‘5’ or ‘7’.

Template([
    Group(['1', '2', '3'], name='numbers'),
    If(InList('numbers', ['1', '5', '7']), [
        String('123')
    ])
])
class kitty.model.low_level.condition.ListCondition(field, value_list)[source]

Bases: kitty.model.low_level.condition.FieldCondition

Base class for comparison between field and list. can’t be instantiated

__init__(field, value_list)[source]
Parameters:
  • field – (name of) field that should meet the condition
  • value_list – list of values that should be compared to the field
hash()[source]
kitty.model.low_level.container module

Containers are fields that group multiple fields into a single logical unit, they all inherit from Container, which inherits from BaseField.

class kitty.model.low_level.container.Conditional(condition, fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Container that its rendering is dependant on a condition

__init__(condition, fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • condition (an object that has a function applies(self, Container) -> Boolean) – condition to evaluate
  • fields – enclosed field(s) (default: [])
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Example:
Template([
    Group(['a', 'b', 'c'], name='letters'),
    If(ConditionCompare('letters', '==', 'a'), [
        Static('dvil')
    ])
])
# results in the mutations: advil, b, c
copy()[source]

Copy the container, put an invalidated copy of the condition in the new container

get_rendered_fields(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Returns:ordered list of the fields that will be rendered
hash()[source]
Return type:int
Returns:hash of the container
is_default()[source]

Checks if the field is in its default form

Returns:True if field is in default form
render(ctx=None)[source]

Only render if condition applies

Parameters:ctx – rendering context in which the method was called
Return type:Bits
Returns:rendered value of the container
class kitty.model.low_level.container.Container(fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

A logical unit to group multiple fields together

__init__(fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • fields (field or iterable of fields) – enclosed field(s) (default: [])
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Example:
Container([
    String('header_name'),
    Delimiter('='),
    String('header_value')
])
append_fields(new_fields)[source]

Add fields to the container

Parameters:new_fields – fields to append
copy()[source]
Returns:a copy of the container
get_field_by_name(name)[source]
Parameters:name – name of field to get
Returns:direct sub-field with the given name
Raises:KittyException if no direct subfield with this name
get_info()[source]

Get info regarding the current fuzzed enclosed node

Returns:info dictionary
get_rendered_fields(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Returns:ordered list of the fields that will be rendered
get_structure()[source]
hash()[source]
Return type:int
Returns:hash of the container
is_default()[source]

Checks if the field is in its default form

Returns:True if field is in default form
num_mutations()[source]
Returns:number of mutations in the container
pop()[source]

Remove a the top container from the container stack

push(field)[source]

Add a field to the container, if the field is a Container itself, it should be poped() when done pushing into it

Parameters:field – BaseField to push
render(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Return type:Bits
Returns:rendered value of the container
replace_fields(new_fields)[source]

Remove all fields from the container and add new fields

Parameters:new_fields – fields to add to the container
reset()[source]

Reset the state of the container and its internal fields

scan_for_field(field_key)[source]

Scan for a field in the container and its enclosed fields

Parameters:field_key – name of field to look for
Returns:field with name that matches field_key, None if not found
set_offset(offset)[source]

Set the absolute offset of current field, if the field should have default value, set the offset of the sub fields as well.

Parameters:offset – absolute offset of this field (in bits)
set_session_data(session_data)[source]

Set session data in the container enclosed fields

Parameters:session_data – dictionary of session data
class kitty.model.low_level.container.ForEach(mutated_field, fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Perform all mutations of enclosed fields for each mutation of mutated_field

__init__(mutated_field, fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • mutated_field – (name of) field to perform mutations for each of its mutations
  • fields – enclosed field(s) (default: [])
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Example:
Template([
    Group(['a', 'b', 'c'], name='letters'),
    ForEach('letters', [
        Group(['1', '2', '3'])
    ])
])
# results in the mutations: a1, a2, a3, b1, b2, b3, c1, c2, c3
hash()[source]
Return type:int
Returns:hash of the container
reset(reset_mutated=True)[source]

Reset the state of the container and its internal fields

Parameters:reset_mutated – should reset the mutated field too (default: True)
class kitty.model.low_level.container.If(condition, fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Conditional

Render only if condition evalutes to True

class kitty.model.low_level.container.IfNot(condition, fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Conditional

Render only if condition evalutes to False

class kitty.model.low_level.container.Meta(fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Don’t render enclosed fields

Example:
Container([
    Static('no sp'),
    Meta([Static(' ')]),
    Static('ace')
])
# will render to: 'no space'
get_rendered_fields(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Returns:ordered list of the fields that will be rendered
render(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Return type:Bits
Returns:empty bits
class kitty.model.low_level.container.OneOf(fields=[], encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Render a single field from the fields (also mutates only one field each time)

Example:
OneOf(fields=[
    String('A'),
    String('B'),
    String('C'),
    String('D'),
])
# 'A', 'AAAA', '%s' ... 'B', 'BBBB' ... 'C' .. 'D'
get_rendered_fields(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Returns:ordered list of the fields that will be rendered
render(ctx=None)[source]

Render only the mutated field (or the first one if not in mutation)

Parameters:ctx – rendering context in which the method was called
Return type:Bits
Returns:rendered value of the container
class kitty.model.low_level.container.Pad(pad_length, pad_data='x00', fields=[], fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Pad the rendered value of the enclosed fields

__init__(pad_length, pad_data='\x00', fields=[], fuzzable=True, name=None)[source]
Parameters:
  • pad_length – length to pad up to (in bits)
  • pad_data – data to pad with (default: ‘’)
  • fields – enclosed field(s) (default: [])
  • fuzzable – is fuzzable (default: True)
  • name – (unique) name of the template (default: None)
Example:

Pad a string with ‘ ‘s so it is at least 20 bytes

Pad(fields=String('padded'), pad_data=' ', pad_length=20)
# default result will be: 'padded              '
hash()[source]
Return type:int
Returns:hash of the container
render(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Return type:Bits
Returns:rendered value of the container, padded if needed
class kitty.model.low_level.container.PseudoTemplate(name)[source]

Bases: kitty.model.low_level.container.Template

A pseudo template is an empty, immutable template, that can be created with any name. Pseudo templates are useful when fuzzing clients and we want to fuzz a template at differemt stages.

Example:

Let’s say you have a protocol in which a given request is performed twice and you don’t only want to check the handling of the response for each request, but what happens when those responses are different.

This use case happens in various protocols. For example, some USB hosts may ask the same descriptor twice. In the first time, they will only read its length and allocate enough memory for it, and in the second time it will copy the descriptor to the allocated buffer. Providing different descriptors in each response may expose bugs that are similar to time-of-check time-of-use.

When working with a graph model, this can be a problem, as you need to connect the same template to itself to match the stages of the stack, but you cannot do that, as it creates a cycle inside the GraphModel.

The solution is to create PseudoTemplates s with the same name.

Example:
g = GraphModel()
stage1 = PseudoTemplate(original.get_name())
stage2 = PseudoTemplate(original.get_name())
g.connect(original)
g.connect(stage1)
g.connect(stage1, original)
g.connect(stage1, stage2)
g.connect(stage2, original)

This will result in the following (interesting) sequences:

original, stage1 -> original and stage1 -> stage2 -> original

__init__(name)[source]
Parameters:name – name of the template
class kitty.model.low_level.container.Repeat(fields=[], min_times=1, max_times=1, step=1, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Repeat the enclosed fields. When not mutated, the repeat count is min_times

__init__(fields=[], min_times=1, max_times=1, step=1, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • fields – enclosed field(s) (default: [])
  • min_times – minimum number of repetitions (default: 1)
  • max_times – maximum number of repetitions (default: 1)
  • step – how many repetitions to add each mutation (default: 1)
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Examples:
Repeat([Static('a')], min_times=5, fuzzable=False)
# will render to: 'aaaaa'
Repeat([Static('a')], min_times=5, max_times=10, step=5)
# will render to: 'aaaaa', 'aaaaaaaaaa'
get_rendered_fields(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Returns:ordered list of the fields that will be rendered
hash()[source]
Return type:int
Returns:hash of the container
render(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Return type:Bits
Returns:rendered value of the container, repeated
class kitty.model.low_level.container.Switch(field_dict, key_field, default_key, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.OneOf

When switch is not mutating, it will render one of the fields, choosing the one with a key that matchs the value of key_field. When switch is mutating, it will mutate and render one of its fields each time, setting the value of the key_field field to the mutated field key.

__init__(field_dict, key_field, default_key, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • field_dict – dictionary of key:field
  • key_field – (name of) field that switch takes the key from
  • default_key – key to use if the key_field’s value doesn’t match any key
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Example:
Container(fields=[
    BE16(name='opcode', value=1),
    Switch(name='opcode_params', key_field='opcode', default_key=1, field_dict={
        1: Container(name='opcode_1_params', fields=[
            BE32(name='param1', value=3),
        ]),
        2: Container(name='opcode_2_params', fields=[
            BE32(name='param1', value=4),
            BE32(name='param2', value=5),
        ]),
    })
])
render(ctx=None)[source]

Render only a single field (see class docstring for more details)

Parameters:ctx – rendering context in which the method was called
Return type:Bits
Returns:rendered value of the container
class kitty.model.low_level.container.TakeFrom(fields=[], min_elements=1, max_elements=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.OneOf

Render to only part of the enclosed fields, performing all mutations on them

__init__(fields=[], min_elements=1, max_elements=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • fields (field or iterable of fields) – enclosed field(s) (default: [])
  • min_elements – minimum number of elements in the sub set
  • max_elements – maximum number of elements in the sub set
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Example:
TakeFrom(fields=[
    Static('A'), Static('B'), Static('C'),
    Static('D'), Static('E'), Static('F'),
])
# 'E', 'B', 'D', 'F', 'C', 'A', 'CE', 'FC', 'CF', 'BD', 'AF', 'BED',
# 'EBC', 'CDB', 'DCA', 'BFAD', 'FCBD', 'DBCF', 'BFACD' ...
get_field_by_name(name)[source]

Since TakeFrom constructs sub-containers and excercises OneOf, It needs to skip this sub-container when looking for field by name.

Parameters:name – name of field to get
hash()[source]
Return type:int
Returns:hash of the container
reset()[source]

Reset the state of the container and its internal fields

class kitty.model.low_level.container.Template(fields=[], encoder=<kitty.model.low_level.encoder.ByteAlignedBitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Top most container of a message, serves a the only interface to the high level model

__init__(fields=[], encoder=<kitty.model.low_level.encoder.ByteAlignedBitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • fields – enclosed field(s) (default: [])
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_BYTE_ALIGNED)
  • fuzzable – is fuzzable (default: True)
  • name – (unique) name of the template (default: None)
Example:
Template([
    Group(['a', 'b', 'c']),
    Group(['1', '2', '3'])
])

# the mutations are: a1, b1, c1, a1, a2, a3
copy()[source]

We might want to change it in the future, but for now...

Raises:KittyException, as it should not be copied
get_info()[source]

Get info regarding the current template state

Returns:info dictionary
class kitty.model.low_level.container.Trunc(max_size, fields=[], fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Truncate the size of the enclosed fields

__init__(max_size, fields=[], fuzzable=True, name=None)[source]
Parameters:
  • max_size – maximum size of the container (in bits)
  • fields – enclosed field(s) (default: [])
  • fuzzable – is fuzzable (default: True)
  • name – (unique) name of the template (default: None)
hash()[source]
Return type:int
Returns:hash of the container
render(ctx=None)[source]
Parameters:ctx – rendering context in which the method was called
Return type:Bits
Returns:rendered value of the container
kitty.model.low_level.container_mutator module

Container mutators treat fields of the container as atomic blocks, and perform mutations over the collection of the field.

Examples of such mutations are:
remove fields from the rendered payload repeat fields in the rendered payload change the order of fields etc.
class kitty.model.low_level.container_mutator.DuplicateMutator(field_count, dup_num, fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container_mutator.FieldRangeMutator

Duplicate X fields Y times in the final payload

__init__(field_count, dup_num, fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • field_count – how many (sequential) fields to duplicate
  • dup_num – how many times to duplicate each of the field
  • fields (field or iterable of fields) – enclosed field(s) (default: [])
  • delim (field) – delimiter between elements in the list (default: None)
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Example:
DuplicateMutator(field_count=2, dup_num=2, fields=[
    Static('A'),
    Static('B'),
    Static('C'),
    Static('D'),
])

will result in: AABBCD, ABBCCD, ABCCDD

class kitty.model.low_level.container_mutator.FieldRangeMutator(field_count, fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.Container

Base class for mutating a field range, it should not be instantiated. Mutators are intended to be used internally in the framework, not in the template declaration directly, and as such, they provide empty response when not mutated.

__init__(field_count, fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • field_count – how many fields to omit in each mutation
  • fields (field or iterable of fields) – enclosed field(s) (default: [])
  • delim (field) – delimiter between elements in the list (default: None)
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
is_default()[source]
render(ctx=None)[source]
reset()[source]
class kitty.model.low_level.container_mutator.List(fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.OneOf

Describe a list of elements in the template. In addition to the standard mutations of its element, a List also performs mutation of full elements, by reordering, duplicating and omitting them.

__init__(fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • fields (field or iterable of fields) – enclosed field(s) (default: [])
  • delim (field) – delimiter between elements in the list (default: None)
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Example:
List([
    BE32(name='id1', value=1),
    BE32(name='id2', value=2),
    BE32(name='id3', value=10),
    BE32(name='id4', value=50),
])
class kitty.model.low_level.container_mutator.OmitMutator(field_count, fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container_mutator.FieldRangeMutator

Omit X fields from the final payload

Example:
OmitMutator(field_count=1, fields=[
    Static('A'),
    Static('B'),
    Static('C'),
    Static('D'),
])

will result in: BCD, ACD, ABD, ABC

class kitty.model.low_level.container_mutator.RotateMutator(field_count, fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container_mutator.FieldRangeMutator

Perform rotation of X fields in the final payload

__init__(field_count, fields=[], delim=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • field_count – how many fields to omit in each mutation
  • fields (field or iterable of fields) – enclosed field(s) (default: [])
  • delim (field) – delimiter between elements in the list (default: None)
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_DEFAULT)
  • fuzzable – is container fuzzable (default: True)
  • name – (unique) name of the container (default: None)
Example:
RotateMutator(field_count=3, fields=[
    Static('A'),
    Static('B'),
    Static('C'),
    Static('D'),
])

will result in: BCAD, CABD, ACDB, ADBC

kitty.model.low_level.encoder module

Encoders are used for encoding fields and containers. The encoders are passed as an argument to the fields/container, during the field rendering, the encoder’s encode method is called.

There are four families of encoders:

Bits Encoders:Used to encode fields/containers that their value is of type Bits (Container, ForEach etc.)
String Encoders:
 Used to encode fields that their value is of type str (String, Delimiter, RandomBytes etc.)
BitField Encoders:
 Used to encode fields that inherit from BitField or contain BitField (UInt8, Size, Checksum etc.) Those encoders are also refferred to as Int Encoders.
FloatingPoint Encoders:
 Used to encode fields that inherit from FloatingPoint field (Float, Double) Those encoders are also refferred to as Float Encoders
class kitty.model.low_level.encoder.BitFieldAsciiEncoder(fmt)[source]

Bases: kitty.model.low_level.encoder.BitFieldEncoder

Encode int as ascii

__init__(fmt)[source]
Parameters:fmt – format for encoding (from BitFieldAsciiEncoder.formats)
encode(value, length, signed)[source]
formats = ['%d', '%x', '%X', '%#x', '%#X']
class kitty.model.low_level.encoder.BitFieldBinEncoder(mode)[source]

Bases: kitty.model.low_level.encoder.BitFieldEncoder

Encode int as binary

__init__(mode)[source]
Parameters:mode (str) – mode of binary encoding. ‘le’ for little endian, ‘be’ for big endian, ‘’ for non-byte aligned
encode(value, length, signed)[source]
Parameters:
  • value – value to encode
  • length – length of value in bits
  • signed – is value signed
class kitty.model.low_level.encoder.BitFieldEncoder[source]

Bases: object

Base encoder class for BitField values

Singleton Name Encoding Class
ENC_INT_BIN Encode as binary bits BitFieldBinEncoder
ENC_INT_LE Encode as a little endian binary bits
ENC_INT_BE Encode as a big endian binary bits
ENC_INT_DEC Encode as a decimal value BitFieldAsciiEncoder
ENC_INT_HEX Encode as a hex value
ENC_INT_HEX_UPPER Encode as an upper case hex value
ENC_INT_DEFAULT Same as ENC_INT_BIN BitFieldBinEncoder
encode(value, length, signed)[source]
Parameters:
  • value (int) – value to encode
  • length (int) – length of value in bits
  • signed (boolean) – is value signed
class kitty.model.low_level.encoder.BitFieldMultiByteEncoder(mode='be')[source]

Bases: kitty.model.low_level.encoder.BitFieldEncoder

Encode int as multi-byte (used in WBXML format)

__init__(mode='be')[source]
Parameters:mode (str) – mode of binary encoding. ‘le’ for little endian, ‘be’ for big endian, ‘’ for non-byte aligned
encode(value, length, signed)[source]
Parameters:
  • value – value to encode
  • length – length of value in bits
  • signed – is value signed
class kitty.model.low_level.encoder.BitsEncoder[source]

Bases: object

Base encoder class for Bits values

The Bits encoders encode function receives a Bits object as an argument and returns an encoded Bits object.

Singleton Name Encoding Class
ENC_BITS_NONE None, returns the same value received BitsEncoder
ENC_BITS_BYTE_ALIGNED Appends bits to the received object to make it byte aligned ByteAlignedBitsEncoder
ENC_BITS_REVERSE Reverse the order of bits ReverseBitsEncoder
ENC_BITS_BASE64 Encode a Byte aligned bits in base64 StrEncoderWrapper
ENC_BITS_BASE64_NO_NL Encode a Byte aligned bits in base64, but removes the new line from the end
ENC_BITS_UTF8 Encode a Byte aligned bits in UTF-8
ENC_BITS_HEX Encode a Byte aligned bits in hex
ENC_BITS_DEFAULT Same as ENC_BITS_NONE  
encode(value)[source]
Parameters:value (Bits) – value to encode
class kitty.model.low_level.encoder.BitsFuncEncoder(func)[source]

Bases: kitty.model.low_level.encoder.BitsEncoder

Encode bits using a given function

__init__(func)[source]
Parameters:func – encoder function(Bits)->Bits
encode(value)[source]
class kitty.model.low_level.encoder.ByteAlignedBitsEncoder[source]

Bases: kitty.model.low_level.encoder.BitsEncoder

Stuff bits for byte alignment

encode(value)[source]
Parameters:value – value to encode
class kitty.model.low_level.encoder.FloatAsciiEncoder(fmt)[source]

Bases: kitty.model.low_level.encoder.FloatEncoder

Encode a floating point number in ascii as described by IEEE 754 (decimal*)

__init__(fmt)[source]
Parameters:fmt (str) – format of ascii encoding (see floating point encoding in string docs.)
encode(value)[source]
Parameters:value – value to encode
class kitty.model.low_level.encoder.FloatBinEncoder(fmt)[source]

Bases: kitty.model.low_level.encoder.FloatEncoder

Encode a floating point number in binary format as described by IEEE 754 (binary32 and binary64)

__init__(fmt)[source]
Parameters:fmt (str) – format of binary encoding (see floating point encoding in struct docs.)
encode(value)[source]
Parameters:value – value to encode
class kitty.model.low_level.encoder.FloatEncoder[source]

Bases: object

Base encoder class for FloatingPoint values

Singleton Name Encoding Class
ENC_FLT_LE Encode as a little endian 32 bit FloatBinEncoder
ENC_FLT_BE Encode as a big endian 32 bit
ENC_DBL_LE Encode as a little endian 64 bit
ENC_DBL_BE Encode as a big endian 64 bit
ENC_FLT_FP Fixed point FloatAsciiEncoder
ENC_FLT_EXP Exponent notation
ENC_FLT_EXP_UPPER Exponent notation, with upper case E
ENC_FLT_GEN General format
ENC_FLT_GEN_UPPER General format, with upper case
ENC_FLT_DEFAULT Same as ENC_FLT_BE FloatBinEncoder
encode(value)[source]
Parameters:value (float) – value to encode
Return type:Bits
Returns:encoded value in bits
class kitty.model.low_level.encoder.ReverseBitsEncoder[source]

Bases: kitty.model.low_level.encoder.BitsEncoder

Reverse the order of bits

encode(value)[source]
Parameters:value – value to encode
class kitty.model.low_level.encoder.StrBase64NoNewLineEncoder[source]

Bases: kitty.model.low_level.encoder.StrEncoder

Encode the string as base64, but without the new line at the end

encode(value)[source]
Parameters:value – value to encode
class kitty.model.low_level.encoder.StrEncodeEncoder(encoding)[source]

Bases: kitty.model.low_level.encoder.StrEncoder

Encode the string using str.encode function

__init__(encoding)[source]
Parameters:encoding (str) – encoding to be used, should be a valid argument for str.encode
encode(value)[source]
Parameters:value – value to encode
class kitty.model.low_level.encoder.StrEncoder[source]

Bases: object

Base encoder class for str values The String encoders encode function receives a str object as an argument and returns an encoded Bits object.

Singleton Name Encoding Class
ENC_STR_UTF8 Encode the str in UTF-8 StrEncodeEncoder
ENC_STR_HEX Encode the str in hex
ENC_STR_BASE64 Encode the str in base64
ENC_STR_BASE64_NO_NL Encode the str in base64 but remove the new line from the end StrBase64NoNewLineEncoder
ENC_STR_DEFAULT Do nothing, just convert the str to Bits object StrEncoder
encode(value)[source]
Parameters:value (str) – value to encode
class kitty.model.low_level.encoder.StrEncoderWrapper(encoder)[source]

Bases: kitty.model.low_level.encoder.ByteAlignedBitsEncoder

Encode the data using str.encode function

__init__(encoder)[source]
Parameters:encoding (StrEncoder) – encoder to wrap
encode(value)[source]
Parameters:value – value to encode
class kitty.model.low_level.encoder.StrFuncEncoder(func)[source]

Bases: kitty.model.low_level.encoder.StrEncoder

Encode string using a given function

__init__(func)[source]
Parameters:func – encoder function(str)->str
encode(value)[source]
class kitty.model.low_level.encoder.StrNullTerminatedEncoder[source]

Bases: kitty.model.low_level.encoder.StrEncoder

Encode the string as c-string, with null at the end

encode(value)[source]
Parameters:value – value to encode
kitty.model.low_level.field module

This module is the “Heart” of the data model. It contains all the basic building blocks for a Template. Each “field” type is a discrete component in the full Template.

class kitty.model.low_level.field.BaseField(value, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.core.kitty_object.KittyObject

Basic type for all fields and containers, it contains the common logic. This class should never be used directly.

__init__(value, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • value – default value
  • encoder (BaseEncoder) – encoder for the field
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
copy()[source]
Returns:a copy of the field
get_field_by_name(name)[source]
Parameters:name – name of field to get
Raises:KittyException if no direct subfield with this name
get_info()[source]
Return type:dictionary
Returns:field information
get_rendered_fields(ctx=None)[source]
Returns:ordered list of the fields that will be rendered
get_structure()[source]
hash()[source]
Return type:int
Returns:hash of the field
is_default()[source]

Checks if the field is in its default form

Returns:True if field is in default form
mutate()[source]

Mutate the field

Return type:boolean
Returns:True if field the mutated
num_mutations()[source]
Returns:number of mutation in this field
render(ctx=None)[source]

Render the current value of the field

Return type:Bits
Returns:rendered value
reset()[source]

Reset the field to its default state

resolve_absolute_name(name)[source]

Resolve a field from an absolute name. An absolute name is just like unix absolute path, starts with ‘/’ and each name component is separated by ‘/’.

Parameters:name – absolute name, e.g. “/container/subcontainer/field”
Returns:field with this absolute name
Raises:KittyException if field could not be resolved
resolve_field(field)[source]

Resolve a field from name

Parameters:field – name of the field to resolve
Return type:BaseField
Returns:the resolved field
Raises:KittyException if field could not be resolved
scan_for_field(field_name)[source]

Scan for field field with given name

Parameters:field_name – field name to look for
Returns:None
set_current_value(value)[source]

Sets the current value of the field

Parameters:value – value to set
Returns:rendered value
set_offset(offset)[source]
Parameters:offset – absolute offset of this field (in bits)
skip(count)[source]

Skip up to [count] cases, default behavior is to just mutate [count] times

Count:number of cases to skip
Return type:int
Returns:number of cases skipped
kitty.model.low_level.field.BitField(value, length, signed=False, min_value=None, max_value=None, encoder=<kitty.model.low_level.encoder.BitFieldBinEncoder object>, fuzzable=True, name=None, full_range=False)[source]

Returns an instance of some BitField class .. note:

Since BitField is frequently used in binary format, multiple aliases were created for it. See aliases.py for more details.
class kitty.model.low_level.field.Delimiter(value, max_size=None, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field.String

Represent a text delimiter, the mutations target common delimiter-related vulnerabilities

__init__(value, max_size=None, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – default value
  • max_size – maximal size of the string before encoding (default: None)
  • encoder (StrEncoder) – encoder for the field (default: ENC_STR_DEFAULT)
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Example:
Delimiter('=', max_size=30, encoder=ENC_STR_BASE64)
lib = None
class kitty.model.low_level.field.Dynamic(key, default_value, length=None, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

A field that gets its value from the fuzzer at runtime

__init__(key, default_value, length=None, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=False, name=None)[source]
Parameters:
  • key (str) – key for the data in the session_data dictionary
  • default_value (str) – default value of the field
  • length – length of the field in bytes. must be set if fuzzable=True (default: None)
  • encoder (StrEncoder) – encoder for the field (default: ENC_STR_DEFAULT)
  • fuzzable – is field fuzzable (default: False)
  • name – name of the object (default: None)
Examples:
Dynamic(key='session id', default_value='')
Dynamic(key='session id', default_value='', length=4, fuzzable=True)
hash()[source]
Return type:int
Returns:hash of the field
is_default()[source]

Checks if the field is in its default form

Returns:True if field is in default form
render(ctx=None)[source]
set_session_data(session_data)[source]
skip(count)[source]
class kitty.model.low_level.field.Float(value, encoder=<kitty.model.low_level.encoder.FloatBinEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field._LibraryField

Represent a floating point number. The mutations target edge cases and invalid floating point numbers.

__init__(value, encoder=<kitty.model.low_level.encoder.FloatBinEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • value (float) – default value
  • encoder (FloatEncoder) – encoder for the field (default: ENC_FLT_DEFAULT)
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Example:
Float(0.3)
lib = None
class kitty.model.low_level.field.Group(values, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field._LibraryField

A field with fixed set of possible mutations

__init__(values, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • values (list of strings) – possible values for the field
  • encoder (StrEncoder) – encoder for the field (default: ENC_STR_DEFAULT)
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Example:

This field will generate exactly 3 mutations: ‘GET’, ‘PUT’ and ‘POST’

Group(['GET', 'PUT', 'POST'], name='http methods')
hash()[source]
Return type:int
Returns:hash of the field
lib = None
class kitty.model.low_level.field.RandomBits(value, min_length, max_length, unused_bits=0, seed=1235, num_mutations=25, step=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

A random sequence of bits. The length of the sequence is between min_length and max_length, and decided either randomally (if step is None) or starts from min_length and inreased by step bits (if step has a value).

__init__(value, min_length, max_length, unused_bits=0, seed=1235, num_mutations=25, step=None, encoder=<kitty.model.low_level.encoder.BitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – default value, the last unsused_bits will be removed from the value
  • min_length – minimal length of the field (in bits)
  • max_length – maximal length of the field (in bits)
  • unused_bits – how many bits from the value are not used (default: 0)
  • seed – seed for the random number generator, to allow consistency between runs (default: 1235)
  • num_mutations – number of mutations to perform (if step is None) (default:25)
  • step (int) – step between lengths of each mutation (default: None)
  • encoder (BitsEncoder) – encoder for the field (default: ENC_BITS_DEFAULT)
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Examples:
RandomBits(value='1234', min_length=0, max_length=75, unused_bits=0, step=15)
RandomBits(value='1234', min_length=0, max_length=75, unused_bits=3, num_mutations=80)
hash()[source]
Return type:int
Returns:hash of the field
reset()[source]
class kitty.model.low_level.field.RandomBytes(value, min_length, max_length, seed=1234, num_mutations=25, step=None, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

A random sequence of bytes The length of the sequence is between min_length and max_length, and decided either randomally (if step is None) or starts from min_length and inreased by step bytes (if step has a value).

__init__(value, min_length, max_length, seed=1234, num_mutations=25, step=None, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – default value
  • min_length – minimal length of the field (in bytes)
  • max_length – maximal length of the field (in bytes)
  • seed – seed for the random number generator, to allow consistency between runs (default: 1234)
  • num_mutations – number of mutations to perform (if step is None) (default:25)
  • step (int) – step between lengths of each mutation (default: None)
  • encoder (StrEncoder) – encoder for the field (default: ENC_STR_DEFAULT)
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Examples:
RandomBytes(value='1234', min_length=0, max_length=75, step=15)
RandomBytes(value='1234', min_length=0, max_length=75, num_mutations=80)
hash()[source]
Return type:int
Returns:hash of the field
reset()[source]
class kitty.model.low_level.field.Static(value, encoder=<kitty.model.low_level.encoder.StrEncoder object>, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

A static field does not mutate. It is used for constant parts of the model

__init__(value, encoder=<kitty.model.low_level.encoder.StrEncoder object>, name=None)[source]
Parameters:
  • value (str) – default value
  • encoder (StrEncoder) – encoder for the field (default: ENC_STR_DEFAULT)
  • name – name of the object (default: None)
Example:
Static('this will never change')
class kitty.model.low_level.field.String(value, max_size=None, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field._LibraryField

Represent a string, the mutation target common string-related vulnerabilities

__init__(value, max_size=None, encoder=<kitty.model.low_level.encoder.StrEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – default value
  • max_size – maximal size of the string before encoding (default: None)
  • encoder (StrEncoder) – encoder for the field
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Example:
String('this is the default value', max_size=5)
hash()[source]
Return type:int
Returns:hash of the field
lib = None
kitty.model.low_level.field.gen_power_list(val, min_power=0, max_power=10, mutation_desc='')[source]
kitty.model.low_level.mutated_field module

Fields to perform mutation fuzzing

In some cases, you might not know the details and structure of the fuzzed protocol (or might just be too lazy) but you do have some examples of valid messages. In these cases, it makes sense to perform mutation fuzzing. The strategy of mutation fuzzing is to take some valid messages and mutate them in various ways. Kitty supports the following strategies described below. The last one, MutableField, combines all strategies, with reasonable parameters, together.

Currently all strategies are inspired by this article: http://lcamtuf.blogspot.com/2014/08/binary-fuzzing-strategies-what-works.html

class kitty.model.low_level.mutated_field.BitFlip(value, num_bits=1, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

Perform bit-flip mutations of N sequential bits on the value

Example:
BitFlip('\x01', 3)
Results in: '\xe1', '\x71', '\x39', '\x1d', '\x0f', '\x06'
__init__(value, num_bits=1, fuzzable=True, name=None)[source]
Parameters:
  • value – value to mutate (str)
  • num_bits – number of consequtive bits to flip (invert)
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Raises:

KittyException if num_bits is bigger than the value length in bits

Raises:

KittyException if num_bits is not positive

get_info()[source]
hash()[source]
class kitty.model.low_level.mutated_field.BitFlips(value, bits_range=[1, 2, 3, 4], fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.OneOf

Perform bit-flip mutations of (N..) sequential bits on the value

__init__(value, bits_range=[1, 2, 3, 4], fuzzable=True, name=None)[source]
Parameters:
  • value (str) – value to mutate
  • bits_range – range of number of consequtive bits to flip (default: range(1, 5))
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Example:
BitFlips('\x00', (3, 5))
Results in: '\xe0', '\x70', '\x38', '\x1c', '\x0e', '\x07' - 3 bits flipped each time
            '\xf8', '\x7c', '\x3e', '\x1f' - 5 bits flipped each time
class kitty.model.low_level.mutated_field.BlockDuplicate(value, block_size, num_dups=2, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.mutated_field.BlockOperation

Duplicate a block of bytes from the default value, each mutation moving one byte forward.

__init__(value, block_size, num_dups=2, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – value to mutate
  • block_size – number of consequtive bytes to duplicate
  • num_dups – number of times to duplicate the block (default: 1)
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Raises:

KittyException if block_size is bigger than the value length in bytes

Raises:

KittyException if block_size is not positive

hash()[source]
class kitty.model.low_level.mutated_field.BlockDuplicates(value, block_size, num_dups_range=(2, 5, 10, 50, 200), fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.OneOf

Perform block duplication with multiple number of duplications

__init__(value, block_size, num_dups_range=(2, 5, 10, 50, 200), fuzzable=True, name=None)[source]
class kitty.model.low_level.mutated_field.BlockOperation(value, block_size, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

Base class for performing block-level mutations

__init__(value, block_size, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – value to mutate
  • block_size – number of consequtive bytes to operate on
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Raises:

KittyException if block_size is bigger than the value length in bytes

Raises:

KittyException if block_size is not positive

hash()[source]
class kitty.model.low_level.mutated_field.BlockRemove(value, block_size, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.mutated_field.BlockOperation

Remove a block of bytes from the default value, each mutation moving one byte forward.

__init__(value, block_size, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – value to mutate
  • block_size – number of consequtive bytes to remove
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Raises:

KittyException if block_size is bigger than the value length in bytes

Raises:

KittyException if block_size is not positive

class kitty.model.low_level.mutated_field.BlockSet(value, block_size, set_chr, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.mutated_field.BlockOperation

Set a block of bytes from the default value to a specific value, each mutation moving one byte forward.

__init__(value, block_size, set_chr, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – value to mutate
  • block_size – number of consequtive bytes to duplicate
  • set_chr – char to set in the blocks
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Raises:

KittyException if block_size is bigger than the value length in bytes

Raises:

KittyException if block_size is not positive

class kitty.model.low_level.mutated_field.ByteFlip(value, num_bytes=1, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.field.BaseField

Flip number of sequential bytes in the message, each mutation moving one byte forward.

Example:
ByteFlip('\x00\x00\x00\x00', 2)
# results in:
'\xff\xff\x00\x00'
'\x00\xff\xff\x00'
'\x00\x00\xff\xff'
__init__(value, num_bytes=1, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – value to mutate
  • num_bytes – number of consequtive bytes to flip (invert)
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Raises:

KittyException if num_bytes is bigger than the value length

Raises:

KittyException if num_bytes is not positive

get_info()[source]
hash()[source]
class kitty.model.low_level.mutated_field.ByteFlips(value, bytes_range=(1, 2, 4), fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.OneOf

Perform byte-flip mutations of (N..) sequential bytes on the value

__init__(value, bytes_range=(1, 2, 4), fuzzable=True, name=None)[source]
Parameters:
  • value (str) – value to mutate
  • bytes_range – range of number of consequtive bytes to flip (default: (1, 2, 4))
  • fuzzable – is field fuzzable (default: True)
  • name – name of the object (default: None)
Example:
ByteFlips('\x00\x00\x00\x00', (2,3))
Results in:
'\xff\xff\x00\x00', '\x00\xff\xff\x00', '\x00\x00\xff\xff'  # 2 bytes flipped each time
'\xff\xff\xff\x00', '\x00\xff\xff\xff'  # 3 bytes flipped each time
class kitty.model.low_level.mutated_field.MutableField(value, encoder=<kitty.model.low_level.encoder.ByteAlignedBitsEncoder object>, fuzzable=True, name=None)[source]

Bases: kitty.model.low_level.container.OneOf

Container to perform mutation fuzzing on a value ByteFlips, BitFlips and block operations

__init__(value, encoder=<kitty.model.low_level.encoder.ByteAlignedBitsEncoder object>, fuzzable=True, name=None)[source]
Parameters:
  • value (str) – value to mutate
  • encoder (BitsEncoder) – encoder for the container (default: ENC_BITS_BYTE_ALIGNED)
  • fuzzable – is fuzzable (default: True)
  • name – (unique) name of the template (default: None)

Indices and tables

Kitty Tools

When installing Kitty using setup.py or pip, it will install several tools:

Template Tester

Usage:
    kitty-template-tester [--fast] [--tree] [--verbose] <FILE> ...

This tool mutates and renders templates in a file, making sure there are no
syntax issues in the templates.
It doesn't prove that the data model is correct, only checks that the it is
a valid model

Options:
    <FILE>      python file that contains templates in dictionaries, lists or globals
    --fast      only import, don't run all mutations
    --tree      print fields tree of the template instead of mutating it
    --verbose   print full call stack upon exception

Kitty Tools

kitty-tool will replace kitty-template tester and provide extended functionality

Tools for testing and manipulating kitty templates.

Usage:
    kitty-tool generate [--verbose] [-s SKIP] [-c COUNT] [-o OUTDIR] [-f FORMAT] <FILE> <TEMPLATE> ...
    kitty-tool list <FILE>
    kitty-tool --version

Commands:

    generate    generate files with mutated payload
    list        list templates in a file

Options:
    <FILE>                  python file that contains the template
    <TEMPLATE>              template name(s) to generate files from
    --out -o OUTDIR         output directory for the generated mutations [default: out]
    --skip -s SKIP          how many mutations to skip [default: 0]
    --count -c COUNT        end index to generate
    --verbose -v            verbose output
    --filename-format -f FORMAT  format for generated file names [default: %(template)s.%(index)s.bin]
    --version               print version and exit
    --help -h               print this help and exit

File name formats:
    You can control the name of an output file by giving a filename format,
    it follows python's dictionary format string.
    The available keywords are:
        template - the template name
        index - the template index

CLI Web Client

Usage:
    kitty-web-client (info [-v]|pause|resume) [--host <hostname>] [--port <port>]
    kitty_web_client.py reports store <folder> [--host <hostname>] [--port <port>]
    kitty_web_client.py reports show <file> ...

Retrieve and parse kitty status and reports from a kitty web server

Options:
    -v --verbose            verbose information
    -h --host <hostname>    kitty web server host [default: localhost]
    -p --port <port>        kitty web server port [default: 26000]

API Reference

Kitty API Reference

Subpackages

kitty.controllers package

The kitty.controllers package provide the basic controller classes. The controller role is to provide means to start/stop the victim and put it in the appropriate state for a test.

BaseController should not be instantiated. It should be extended when performing server fuzzing.

ClientController is used for client fuzzing. It should be extended with an implementation for the trigger() function to trigger the victim to start the communications.

EmptyController is used when no actual work should be done by the controller.

Submodules
kitty.controllers.base module

The controller is in charge of preparing the victim for the test. It should make sure that the victim is in an appropriate state before the target initiates the transfer session. Sometimes it means doing nothing, other times it means starting or reseting a VM, killing a process or performing a hard reset to the victim hardware. Since the controller is reponsible for the state of the victim, it is expected to perform a basic monitoring as well, and report whether the victim is ready for the next test.

class kitty.controllers.base.BaseController(name, logger=None, victim_alive_check_delay=0.3)[source]

Bases: kitty.core.actor.KittyActorInterface

Base class for controllers. Defines basic variables and implements basic behavior.

kitty.controllers.client module

ClientController is a controller for victim in client mode, it inherits from BaseController, and implements one additional method: trigger().

class kitty.controllers.client.ClientController(name, logger=None, victim_alive_check_delay=0.3)[source]

Bases: kitty.controllers.base.BaseController

Base class for client controllers.

trigger()[source]

Trigger a data exchange from the tested client

kitty.controllers.empty module

EmptyController does nothing, implements both client and server controller API

class kitty.controllers.empty.EmptyController(name='EmptyController', logger=None)[source]

Bases: kitty.controllers.client.ClientController

EmptyController does nothing, implements both client and server controller API

__init__(name='EmptyController', logger=None)[source]
trigger()[source]

Trigger a data exchange from the tested client

kitty.core package

The classes in this module has very little to do with the fuzzing process, however, those classes and functions are used all over kitty.

exception kitty.core.KittyException[source]

Bases: exceptions.Exception

Simple exception, used mainly to make tests better, and identify exceptions that were thrown by kitty directly.

kitty.core.khash(*args)[source]

hash arguments. khash handles None in the same way accross runs (which is good :))

Submodules
kitty.core.actor module

This module contains KittyActorInterface which is the base class for both monitors and controllers.

class kitty.core.actor.KittyActorInterface(name, logger=None, victim_alive_check_delay=0.3)[source]

Bases: kitty.core.kitty_object.KittyObject

Base class for monitors and controllers, its defines (and partially implements) the Kitty Actor API:

__init__(name, logger=None, victim_alive_check_delay=0.3)[source]
Parameters:
  • name – name of the actor
  • logger – logger for the actor (default: None)
  • victim_alive_check_delay – delay between checks if alive (default: 0.3)
get_report()[source]
Return type:Report
Returns:a report about the victim since last call to pre_test
is_victim_alive()[source]

Called during pre_test in loop until the target becomes alive

Returns:whether target is alive (ready for test) or not

Note

by default, it returns true, override if you have a way to check in your actor

post_test()[source]

Called when test is done. Call super if overriden.

pre_test(test_number)[source]

Called before a test is started. Call super if overriden.

Parameters:test_number – current test number
setup()[source]

Called at the beginning of the fuzzing session. You should override it with the actual implementation of victim setup.

teardown()[source]

Called at the end of the fuzzing session. You should override it with the actual implementation of victim teardown.

kitty.core.kassert module

This module provides various assertion functions used by kitty, not that important, but makes the life easier. Useful for making assertions that throw KittyException

kitty.core.kassert.is_in(obj, it)[source]
Parameters:
  • obj – object to assert
  • it – iterable of elements we assert obj is in
Raise:

an exception if obj is in an iterable

kitty.core.kassert.is_int(obj)[source]
Parameters:obj – object to assert
Raise:an exception if obj is not an int type
kitty.core.kassert.is_of_types(obj, the_types)[source]
Parameters:
  • obj – object to assert
  • the_types – iterable of types, or a signle type
Raise:

an exception if obj is not an instance of types

kitty.core.kassert.not_none(obj)[source]
Parameters:obj – object to assert
Raise:an exception if obj is not None
kitty.core.kitty_object module

KittyObject is subclassed by most of Kitty’s objects.

It provides logging, naming, and description of the object.

class kitty.core.kitty_object.KittyObject(name, logger=None)[source]

Bases: object

Basic class to ease logging and description of objects.

__init__(name, logger=None)[source]
Parameters:name – name of the object
get_description()[source]
Return type:str
Returns:the description of the object. by default only prints the object type.
classmethod get_log_file_name()[source]
Returns:log file name
classmethod get_logger()[source]
Returns:the class logger
get_name()[source]
Return type:str
Returns:object’s name
log_file_name = './kittylogs/kitty_20170613-222149.log'
not_implemented(func_name)[source]

log access to unimplemented method and raise error

Parameters:func_name – name of unimplemented function.
Raise:NotImplementedError detailing the function the is not implemented.
classmethod set_verbosity(verbosity)[source]

Set verbosity of logger

Parameters:verbosity – verbosity level. currently, we only support 1 (logging.DEBUG)
kitty.core.threading_utils module

Threading Utils

class kitty.core.threading_utils.FuncThread(func, *args)[source]

Bases: threading.Thread

FuncThread is a thread wrapper to create thread from a function

__init__(func, *args)[source]
Parameters:
  • func – function to be called in this thread
  • args – arguments for the function
run()[source]

run the the function in this thread’s context

class kitty.core.threading_utils.LoopFuncThread(func, *args)[source]

Bases: threading.Thread

FuncThread is a thread wrapper to create thread from a function

__init__(func, *args)[source]
Parameters:
  • func – function to be call in a loop in this thread
  • args – arguments for the function
run()[source]

run the the function in a loop until stoped

set_func_stop_event(func_stop_event)[source]
Parameters:func_stop_event (Event) – event to signal stop to the _func
stop()[source]

stop the thread, return after thread stopped

kitty.data package

This package contains class for managing data related to the fuzzing session.

Submodules
kitty.data.data_manager module

This module is usde to store the fuzzing session related data. It provides both means of communications between the fuzzer and the user interface, and persistent storage of the fuzzing session results.

class kitty.data.data_manager.DataManager(dbname)[source]

Bases: threading.Thread

Manages data on a dedicated thread. All calls to it should be done by submitting DataManagerTask

Example:
dataman = DataManager('fuzz_session.sqlite`)
dataman.start()
def get_session_info(manager):
    return manager.get_session_info_manager().get_session_info()
get_info_task = DataManagerTask(get_session_info)
dataman.submit_task(get_info_task)
session_info = get_info_task.get_results()
__init__(dbname)[source]
Parameters:dbname – database name for storing the data
close()[source]

close the database connection

get(*args, **kwargs)[source]

Actual wrapper for the synchronous function

get_report_by_id(*args, **kwargs)[source]

Actual wrapper for the synchronous function

get_report_list(*args, **kwargs)[source]

Actual wrapper for the synchronous function

get_report_test_ids(*args, **kwargs)[source]

Actual wrapper for the synchronous function

get_reports_manager(*args, **kwargs)[source]

Actual wrapper for the synchronous function

get_session_info(*args, **kwargs)[source]

Actual wrapper for the synchronous function

get_session_info_manager(*args, **kwargs)[source]

Actual wrapper for the synchronous function

open()[source]

open the database

run()[source]

thread function

set(*args, **kwargs)[source]

Actual wrapper for the synchronous function

set_session_info(*args, **kwargs)[source]

Actual wrapper for the synchronous function

stop()[source]

Stop the data manager

store_report(*args, **kwargs)[source]

Actual wrapper for the synchronous function

submit_task(task)[source]

submit a task to the data manager, to be proccessed in the DataManager context

Parameters:task (DataManagerTask) – task to perform
class kitty.data.data_manager.DataManagerTask(task, *args)[source]

Bases: object

Task to be performed in the DataManager context

__init__(task, *args)[source]
Parameters:task (function(DataManager) -> object) – task to be performed
execute(dataman)[source]

run the task

Parameters:dataman (DataManager) – the executing data manager
get_results()[source]
Returns:result from running the task
class kitty.data.data_manager.ReportsTable(connection, cursor)[source]

Bases: kitty.data.data_manager.Table

Table for storing the reports

__init__(connection, cursor)[source]
Parameters:
  • connection – the database connection
  • cursor – the cursor for the database
get(test_id)[source]

get report by the test id

Parameters:test_id – test id
Returns:Report object
get_report_list()[source]
Returns:ids of test reports
get_report_test_ids()[source]
Returns:ids of test reports
store(report, test_id)[source]
Parameters:
  • report – the report to store
  • test_id – the id of the test reported
Returns:

report id

class kitty.data.data_manager.SessionInfo(orig=None)[source]

Bases: object

session information manager

__init__(orig=None)[source]
Parameters:orig – SessionInfo object to copy (default: None)
as_dict()[source]
Returns:dictionary with the object fields
copy(orig)[source]
Parameters:orig – SessionInfo object to copy
Returns:True if changed, false otherwise
fields = ['start_time', 'start_index', 'end_index', 'current_index', 'failure_count', 'kitty_version', 'data_model_hash', 'test_list_str']
classmethod from_dict(info_d)[source]
Parameters:info_d – the info dictionary
Return type:SessionInfo
Returns:object that corresponds to the info dictionary
i = ('test_list_str', 'BLOB')
class kitty.data.data_manager.SessionInfoTable(connection, cursor)[source]

Bases: kitty.data.data_manager.Table

Table for storing the session info

__init__(connection, cursor)[source]
Parameters:
  • connection – the database connection
  • cursor – the cursor for the database
get_session_info()[source]
Return type:SessionInfo
Returns:current session info
read_info()[source]
Return type:SessionInfo
Returns:current session info
set_session_info(info)[source]
Parameters:info (SessionInfo) – info to set
class kitty.data.data_manager.Table(connection, cursor)[source]

Bases: object

Base class for data manager tables

__init__(connection, cursor)[source]
Parameters:
  • connection – the database connection
  • cursor – the cursor for the database
insert(fields, values)[source]

insert new db entry

Parameters:
  • fields – list of fields to insert
  • values – list of values to insert
Returns:

row id of the new row

row_to_dict(row)[source]

translate a row of the current table to dictionary

Parameters:row – a row of the current table (selected with *)
Returns:dictionary of all fields
select(to_select, where=None, sql_params=None)[source]

select db entries

Parameters:
  • to_select – string of fields to select
  • where – where clause (default: None)
  • sql_params – params for the where clause
update(field_dict, where_clause=None)[source]

update db entry

Parameters:
  • field_dict – dictionary of fields and values
  • where_clause – where clause for the update
kitty.data.data_manager.synced(func)[source]

Decorator for functions that should be called synchronously from another thread

Parameters:func – function to call
kitty.data.report module

This module defines the Report class

class kitty.data.report.Report(name, default_failed=False)[source]

Bases: object

This class represent a report for a single test. This report may contain subreports from nested entities.

Example:

In this example, the report, generated by the controller, indicates failure.

report = Report('Controller')
report.add('generation time', 0)
report.failed('target does not respond')
ERROR = 'error'
FAILED = 'failed'
PASSED = 'passed'
__init__(name, default_failed=False)[source]
Parameters:
  • name – name of the report (or the issuer)
  • default_failed – is the default status of the report failed (default: False)
add(key, value)[source]

Add an entry to the report

Parameters:
  • key – entry’s key
  • value – the actual value
Example:
my_report.add('retry count', 3)
allowed_statuses = ['passed', 'failed', 'error']
clear()[source]

Set the report to its defaults. This will clear the report, keeping only the name and setting the failure status to the default.

error(reason=None)[source]

Set the test status to Report.ERROR, and set the error reason

Parameters:reason – error reason (default: None)
failed(reason=None)[source]

Set the test status to Report.FAILED, and set the failure reason

Parameters:reason – failure reason (default: None)
classmethod from_dict(d, encoding='base64')[source]

Construct a Report object from dictionary.

Parameters:
  • d (dictionary) – dictionary representing the report
  • encoding – encoding of strings in the dictionary (default: ‘base64’)
Returns:

Report object

get(key)[source]

Get a value for a given key

Parameters:key – entry’s key
Returns:corresponding value
get_name()[source]
Returns:the name of the report
get_status()[source]

Get the status of the report and its sub-reports.

Return type:str
Returns:report status (‘passed’, ‘failed’ or ‘error’)
is_failed()[source]

Deprecated since version 0.6.7: use get_status()

passed()[source]

Set the report status to PASSED

reserved_keys = {'status': 'set_status(status), success(), failed(reason) or error(reason)', 'failed': 'failed(reason)'}
set_status(new_status)[source]

Set the status of the report.

Parameters:new_status – the new status of the report (either PASSED, FAILED or ERROR)
success()[source]

Set the report status to PASSED.

Deprecated since version 0.6.7: use passed()

to_dict(encoding='base64')[source]

Return a dictionary version of the report

Parameters:encoding – required encoding for the string values (default: ‘base64’)
Return type:dictionary
Returns:dictionary representation of the report
kitty.fuzzers package

The kitty.fuzzers module provides fuzzer classes. In most cases, there is no need to extend those classes, and they may be used as is.

BaseFuzzer should not be instantiated, and only serves as a common parent for ClientFuzzer and ServerFuzzer.

ClientFuzzer should be used when the fuzzer provides payloads to some server stack in a client fuzzing session.

ServerFuzzer should be used when the fuzzer instantiates the communication, in cases such as fuzzing a server of some sort or when writing payloads to files.

Submodules
kitty.fuzzers.base module

This module contains BaseFuzzer, which implements most of the fuzzing logic for both Server and Client fuzzing.

This module should not be overriden/referenced by entities outside of kitty, as it is tightly coupled to the implementation of the Client and Server fuzzer, and will probably be changed in the future.

class kitty.fuzzers.base.BaseFuzzer(name='', logger=None, option_line=None)[source]

Bases: kitty.core.kitty_object.KittyObject

Common members and logic for client and server fuzzers. This class should not be instantiated, only subclassed.

__init__(name='', logger=None, option_line=None)[source]
Parameters:
  • name – name of the object
  • logger – logger for the object (default: None)
  • option_line – cmd line options to the fuzzer (dafult: None)
handle_stage_changed(model)[source]

handle a stage change in the data model

Parameters:model – the data model that was changed
set_delay_between_tests(delay_secs)[source]

Set duration between tests

Parameters:delay_secs – delay between tests (in seconds)
set_delay_duration(delay_duration)[source]

Deprecated since version use: set_delay_between_tests()

set_interface(interface)[source]
Parameters:interface – user interface
set_max_failures(max_failures)[source]
Parameters:max_failures – maximum failures before stopping the fuzzing session
set_model(model)[source]

Set the model to fuzz

Parameters:model (BaseModel or a subclass) – Model object to fuzz
set_range(start_index=0, end_index=None)[source]

Set range of tests to run

Deprecated since version use: set_test_list()

Parameters:
  • start_index – index to start at (default=0)
  • end_index – index to end at(default=None)
set_session_file(filename)[source]

Set session file name, to keep state between runs

Parameters:filename – session file name
set_skip_env_test(skip_env_test=True)[source]

Set whether to skip the environment test. Call this if the environment test cannot pass and you prefer to start the tests without it.

Parameters:skip_env_test – skip the environment test (default: True)
set_store_all_reports(store_all_reports)[source]
Parameters:store_all_reports – should all reports be stored
set_target(target)[source]
Parameters:target – target object
set_test_list(test_list_str='')[source]
Parameters:test_list_str – listing of the test to execute

The test list should be a comma-delimited string, and each element should be one of the following forms:

‘-x’ - run from test 0 to test x ‘x-‘ - run from test x to the end ‘x’ - run test x ‘x-y’ - run from test x to test y

To execute all tests, pass None or an empty string

start()[source]

Start the fuzzing session

If fuzzer already running, it will return immediatly

stop()[source]

stop the fuzzing session

kitty.fuzzers.client module

This module contains the ClientFuzzer class.

class kitty.fuzzers.client.ClientFuzzer(name='ClientFuzzer', logger=None, option_line=None)[source]

Bases: kitty.fuzzers.base.BaseFuzzer

ClientFuzzer is designed for fuzzing clients. It does not preform an active fuzzing, but rather returns a mutation of a response when in the right state. It is designed to be a module that is integrated into different stacks.

You can see its usahe examples in the following places:

  • examples/02_client_fuzzer_browser_remote
  • examples/03_client_fuzzer_browser
STAGE_ANY = '******************'
__init__(name='ClientFuzzer', logger=None, option_line=None)[source]
Parameters:
  • name – name of the object
  • logger – logger for the object (default: None)
  • option_line – cmd line options to the fuzzer
get_mutation(stage, data)[source]

Get the next mutation, if in the correct stage

Parameters:
  • stage – current stage of the stack
  • data – a dictionary of items to pass to the model
Returns:

mutated payload if in apropriate stage, None otherwise

is_done()[source]

check if fuzzer is done fuzzing

Returns:True if done
stop()[source]

Stop the fuzzing session

wait_until_done()[source]

wait until fuzzer is done

kitty.fuzzers.server module
class kitty.fuzzers.server.ServerFuzzer(name='ServerFuzzer', logger=None, option_line=None)[source]

Bases: kitty.fuzzers.base.BaseFuzzer

ServerFuzzer is a class that is designed to fuzz servers. It does not create the mutations, as those are created by the Session object. The idea is to go through every path in the model, execute all requsets in the path, and mutating the last request.

__init__(name='ServerFuzzer', logger=None, option_line=None)[source]
Parameters:
  • name – name of the object
  • logger – logger for the object (default: None)
  • option_line – cmd line options to the fuzzer
kitty.interfaces package

This package provides User Interface classes

BaseInterface is not well-maintained, and should not be used.

WebInterface starts a web server that provides information about the fuzzing state, as well as reports.

Submodules
kitty.interfaces.base module
class kitty.interfaces.base.BaseInterface(name='BaseInterface', logger=None)[source]

Bases: kitty.core.kitty_object.KittyObject

User interface API

__init__(name='BaseInterface', logger=None)[source]
Parameters:
  • name – name of the object
  • logger – logger for the object (default: None)
failure_detected()[source]

handle failure detection

finished()[source]

handle finished

is_paused()[source]
Returns:whether current state is paused
pause()[source]

pause the fuzzer

progress()[source]

handle progress

resume()[source]

resume the fuzzer

set_continue_event(event)[source]
Parameters:event – used to control pause/continue
set_data_provider(data)[source]
Parameters:data – the data provider
start()[source]

start the monitor

stop()[source]

stop the monitor

class kitty.interfaces.base.EmptyInterface(name='EmptyInterface', logger=None)[source]

Bases: kitty.interfaces.base.BaseInterface

This interface may be used when there is no need for user interface

__init__(name='EmptyInterface', logger=None)[source]
Parameters:
  • name – name of the object
  • logger – logger for the object (default: None)
failure_detected()[source]

handle failure detection

finished()[source]

handle finished

progress()[source]

handle progress

kitty.interfaces.web module
class kitty.interfaces.web.WebInterface(host='127.0.0.1', port=26000)[source]

Bases: kitty.interfaces.base.EmptyInterface

Web UI for the fuzzer

__init__(host='127.0.0.1', port=26000)[source]
Parameters:
  • host – listening address
  • port – listening port
get_description()[source]
Returns:description string (with listening host:port)
kitty.monitors package

The kitty.monitors package provides the basic monitor class BaseMonitor should not be instantiated, only extended. By default, it runs a separate thread and calls _monitor_func in a loop. If a non-threaded monitor is required, one should re-implement multiple parts of the BaseMonitor class.

Submodules
kitty.monitors.base module

This module defines BaseMonitor - the base (abstract) monitor class

class kitty.monitors.base.BaseMonitor(name, logger=None, victim_alive_check_delay=0.3)[source]

Bases: kitty.core.actor.KittyActorInterface

Base (abstract) monitor class

__init__(name, logger=None, victim_alive_check_delay=0.3)[source]
Parameters:
  • name – name of the actor
  • logger – logger for the actor (default: None)
  • victim_alive_check_delay – delay between checks if alive (default: 0.3)
pre_test(test_number)[source]

Called when a test is started

Parameters:test_number – current test number
setup()[source]

Make sure the monitor is ready for fuzzing

teardown()[source]

cleanup the monitor data and

kitty.remote package

kitty.remote The remote package provides RPC mechanism for kitty Currently, all RPC communication is done over TCP

Submodules
kitty.remote.actor module
class kitty.remote.actor.RemoteActor(host, port)[source]

Bases: kitty.remote.rpc.RpcClient

get_report()[source]

need to wrap get_report, since we need to parse the report

class kitty.remote.actor.RemoteActorServer(host, port, impl)[source]

Bases: kitty.remote.rpc.RpcServer

get_report()[source]
kitty.remote.rpc module

RPC implementation, based on jsonrpc https://json-rpc.readthedocs.io/

class kitty.remote.rpc.RpcClient(host, port)[source]

Bases: object

__init__(host, port)[source]
Parameters:url – URL of the RPC server
get_unique_msg_id()[source]
Returns:a unique message id
stop_remote_server()[source]

Stop the remote server (after responding to this message)

class kitty.remote.rpc.RpcHandler(request, client_address, server)[source]

Bases: BaseHTTPServer.BaseHTTPRequestHandler

do_POST()[source]

Handle POST requests

error_response(code, msg)[source]

Send an error response

Parameters:
  • code – error code
  • msg – error message
log_message(fmt, *args)[source]

Override default log and do nothing

send_result(additional_dict)[source]

Send a result to the RPC client

Parameters:additional_dict – the dictionary with the response
valid_response(result)[source]

Send a valid response with the result

Parameters:result – the result of the call
class kitty.remote.rpc.RpcHttpServer(server_address, handler, impl, meta)[source]

Bases: BaseHTTPServer.HTTPServer

__init__(server_address, handler, impl, meta)[source]
Parameters:
  • server_address – address of the server
  • handler – handler for requests
  • impl – reference to the implementation object
log_message(fmt, *args)[source]

Override default log and do nothing

class kitty.remote.rpc.RpcServer(host, port, impl)[source]

Bases: object

__init__(host, port, impl)[source]
Parameters:
  • host – listening address
  • port – listening port
  • impl – implementation class
is_running()[source]

Check if the server is currently running

Returns:whether the server is currently running
start()[source]

Serving loop

stop_server()[source]

Mark the server state to be stopped. No further action needed when called from remote RPC client (stop_remote_server), but requires another request if called directly

kitty.remote.rpc.decode_data(data)[source]

Decode data - list, dict, string, bool or int (and nested)

Parameters:
  • data – data to decode
  • encoding – encoding to use (default: ‘hex’)
Returns:

decoded object of the same type

kitty.remote.rpc.decode_string(data, encoding='hex')[source]

Decode string

Parameters:
  • data – string to decode
  • encoding – encoding to use (default: ‘hex’)
Returns:

decoded string

kitty.remote.rpc.encode_data(data)[source]

Encode data - list, dict, string, bool or int (and nested)

Parameters:
  • data – data to encode
  • encoding – encoding to use (default: ‘hex’)
Returns:

encoded object of the same type

kitty.remote.rpc.encode_string(data, encoding='hex')[source]

Encode string

Parameters:
  • data – string to encode
  • encoding – encoding to use (default: ‘hex’)
Returns:

encoded string

kitty.targets package

The kitty.target package provides basic target classes. The target classes should be extended in most cases.

BaseTarget should not be instantiated, and only serves as a common parent for ClientTarget, EmptyTarget and ServerTarget.

ClientTarget should be used when fuzzing a client. In most cases it should not be extended, as the special functionality (triggering the victim) is done by its ClientController

EmptyTarget can be used in server-like fuzzing when no communication should be done.

ServerTarget should be used when fuzzing a server. In most cases it should be extended to provide the appropriate communication means with the server.

Submodules
kitty.targets.base module

This module defines BaseTarget - the basic target

class kitty.targets.base.BaseTarget(name='BaseTarget', logger=None)[source]

Bases: kitty.core.kitty_object.KittyObject

BaseTarget contains the common logic and behaviour of all target.

__init__(name='BaseTarget', logger=None)[source]
add_monitor(monitor)[source]

Add a monitor

get_report()[source]
get_session_data()[source]

Session related data dictionary to be used by data model.

Returns:dictionary (str, bytes)
post_test(test_num)[source]

Called when test is completed, a report should be prepared now

pre_test(test_num)[source]

Called when a test is started

set_controller(controller)[source]

Set a controller

set_fuzzer(fuzzer)[source]
setup()[source]

Make sure the target is ready for fuzzing, including monitors and controllers

teardown()[source]

Clean up the target once all tests are completed

kitty.targets.client module
class kitty.targets.client.ClientTarget(name, logger=None, mutation_server_timeout=3)[source]

Bases: kitty.targets.base.BaseTarget

This class represents a target when fuzzing a client.

__init__(name, logger=None, mutation_server_timeout=3)[source]
Parameters:
  • name – name of the target
  • logger – logger for this object (default: None)
  • mutation_server_timeout – timeout for receiving mutation request from the server stack
set_mutation_server_timeout(mutation_server_timeout)[source]

Set timeout for receiving mutation request from the server stack.

Parameters:mutation_server_timeout – timeout for receiving mutation request from the server stack
set_post_fuzz_delay(post_fuzz_delay)[source]

Set how long to wait before moving to the next mutation after each test.

Parameters:post_fuzz_delay – time to wait (in seconds)
signal_mutated()[source]

Called once a mutation was provided to the server stack.

trigger()[source]

Trigger the target (e.g. the victim application) to start communication with the fuzzer.

kitty.targets.empty module
class kitty.targets.empty.EmptyTarget(name, logger=None)[source]

Bases: kitty.targets.server.ServerTarget

Target that does nothing. Weird, but sometimes it is required.

__init__(name, logger=None)[source]
Parameters:
  • name – name of the target
  • logger – logger for this object (default: None)
kitty.targets.server module
class kitty.targets.server.ServerTarget(name, logger=None, expect_response=False)[source]

Bases: kitty.targets.base.BaseTarget

This class represents a target when fuzzing a server. Its main job, beside using the Controller and Monitors, is to send and receive data from/to the target.

__init__(name, logger=None, expect_response=False)[source]
Parameters:
  • name – name of the target
  • logger – logger for this object (default: None)
  • expect_response – should wait for response from the victim (default: False)
post_test(test_num)[source]

Called after each test

Parameters:test_num – the test number
pre_test(test_num)[source]

Called before each test

Parameters:test_num – the test number
set_expect_response(expect_response)[source]
Parameters:expect_response – should wait for response from the victim (default: False)
transmit(payload)[source]

Transmit single payload, and receive response, if expected. The actual implementation of the send/receive should be in _send_to_target and _receive_from_target.

Parameters:payload (str) – payload to send
Return type:str
Returns:the response (if received)

Controllers

The controller is in charge of preparing the victim for the test. It should make sure that the victim is in an appropriate state before the target initiates the transfer session. Sometimes it means doing nothing, other times it means starting or reseting a VM, killing a process or performing a hard reset to the victim hardware. Since the controller is reponsible for the state of the victim, it is expected to perform a basic monitoring as well, and report whether the victim is ready for the next test.

Core Classes

BaseController

kitty.controllers.base.BaseController (kitty.core.kitty_object.KittyObject)

setup(self)

Called at the beginning of the fuzzing session, override with victim setup

teardown(self)

Called at the end of the fuzzing session, override with victim teardown

pre_test(self, test_number)

Called before a test is started Call super if overriden

post_test(self)

Called when test is done Call super if overriden

get_report(self)

Returns a report about the victim since last call to pre_test

ClientController

kitty.controllers.client.ClientController (kitty.controllers.base.BaseController)

ClientController is a controller for victim in client mode

trigger(self)

Trigger a data exchange from the tested client

EmptyController

kitty.controllers.empty.EmptyController (kitty.controllers.client.ClientController) EmptyController does nothing, implements both client and server controller API

Implementation Classes

Implemented controllers for different victim types.

ClientGDBController

kitty.controllers.client_gdb.ClientGDBController (kitty.controllers.client.ClientController)

ClientGDBController runs a client target in gdb to allow further monitoring and crash detection.

Requires pygdb

__init__(self, name, gdb_path, process_path, process_args, max_run_time, logger=None)

ClientUSBController

kitty.controllers.client_usb.ClientUSBController (kitty.controllers.client.ClientController)

ClientUSBController is a controller that triggers USB device connection by switching its Vbus signal. It is done by controlling EL7156 from arduino. The arduino is loaded with firmata, which allows remote control over serial from the PC, using pyduino.

__init__(self, name, controller_port, connect_delay, logger=None)

  • controller_port: tty port of the controller
  • connect_delay: delay between disconnecting and reconnecting the USB, in seconds

ClientProcessController

kitty.controllers.client_process.ClientProcessController (kitty.controllers.client.ClientController)

Starts the client with subprocess.Popen, collects stdout and stderr.

__init__(self, name, process_path, process_args, logger=None)

TcpSystemController

kitty.controllers.tcp_system.TcpSystemController (kitty.controllers.base.BaseController)

this controller controls a process on a remote machine by sending tcp commands over the network to a local agent on the remote machine to execute using popen

__init__(self, name, logger, proc_name, host, port)

  • proc_name: name of victim process
  • host: hostname of agent
  • port: port of agent

Indices and tables