Example #1
0
def check_env():
    ''' this function checks relevant environment variable that must be set before we stat our fuzzer..'''
    
    gau.log_print( "[*] Checking environment variables" )

    if os.getenv('PIN_ROOT') == None:
        gau.die("[-] PIN_ROOT env is not set. Run export PIN_ROOT=path_to_pin_exe")
    fd1 = open("/proc/sys/kernel/randomize_va_space", 'r')
    b = fd1.read(1)
    fd1.close()
    if int(b) != 0:
        gau.die(
            "[-] ASLR is not disabled. Run: echo 0 | sudo tee /proc/sys/kernel/randomize_va_space")
    fd = open("/proc/sys/kernel/yama/ptrace_scope", 'r')
    b = fd.read(1)
    fd.close()
    if int(b) != 0:
        gau.die(
            "[-] Pintool may not work. Run: echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope")
    if os.path.ismount(config.BASETMP) == False:
        tmp = raw_input(
            "[-] It seems that config.BASETMP is not mounted as tmpfs filesystem. Making it a tmpfs may give you gain on execution speed. Press [Y/y] to mount it OR press [N/n] to continue.")
        if tmp.upper() == "Y":
            print "[-] run: sudo mount -t tmpfs -o size=1024M tmpfs %s" % config.BASETMP
            raise SystemExit(1)
Example #2
0
def initPatterns():

    # TODO : check error

    for file in os.listdir("patterns/definitions"):
        if file.endswith(".py"):

            pathStr = os.path.join("patterns/definitions", file)

            if path.exists(pathStr):
                patternDefinition.append(imp.load_source('PatternDefinition', pathStr).PatternDefinition())

    patternCount = 0

    # create each pattern objects
    for file in patternDefinition:
        for sign in file.FILES:

            pathStr = os.path.join("patterns/signatures", sign)

            # create each pattern objects
            if path.exists(pathStr):
                file.patternList.append(patternSystem.Pattern(pathStr))
                patternCount += 1

    gau.log_print("[*] %d patternDefinition detected (%d total)" % (len(patternDefinition), patternCount))
Example #3
0
def run_error_bb(pt):

    gau.log_print("[*] Starting run_error_bb")
    files = os.listdir(config.INPUTD)
    for fl in files:
        tfl = os.path.join(config.INPUTD, fl)
        (bbs, retc) = execute(tfl)
        # if retc < 0:
        #    print "[*] crashed while executing %s"%(fl,)
        #    gau.die("Bye...")
        form_bitvector(bbs)
    calculate_error_bb()
Example #4
0
def reduction(files):

    if not checkAflcmin():
        g.log_print("[-] ALFCMINEXECUTABLEPATH is not set or wrong")
        return False

    inputDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "vutemp", "aflinput")
    ouputDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "vutemp","aflouput")

    if not checkDir(inputDir,ouputDir):
        return False

    # NOTE : copy each file is not a good idea, but if we want to split child inputs
    #        and normal population, need to make differents dir

    for f in files:
        shutil.copy(f, os.path.join(inputDir, os.path.basename(f)))

    cmd = "%s -Q -i %s -o %s -- %s @@" % (config.ALFCMINEXECUTABLEPATH,inputDir,ouputDir,config.SUT % "")

    g.log_print("[*] Launching AFL-CMIN")

    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    
    stdout, stderr = proc.communicate()
    
    inputNumberAfterReduction = len(os.listdir(ouputDir))

    if inputNumberAfterReduction == 0:
        g.log_print("[-] Reduction failed : AFL-CMIN output is null")

        # checked inputs are useless, so we remove them
        for f in files:
            os.remove(f)

    else:
        erasedPer = 100.0 - float((float(inputNumberAfterReduction)/float(len(files))) * 100.0) 

        g.log_print("[+] AFL-CMIN output is about %d files (%.1f%% erased)" % (inputNumberAfterReduction, erasedPer))
        
        validInputPaths = os.listdir(ouputDir)

        validInputs = []

        for f in validInputPaths:
            validInputs.append(os.path.basename(f))

        # removing old not interesting files
        for f in files:
            if os.path.basename(f) not in validInputs:
                os.remove(f)

        g.emptyDir(ouputDir)

    g.emptyDir(inputDir)

    return True
