---
title: "Bug Hunting Using Fuzzing and Static Analysis"
author: ["Z. Cliffe Schreuders"]
license: "CC BY-SA 4.0"
description: "Learn advanced bug hunting techniques including fuzzing and static analysis to identify software vulnerabilities. This lab covers manual code auditing, fuzzing with Spike, Metasploit FTP fuzzing, and CTF challenges."
overview: |
  Identifying and fixing software vulnerabilities is of paramount importance. This lab introduces two techniques for bug hunting: Fuzzing and Static Analysis. These methods are essential for uncovering hidden security flaws in software, which can be exploited for malicious purposes if left unaddressed. Fuzzing involves sending unexpected and often malformed data as input to a program, searching for weaknesses, while Static Analysis employs automated tools to analyze the code structure for potential issues. This lab provides a hands-on experience in finding and exploiting vulnerabilities.

  In this lab, you will learn how to manually audit C code to spot errors and use various fuzzing techniques to test network programs for security flaws. You will start by auditing and securing C code, identifying vulnerabilities, and fixing potential issues. You will then explore the world of fuzzing, where you will learn to use tools like Spike to send various inputs to network services to uncover potential vulnerabilities. The lab also guides you through Metasploit's FTP fuzzing module. Finally, you will apply your knowledge to CTF challenges, running and fuzzing network services to crash programs and uncover flags. By the end of this lab, you will have gained practical skills and knowledge in software security testing and vulnerability detection.
tags: ["fuzzing", "static-analysis", "bug-hunting", "vulnerability-detection", "spike", "metasploit"]
categories: ["software_security_exploitation"]
lab_sheet_url: "https://docs.google.com/document/d/1yuDcFkI2-KD4Xfti4PahE038o-6324LKSx075ZSsuKw/edit?usp=sharing"
type: ["ctf-lab", "lab-sheet"]
difficulty: "advanced"
cybok:
  - ka: "SS"
    topic: "Detection of Vulnerabilities"
    keywords: ["dynamic detection"]
---

## Introduction to bug hunting {#introduction-to-bug-hunting}

There are many kinds of programming and design mistakes that can lead to serious security flaws. Auditing software for flaws is an important component of secure software development and testing. After software has been created (and during the development process), programs can be tested *with or without access to the source code* to look for vulnerabilities. If a security researcher can find a flaw that the software authors do not know about, this can have serious security ramifications. If they then weaponise the attack, and create an exploit to attack, this is known as a ***zero-day exploit***, if there is currently no fix available to defend against the attack.

Zero-day exploits can be used for malicious purposes, such as building worms or in targeted attacks, and can be sold on the blackmarket. White hat hackers (the good guys, like you\!) will generally follow *responsible disclosure*, and notify the vendor and give them time to fix the problem before going public with the information, after a fair amount of time. Many of the larger vendors offer rewards for reporting vulnerabilities. 

> Warning:  Please follow responsible disclosure, when you discover zero-days. This will give you warm-fuzzy feelings, build your reputation within the security industry, and you may even make yourself some money in the process.


## The most popular approaches to bug hunting {#the-most-popular-approaches-to-bug-hunting}

When you have access to the source code you can perform:

* *manual code review*, to look for security mistakes  
* *static analysis*, which is when you use software to automatically analyse the code

If you do not have access to the source code, you can use:

* *reverse engineering* to transform the the compiled code (program) to get a (harder to read) version of the source code — binary static analysis is possible analysing a program without the original source code  
* *fuzzing* to do blackbox testing to find faults by feeding in variations of unexpected input into a program in an attempt to uncover unexpected behaviour

Even with access to source code, fuzz testing provides an insight into the behaviour of the program under unexpected inputs.

## Manual code review {#manual-code-review}

Being able to spot errors in code is an important skill for developers and security professionals. 

Spot the error:

