def dumpFindings(): r = results.loadResults() for finding in findings: tag = findings[finding]["tag"] res = findings[finding]["result"] tc = findings[finding]["tc"] r = results.addResult(r, res, tc, finding, "", "", tag) results.saveResults(r)
def call_analyse(identifier=None, fname="func"): # Note: findings shall not repeat themselves, if found more than once findings = {} # function that adds a potential new finding to the findings database def addFinding(finding, tag=""): if not (finding in findings): findings[finding] = tag def loadYamlFile(inFile): _inDict = {} try: with open(inFile) as f: _inDict = yaml.load(f, Loader=yaml.FullLoader) except Exception as e: #print("WARNING: "+str(e)) pass return _inDict nLinesInjected = {} I = loadYamlFile(injectFileName) for fName in I: nLinesInjected[fName] = len(I[fName]) chalConfig = loadYamlFile("config.yaml") ########################################################################## # Main # ########################################################################## #fNameR = r"func(?:_\d+)?\.cp?p?" challengeFiles = chalConfig["files"] if type(challengeFiles) is str: challengeFiles = [challengeFiles] for fname in challengeFiles: # NOTE: we need to split this for loop - it is now very big...! fNameR = fname # +------------------------------------------+ # | Process compilaton errors | # +------------------------------------------+ print("Search file:", fNameR) try: with open("compile.txt") as f: errLines = f.read().split("\n") lineNum = 0 while (lineNum < len(errLines)): errLine = errLines[lineNum] if re.search("redirected_", errLine): errLine = re.sub("redirected_", "", errLine) # search for messages related only to the user file # Errors that can be bypassed if re.match(r"^collect2: error: ld returned 1 exit status$", errLine): finding = "Linking failed!" addFinding(finding) #if re.search(r"\.o: No such file or directory$",errLine): # finding = "File or directory missing!" # addFinding(finding) # deal with redirected funtions # redirect.h:17:14: error: too few arguments to function ‘redirected_time’ m = re.match(r"^" + fNameR + r":(\d+):(\d+): error: (.*)", errLine) if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colNumber = int(m.group(2)) errMsg = m.group(3) m = re.search("redirected_", errMsg) if m: errMsg = re.sub("redirected_", "", errMsg) # continue searching for the line number # func_7453459449.c:21:19: note: in expansion of macro ‘time’ while (lineNum < len(errLines)): errLine = errLines[lineNum] lineNum = lineNum + 1 m = re.search( fNameR + r":(\d+):(\d+): note: in expansion of macro (.*)", errLine) if m: lineNumber = int( m.group(1)) - nLinesInjected.get( fNameR, 0) finding = "ERROR ({fileName},{lineNumber}): {errMsg}".format( fileName=fNameR, lineNumber=lineNumber, errMsg=errMsg) addFinding(finding) break # # TODO: capture name of file and compare to our files that we are searching for... # if not in our dictionary, then this is a "Yikes" error # #### # Compiler error in a file not func* #### # This needs to be improved here... #### m = re.match(r"^((?!" + fNameR + r")).*error:.*",errLine) #### if m: #### print("Yikes: ("+str(m.groups()))+")",errLine) #### finding = "ERROR in project! Help, Hurry ... call someone!?!?! Yikes!" #### addFinding(finding) # Compiler error in func.c or func.cpp - Type 1 (line number + column number) # func.c:12:5: error: implicit declaration of function ‘xstrcpy’; did you mean ‘strcpy’? [-Werror=implicit-function-declaration] m = re.search(fNameR + r":(\d+):(\d+): error: (.*)", errLine) if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colnNumber = int(m.group(2)) errorMsg = m.group(3) finding = "ERROR ({fileName},{lineNumber}): {errMsg}".format( fileName=fNameR, lineNumber=lineNumber, errMsg=errorMsg) addFinding(finding) # Compiler error in func.c or func.cpp - Type 2 (only line number) m = re.search(fNameR + r":(\d+): error: (.*)", errLine) if m: #print("BBB",errLine) if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colnNumber = int(m.group(2)) errorMsg = m.group(3) finding = "ERROR ({fileName},{lineNumber}): {errMsg}".format( fileName=fNameR, lineNumber=lineNumber, errMsg=errorMsg) addFinding(finding) # Compiler error in func.c or func.cpp - Type 3 (fatal error + line number + column number) m = re.search(fNameR + r":(\d+):(\d+): fatal error: (.*)", errLine) if m: if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colnNumber = int(m.group(2)) errorMsg = m.group(3) finding = "ERROR ({fileName},{lineNumber}): {errMsg}".format( fileName=fNameR, lineNumber=lineNumber, errMsg=errorMsg) addFinding(finding) # Usage of deprecated functions m = re.search( fNameR + r":(\d+):(\d+): warning: ‘(.*)’ * is deprecated \[-Wdeprecated-declarations\]", errLine) if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colnNumber = int(m.group(2)) funcName = m.group(3) finding = "WARNING ({fileName},{lineNumber}): {errMsg}".format( fileName=fNameR, lineNumber=lineNumber, errMsg=errorMsg) addFinding(finding) # func.c:28:9: warning: format not a string literal and no format arguments [-Wformat-security] if 'format not a string literal and no format arguments [-Wformat-security]' in errLine: # func.c:22:14: runtime error: signed integer overflow: 244140625 * 25 cannot be represented in type 'int' m = re.search(fNameR + r":(\d+):(\d+):", errLine) if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colNumber = int(m.group(2)) finding = "WARNING ({fileName},{lineNumber}): A format string attack is possible".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) lineNum = lineNum + 1 except Exception as e: print("Exception: " + str(e)) # +------------------------------------------+ # | Process findings from stderr | # +------------------------------------------+ try: with open("stderr.txt") as f: errLines = f.read().split("\n") lineNum = 0 found_asan = False added_asan_finding = False while (lineNum < len(errLines)): errLine = errLines[lineNum] if "runtime error: signed integer overflow" in errLine: # func.c:22:14: runtime error: signed integer overflow: 244140625 * 25 cannot be represented in type 'int' m = re.search( fNameR + r":(\d+):(\d+): runtime error: signed integer overflow", errLine) if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colNumber = int(m.group(2)) finding = "ERROR ({fileName},{lineNumber}): There is a signed integer overflow vulnerability".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) if "runtime error: division by zero" in errLine: # func.c:25:17: runtime error: division by zero m = re.search( fNameR + r":(\d+):(\d+): runtime error: division by zero", errLine) if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colNumber = int(m.group(2)) finding = "ERROR ({fileNamer},{lineNumber}): There is a division-by-zero vulnerability".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) if "runtime error: reference binding to null pointer of type" in errLine: m = re.search( fNameR + r":(\d+):(\d+): runtime error: member call on null pointer of type \.*", errLine) if m: lineNumber = int(m.group(1)) - nLinesInjected.get( fNameR, 0) colNumber = int(m.group(2)) finding = "ERROR ({fileName},{lineNumber}): Null pointer access".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) # findings by AddressSanitizer and LeakSanitizer # ============================================== if re.search(r"AddressSanitizer: ", errLine): found_asan = True # search for AddressSanitizer: buffer overflow if re.search( r"AddressSanitizer: stack-buffer-overflow on address", errLine): lineNum = lineNum + 1 while (lineNum < len(errLines)): errLine = errLines[lineNum] m = re.search( r"'(.*?)'.*<== Memory access at offset \d+ overflows this variable", errLine) if m: varName = m.group(1) finding = "Stack overflow on variable '{varName}'".format( varName=varName) addFinding(finding) added_asan_finding = True break m = re.search( r"'(.*?)'.*<== Memory access at offset \d+ underflows this variable", errLine) if m: varName = m.group(1) finding = "Stack underflow on variable '{varName}'".format( varName=varName) addFinding(finding) added_asan_finding = True break lineNum = lineNum + 1 # search for AddressSanitizer: buffer overflow elif re.search(r"^\*\*\* stack smashing detected \*\*\*", errLine): finding = "Possible stack smashing was detected" addFinding(finding) # Example: ==4==ERROR: AddressSanitizer: SEGV on unknown address 0x5566f04f9933 (pc 0x5566f04db4d6 bp 0x7ffe1f0c2eb0 sp 0x7ffe1f0c2df0 T0) elif re.search( r"^==\d+==ERROR: AddressSanitizer: SEGV on unknown address", errLine): lineNum = lineNum + 1 while (lineNum < len(errLines)): errLine = errLines[lineNum] # #0 0x557a3f99c4d5 in func /home/gasiba/Git/sifu/upload/edbd33d4-6ece-4cec-9da9-4b66084db79e/func.c:13 m = re.search(r"^.*in (.*) .*\/" + fNameR + r":(.*?)$", errLine) if m: functionName = m.group(1) lineNumber = int(m.group(2)) - nLinesInjected.get( fNameR, 0) finding = "ERROR ({fileName},{lineNumber}): Segmentation fault".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) added_asan_finding = True break lineNum = lineNum + 1 # search for AddressSanitizer: heap-buffer-overflow # Example: ==2==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000015 at pc 0x55b9ad0e93fd bp 0x7ffce65329b0 sp 0x7ffce65329a0 elif re.search( r"^==\d+==ERROR: AddressSanitizer: heap-buffer-overflow", errLine): lineNum = lineNum + 1 while (lineNum < len(errLines)): errLine = errLines[lineNum] # #0 0x55b9ad0e93fc in func /home/gasiba/Git/sifu/upload/51b30a8b-acde-4bf3-8c64-8d2f88fd932c/func.c:14 m = re.search( r"^in (.*) .*\/" + fNameR + r".cp?p?:(.*?)$", errLine) if m: functionName = m.group(1) lineNumber = int(m.group(2)) - nLinesInjected.get( fNameR, 0) finding = "ERROR ({fileName},{lineNumber}): Heap Buffer Overflow/Underflow".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) added_asan_finding = True break lineNum = lineNum + 1 # search for memory leaks # Example: ==2==ERROR: LeakSanitizer: detected memory leaks elif re.search( r"==\d+==ERROR: LeakSanitizer: detected memory leaks", errLine): lineNum = lineNum + 1 while (lineNum < len(errLines)): errLine = errLines[lineNum] # #1 0x5602ed79db34 in get_filled_buffer /home/gasiba/Git/sifu/Challenges/test/chal_0007/func.c:25 m = re.search(r"^.*in (.*) .*\/" + fNameR + ":(.*?)$", errLine) if m: functionName = m.group(1) lineNumber = int(m.group(2)) - nLinesInjected.get( fNameR, 0) finding = "ERROR ({fileName},{lineNumber}): Memory leak".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) break # SUMMARY: AddressSanitizer: 120 byte(s) leaked in 1 allocation(s). m = re.search( r"SUMMARY: AddressSanitizer: \d+ byte\(s\) leaked in \d+ allocation\(s\).$", errLine) if m: addFinding("Detected memory leak") break lineNum = lineNum + 1 # search for free memory that was not malloc'ed # Example: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x7ffffa10fcd0 in thread T0 # #0 0x560d6e9f491f in __interceptor_free (/home/gasiba/Git/sifu/Challenges/test/chal_0007/main+0x10591f) # #1 0x560d6ea42783 in get_y_no /home/gasiba/Git/sifu/Challenges/test/chal_0007/func.c:17 # #2 0x560d6ea4191b in Test_Main /home/gasiba/Git/sifu/Challenges/test/chal_0007/main.c:49 # #3 0x560d6ea41ae1 in main /home/gasiba/Git/sifu/Challenges/test/chal_0007/main.c:73 # #4 0x7fe368b1cb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) # #5 0x560d6e90b449 in _start (/home/gasiba/Git/sifu/Challenges/test/chal_0007/main+0x1c449) elif re.search( r"AddressSanitizer: attempting free on address which was not malloc", errLine): lineNum = lineNum + 1 while (lineNum < len(errLines)): errLine = errLines[lineNum] # #1 0x560d6ea42783 in get_y_no /home/gasiba/Git/sifu/Challenges/test/chal_0007/func.c:17 m = re.search(r"^.*in (.*) .*\/" + fNameR + ":(.*?)$", errLine) if m: functionName = m.group(1) lineNumber = int(m.group(2)) - nLinesInjected.get( fNameR, 0) finding = "ERROR ({fileName},{lineNumber}): Trying to free memory that was not malloc'ed".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) added_asan_finding = True break lineNum = lineNum + 1 # ==2==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x55daab485ac3 bp 0x7ffd89bea910 sp 0x7ffd89bea860 T0) # ==2==The signal is caused by a READ memory access. # ==2==Hint: address points to the zero page. # #0 0x55daab485ac2 in get_y_no /home/gasiba/Git/sifu/upload/a4438855-2f36-4c53-9e1f-52e6a46f3b24/func.c:36 # #1 0x55daab484b9e in Test_Main /home/gasiba/Git/sifu/upload/a4438855-2f36-4c53-9e1f-52e6a46f3b24/main.c:49 # #2 0x55daab484d96 in main /home/gasiba/Git/sifu/upload/a4438855-2f36-4c53-9e1f-52e6a46f3b24/main.c:74 # #3 0x7f60b21b6b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) # #4 0x55daab34e6c9 in _start (/chal/main+0x1c6c9) elif re.search(r"AddressSanitizer: SEGV on unknown address", errLine): lineNum = lineNum + 1 while (lineNum < len(errLines)): errLine = errLines[lineNum] # #0 0x55daab485ac2 in get_y_no /home/gasiba/Git/sifu/upload/a4438855-2f36-4c53-9e1f-52e6a46f3b24/func.c:36 m = re.search(r"^.*in (.*) .*\/" + fNameR + ":(.*?)$", errLine) if m: functionName = m.group(1) lineNumber = int(m.group(2)) - nLinesInjected.get( fNameR, 0) finding = "ERROR ({fileName},{lineNumber}): segmentation fault".format( fileName=fNameR, lineNumber=lineNumber) addFinding(finding) added_asan_finding = True break lineNum = lineNum + 1 lineNum = lineNum + 1 if found_asan and not added_asan_finding: addFinding("There is a security vulnerability with your code.") #TODO: report to ourselves except Exception as e: print("Exception: " + str(e)) # Dump the results to the Sifu backend r = results.loadResults() for finding in findings: tag = findings[finding] r = results.addResult(r, 0, "TEST_100", finding, "", "", tag, identifier) # in the end, if we have no results, something wrong or very bad has happen! if len(r) == 0: r = results.addResult(r, 0, "TEST_0", "This challenge seems to be broken!", "", "", "NO_TAG") results.saveResults(r)
ret = subprocess.run(["make jail"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = ret.stdout.decode("utf-8") ################################################################################################### # My own tests # ################################################################################################### # Add the results to unit_test.json allResults = results.loadResults() for finding in findings: tag = findings[finding]["tag"] res = findings[finding]["result"] allResults = results.addResult(allResults, res, "TEST_90", finding, "", "", tag) results.saveResults(allResults) ################################################################################################### # Run Analyse, i.e. collect results from compilation error and ASAN # Note: 1) analyse needs at least ONE TC that is OK or FAIL, otherwise it "thinks" that # the challenge is broken! # 2) analyse.py will add ASAN and compiler errors to the unit_test.json ret = subprocess.run(["make analyse"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = ret.stdout.decode("utf-8") # re-load the results which now contain ASAN and compiler errors
################### # int main ( void ) f_main = extractFunctionFromCode( "func.c", "int\s+_main\s*\(\s*void\s*\)\s*" ) finding = "ERROR: could not find '_main' function declaration" addFinding(finding,not(f_main=="")) # return 0; // _main: do not change this line rx = "^\s*return 0; // _main: do not change this line\s*$" r=searchFunctionString(f_main,rx) finding = "ERROR: could not find return from '_main' function" addFinding(finding,not(r==0)) finding = "ERROR: found too many returns from '_main' function" addFinding(finding,not(r>1)) # "make" will clean the project ret = subprocess.run(["COPTS='' make"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = ret.stdout.decode("utf-8") # Add the results to unit_test.json r = results.loadResults() for finding in findings: tag = findings[finding]["tag"] res = findings[finding]["result"] r = results.addResult(r,res,"TEST_90",finding,"","",tag) results.saveResults(r) # re-run ai.py to take our results into consideration ret = subprocess.run(["./ai.py"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = ret.stdout.decode("utf-8")