def change_bytes(self, original, fl): if len(self.currentTaintMap) == 0: return original cmp = self.getOffset(fl, original) if len(cmp) == 0: gautils.debug_print( "[-] Fail to find offset (it could be because of conflict)") return original cmp = cmp[0] gautils.debug_print("[*] Mutation will be on 0x%x" % cmp.offsetsInInput[0]) return self.change_bytes_from_cmp(original, fl, cmp)
def check_mutation_conflict_from_register_size(self, offset, size, file): ''' check if a new mutation will override an old one with the size of the register ''' conflictList = [] for mu in self.mutationHistory[file]: gautils.debug_print("[*] Checking 0x%x to 0x%x with 0x%x to 0x%x" % (offset, offset + size, mu, mu + self.mutationHistory[file][mu][0].cmpSize)) baseOffsetInMutation = offset >= mu and offset <= ( mu + self.mutationHistory[file][mu][0].cmpSize) endOffsetInMutation = offset + size >= mu and offset + size <= ( mu + self.mutationHistory[file][mu][0].cmpSize) if baseOffsetInMutation or endOffsetInMutation: conflictList.append(self.mutationHistory[file][mu][0]) return conflictList
def write_child_input_update(self, name, inputBuffer): # compute its hash inputHash = hashlib.md5(inputBuffer).hexdigest() # check if the child input already exist if inputHash not in self.childMap.values(): # override the file path = os.path.join(config.INPUTD, name) gautils.writeFile(path, inputBuffer) # change its hash in history self.childMap[name] = inputHash return True else: gautils.debug_print("[-] Child input %s already exist" % (inputHash)) return False
def detectPatterns(cmpList, fpath): # test each patterns for each index ? # or test each index of each patterns ???? # currently I choose each index of each patterns because of end offsets patternFound = 0 for patternDef in patternDefinition: for pattern in patternDef.patternList: index = 0 # loop through each offset of cmp for a pattern while index != len(cmpList): endOffset = pattern.executeFromCmpObjects(index, cmpList) # check if a pattern is found if endOffset != -1: gau.debug_print( "\033[0;32m[+] Found %s (%s) from %d to %d" % (pattern.name, patternDef.name, (index),(endOffset)) + " !\033[0m" ) # execute the effect of a found pattern cmpList = patternDef.effect(pattern.name, cmpList, index, endOffset, fpath) # jump to the end of the pattern index = endOffset+1 patternFound += 1 else: index += 1 if patternFound > 0: gau.log_print("\033[0;32m[+] %d pattern found\033[0m" % (patternFound)) return cmpList
def dry_run(): ''' this function executes the initial test set to determine error handling BBs in the SUT. Such BBs are given zero weights during actual fuzzing.''' gau.log_print("[*] Starting dry run") tempbad = [] dfiles = os.listdir(config.INITIALD) if len(dfiles) < 3: gau.die("[-] Not sufficient initial files") for fl in dfiles: tfl = os.path.join(config.INITIALD, fl) try: f = open(tfl, 'r') f.close() except: gau.die("[-] Can not open our own input %s !" % (tfl)) (bbs, retc) = execute(tfl) if retc < 0: print "Signal: %d" % (retc,) gau.die("[-] Looks like we already got a crash !") config.GOODBB |= set(bbs.keys()) gau.log_print("[*] Finished good inputs (%d BB)" % (len(config.GOODBB),)) # now lets run SUT of probably invalid files. For that we need to create them first. gau.log_print("[*] Starting bad inputs") lp = 0 badbb = set() while lp < 2: try: shutil.rmtree(config.INPUTD) except OSError: pass os.mkdir(config.INPUTD) gau.create_files_dry(30) dfiles = os.listdir(config.INPUTD) for fl in dfiles: tfl = os.path.join(config.INPUTD, fl) (bbs, retc) = execute(tfl) if retc < 0: gau.log_print( "Signal: %d" % (retc,)) gau.die("[-] Looks like we already got a crash !") tempbad.append(set(bbs.keys()) - config.GOODBB) tempcomn = set(tempbad[0]) for di in tempbad: tempcomn.intersection_update(set(di)) badbb.update(tempcomn) lp += 1 config.ERRORBBALL = badbb.copy() gau.log_print("[*] Finished common basic blocks (%d BB)" % (len(badbb))) for ebb in config.ERRORBBALL: gau.debug_print( "[-] Error on BB : 0x%x" % (ebb,)) time.sleep(5) if config.LIBNUM == 2: baseadr = config.LIBOFFSETS[1] for ele in tempcomn: if ele < baseadr: config.ERRORBBAPP.add(ele) else: config.ERRORBBLIB.add(ele-baseadr) del tempbad del badbb # del tempgood return len(config.GOODBB), len(config.ERRORBBALL)
def read_taint(fpath): ''' This function read cmp.out file and parses it to extract offsets and coresponding values and returns a tuple(alltaint, dict). dictionary: with key as offset and values as a set of hex values checked for that offset in the cmp instruction. Currently, we want to extract values s.t. one of the operands of CMP instruction is imm value for this set of values. ADDITION: we also read lea.out file to know offsets that were used in LEA instructions. There offsets are good candidates to fuzz with extreme values, like \xffffffff, \x80000000. ''' #print "--------------------------------------------" gau.log_print( "[*] Parsing taint analysis of %s" % ( os.path.basename(fpath) ) ) exploitableTaintedOffset = dict() allTaintedOffsets = dict() fsize = os.path.getsize(fpath) offlimit = 0 # check if taint was generated, else exit if (os.path.getsize("cmp.out") == 0): gau.die("[-] Empty cmp.out file! Perhaps taint analysis did not run") cmpFD = open("cmp.out", "r") # each line of the cmp.out has the following format: # 32 reg imm 0xb640fb9d {155} {155} {155} {155} {} {} {} {} 0xc0 0xff # g1 g2 g3 g4 g5 g6 g7 g8 g9 g10 g11 g12 g13 g14 # we need a regexp to parse this string. if config.BIT64 == False: pat = re.compile( r"(\d+) ([a-z]+) ([a-z]+) (\w+) \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} (\w+) (\w+)", re.I) else: pat = re.compile( r"(\d+) ([a-z]+) ([a-z]+) (\w+) \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} \{([0-9,]*)\} (\w+) (\w+)", re.I) cmpO.id = 0 goodToProcessList = list() allTaintedOffsetsList = list() if config.FILTERDUPLICATED: rawCmpLines = set() else: rawCmpLines = list() cmpoutLineCount = 0 # remove duplicates for line in cmpFD: cmpoutLineCount += 1 if config.FILTERDUPLICATED: if line not in rawCmpLines: rawCmpLines.add(line) else: rawCmpLines.append(line) cmpFD.close() #print "original cmp.out size : " + str(cmpoutLineCount) #print "cmp.out after size : " + str(len(rawCmpLines)) allCmps = list() for ln in rawCmpLines: if offlimit > config.MAXFILELINE: break offlimit += 1 mat = pat.match(ln) try: # this is a check to see if CMP entry is complete. if config.BIT64 == False: rr = mat.group(14) else: rr = mat.group(22) except: continue cmp = cmpO.cmpOperation(mat) # TODO : if cmp.offset == -1 # the cmp is not valid or will not be handle if len(cmp.offsetsInInput) == 0: continue # save each valid cmp allCmps.append(cmp) # detect any pattern in cmp.out if config.USEPATTERNDETECTION == True: gau.log_print("[*] Running pattern detection for %s" % ( os.path.basename(fpath) )) # search and apply patterns allCmps = detectPatterns(allCmps, fpath) for cmp in allCmps: # if the cmp is valid and good to be used with taint based changes if cmp.isGoodToTaintChanges == True: goodToProcessList.append(cmp) # if it's just a normal cmp else: allTaintedOffsetsList.append(cmp) gau.debug_print("[*] allTaintedOffsetsList size : %d" % (len(allTaintedOffsetsList))) gau.debug_print("[*] goodToProcessList size : %d" % (len(goodToProcessList))) return (allTaintedOffsetsList, goodToProcessList)
def getOffset(self, file, org): org = list(org) usedFile = file if file not in self.currentTaintMap.keys(): randomFile = self.r.choice(list(self.currentTaintMap.keys())) alloffset = self.currentTaintMap[randomFile][0] usedFile = randomFile else: alloffset = self.currentTaintMap[file][0] if len(alloffset) == 0: gautils.debug_print("[-] Fail to find offset in map") return [] attemps = 10 cmp = -1 # I try to optimize the offset selection by selecting only not used offsets while attemps != 0: cmp = self.r.choice(alloffset) if cmp.offsetsInInput[0] in self.mutationHistory[file]: attemps -= 1 continue else: break gautils.debug_print("[*] Testing offset 0x%x" % (cmp.offsetsInInput[0])) conflictList = self.check_mutation_conflict_from_register_size( cmp.offsetsInInput[0], cmp.cmpSize, file) conflictWithParent = False createdChild = -1 # create the mutation buffer self.currentMutation = self.r.sample(self.r.choice(self.allStrings), cmp.cmpSize) # is there is not offset without conflicts available if len(conflictList) > 0: gautils.debug_print("[-] There is conflict with the parent") createTheChild = random.uniform( 0.1, 1.0) > (1.0 - config.CHILDINPUTCREATIONRANDOMNESS) # creating a child for each conflict if config.HEAVYCHILDINPUTCREATION == True and self.childCount < config.CHILDINPUTMAXSIZE and createTheChild: conflictWithParent = True # create the input child name, inputBuffer = self.create_child_input( file, org, conflictList, cmp) # check is exist and write the file if not self.write_child_input(name, inputBuffer): del self.mutationHistory[name] createdChild = -1 else: gautils.debug_print("[+] Child input created %s" % (name)) createdChild = name else: gautils.debug_print("[+] There is no conflict in parent") # apply the cmp to the child inputs history if len(self.childMap) > 0 and config.HEAVYCHILDINPUTCREATION == True: # childMap will change during this tmpChildMap = self.childMap.copy() for childInput in tmpChildMap: if createdChild != -1 and childInput == createdChild: continue # get conflicts for each child input conflictChildList = self.check_mutation_conflict_from_register_size( cmp.offsetsInInput[0], cmp.cmpSize, childInput) path = os.path.join(config.INPUTD, childInput) # get child input buffer childInputBuffer = list(gautils.readFile(path)) createTheChild = random.uniform( 0.1, 1.0) > (1.0 - config.CHILDINPUTCREATIONRANDOMNESS) # conflict found # NOTE : we still apply cmp to child inputs if there is no conflicts if len( conflictChildList ) > 0 and self.childCount < config.CHILDINPUTMAXSIZE and createTheChild: gautils.debug_print( "[-] There is conflict with child inputs") # create input name # get the extension # TODO : find a better way to do it bn, ext = gautils.splitFilename(usedFile) name = "heavy-child-g%d-%d.%s" % (config.CURRENTGEN, self.childCount, ext) # create the input child inputBuffer = self.create_new_child_from_old( name, childInput, childInputBuffer, conflictChildList, cmp) # check is exist and write the file if not self.write_child_input(name, ''.join(inputBuffer)): del self.mutationHistory[name] else: gautils.debug_print("[+] Child input created %s" % (name)) # no conflict (maybe while self.childCount == config.CHILDINPUTMAXSIZE) elif len(conflictChildList) == 0: gautils.debug_print( "[+] There is no conflict in child inputs") gautils.debug_print("[+] Updating child input %s" % (childInput)) # add the new mutation to the history self.mutationHistory[childInput].update( {cmp.offsetsInInput[0]: [cmp]}) # apply the changes and add it to history childInputBuffer = self.change_bytes_from_cmp( childInputBuffer, childInput, cmp) # check is exist and write the file if not self.write_child_input_update( childInput, ''.join(childInputBuffer)): del self.mutationHistory[childInput][ cmp.offsetsInInput[0]] else: gautils.debug_print("[+] Child input updated %s" % (childInput)) # true only if config.HEAVYCHILDINPUTCREATION == True if conflictWithParent == True: # the parent will not use the mutation because of the conflict return [] else: # TODO : improve self.mutationHistory[file].update({cmp.offsetsInInput[0]: [cmp]}) # we will not use this cmp again self.currentTaintMap[usedFile][0].remove(cmp) # python tricks to not access to cmp function is it None return [cmp]
def change_bytes_from_cmp(self, original, fl, cmp): if len(self.currentTaintMap) == 0: return original if self.currentMutation == -1: gautils.die("[-] Mutation not created : impossible !") return original buffer = list(original) if cmp.cmpSize == -1: #print "change_bytes: cmp.cmpSize == -1" return original bytesChanged = 0 mutationHistory = self.mutationHistory[fl][cmp.offsetsInInput[0]] if cmp.offsetsInInput[0] in self.mutationHistory[fl] and len( mutationHistory) == 1: mutationHistory.append(dict()) mutationHistoryData = mutationHistory[1] for offset in cmp.offsetsInInput: if offset >= len(buffer): continue mutationHistoryData.update({offset: []}) mutationHistoryDataCurrent = mutationHistoryData[offset] for i in range(0, cmp.cmpSize): currentOffset = int(offset + i) if currentOffset >= len(buffer): break if cmp.taintType == taintTypeEnum.UNKNOWN: # save the value mutationHistoryDataCurrent.append(buffer[currentOffset]) buffer[currentOffset] = self.currentMutation[i] bytesChanged += 1 elif cmp.taintType == taintTypeEnum.SINGLE_BYTE: # save the value mutationHistoryDataCurrent.append(buffer[currentOffset]) buffer[currentOffset] = self.currentMutation[0] bytesChanged += 1 elif cmp.taintType == taintTypeEnum.ARRAY: # save the value mutationHistoryDataCurrent.append(buffer[currentOffset]) # TODO : improve strategy buffer[currentOffset] = self.currentMutation[i] bytesChanged += 1 gautils.debug_print("[+] Mutation applied 0x%x to 0x%x" % (offset, offset + bytesChanged)) return ''.join([e for e in buffer])