```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXPASS 7
int main()
{
    char correct_pass[7] = "SecreT\0";
    char input_pass[7];
    do{
        printf("Enter password:\n");
        scanf("%s", &input_pass);
        // Uncomment the next line to see what is happening...
        // printf("You entered %s, pass is %s\n", input_pass, correct_pass);
        if(strcmp(input_pass, correct_pass) == 0) {
            printf("Access granted\n");
        } else {
            printf("Access denied\n");
        }
    } while(strcmp(input_pass, correct_pass) != 0);
}
```

> Question: Can you spot the error, without compiling and running the code?

==VM: On Desktop Debian Linux VM==

==action: Save, compile, debug, and test this program==.

==action: Try uncommenting the printf line, and see what happens when various inputs are used==.

> Question: Is this program secure? Why not?

==action: Run and manually exploit this code==.

> Tip: *If you cannot find a way of exploiting the vulnerability*, it is possible that the compiler you are using is generating instructions that allocate memory addresses in a different order. Try swapping the two variable declaration lines (as below), recompile, and run the new version.

```c
char correct_pass[7] = "SecreT\0";
char input_pass[7];
```

> Action: Update the above code to prevent the attack.

==action: Spot the error:==

```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXPASS 7
int main()
{
    char *correct_pass, *input_pass;
    correct_pass = malloc(sizeof(char)*MAXPASS);
    input_pass = malloc(sizeof(char)*MAXPASS);
    strcpy(correct_pass, "SecreT\0");

    printf("Please enter the password: ");
    scanf("%7s", input_pass);
    // uncomment to see what is happening...
    // printf("You entered %s, pass is %s\n", input_pass, correct_pass);
    if(strcmp(input_pass, correct_pass) == 0) {
        printf("Access granted\n");
    } else {
        printf("Access denied\n");
    }
}
```

> Question: Can you spot the error, without compiling and running the code?

> Hint: Consider defensive programming and memory allocation. Note that it might not be practical to exploit this.

> Warning: **SPOILER ALERT!**

> Note: This is more of a potential bug, rather than a glaring exploitable vulnerability. The code does not check that malloc successfully allocated memory, if you then read or write to memory that failed to be allocated, it will cause the program to behave incorrectly.

==action: Update the code to prevent the problem==.

## Fuzzing {#fuzzing}

Fuzzing involves sending deliberately unexpected data as input to a program in an attempt at discovering programming flaws. Fuzzers are often used by security researchers to find new vulnerabilities in software.

Three popular network fuzzers are Spike, Sulley, and Peach. If you are testing Web apps or GUI programs, other specialised fuzzers exist, such as the Web app fuzzers in Burp Suite and OWASP Zap.

Spike is a popular fuzzer written in C. It works by reading in a scripted template file describing the normal protocol (what is normally expected as input), and it automatically feeds in data based on its collection of strings that are likely to crash the program by deviating from what is expected. For example, it may try invalid characters/symbols, and longer than expected inputs. 

> Tip: Although popular, Spike’s lack of documentation can be frustrating.

==VM: On your Desktop Debian Linux VM==

==action: Run Ncat as a network service on port 4444==:

```bash
ncat -vlk -p 4444
```

==VM: On your Kali Linux VM==

==action: Start Wireshark, and monitor the traffic to view the stream of traffic==:

```bash
sudo wireshark
```

Next, let's see Spike in action…

==action: Open a new terminal tab (Shift+Ctrl+T)==.

==action: Create and edit a file named "my_first_spike.spk". Enter this text==:

```bash
s_string("Hello, world!");
```

This spike script simply sends the greeting string to the server.

To summarise:

* `s_string()` sends a string

Spike has a number of interpreters for interacting with programs. 

Since we are using Ncat to simulate a network service, we can use one of Spike’s most common interpreters, `generic_send_tcp`. 

The usage for `generic_send_tcp` is:

```
Usage: ./generic_send_tcp host port spike_script SKIPVAR SKIPSTR
```

==action: Run our spike script==:

```bash
generic_send_tcp ==edit:DESKTOP_IP_ADDRESS== 4444 my_first_spike.spk 0 0
```

==action: View the Ncat output on the Desktop VM to confirm that Spike sends out the expected text==.

==VM: On your Desktop Debian Linux VM==

==action: Use Wireshark, and follow the TCP stream, to view the behaviour of the fuzzer==.

