Author Topic: [Python] IRC Fuzzer - IRCdFuzz.py  (Read 2568 times)

0 Members and 1 Guest are viewing this topic.

Offline Thor

  • Serf
  • *
  • Posts: 29
  • Cookies: 15
  • whoami?
    • View Profile
[Python] IRC Fuzzer - IRCdFuzz.py
« on: August 09, 2013, 05:44:23 am »
A few days ago bluechill was talking in irc about an IRCd he is developing, at the time I mentioned he should use an IRC fuzzer, well today I was bored so I decided to make one myself.

It's very primitive, however it was only made due to boredom, so what can you expect :P

Before I go any further, for those of you who don't know, a fuzzer just sends unexpected data to an application with the intention of causing crashes and memory leaks. For more information see Wikipedia and OWASP

The script loads tests to be performed from a file called "tests.txt". This is all the commands you want fuzzed. <FUZZ> will be replaced by fuzzing payloads, such as random characters and very long strings. An example tests.txt is shown below:
Code: [Select]
/JOIN #test
<FUZZ>
/NICK <FUZZ>
/PART #test
/JOIN <FUZZ>
/MSG NickServ <FUZZ>

Notice that you can execute commands without <FUZZ> in them, for example to join a channel. In this example, once we have joined the channel #test we fuzz the messages sent to the channel then leave it.

The payloads used by the fuzzer can be edited in the source code, the ones included are just there as an example, mostly taken from https://www.owasp.org/index.php/OWASP_Testing_Guide_Appendix_C:_Fuzz_Vectors

All networking is logged in "log.txt", the log includes connections made, data sent and any errors. If the IRCd crashes it's easy to find what crashed it this way.

Here's a screenshot:


And finally, here's the code:
Code: (Python) [Select]
#!/usr/bin/python

#    This is a basic IRCd Fuzzer created out of boredom
#    Go to line 100 to edit the payloads used
#    All tests to be performed are loaded from tests.txt
#    All logs are written to log.txt
#   Created by Thor from EvilZone.org

import os
import errno
import re
import time
from optparse import OptionParser
from socket import *

# Deals with all the networking in the application,
# connecting to hosts, sending data etc
class Networking():
    def __init__(self):
        self.host = "127.0.0.1"
        self.port = "6667"
        self.s = socket(AF_INET,SOCK_STREAM)
        self.logger = Logger()
        self.logger.open()
   
    # Set the current host
    def setHost(self,host):
        self.host = host
       
    # Set the current port after chacking the value is valid
    def setPort(self,port):
        if port < 1 or port > 65535:
            print("invalid port. Value must be > 0 and <= 65535!")
            return 0
        if port < 1024:
            print("<WARNING> Targeting ports 1-1023 can be dangerous!")
        self.port = port
        return 1
   
    # Connect to the host on the specified port
    def connect(self):
        try:
            self.s.connect((self.host, self.port))
            self.logger.write("Connected to %s on %d" % (self.host,self.port))
        except error, e:
            errorcode = e[0]
            if errorcode == errno.ECONNREFUSED:
                print("Connection Refused!")
            elif errorcode == errno.ETIMEDOUT:
                print("Connection Timed Out!")
            else:
                print("IOError")
            return 0
        return 1
       
    # Authenticate with IRC server
    def authenticate(self):
        nick = "fuzzer"
        hostname = "fuzzer"
        username = "fuzzer"
        realname = "fuzzer"
        self.sendData("NICK %s\r\n" % nick)
        self.sendData("USER %s %s evilzone :%s\r\n" % (username, hostname, realname))
       
    # Send data to the host
    def sendData(self, data):
        # Ensure we don't flood the server
        time.sleep(3)
        try:
            self.s.send(data)
            self.logger.write("Sent: %s" % data)
        except error, e:
            self.logger.writeError(e)
            errorcode=e[0]
            if errorcode==errno.ECONNREFUSED:
                print("Connection Refused!")
            elif errorcode==errno.ETIMEDOUT:
                print("Connection Timed Out!")
            else:
                print("An unknown error occured")
                #print(e)
               

