Fuzzing Network Protocols

BooFuzz is a Python module for fuzzing network protocols. It is successor to the Sulley fuzzing framework. It supports targetting Linux and Windows executables. The Python module can be installed with:

pip install boofuzz

Vulnerable Application

As a test, we will be using the vulnerable application listed below. It can be compiled with:

gcc vulnerable_server.c -o vulnerable_server -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -m32cc vulnerable_server.c -o vulnerable_server -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -m32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h> 

// Compile With gcc vulnerable_server.c -o vulnerable_server -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -m32

void processDataBordergate(char* input) {
    char buffer[20];
    strcpy(buffer, input);
}
void processDataLaunch(char* input) {
    char buffer[400];
    strcpy(buffer, input);
}

int main(int argc, char *argv[])
{
    int listenfd = 0, connfd = 0;
    struct sockaddr_in serv_addr; 

    char sendBuff[1025];
    time_t ticks; 
    char client_message[3000];

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&serv_addr, '0', sizeof(serv_addr));
    memset(sendBuff, '0', sizeof(sendBuff)); 
    int port = 8080;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(port); 

    bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 

    listen(listenfd, 10); 
    puts("Server ready...");

    while(1)
    {
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL); 
        write(connfd , "[i] Welcome! Type HELP for commands.\n" , 37);

        while( (recv(connfd , client_message , 2000 , 0)) > 0 )
            {
                if (strncmp(client_message, "HELP", 4) == 0){
                        write(connfd, "[i] Available Commands:\nHELP\nBORDERGATE\nLAUNCH\n", 39); 
                }
                
                else if (strncmp(client_message, "BORDERGATE", 10) == 0){
                        puts(client_message);
                        processDataBordergate(client_message);
                }

                else if (strncmp(client_message, "LAUNCH", 6) == 0){
                        puts(client_message);
                        processDataLaunch(client_message);
                } 
                else{
                    write(connfd, "[i] Available Commands:\nHELP\nBORDERGATE\nLAUNCH\n\n", 39); 
                }
            }
    }
}

Fuzzing ASCII Protocols

The definition of protocol blocks is rather trivial;

s_initialize("BORDERGATECMD")           # Initialize a new block
s_static("BORDERGATE")                  # Set a static text string
s_delim(" ")                            # Create a static delimiter
s_string(" ")                           # An s_string field will be filled with fuzzing characters
s_static("\r\n")                        # Add a static line feed
session.connect(s_get("BORDERGATECMD")) # Finally ensure the block will be used during the run

The below script can be used to fuzz our simple ASCII protocol. Procmon is used to monitor the target process and ensure it’s restarted if it crashes.

#!/usr/bin/env python
from boofuzz import *
import sys

def fuzz_target(ip,port):
    session = Session(
    target=Target(
        connection=SocketConnection(ip, port, proto='tcp')))

    target_ip = "127.0.0.1"
    start_cmd = ["./vulnerable_server"]

    procmon = ProcessMonitor(target_ip, 26002)
    procmon.set_options(start_commands=[start_cmd])

    session = Session(
        target=Target(
            connection=TCPSocketConnection(target_ip, port),
            monitors=[procmon],
        ),
        sleep_time=1,
    )

    s_initialize("HELPCMD")
    s_static("HELP ")
    s_delim(" ")
    s_string(" ")
    s_static("\r\n")

    s_initialize("LAUNCHCMD")
    s_static("LAUNCH")
    s_delim(" ")
    s_string(" ")
    s_static("\r\n")

    s_initialize("BORDERGATECMD")
    s_static("BORDERGATE")
    s_string(" ")
    s_static("\r\n")

    session.connect(s_get("BORDERGATECMD"))
    session.connect(s_get("HELPCMD"))
    session.connect(s_get("LAUNCHCMD"))

    session.fuzz()


if __name__ == "__main__":

    if len(sys.argv) == 3:
        ip = sys.argv[1]
        port = int(sys.argv[2])
        fuzz_target(ip,port)
    else:
        print("fuzzer.py 127.0.0.1 8080")
        exit(1)

To ensure the application is restarted when it crashes, run process_monitor_unix.py, in the background then start the fuzzing code:

python3 process_monitor_unix.py
[04:58.40] Process Monitor PED-RPC server initialized:
[04:58.40]       listening on:  0.0.0.0:26002
[04:58.40]       crash file:    /home/kali/BOO/boofuzz-crash-bin
[04:58.40]       # records:     0
[04:58.40]       proc name:     None
[04:58.40]       log level:     1
[04:58.40] awaiting requests...

The main fuzzer code can then be invoked with:

python3 fuzzer.py 127.0.0.1 8080

Detected crashes will be saved to a database file. The results from these files can be investigated using the boo open command:

┌──(kali㉿kali)-[~/BOO/boofuzz-results]
└─$ /home/kali/.local/bin/boo open run-2022-09-05T19-58-50.db 
Serving web page at http://localhost:26000. Hit Ctrl+C to quit.

The bytes that triggered the crash can then be investigated using a web browser:

Binary Protocols

BooFuzz can also be used to target binary protocols, using the s_bytes method:

#!/usr/bin/env python
from boofuzz import *
import sys

def fuzz_target(ip,port):
    session = Session(
    target=Target(
        connection=SocketConnection(ip, port, proto='udp')))    

    s_initialize("write_request")
    s_bytes(b'\x77\x77')              # OPCODE
    s_bytes(b'\x00\x00\x00\x00')
    s_bytes(b'\x00\x00\x00\x00')
    s_bytes(b'\x00\x00\x00\x00')      

    session.connect(s_get("write_request"))

    session.fuzz()


if __name__ == "__main__":

    if len(sys.argv) == 3:
        ip = sys.argv[1]
        port = int(sys.argv[2])
        fuzz_target(ip,port)
    else:
        print("fuzzer.py 127.0.0.1 6666")
        exit(1)

Closing Thoughts

BooFuzz can be highly effective for targetting Linux binaries that implement network protocols. Unfortunatly, at the moment process monitoring for Windows executables does not appear to work correctly.