def promptAndOutput(outputMessageNum, autogenerateAllClient=False): # How many of the messages to output to the .fuzzer if args.force or autogenerateAllClient: finalMessageNum = len(fuzzerData.messageCollection.messages) - 1 else: finalMessageNum = promptInt( "What is the last message number you want output?", defaultResponse=len(fuzzerData.messageCollection.messages) - 1) # Any messages previously marked for fuzzing, unmark first # Inefficient as can be, but who cares for message in fuzzerData.messageCollection.messages: if message.isFuzzed: message.isFuzzed = False for subcomponent in message.subcomponents: subcomponent.isFuzzed = False if not autogenerateAllClient: while True: tmp = promptString( "Which message numbers should be fuzzed? Valid: 0-%d" % (finalMessageNum), defaultResponse=str(outputMessageNum), validateFunc=validateNumberRange) if len(tmp) > 0: outputFilenameEnd = tmp for messageIndex in validateNumberRange(tmp, flattenList=True): fuzzerData.messageCollection.messages[ messageIndex].isFuzzed = True for subcomponent in fuzzerData.messageCollection.messages[ messageIndex].subcomponents: subcomponent.isFuzzed = True break else: outputFilenameEnd = str(outputMessageNum) fuzzerData.messageCollection.messages[outputMessageNum].isFuzzed = True for subcomponent in fuzzerData.messageCollection.messages[ outputMessageNum].subcomponents: subcomponent.isFuzzed = True outputFilePath = "{0}-{1}.fuzzer".format( os.path.splitext(inputFilePath)[0], outputFilenameEnd) actualPath = fuzzerData.writeToFile(outputFilePath, defaultComments=True, finalMessageNum=finalMessageNum) print(GREEN) print("Wrote .fuzzer file: {0}".format(actualPath)) print(CLEAR) if autogenerateAllClient: nextMessage = getNextMessage(outputMessageNum + 1, Message.Direction.Outbound) # Will return None when we're out of messages to auto-output if nextMessage: promptAndOutput(nextMessage, autogenerateAllClient=True) return finalMessageNum
def setMessagesToFuzzFromString(self, messagesToFuzzStr): self.messagesToFuzz = validateNumberRange(messagesToFuzzStr, flattenList=True)
sys.exit("Invalid test range given: %s" % args) else: # If they pass a non-int, allow this to bomb out return (int(strArgs), int(strArgs)) #---------------------------------------------------- #Populate global arguments from parseargs fuzzerFilePath = args.prepped_fuzz host = args.target_host #Assign Lower/Upper bounds on test cases as needed if args.range: (MIN_RUN_NUMBER, MAX_RUN_NUMBER) = getRunNumbersFromArgs(args.range) elif args.loop: SEED_LOOP = validateNumberRange(args.loop, True) #Check for dependency binaries if not os.path.exists(RADAMSA): sys.exit("Could not find radamsa in %s... did you build it?" % RADAMSA) #Logging options isReproduce = False logAll = False if args.quiet: isReproduce = True elif args.logAll: logAll = True outputDataFolderPath = os.path.join(
def readFromFD(self, fileDescriptor, quiet=False): messageNum = 0 # This is used to track multiline messages lastMessage = None # Build up comments in this string until we're ready to push them out to the dictionary # Basically, we build lines and lines of comments, then when a command is encountered, # push them into the dictionary using that command as a key # Thus, when we go to write them back out, we can print them all before a given key self._readComments = "" for line in fileDescriptor: # Record comments on read so we can play them back on write if applicable if line.startswith("#") or line == "\n": self._readComments += line # Skip all further processing for this line continue line = line.replace("\n", "") # Skip comments and whitespace if not line.startswith("#") and not line == "" and not line.isspace(): args = line.split(" ") # Populate FuzzerData obj with any settings we can parse out try: if args[0] == "processor_dir": self.processorDirectory = args[1] self._pushComments("processor_dir") elif args[0] == "failureThreshold": self.failureThreshold = int(args[1]) self._pushComments("failureThreshold") elif args[0] == "failureTimeout": self.failureTimeout = int(args[1]) self._pushComments("failureTimeout") elif args[0] == "proto": self.proto = args[1] self._pushComments("proto") elif args[0] == "port": self.port = int(args[1]) self._pushComments("port") elif args[0] == "sourcePort": self.sourcePort = int(args[1]) self._pushComments("sourcePort") elif args[0] == "sourceIP": self.sourceIP = args[1] self._pushComments("sourceIP") elif args[0] == "shouldPerformTestRun": # Use 0 or 1 for setting if args[1] == "0": self.shouldPerformTestRun = False elif args[1] == "1": self.shouldPerformTestRun = True else: raise RuntimeError("shouldPerformTestRun must be 0 or 1") self._pushComments("shouldPerformTestRun") elif args[0] == "receiveTimeout": self.receiveTimeout = float(args[1]) self._pushComments("receiveTimeout") elif args[0] == "messagesToFuzz": print("WARNING: It looks like you're using a legacy .fuzzer file with messagesToFuzz set. This is now deprecated, so please update to the new format") self.messagesToFuzz = validateNumberRange(args[1], flattenList=True) # Slight kludge: store comments above messagesToFuzz with the first message. *shrug* # Comment saving is best effort anyway, right? self._pushComments("message0") elif args[0] == "unfuzzedBytes": print("ERROR: It looks like you're using a legacy .fuzzer file with unfuzzedBytes set. This has been replaced by the new multi-line format. Please update your .fuzzer file.") sys.exit(-1) elif args[0] == "inbound" or args[0] == "outbound": message = Message() message.setFromSerialized(line) self.messageCollection.addMessage(message) # Legacy code to handle old messagesToFuzz format if messageNum in self.messagesToFuzz: message.isFuzzed = True if not quiet: print "\tMessage #{0}: {1} bytes {2}".format(messageNum, len(message.getOriginalMessage()), message.direction) self._pushComments("message{0}".format(messageNum)) messageNum += 1 lastMessage = message # "sub" means this is a subcomponent elif args[0] == "sub": if not 'message' in locals(): print "\tERROR: 'sub' line declared before any 'message' lines, throwing subcomponent out: {0}".format(line) else: message.appendFromSerialized(line) if not quiet: print "\t\tSubcomponent: {1} additional bytes".format(messageNum, len(message.subcomponents[-1].message)) elif line.lstrip()[0] == "'" and 'message' in locals(): # If the line begins with ' and a message line has been found, # assume that this is additional message data # (Different from a subcomponent because it can't have additional data # tacked on) message.appendFromSerialized(line.lstrip(), createNewSubcomponent=False) else: if not quiet: print "Unknown setting in .fuzzer file: {0}".format(args[0]) # Slap any messages between "message" and "sub", etc (ascii same way) above message # It's way too annoying to print these out properly, as they get # automagically outserialized by the Message object # Plus they may change... eh, forget it, user can fix up themselves if they want self._appendComments("message{0}".format(messageNum-1)) except Exception as e: print "Invalid line: {0}".format(line) raise e # Catch any comments below the last line self._pushComments("endcomments")
def __init__(self, args): self.args = args # Test number to start from, 0 default self.MIN_RUN_NUMBER = 0 # Test number to go to, -1 is unlimited self.MAX_RUN_NUMBER = -1 # For seed loop, finite range to repeat self.SEED_LOOP = [] # For dumpraw option, dump into log directory by default, else 'dumpraw' self.DUMPDIR = "" #Populate global arguments from parseargs self.fuzzerFilePath = args.prepped_fuzz self.host = args.target_host #Assign Lower/Upper bounds on test cases as needed if args.range: (self.MIN_RUN_NUMBER, self.MAX_RUN_NUMBER) = getRunNumbersFromArgs(args, args.range) elif args.loop: self.SEED_LOOP = validateNumberRange(args.loop, True) #Check for dependency binaries if not os.path.exists(RADAMSA): sys.exit("Could not find radamsa in %s... did you build it?" % RADAMSA) #Logging options self.isReproduce = False self.logAll = False if args.quiet: self.isReproduce = True elif args.logAll: self.logAll = True self.outputDataFolderPath = os.path.join( "%s_%s" % (os.path.splitext(self.fuzzerFilePath)[0], "logs"), datetime.datetime.now().strftime("%Y-%m-%d,%H%M%S")) self.fuzzerFolder = os.path.abspath( os.path.dirname(self.fuzzerFilePath)) ########## Declare variables for scoping, "None"s will be assigned below self.messageProcessor = None ###Here we read in the fuzzer file into a dictionary for easier variable propagation self.optionDict = {"unfuzzedBytes": {}, "message": []} self.fuzzerData = FuzzerData() print "Reading in fuzzer data from %s..." % (self.fuzzerFilePath) self.fuzzerData.readFromFile(self.fuzzerFilePath) #clumsden TODO - make this pretty, maybe add field to fuzzerData #clumsden - hacky way to start http fuzzer at a different seed if self.fuzzerData.port == 80 and self.fuzzerData.proto == "tcp": self.MIN_RUN_NUMBER = 58247530 if self.fuzzerData.proto == "udp": #clumsden, set udp fuzzer at different seed self.MIN_RUN_NUMBER = 13243441 #clumsden end ######## Processor Setup ################ # The processor just acts as a container # # class that will import custom versions # # messageProcessor/exceptionProessor/ # # monitor, if they are found in the # # process_dir specified in the .fuzzer # # file generated by fuzz_prep.py # ########################################## # Assign options to variables, error on anything that's missing/invalid self.processorDirectory = self.fuzzerData.processorDirectory if self.processorDirectory == "default": # Default to fuzzer file folder self.processorDirectory = self.fuzzerFolder else: # Make sure fuzzer file path is prepended self.processorDirectory = os.path.join(self.fuzzerFolder, self.processorDirectory) #Create class director, which import/overrides processors as appropriate self.procDirector = ProcDirector(self.processorDirectory) global global_monitor self.monitor = None ########## Launch child monitor thread ### monitor.task = spawned thread ### monitor.crashEvent = threading.Event() if wantGlobalMonitor == True and global_monitor == None: global_monitor = self.procDirector.startMonitor( self.host, self.fuzzerData.port) print global_monitor time.sleep( 10 ) #clumsden added so pid_watcher has time to connect to monitor #! make it so logging message does not appear if reproducing (i.e. -r x-y cmdline arg is set) self.logger = None if not self.isReproduce: print "Logging to %s" % (self.outputDataFolderPath) self.logger = Logger(self.outputDataFolderPath) if self.args.dumpraw: if not isReproduce: self.DUMPDIR = self.outputDataFolderPath else: self.DUMPDIR = "dumpraw" try: os.mkdir("dumpraw") except: print "Unable to create dumpraw dir" pass self.exceptionProcessor = self.procDirector.exceptionProcessor() self.messageProcessor = self.procDirector.messageProcessor() ########## Begin fuzzing self.i = self.MIN_RUN_NUMBER - 1 if self.fuzzerData.shouldPerformTestRun else self.MIN_RUN_NUMBER self.failureCount = 0 self.loop_len = len(self.SEED_LOOP) # if --loop