# Handles all fuzzing. Loads tests, generates payloads,
# combines them and formats them for the irc daemon.
class Fuzzer():
    def __init__(self, host, port):
        self.testFile = "tests.txt"
        self.fuzzPayloads = []
        self.tests = []
        self.testCount = 0
        self.fuzzCount = 0
        self.host = host
        self.port = port
        self.sas = 1
        self.commands = ["JOIN", "PART", "LEAVE", "QUIT", "WHOIS", "WHOWAS", "WHO", "NAMES",\
                        "MSG", "QUERY", "NICK", "ME", "AWAY", "LIST", "INVITE", "IGNORE",\
                        "KICK", "MODE"]
                       
    # Generate fuzzing payloads and add them to fuzzPayloads list
    # Feel free to edit these
    # More info can be found here: https://www.owasp.org/index.php/OWASP_Testing_Guide_Appendix_C:_Fuzz_Vectors       
    def generateFuzzPayloads(self):
        self.fuzzPayloads.append("ABCDE")
        self.fuzzPayloads.append("A"*513)
        self.fuzzPayloads.append("A"*1025)
        self.fuzzPayloads.append("http://google.com")
        self.fuzzPayloads.append("http://g" + "o"*512 + ".com/")
        self.fuzzPayloads.append("%#0123456x%08x%x%s%p%d%n%o%u%c%h%l%q%j%z%Z%t%i%e%g%f%a%C%S%08x%%")
        self.fuzzPayloads.append("%x"*1025)
        self.fuzzPayloads.append("-1")
        self.fuzzPayloads.append("0")
        self.fuzzPayloads.append(os.urandom(513))
        self.fuzzPayloads.append(os.urandom(1025))
        self.fuzzPayloads.append("\x00\x00\x00\x00")   
   
    # Return a test to be performed eg "/msg <FUZZ>"
    def getTest(self):
        if self.testCount < len(self.tests):
            self.testCount+=1
            return self.tests[self.testCount-1]
        else:
            return 0
       
    # Set the current file the tests will be loaded from   
    def setTestFile(self,testFile):
        if os.path.exists(testFile):
            self.testFile = testFile
        else:
            print("Invalid file")
   
    # Load tests to be performed from testFile       
    def loadTests(self):
        for line in open(self.testFile,"r").read().split("\n"):
            self.tests.append(line)
   

    # Return a fuzzing payload
    def getFuzzPayload(self):
        if self.fuzzCount < len(self.fuzzPayloads):
            self.fuzzCount+=1
            return self.fuzzPayloads[self.fuzzCount-1]
        else:
            self.fuzzCount = 0
            return 0       

    # Connects to host, generates fuzzing payloads, loads tests to be
    # performed and then begins fuzzing.
    def fuzz(self):
        # Flag to display if fuzzer has joined a channel
        joinFlag = 0
        currentChannel = ""
       
        # Configure networking
        net = Networking()
        net.setHost(self.host)
        net.setPort(self.port)
       
        # If connection successful, run every test individually, only
        # moving to another test after every fuzz payload has been used
        if net.connect():
            net.authenticate()
            self.loadTests()
            self.generateFuzzPayloads()
            test = self.getTest()
            while test != 0:
                # Remove "/" at start as "/" is only used by irc clients
                if test.startswith("/"):
                    test = test[1:]
                   
                # Replace <FUZZ> with fuzz payload
                if test.find("<FUZZ>") != -1:
                    payload = self.getFuzzPayload()
                    while payload != 0:
                        fuzzedTest = re.sub("<FUZZ>", payload, test)
                        if joinFlag == 0:
                            net.sendData(fuzzedTest+"\r\n")
                           
                        # If we're in a channel, check if we're sending a command,
                        # or are we sending a message to the channel
                        elif joinFlag == 1:
                            for x in self.commands:
                                if fuzzedTest.startswith(x):
                                    net.sendData(fuzzedTest+"\r\n")
                                    break
                            # No command found, assume data is a message for joined channel
                            else:
                                net.sendData("PRIVMSG %s %s\r\n" % (currentChannel,fuzzedTest))
                       
                       
                        payload = self.getFuzzPayload()
                       
                # Else no fuzz data required so send raw request
                else:
                    # Joining a channel so set joinFlag
                    if test.startswith("JOIN"):
                        joinFlag = 1
                        channel = re.split(" ", test)
                        currentChannel = channel[1]
                    # Leaving a channel so reset joinFlag
                    elif test.startswith("PART") or test.startswith("LEAVE"):
                        joinFlag = 0
                    net.sendData(test+"\r\n")
                test = self.getTest()
       
# Logs all connections and data sent as well as errors.
class Logger():
    def __init__(self):
        self.logFile = None
    def open(self, fileName="log.txt"):
        try:
            self.logFile = open(fileName, "w")
        except:
            print("An error occured when opening the log file!")
            exit()
    def write(self, text):
            self.logFile.write("%s\n" % text)
    def writeError(self, error):
        self.logFile.write("\n\n~~ERROR~~  >>  %s\n\n" % error)
    def close(self):
        self.logFile.close()
       
       