Example #5
0
def checkDir(inputDir, ouputDir):

    if not dirExists(inputDir):

        try:
            os.mkdir(inputDir)
        except OSError:
            g.log_print("[-] Failed to create input directory")
            return False

    if not dirExists(ouputDir):

        try:
            os.mkdir(ouputDir)
        except OSError:
            g.log_print("[-] Failed to create ouput directory")
            return False

    return True
Example #6
0
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
Example #7
0
def main():

    banner()

    # first lets create the base directorty to keep all temporary data
    try:
        shutil.rmtree(config.BASETMP)
    except OSError:
        pass
    if os.path.isdir(config.BASETMP) == False:
        os.mkdir(config.BASETMP)
    check_env()
    ## parse the arguments #########
    parser = argparse.ArgumentParser(description='VUzzer options')
    parser.add_argument('-s', '--sut', help='SUT commandline', required=True)
    parser.add_argument(
        '-i', '--inputd', help='seed input directory (relative path)', required=True)
    parser.add_argument(
        '-w', '--weight', help='path of the pickle file(s) for BB wieghts (separated by comma, in case there are two) ', required=True)
    parser.add_argument(
        '-n', '--name', help='Path of the pickle file(s) containing strings from CMP inst (separated by comma if there are two).', required=True)
    parser.add_argument(
        '-l', '--libnum', help='Nunber of binaries to monitor (only application or used libraries)', required=False, default=1)
    parser.add_argument('-o', '--offsets', help='base-address of application and library (if used), separated by comma',
                        required=False, default='0x00000000')
    parser.add_argument(
        '-b', '--libname', help='library name to monitor', required=False, default='#')
    args = parser.parse_args()
    config.SUT = args.sut
    config.INITIALD = os.path.join(config.INITIALD, args.inputd)
    config.LIBNUM = int(args.libnum)
    config.LIBTOMONITOR = args.libname
    config.LIBPICKLE = [w for w in args.weight.split(',')]
    config.NAMESPICKLE = [n for n in args.name.split(',')]
    config.LIBOFFSETS = [o for o in args.offsets.split(',')]
    config.LIBS = args.libname
    # this is just to find the index of the placeholder in BBCMD list to replace it with the libname
    ih = config.BBCMD.index("LIBS=")
    config.BBCMD[ih] = "LIBS=%s" % args.libname

    gau.log_print( "[*] Checking tmps files" )

    if config.CLEARSPECIALOUTPUT:
        gau.delete_special_out_file(config.SPECIALOUTPUT)

    if path.exists("vuzzerRun.log"):
        os.remove("vuzzerRun.log")

    gau.log_print( "[*] Checking directories" )

    if config.USEPATTERNDETECTION == True:
        initPatterns()

    ###################################

    afl.clearDir()

    config.minLength = get_min_file(config.INITIALD)
    try:
        shutil.rmtree(config.KEEPD)
    except OSError:
        pass
    os.mkdir(config.KEEPD)

    try:
        os.mkdir("outd")
    except OSError:
        pass

    try:
        os.mkdir("outd/crashInputs")
    except OSError:
        gau.emptyDir("outd/crashInputs")

    crashHash = []
    try:
        os.mkdir(config.SPECIAL)
    except OSError:
        gau.emptyDir(config.SPECIAL)

    try:
        os.mkdir(config.INTER)
    except OSError:
        gau.emptyDir(config.INTER)

    gau.log_print( "[*] Checking executable base address" )

    #############################################################################
    # let us get the base address of the main executable.
    ifiles = os.listdir(config.INITIALD)
    for fl in ifiles:
        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))
        (ibbs, iretc) = execute(tfl)

        if iretc != 128: # 0
            gau.die("[-] Can't run the target program '%s' !" % (os.path.basename(config.SUT.replace(" ","").replace("%s",""))))
        break  # we just want to run the executable once to get its load address

    imgOffFd = open("imageOffset.txt", 'r')
    for ln in imgOffFd:
        if "Main:" in ln:
            lst = ln.split()
            break
    config.LIBOFFSETS[0] = lst[1][:]
    imgOffFd.close()

    if config.LIBTOMONITOR != '' and config.LIBTOMONITOR != '#':
        gau.log_print("[+] Lib %s is at 0x%x" % (config.LIBTOMONITOR, int(config.LIBOFFSETS[1], 0)))

    gau.log_print( "[*] Checking pickles" )

    #############################################################################

    # open names pickle files
    gau.prepareBBOffsets()

    # lets initialize the BBFORPRUNE list from thie cALLBB set.
    if len(config.cALLBB) > 0:
        config.BBFORPRUNE = list(config.cALLBB)
    else:
        gau.log_print("[*] cALLBB is not initialized. something is wrong!!\n")
        system.exit()

    if config.PTMODE:
        pt = simplept.simplept()
    else:
        pt = None

    gau.log_print("[*] Running vuzzer for '%s'" % (os.path.basename(config.SUT.replace(" ","").replace("%s",""))))

    if config.ERRORBBON == True:
        gbb, bbb = dry_run()
    else:
        gbb = 0

    # gau.die("dry run over..")
    import timing
    # selftest()
    noprogress = 0
    currentfit = 0
    lastfit = 0

    config.CRASHIN.clear()
    stat = open("stats.log", 'w')
    stat.write("**** Fuzzing started at: %s ****\n" %
               (datetime.now().isoformat('+'),))
    stat.write("**** Initial BB for seed inputs: %d ****\n" % (gbb,))
    stat.flush()
    os.fsync(stat.fileno())
    stat.write(
        "Genaration\t MINfit\t MAXfit\t AVGfit MINlen\t Maxlen\t AVGlen\t #BB\t AppCov\t AllCov\n")
    stat.flush()
    os.fsync(stat.fileno())
    starttime = time.clock()
    allnodes = set()
    alledges = set()
    try:
        shutil.rmtree(config.INPUTD)
    except OSError:
        pass
    shutil.copytree(config.INITIALD, config.INPUTD)

    # fisrt we get taint of the intial inputs
    get_taint(config.INITIALD, 1)

    # print "MOst common offsets and values:", config.MOSTCOMMON
    # print "Base address: %s"%config.LIBOFFSETS[0]
    # raw_input("Press enter to continue..")
    config.MOSTCOMFLAG = True
    crashhappend = False
    filest = os.listdir(config.INPUTD)
    filenum = len(filest)

    if filenum < config.POPSIZE:
        gau.create_files(config.POPSIZE - filenum)

    gau.log_print( '[*] Population at start is about %d files' % (len(os.listdir(config.INPUTD))))

    efd = open(config.ERRORS, "w")
    gau.prepareBBOffsets()
    writecache = True
    genran = 0
    bbslide = 40  # this is used to call run_error_BB() functions
    keepslide = 3
    keepfilenum = config.BESTP
    config.SEENBB.clear()  # initialize set of BB seen so far, which is 0
    del config.SPECIALENTRY[:]
    todelete = set()  # temp set to keep file names that will be deleted in the special folder
    
    oldPrintSize = 0

    while True:
        # print "[**] Generation %d\n***********"%(genran,)

        del config.TEMPTRACE[:]
        del config.BBSEENVECTOR[:]
        # this is set when a config.SPECIAL gets at least one new input per generation.
        SPECIALCHANGED = False
        config.TMPBBINFO.clear()
        config.TMPBBINFO.update(config.PREVBBINFO)

        fitnes = dict()
        execs = 0
        config.cPERGENBB.clear()
        config.GOTSTUCK = False

        if config.ERRORBBON == True:
            if genran > config.GENNUM/5:
                bbslide = max(bbslide, config.GENNUM/20)
                keepslide = max(keepslide, config.GENNUM/100)
                keepfilenum = keepfilenum/2

            if 0 < genran < config.GENNUM/5 and genran % keepslide == 0:
                copy_files(config.INPUTD, config.KEEPD, keepfilenum)

        # lets find out some of the error handling BBs
            if genran > 40 and genran % bbslide == 0:
                stat.write("\n**** Error BB cal started ****\n")
                stat.flush()
                os.fsync(stat.fileno())
                run_error_bb(pt)
                copy_files(config.KEEPD, config.INPUTD,
                           len(os.listdir(config.KEEPD))*1/10)
            # copy_files(config.INITIALD,config.INPUTD,1)
        files = os.listdir(config.INPUTD)
        per_gen_fnum = 0
        for fl in files:
            per_gen_fnum += 1
            tfl = os.path.join(config.INPUTD, fl)
            iln = os.path.getsize(tfl)
            args = (config.SUT % tfl).split(' ')
            progname = os.path.basename(args[0])
            (bbs, retc) = execute(tfl)
            filecount = len(os.listdir(config.INPUTD))
            inputname = os.path.basename(args[1])

            per_current_stat = (float(per_gen_fnum)/float(filecount)) * 100

            per_show = (int(float(filecount) / 10.0))

            if per_show == 0:
                per_show = 10

            if per_gen_fnum % per_show == 0 or per_gen_fnum == filecount:

                logStr = "[%s] Gen %d : %d%% Executed %d of %d" % (time.strftime("%H:%M:%S"),genran,int(per_current_stat),per_gen_fnum, filecount)
                
                if oldPrintSize > 0:
                    dif = oldPrintSize - len(logStr)
                    if dif > 0: 
                        logStr += " "*dif

                oldPrintSize = len(logStr)

                gau.log_print( logStr )
            else:

                gau.log_print( ' '*oldPrintSize )
                
                sys.stdout.write("\033[F")

                logStr = "[%s] Gen %d : %d%% Executed %d of %d [%s]" % (time.strftime("%H:%M:%S"),genran,int(per_current_stat),per_gen_fnum, filecount,inputname)

                if oldPrintSize > 0:
                    dif = oldPrintSize - len(logStr)
                    if dif > 0: 
                        logStr += " "*dif

                oldPrintSize = len(logStr)

                gau.log_print( logStr )

                sys.stdout.write("\033[F")

            if config.BBWEIGHT == True:
                fitnes[fl] = gau.fitnesCal2(bbs, fl, iln)
            else:
                fitnes[fl] = gau.fitnesNoWeight(bbs, fl, iln)
            # raw_input()
            execs += 1
            # let us prune the inputs(if at all), whose trace is subset of the new input just got executed.
            SPECIALADDED = False
            if config.GOTSPECIAL == True:
                SPECIALCHANGED = True
                SPECIALADDED = True
                todelete.clear()
                form_bitvector2(bbs, fl, config.BBFORPRUNE,
                                config.SPECIALBITVECTORS)
                shutil.copy(tfl, config.SPECIAL)
                config.SPECIALENTRY.append(fl)
                for sfl, bitv in config.SPECIALBITVECTORS.iteritems():
                    if sfl == fl:
                        continue
                    if (config.SPECIALBITVECTORS[fl] & bitv) == bitv:
                        tpath = os.path.join(config.SPECIAL, sfl)
                        os.remove(tpath)
                        todelete.add(sfl)
                        config.SPECIALENTRY.remove(sfl)
                        if sfl in config.TAINTMAP:
                            del config.TAINTMAP[sfl]
                for ele in todelete:
                    del config.SPECIALBITVECTORS[ele]

            if retc < 0 and retc != -2:
                efd.write("%s: %d\n" % (tfl, retc))
                efd.flush()
                os.fsync(efd)
                tmpHash = sha1OfFile(config.CRASHFILE)
                if tmpHash not in crashHash:
                    crashHash.append(tmpHash)
                    tnow = datetime.now().isoformat().replace(":", "-")
                    nf = "%s-%s.%s" % (progname, tnow,
                                       gau.splitFilename(fl)[1])
                    npath = os.path.join("outd/crashInputs", nf)
                    shutil.copyfile(tfl, npath)
                    if SPECIALADDED == False:
                        shutil.copy(tfl, config.SPECIAL)

                    config.CRASHIN.add(fl)
                if config.STOPONCRASH == True:
                    # efd.close()
                    crashhappend = True
                    break
        fitscore = [v for k, v in fitnes.items()]
        maxfit = max(fitscore)
        avefit = sum(fitscore)/len(fitscore)
        mnlen, mxlen, avlen = gau.getFileMinMax(config.INPUTD)

        gau.log_print( "[*] Done with all input in Gen %d" % (genran) )
        gau.log_print( "[*] Calculating code coverage" )

        appcov, allcov = gau.calculateCov()
        tnow = datetime.now().isoformat().replace(":", "-")
        # stat.write("\t%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %s\n"%(genran,min(fitscore),maxfit,avefit,mnlen,mxlen,avlen,len(config.cPERGENBB),appcov,allcov,tnow))
        stat.write("\t%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %s\n" % (genran, min(
            fitscore), maxfit, avefit, mnlen, mxlen, avlen, len(config.SEENBB), appcov, allcov, tnow))
        stat.flush()
        os.fsync(stat.fileno())
        gau.log_print( "[*] Wrote to stat.log" )

        seenBB = len(config.SEENBB)

        codeCovStr = "[+] BB Code coverage is %d" % (seenBB)

        if config.LASTTURNCODECOV != 0:

            difLastTurnStr = "(no new BB found)"

            codeCovDif = seenBB - config.LASTTURNCODECOV

            if codeCovDif > 0:
                difLastTurnStr = "\033[0;32m(+%d BB)\033[0m" % (codeCovDif)
            elif codeCovDif < 0:
                difLastTurnStr = "\033[0;31m(-%d BB)\033[0m" % (codeCovDif)

            perBBTotal = (float(seenBB) / float(len(config.ALLBB))) * 100

            difGoodBB = seenBB - len(config.GOODBB)

            difGoodBBStr = "no"

            if difGoodBB > 0:
                difGoodBBStr = "+%d" % (difGoodBB)
            elif difGoodBB < 0:
                difGoodBBStr = "-%d" % (difGoodBB)

            codeCovStr += " %s (%d%% of all BB) (%s BB dif with good path)" % (difLastTurnStr, perBBTotal, difGoodBBStr)
            
        config.LASTTURNCODECOV = seenBB

        gau.log_print( "-"*len(codeCovStr) ) 
        gau.log_print( codeCovStr )
        gau.log_print( "-"*len(codeCovStr) )
        
        if crashhappend == True:
            break
        # lets find out some of the error handling BBs
        # if genran >20 and genran%5==0:
         #   run_error_bb(pt)
        genran += 1
        config.CURRENTGEN += 1
        # this part is to get initial fitness that will be used to determine if fuzzer got stuck.
        lastfit = currentfit
        # currentfit=maxfit
        currentfit = len(config.SEENBB)
        # lastfit-config.FITMARGIN < currentfit < lastfit+config.FITMARGIN:
        if currentfit == lastfit:
            noprogress += 1
        else:
            noprogress = 0
        if noprogress > 20:
            config.GOTSTUCK = True
            stat.write("Heavy mutate happens now..\n")
            noprogress = 0
        if (genran >= config.GENNUM) and (config.STOPOVERGENNUM == True):
            break
        if len(os.listdir(config.SPECIAL)) > 0 and SPECIALCHANGED == True:
            if len(os.listdir(config.SPECIAL)) < config.NEWTAINTFILES:
                get_taint(config.SPECIAL)
            else:
                try:
                    os.mkdir(config.TAINTTMP)
                except OSError:
                    gau.emptyDir(config.TAINTTMP)
                if conditional_copy_files(config.SPECIAL, config.TAINTTMP, config.NEWTAINTFILES) == 0:
                    get_taint(config.TAINTTMP)
            # print "MOst common offsets and values:", config.MOSTCOMMON
            # gg=raw_input("press any key to continue..")

        gau.log_print( "[*] Going for new generation creation" )
        gau.createNextGeneration3(fitnes, genran)
        # raw_input("press any key...")

    efd.close()
    stat.close()
    libfd_mm.close()
    libfd.close()
    endtime = time.clock()

    gau.log_print( "[**] Totol time %f sec" % (endtime-starttime,) )
    gau.log_print( "[**] Fuzzing done. Check %s to see if there were crashes" % (
        config.ERRORS,))