==VM: On your Kali Linux VM==

Note that it tries a number of different network connection attempts, sending this same payload data (with different TCP options).

At this point the fuzzer is not really doing anything interesting, as we have not specified any values to fuzz.

==action: Edit "my_first_spike.spk". Change the line to==:

```bash
s_string_variable("Hello, world!");
```

==action: Try running this new version of the script==:

```bash
generic_send_tcp ==edit:DESKTOP_IP_ADDRESS== 4444 my_first_spike.spk 0 0
```

==action: Watch the Ncat output in the previous tab==.

As we can see the spike script now involves some visible “fuzziness”. The first time the text “Hello, world\!” is sent, and subsequently other malformed versions are sent.

In this case we are not going to find anything that breaks our Ncat server, so let's move on to fuzzing some vulnerable software.

==VM: On your Desktop Debian Linux VM==

==action: Create and edit a new file "another_vulnerable_service.c"==:

```bash
vi another_vulnerable_service.c
```

==action: Enter the below C code==:

```c
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
  // variables
  int sock_fd, connection_fd;
  const int buffer2_len = 50;
  const int buffer1_len = 1000;
  char buffer2[buffer2_len];
  char buffer1[buffer1_len];
  struct sockaddr_in addr;

  // socket details
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htons(INADDR_ANY);
  addr.sin_port = htons(5555);

  // create socket
  sock_fd = socket(AF_INET, SOCK_STREAM, 0);
   
  // set socket details
  bind(sock_fd, (struct sockaddr *) &addr, sizeof(addr));
   
  // start listening for connections
  listen(sock_fd, 10);
  // keep listening for new connections
  while(1) {
    // accept connection
    connection_fd = accept(sock_fd, NULL, NULL);
    // get name
    bzero(buffer1, buffer1_len);
    sprintf(buffer1, "What is your name?\r\n");
    write(connection_fd, buffer1, strlen(buffer1)+1);
      
    bzero(buffer1, buffer1_len);
    read(connection_fd, buffer1, buffer1_len);
    printf("Connection from %s\n", buffer1);
    // reply
    bzero(buffer2, buffer2_len);
    snprintf(buffer2, buffer2_len, "Hello, %s\r\n", buffer1);
    write(connection_fd, buffer2, strlen(buffer2)+1);
    // if name starts with Cliffe, ask for another string, and print back
    if(strncmp(buffer1, "Cliffe", 6) == 0 ||strncmp(buffer1, "Tom", 3) == 0) {
      printf("(Access granted)\n");
      bzero(buffer1, buffer1_len);
      sprintf(buffer1, "Access granted... Please enter a string:\n");
      write(connection_fd, buffer1, strlen(buffer1)+1);

      bzero(buffer1, buffer1_len);
      read(connection_fd, buffer1, buffer1_len);
      printf("Received string: %s\n", buffer1);

      strcpy(buffer2, buffer1);
      sprintf(buffer1, "You entered: %s\r\n", buffer2);
      write(connection_fd, buffer1, strlen(buffer1)+1);
    }

    close(connection_fd);
  }
}
```

==action: Compile this program to "another_vulnerable_service"==:

```bash
gcc -g -m32 -fno-stack-protector -z norelro another_vulnerable_service.c -o another_vulnerable_service
```

==action: Start our vulnerable service (listening on port 5555)==.

```bash
./another_vulnerable_service
```

==VM: On your Kali Linux VM==

Connecting to your vulnerable service:

==action: Open a new terminal tab (Ctrl-Shift-T)==.

```bash
nc -v ==edit:DESKTOP_IP_ADDRESS== 5555
```

==action: Type some text and press Enter, then some more text and Enter again==. This sends the text to the server, which responds with the output from your program.

==action: Identify the order that messages are send to and received from the vulnerable service==.

==action: Create and edit a file named "my_name_spike.spk". Enter this text==:

```bash
printf("Fuzzing...\n");
s_readline();
s_string_variable("==edit:Bob==");
s_string("\r\n");
spike_send();

s_readline();

s_string_variable("==edit:A string!==");
s_string("\r\n");
s_readline();
spike_send();
```