while __name__ == "__main__":
    print '''
     _ _____  _____      _ _______ _     _  _______ _______         
    | |  __ \/  ___|    | |   ____| |   | ||____  /|____  /               
    | | |__| | /        | |  |____| |   | |    / /     / /               
    | |  ___/ |     ____| |   ____| |   | |   / /     / /             
    | | |\ \  |    /  __  |  |    | |   | |  / /     / /
    | | | \ \  \___| |__| |  |    | |___| | / /___  / /____     
    |_|_|  \_\_____|______|__|    |_______|/______|/_______|   

                   
    '''
   
   
                                                   
   
   
    parser = OptionParser()
    parser.add_option("-H", "--host", type="string", dest="host", default="127.0.0.1", help="Host the irc daemon is running on (default 127.0.0.1)")
    parser.add_option("-p", "--port", type="int", dest="port", default="6667", help="Port the irc daemon is listening on (default 6667)")
    (options, args) = parser.parse_args()
   

    fuzz = Fuzzer(options.host, options.port)
    print("Attempting to fuzz %s on port %d" % (options.host, options.port))
    print("Logs are being written to logs.txt")
    print("This may take a while..")
    fuzz.fuzz()
    print("Fuzzing complete!")
   
    quit()

TO DO:
    - Add SSL support
    - Respond to PINGs to keep session going (shouldn't be a problem if you control the IRCd)
    - Possibly allow users to edit sleep time between requests and filenames using cli arguments
« Last Edit: August 09, 2013, 06:26:08 am by RedBullAddicted »
They who can give up essential liberty to obtain a little temporary safety, deserve neither liberty nor safety.

Offline proxx

  • Avatarception
  • Global Moderator
  • Titan
  • *
  • Posts: 2803
  • Cookies: 256
  • ФФФ
    • View Profile
Re: [Python] IRC Fuzzer - IRCdFuzz.py
« Reply #1 on: August 09, 2013, 06:15:18 am »
Great post , code looks good.
Thanks :) +1
Wtf where you thinking with that signature? - Phage.
This was another little experiment *evillaughter - Proxx.
Evilception... - Phage

Offline RedBullAddicted

  • Moderator
  • Sir
  • *
  • Posts: 519
  • Cookies: 189
    • View Profile
Re: [Python] IRC Fuzzer - IRCdFuzz.py
« Reply #2 on: August 09, 2013, 06:25:53 am »
Great post , code looks good.

Indeed :) Don't have much time yet and only skimmed through the code. Will have a closer look later.

PS. I added python to your code tag to make it look a bit better.

Cheers,
RBA
Deep into that darkness peering, long I stood there, wondering, fearing, doubting, dreaming dreams no mortal ever dared to dream before. - Edgar Allan Poe

xC

  • Guest
Re: [Python] IRC Fuzzer - IRCdFuzz.py
« Reply #3 on: August 09, 2013, 07:39:07 pm »
I guess I don't see the point of making this other than being bored. You can execute such from the IRC clients themselves without the use of said "fuzzer". +1 for effort.

Offline ande

  • Owner
  • Titan
  • *
  • Posts: 2664
  • Cookies: 256
    • View Profile
Re: [Python] IRC Fuzzer - IRCdFuzz.py
« Reply #4 on: August 09, 2013, 08:26:00 pm »
I guess I don't see the point of making this other than being bored. You can execute such from the IRC clients themselves without the use of said "fuzzer". +1 for effort.

Automation, special characters and buffer limitations....


@OP:

Might I suggest you do argument count fuzzing as well. No arguments, the correct amount of arguments, hell of a lot of arguments and fuzz the arguments themselves with a mix of valid and invalid arguments etc. The idea here is to get past some input checks to fuzz the most amount of code.
if($statement) { unless(!$statement) { // Very sure } }
https://evilzone.org/?hack=true

Offline Thor

  • Serf
  • *
  • Posts: 29
  • Cookies: 15
  • whoami?
    • View Profile
Re: [Python] IRC Fuzzer - IRCdFuzz.py
« Reply #5 on: August 09, 2013, 09:05:28 pm »
Automation, special characters and buffer limitations....


@OP:

Might I suggest you do argument count fuzzing as well. No arguments, the correct amount of arguments, hell of a lot of arguments and fuzz the arguments themselves with a mix of valid and invalid arguments etc. The idea here is to get past some input checks to fuzz the most amount of code.

Thanks for the feedback.

What you're talking about is already possible with this, however the user would have to enter each argument individually in the tests.txt file which isn't ideal, and I agree it should be automated or else it defeats the purpose of using this. I'll get on it eventually :P
They who can give up essential liberty to obtain a little temporary safety, deserve neither liberty nor safety.