def process(report: ReportWrapper) -> Optional[VulnTestInfo]: """ Process the given report into a VulnTestInfo named tuple """ # If the user has not yet been prompted for automatic triaging if not report.botHasCommented(): token = AutoTriageUtils.generateToken() return VulnTestInfo(reproduced=False, message=constants.initialMessage(token, 'redirect to a domain', 'Open Redirect'), type='Open Redirect', info={}) elif report.shouldBackoff(): if not report.hasPostedBackoffComment(): addFailureToDB(report.getReporterUsername(), report.getReportID()) return VulnTestInfo(reproduced=False, message=('Automatic verification of vulnerability has failed, Backing off! Falling ' 'back to human verification. '), type='Open Redirect', info={}) else: return None elif report.isVerified(): return None try: if isStructured(report.getLatestActivity()): return processStructured(report, token=report.getToken()) else: return processUnstructured(report, token=report.getToken()) except Exception as e: print("Caught exception: %s" % str(e)) traceback.print_exc() print("+" * 80) return VulnTestInfo(reproduced=False, message=('Internal error detected! Backing off...'), type='Open Redirect', info={})
def test_ReportWrapperGetters(): r = ReportWrapper(openRedirectReproJson) assert r.getReportID() == '239981' assert r.getLatestActivity() == ("blah open_redirect\n\n[some](http://example.com/redir.php?QUERY_STRING=" "https://google.com)") assert r.getReportBody() == ("blah open_redirect\n\n[some](http://example.com/redir.php?QUERY_STRING=" "https://google.com)") assert r.getReportWeakness() == "Open Redirect" assert r.getReportTitle() == "open redirect" assert r.getVulnDomains() == ['example.com'] r = ReportWrapper(openRedirectUnreproJson) assert r.getReportID() == '240035' assert r.getLatestActivity() == ("this is detected as an open redirect but there is no markdown link to it\n\n" "https://example.com/redir.php?QUERY_STRING=https://google.com") assert r.getReportBody() == ("this is detected as an open redirect but there is no markdown link to it\n\n" "https://example.com/redir.php?QUERY_STRING=https://google.com") assert r.getReportWeakness() == "Open Redirect" assert r.getReportTitle() == "malformed open redirect" assert r.getVulnDomains() == ['example.com']
def test_api(): ids = json.loads( requests.post('http://api:8080/v1/getReportIDs', json={ 'time': '1970-01-01T00:00:00Z', 'openOnly': False }, auth=HTTPBasicAuth('AutoTriageBot', secrets.apiBoxToken)).text) openIDs = json.loads( requests.post('http://api:8080/v1/getReportIDs', json={ 'time': '1970-01-01T00:00:00Z', 'openOnly': True }, auth=HTTPBasicAuth('AutoTriageBot', secrets.apiBoxToken)).text) assert isinstance(ids, list) assert isinstance(openIDs, list) assert len(openIDs) <= len( ids ) # There should be an equal or lesser number of open bugs than all bugs assert all([(id in ids) for id in openIDs]) # All open ids should be in ids for id in ids: # They should be strings but they should be parseable into integers assert isinstance(id, str) and isinstance(int(id), int) # There should be no duplicate IDs assert len(set(ids)) == len(ids) for id in ids[:10]: ser = requests.post('http://api:8080/v1/getReport', json={ 'id': id }, auth=HTTPBasicAuth('AutoTriageBot', secrets.apiBoxToken)).text try: r = ReportWrapper().deserialize(ser) except: assert False for serRep in json.loads( requests.post('http://api:8080/v1/getReports', auth=HTTPBasicAuth('AutoTriageBot', secrets.apiBoxToken)).text)[:10]: try: r = ReportWrapper().deserialize(serRep) except: assert False assert r.getReportID() in ids
def processReport(report: ReportWrapper, startTime: datetime) -> Optional[VulnTestInfo]: """ Attempt to verify a given report """ if report.needsBotReply(): if startTime > report.getReportedTime(): return None if config.DEBUG: print("Processing %s" % report.getReportTitle()) for module in modules: if module.match(report.getReportBody(), report.getReportWeakness()): # type: ignore if config.DEBUG: print(module.__file__.split('/')[-1] + " matched id=%s!" % report.getReportID()) vti = module.process(report) # type: ignore if config.DEBUGVERBOSE: print(vti) if vti: postComment(report.getReportID(), vti, addStopMessage=True) if vti.reproduced and config.metadataLogging: metadataVTI = generateMetadataVTI(report, vti) postComment(report.getReportID(), metadataVTI, internal=True) return vti if config.DEBUG: print("No matches") return None
def processReport(report: ReportWrapper) -> None: """ Process the given report and post a private comment with a suggested bounty """ if config.payoutDB: bountyInfo = suggestPayout(report) if bountyInfo: postComment(report.getReportID(), VulnTestInfo( reproduced=False, info={}, message='Suggested bounty: %.2f with a σ of %.2f' % (bountyInfo.average, bountyInfo.std), type=''), internal=True) else: if config.DEBUGVERBOSE: print("Not suggesting a payout beause config.payoutDB is falsy")
def generateMetadataVTI(report: ReportWrapper, vti: VulnTestInfo) -> VulnTestInfo: """ Given the results of a vulnerability test thar reproduced a vulnerability and a report, generate an internal VTI used to hold metadata about the vulnerability """ internalMetadata = {'id': report.getReportID(), 'title': report.getReportTitle(), 'reportedTime': str(report.getReportedTime()), 'verifiedTime': str(datetime.now()), 'type': vti.type, 'exploitURL': vti.info['src'], 'method': vti.info['method']} if vti.type == 'XSS': internalMetadata['confirmedBrowsers'] = vti.info['confirmedBrowsers'] internalMetadata['alertBrowsers'] = vti.info['alertBrowsers'] internalMetadata['httpType'] = vti.info['httpType'] internalMetadata['cookies'] = vti.info['cookies'] elif vti.type == 'SQLi': internalMetadata['delay'] = vti.info['delay'] internalMetadata['httpType'] = vti.info['httpType'] internalMetadata['cookies'] = vti.info['cookies'] elif vti.type == 'Open Redirect': internalMetadata['redirect'] = vti.info['redirect'] internalMetadata['httpType'] = vti.info['httpType'] internalMetadata['cookies'] = vti.info['cookies'] message = '# Internal Metadata: \n\n```\n%s\n```\n' % json.dumps(internalMetadata, sort_keys=True, indent=4, separators=(',', ': ')) if config.DEBUGVERBOSE: print(internalMetadata) internalVTI = VulnTestInfo(reproduced=False, message=message, info={}, type='') return internalVTI
def processReport(report: ReportWrapper) -> bool: """ Process a report via searching for duplicates and posting comments based off of the confidence levels Returns whether or not the report was classified as a duplicate with a high confidence """ if report.getState() == "new" and not report.hasDuplicateComment( ) and not report.isVerified(): earlierReports = getAllOpenReports( report.getReportedTime()) # type: List[ReportWrapper] idConfTuples = [] # type: List[Tuple[str, int]] matches = [] # type: List[str] for earlierReport in earlierReports: for module in modules: if (module.match(report.getReportBody(), report.getReportWeakness()) and # type: ignore module.match(earlierReport.getReportBody(), earlierReport.getReportWeakness()) ): # type: ignore matches.append(earlierReport.getReportID()) try: confidence = int(isDuplicate(earlierReport, report)[0]) except TypeError: confidence = 0 if confidence == 99: AutoTriageUtils.postComment( report.getReportID(), VulnTestInfo( message='Found a duplicate with 99%% confidence: #%s' % earlierReport.getReportID(), info={}, reproduced=False, type=''), internal=True) if config.DEBUG: print("Detected that %s (%s) is a duplicate of %s (%s)!" % (report.getReportID(), report.getReportTitle(), earlierReport.getReportID(), earlierReport.getReportTitle())) return False # Change to return True to make the bot stop interacting after finding a duplicate elif confidence > 50: idConfTuples.append((earlierReport.getReportID(), confidence)) # If you update the phrases here, you must also update them in AutoTriageUtils.ReportWrapper.hasDuplicateComment if len(idConfTuples) > 0: def idConfToStr(tuple: Tuple) -> str: return ( 'Detected a possible duplicate report with confidence of %s: #%s' % (tuple[1], tuple[0])) AutoTriageUtils.postComment(report.getReportID(), VulnTestInfo(message='\n'.join([ idConfToStr(t) for t in idConfTuples ]), info={}, reproduced=False, type=''), internal=True) if config.DEBUG: print('Found partial matches: %s' % str(idConfTuples)) if len(matches) > 0 and len(matches) <= 5: AutoTriageUtils.postComment( report.getReportID(), VulnTestInfo(message=( 'There are currently %s open reports about this type of ' 'vulnerability: %s' % (str(len(matches)), ', '.join(['#' + id for id in matches]))), info={}, reproduced=False, type=''), internal=True) if config.DEBUG: print( 'Found %s reports on the same type of vulnerability as %s: %s' % (str(len(matches)), str(report.getReportID()), ', '.join( ['#' + id for id in matches]))) return False