Example #8
0
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)
Example #9
0
def get_taint(dirin, is_initial=0):
    ''' This function is used to get taintflow for each CMP instruction to find which offsets in the input are used at the instructions. It also gets the values used in the CMP.'''

    files = os.listdir(dirin)

    newTaint = []

    for f in files:
        if f not in config.TAINTMAP:
            newTaint.append(f)

    gau.log_print("[*] Starting taintflow calculation for %d files" % (len(newTaint)))

    for fl in files:

        if fl in config.TAINTMAP:
            continue

        pfl = os.path.abspath(os.path.join(dirin, fl))

        if is_initial == 1:
            tnow1 = datetime.now()

        gau.log_print( "[*] Launching taint analysis of %s" % ( os.path.basename(fl) ) )

        rcode = execute2(pfl, fl, is_initial)

        if is_initial == 1:
            tnow2 = datetime.now()
            config.TIMEOUT = max(
                config.TIMEOUT, 2*((tnow2-tnow1).total_seconds()))

        if rcode == 255:
            gau.die("pintool terminated with error 255 on input %s" % (pfl,))
            continue

        config.TAINTMAP[fl] = read_taint(pfl)
        config.LEAMAP[fl] = read_lea()

    gau.log_print("[*] Taintflow parsing done")

    if config.MOSTCOMFLAG == False:

        gau.log_print("[*] Computing MOSTCOMMON calculation")

        tmpTaintMap = config.TAINTMAP.copy()

        for file, values in tmpTaintMap.iteritems():

            for cmp in values[1]:

                offset = cmp.offsetsInInput[0]

                foundInAll = True

                foundMap = list()

                for file2, values2 in tmpTaintMap.iteritems():
                    
                    foundInThisFile = False

                    for cmp2 in values2[1]:
                        
                        if offset in cmp2.offsetsInInput and cmp.taintValue == cmp2.taintValue:
                            foundInThisFile = True

                    foundMap.append(foundInThisFile)

                    if foundInThisFile == False:
                        break

                for file1 in foundMap:
                    if file1 == False:
                        foundInAll = False
                
                if foundInAll == True:
                    config.MOSTCOMMON.append(cmp)

                    # delete it from taint map
                    config.TAINTMAP[file][1].remove(cmp)
                                
            break

    else:
        
        gau.log_print("[*] Computing MORECOMMON calculation")

        tmpTaintMap = config.TAINTMAP.copy()

        for file, values in tmpTaintMap.iteritems():

            for cmp in values[1]:

                offset = cmp.offsetsInInput[0]

                foundInAll = True

                foundMap = list()

                for file2, values2 in tmpTaintMap.iteritems():
                    
                    foundInThisFile = False

                    for cmp2 in values2[1]:
                        
                        if offset in cmp2.offsetsInInput:

                            dif = cmp.taintValue - cmp2.taintValue

                            if dif < 0: dif *= -1

                            if dif < 3:
                                foundInThisFile = True

                    foundMap.append(foundInThisFile)

                    if foundInThisFile == False:
                        break

                for file1 in foundMap:
                    if file1 == False:
                        foundInAll = False
                
                if foundInAll == True:
                    config.MORECOMMON.append(cmp)

                    # delete it from taint map
                    config.TAINTMAP[file][1].remove(cmp)
                                
            break

    #print '------------------------------------------------------------------------------'

    if len(config.MOSTCOMMON) > 0:
        gau.log_print("[*] MOSTCOMMON size is about %d cmps" % (len(config.MOSTCOMMON)))

    #for cmp in config.MOSTCOMMON:
    #    print cmp

    if len(config.MORECOMMON) > 0:
        gau.log_print("[*] MORECOMMON size is about %d cmps" % (len(config.MORECOMMON)))

    #for cmp in config.MORECOMMON:
    #    print cmp

    gau.log_print("[*] Taintflow finished")
Example #10
0
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)