Automate Local Fuzzing To Find Bug in Vulnerable Linux Command Line using Python for Fun (day 74) (ง ͠° ͟ل͜ ͡°)ง
Disclaimer: This post only for education only ! not to cause any destruction on any living system. Be smart!
In the previous post about fuzzing we take a look at how we can conduct network fuzzing using boofuzz to find a bug that leads to us to get Remote Code Execution in one of the old software in windows XP (check the post) but that was just an introduction and we only scratch the surface on how we can utilize fuzzing.
So in this post, we are going to take a deeper look on what is actually fuzzing we will cover how many types of fuzzing out there, rule of thumbs on fuzzing and how we can create our own fuzzing to fit our objectives using python
so put on your black hoodie because we are going to hacker mode
This post is inspired by this paper: https://www.exploit-db.com/papers/12965
What is Fuzzing
the two highlighted passages already explain the aim and the process of fuzzing respectively.
Basically, We do fuzzing to find bugs although there are numerous way to find a bug in a system such as a taint analysis but fuzzing is the most favorable technique use by many security testers because it's fast and you don't have to really care about the detail
To find bugs in software you need to think outside of the box, meaning you must supply them with unexpected data that could possibly crash the program
Types of Fuzzing
In terms of execution, we can categorize fuzzing into two types:
|
|_______[ Dumb (like the name it's fuzz without any context or guideline but it is easy to write and easy to use) ]
|
|_______[ Smart (the opposite of dumb fuzz so it knows how to handle the target data for fuzzing and of course in this post, the fuzzer that we create will be based on smart fuzzer)]
In terms of deliverance, fuzzing can be done locally or remotely:
|
|_______[ Local (test the software that installed locally through the command line, manipulating file format, user interface input, etc) ]
|
|_______[ Remote (usually test the protocol of the service)]
Little Bit of Jargon
in the world of fuzzing you need to be familiar with terms of "Fuzzing Oracle", this is basically just the data that you used to test with and having a high-quality fuzzing oracle is essential to nail your fuzzing. if the name of fuzzing oracle is too fancy for you, you can consider it as a list test case
Fuzzing oracle could contain random data or not random data at all is up to you which proportion you want the most as long as it still provides reliable angle to fuzz and triggering a bug
In terms of software security you want your Fuzzing oracle to be able to cover this test:
- Buffer overflow
- Format String Exploit
- Integer Overflow
- Out of bounds breakage (command injection)
Rule of thumbs in Fuzzing
When you conducting fuzzing or creating your own fuzzer always remember that "There is no standard when conducting a fuzzing", you either do it or don't that what's really matter. It's going to include a lot... a lot ... a lot... of testing that could make you spend hours just modifying, compiling and running the same but slightly different code just to get a better result.
but of course, although there is no standard you want your fuzzer to have this quality:
- It must contain a robust fuzzing oracle (Test case)
- Able to handle specific data format to be delivered to the target
- Know how to communicate with the target
Get your Hands Dirty
For the demonstration, we will fuzz mbse-bbs program (download: https://www.exploit-db.com/exploits/3154) it is a command-line program that acts as a wrapper for useradd utility, according to the vulnerability database there is a local buffer overflow in its suid "mbuseradd" programNotes:
You should be aware that this is an old vulnerability it might not be relevant in today's attack but nevertheless what we want to achieve here is the concept of how security researchers can use fuzzing to find the vulnerability in the software.
Also if you follow along with the paper(https://www.exploit-db.com/papers/12965) that I just show you at the beginning of the post you notice that it used 32-bit Linux environment for delivering POC but in this post, I will be using 64 bit environment in Kali Linux OS.
It's up to you which one of the environment that you want to use I used 64-bit environment because I'm curious with what kinda results that I will get but the overall process is still the same for both of the architecture
Setting up the environment

after downloading the source code, extract the package in your current directory

then go to Unix directory and type "make" to build the executable. Once it is done you need to create a directory of "/opt/mbse/bin" in your machine (make sure you have a root privileges to this step)


after creating the directory you can type "make install" to let the binary store in the /opt/mbse/bin path


In the directory, you can see that mbuseradd is set with SUID this means that you can execute the program with the privilege of the user who created it, in this case it will be the root

To call the binary you need to provide a full path of the program like the above figure.
Time to nut up or shut up
Like every technical project the first step is to conduct information gathering, from the previous figure we can see that the program need 4 arguments in order to runAccording to the "Secure Programming with Static Analysis" book we are dealing with a "privilege programs" there are two ways to generally the programs interact with the user it's either through command line like we saw earlier or the environment. Since we have the original source code of the package we can do a simple a static analysis to find this out using grep

from the result, we can see that most of the input is tied with the command line which is represented with argv or args but take a look at the fifth line we can see that the program did take a value from the environment using getenv() function and the environment path that they use is "MBSE_ROOT"
from this information, we have enough detail to create our own fuzzing tools
the following code is created using python script
from subprocess import Popen,PIPE
import os
#just a global variable that will be used to as the input for all of the 4 parameter needed by the program
GID = "1"
NAME = "mbsefuzz"
COMMENT = "fuzzing"
USERSDIR = "/tmp"
#mbuseradd [gid] [name] [comment] [usersdir]
bin_location = "/opt/mbse/bin/mbuseradd"
#test case for the program to fuzz the parameter
fuzz_oracle = [
#buffer overflow testing
"A" * 512,"A" * 1024,"A" * 2048,"A" * 4096,"A" * 8012,
#format string exploit testing
"%n%n%n%n","%5$n","%1024$p","%.1024d",
#integer overflow testing
"-1,32767","65535","-2416647762","0xfffffff",
#command injection
"a|id > /tmp/FZ|b","a`id > /tmp/FZ`b","a'id > /tmp/FZ'b","a;id > /tmp/FZ;b","a&&id > /tmp/FZ&&b"
]
#for handling the environment settings since mbuseradd also take input from environment variable
def set_env(data):
print "--------------------------------------------"
os.environ['MBSE_ROOT'] = data
print "environment: " + str(data)
#execute the fuzzing based on the parameter passed in the function
def fuzz(bin_path,gid,name,comment,usersdir):
shell_command = ' '.join([bin_path, gid, name, comment, usersdir])
proc = Popen(shell_command, shell=True, stdout=PIPE, stderr=PIPE)
out,err = proc.communicate()
print shell_command
print "error: " + err
print "\n"
def main():
#try to fuzz every parameters
print "#############################fuzz parameter"
for x in range(len(fuzz_oracle)):
fuzz(bin_location,fuzz_oracle[x],NAME,COMMENT,USERSDIR)
for x in range(len(fuzz_oracle)):
fuzz(bin_location,GID,fuzz_oracle[x],COMMENT,USERSDIR)
for x in range(len(fuzz_oracle)):
fuzz(bin_location,GID,NAME,fuzz_oracle[x],USERSDIR)
for x in range(len(fuzz_oracle)):
fuzz(bin_location,GID,NAME,COMMENT,fuzz_oracle[x])
#try to fuzz the environment
print "#############################fuzz environment path"
for x in range(len(fuzz_oracle)):
set_env(fuzz_oracle[x])
fuzz(bin_location,GID,NAME,COMMENT,USERSDIR)
main()
You can use the above source code or you can make your own python script
if you execute the script it will overflow with many results but with a quick skimming through the result, we can see that the parameter did not contain any buffer overflow



but once we start fuzzing the environment by inputting a large chunk of character it shows segmentation fault in the error status (Gotcha !!)

we can verify this finding by manually set the environment and run the program in gdb as the root user



there you go! when we run the program in gdb we can see it hit segfault and the register is overwritten without our value but notice that we weren't able to overwrite the RIP register at this point I try to adjust the length of the payload but the result always the same it seems that we only able to overwrite the instruction pointer if it's in 32-bit binary. But all it matters that we were able to found the crash using our own fuzzing tools
That's all folks :)
If you have any thoughts or suggestion that could be used to make this post better, Leave a comment below :)
Hope you enjoy this post :)
Comments
Post a Comment