Note that:

* `printf()` writes to the local console  
* `s_readline()` reads and displays a line of response  
* `s_string_variable()` writes a string that is fuzzed  
* `spike_send()` sends what is in the buffer now  
* The best way to know what commands are available is to look at the source code of spike.h (if you like, you can [view the source code here](https://github.com/guilhermeferreira/spikepp/blob/master/SPIKE/include/spike.h))

==action: Try running the spike script to test the vulnerable program==:

```bash
generic_send_tcp ==edit:DESKTOP_IP_ADDRESS== 5555 my_name_spike.spk 0 0
``` 

The script will try to run through a large list of fuzzed inputs, in an attempt to crash the program. 

*If the fuzzer stops while the program is still running*, simply restart the fuzzer on the next variable. For example, if the fuzzer stops on:

```
Fuzzing Variable 0:==edit:661==
Fuzzing...
line read=What is your name?
Variablesize= 0
returning end of line\!
```

==action: Press Ctrl-C and continue on variable 0 iteration 662==:

```bash
generic_send_tcp ==edit:DESKTOP_IP_ADDRESS== 5555 my_name_spike.spk 0 ==edit:662==
``` 

You may find that the fuzzer does not find a way of crashing the program. 

==action: Edit the spike script, so that the fuzzer gets past the first condition within the vulnerable service code==. 

> Hint: edit the spike script so it sends a name that passes the string comparison.

==action: Try running the spike script again==:

```bash
generic_send_tcp ==edit:DESKTOP_IP_ADDRESS== 5555 my_name_spike.spk 0 0
``` 

If you have created an effective spike, the program will eventually crash after some fuzzing. When the program crashes, the message "tried to send to a closed socket\!" will appear in the terminal tab where Spike is running. Press Ctrl+C to stop fuzzing, and the number of attempts will be shown. This can be used to determine the string that was used to crash the program. 

==action: View the traffic in Wireshark, and determine the last input that the fuzzer sent==.

==VM: On your Desktop Debian Linux VM==

==action: Restart the service (in the previous tab)==:

```bash
./another_vulnerable_service
```

==VM: On your Kali Linux VM==

==action: Connect manually with Ncat==:

```bash
nc -v ==edit:DESKTOP_IP_ADDRESS== 5555
```

==action: Enter the input (possibly multiple lines) that the fuzzer used to crash the program==.

==action: Make sure you can reproduce the crash==.

==action: Save the crash inducing input (multiple lines) into a text file named "**fuzzed**"==.

==VM: On your Desktop Debian Linux VM==

==action: Run the vulnerable server in a debugger==:

```bash
gdb ./another_vulnerable_service
```

```bash
(gdb) run
```

==VM: On your Kali Linux VM==

==action: Feed in the fuzzed input==:

```bash
nc -v ==edit:DESKTOP_IP_ADDRESS== 5555 < fuzzed
```

==VM: On your Desktop Debian Linux VM==

==action: Back in the GDB terminal, look at the details in the debugger==:

```bash
(gdb) backtrace
```

==action: Display the value stored in registers==:

```bash
(gdb) layout split
```

```bash
(gdb) layout regs
```

> Question: Explain any interesting values and their significance. ==hint:Hint: look for the ASCII values of the input==.

==action: Resume the fuzzing at the point that it stopped for the crash==, by changing the last two arguments (previously you used “0 0”). The first number represents the fuzzing variable (such as string\_variable) to skip to, and the second number is the fuzzing iteration to skip to for that variable (for example, “AAAAAAA”).

In order to fuzz more complex programs, the spike script file needs to have added complexity to send certain parts of the input as expected, and focus on fuzzing the inputs that are most likely to trigger flaws:

* Altering input lengths and command options  
* Altering integers, to test for boundary conditions and outliers  
* Command injections, invalid characters and so on

Fuzzers are very good at finding shallow bugs. However, it can be quite hard for a fuzzer to find deep bugs (behind a number of conditional code statements), since the fuzzer needs to try to get good code coverage, to test the behaviour of all the possible control flows. However, complete coverage can be impractical or impossible for complex programs.

One of the most effective ways to fuzz a network program is to use a sniffer to record examples of normal traffic (and/or study protocol RFC specifications), and then create a fuzzer that follows the normal expected flow of communication, with fuzzed input for the situations that are most likely to be vulnerable.

> Action: Fix the security error(s) in another_vulnerable_service.

> Tip: Also, consider fixing the error handling: for example, gracefully exit with an error message if the port is already in use. And if you want more practice writing C (and with multithreading), update the program to accept multiple connections at once (search for example code online).

## Fuzzing FTP servers {#fuzzing-ftp-servers}

==VM: On your Windows VM==

==action: Log in as your randomised username with password "tiaspbiqe2r"==.

The programs you will be fuzzing first are vulnerable (and real) FTP servers “FreeFloat”, and “EasyFTP”.

==action: Open file explorer, and browse to "C:\\Users\\vagrant\\Downloads\\freefloatftpserver\\"==

One of these FTP servers may already be running. You may want to check by connecting manually via ncat.

> Tip: Note that **each time the fuzzer crashes the program** you will need to make note of the values that crashed the server then **restart it.**

==action: Make sure FreeFloat FTP server is running==.

==VM: On your Kali Linux VM==

==action: Run an FTP spike script: (where *Win_IP_address* it the IP of your Windows VM)==

```bash
generic_send_tcp ==edit:Win_IP_address== 21 /usr/share/spike/audits/FTPD/ftpd1.spk 0 0
``` 

> Note: Again, *if the fuzzer stops without crashing the FTP server program* try restarting at the same point, or over from the beginning.

> Question: Does it crash the program? What input crashed the program? 

> Hint: Using Wireshark may be the easiest way to identify the input.

==action: Have a look through the spike script file, and try to understand how it works==.

> Question: Does it crash the program? What input crashed the program?

Metasploit has an FTP fuzzing module, try using it:

==action: Restart the FTP server on your Windows VM, if it has crashed==.

```bash
msfconsole
```

```bash
msf > use auxiliary/fuzzers/ftp/ftp_pre_post
```

```bash
msf auxiliary(ftp_pre_post) > set RHOSTS ==edit:Win_IP_address==
```

```bash
msf auxiliary(ftp_pre_post) > exploit
```

> Question: Does it crash the program? What input crashed the program?

==action: Have a look at the Metasploit module, and try to understand how it works==.

```bash
less /usr/share/metasploit-framework/modules/auxiliary/fuzzers/ftp/ftp_pre_post.rb
```

[or **view the code online**](https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/fuzzers/ftp/ftp_pre_post.rb)

==action: **Repeat the above steps to fuzz the EasyFTP server.**== 

EasyFTP can be found in C:\\Users\\vagrant\\Downloads\\easyftp

==action: Make a note of your findings==.

## Fuzzing CTF challenges {#fuzzing-ctf-challenges}

==VM: On your Desktop Debian Linux VM==

==action: Browse the challenges directory==

```bash
ls ~/challenges
```

When you run each program it will give you instructions and hints on how to solve the challenge. 

> Action: Run each of the challenges (always after changing to the directory first):

```bash
cd ~/challenges/Ch_Fuzz_1
```

```bash
ncat -kl -p 3333 -e ./Ch_Fuzz_1
```

This runs the command as a network service, so you can connect from the Kali system over the network, and fuzz test the service. 

For example, from Kali you can connect to manually test it:

```bash
nc ==edit:DESKTOP_IP_ADDRESS== 3333
```

And use Spike to fuzz it.

Alternatively, you can run the command directly.

You can apply the techniques you have learned above to solve the challenge, to crash the program, which will then provide you with a flag.

> Flag: Find the flag hidden via each challenge program and submit them to Hacktivity!

## Conclusion {#conclusion}

At this point you have:

* Manually audited C code for harder-to-spot errors  
* Used various fuzzers to test network programs for serious security flaws

Well done\!

Next topic we will dive further into how these kinds of vulnerabilities can be exploited.


