def run(): initEngine() # Coroutine mode outputscreen.success('[+] Coroutine mode') gevent.joinall([gevent.spawn(scan) for i in range(0, th.thread_num)]) if 'errmsg' in th: outputscreen.error(th.errmsg)
def ScriptRegister(args): # handle no scripts if not args.script_name: msg = '[-] Use -s to load script. Example: [-s spider] or [-s ./script/spider.py]' outputscreen.error(msg) sys.exit() # handle input: "-s ./script/spider.py" if os.path.split(args.script_name)[0]: if os.path.exists(args.script_name): if os.path.isfile(args.script_name): if args.script_name.endswith('.py'): conf.module_name = os.path.split(args.script_name)[-1] conf.module_path = os.path.abspath(args.script_name) else: msg = '[-] [%s] not a Python file. Example: [-s spider] or [-s ./scripts/spider.py]' % args.script_name outputscreen.error('[-] ' + msg) sys.exit() else: msg = '[-] [%s] not a file. Example: [-s spider] or [-s ./scripts/spider.py]' % args.script_name outputscreen.error(msg) sys.exit() else: msg = '[-] [%s] not found. Example: [-s spider] or [-s ./scripts/spider.py]' % args.script_name outputscreen.error(msg) sys.exit() # handle input: "-s spider" "-s spider.py" else: if not args.script_name.endswith('.py'): args.script_name += '.py' _path = os.path.abspath( os.path.join(paths.SCRIPT_PATH, args.script_name)) if os.path.isfile(_path): conf.module_name = args.script_name conf.module_path = os.path.abspath(_path) else: msg = '[-] Script [%s] not exist. Use [--show] to view all available script in ./scripts/' % args.script_name outputscreen.error(msg) sys.exit() # loader POC module to conf.module_obj msg = '[+] Load custom script: %s' % conf.module_name outputscreen.success(msg) fp, pathname, description = imp.find_module( os.path.splitext(conf.module_name)[0], [paths.SCRIPT_PATH]) try: conf.module_obj = imp.load_module("_", fp, pathname, description) for each in ESSENTIAL_MODULE_METHODS: if not hasattr(conf.module_obj, each): msg = "[-] Can't find essential method:'%s' in current script,Please modify your script." % each outputscreen.error(msg) sys.exit(0) except ImportError as e: msg = "[-] Your current script [%s.py] caused this exception\n%s\n%s" \ % (conf.module_name, '[Error Msg]: ' + str(e), 'Maybe you can download this module from pip or easy_install') outputscreen.error(msg) sys.exit(0)
def TargetRegister(args): """ 加载目标模块 """ msg = '[*] Initialize targets...' outputscreen.warning(msg) #初始化目标队列 conf.target = queue.Queue() # 用户输入入队 if args.target_input: # 尝试解析目标地址 try: lists = parseTarget(args.target_input) except: helpmsg = "Invalid input in [-i], Example: -i [http://]target.com or 192.168.1.1[/24] or 192.168.1.1-192.168.1.100" outputscreen.error(helpmsg) sys.exit() # 判断处理量 if (len(lists)) > 100000: warnmsg = "[*] Loading %d targets, Maybe it's too much, continue? [y/N]" % ( len(lists)) outputscreen.warning(warnmsg) flag = input() if flag in ('Y', 'y', 'yes', 'YES', 'Yes'): pass else: msg = '[-] User quit!' outputscreen.warning(msg) sys.exit() msg = '[+] Load targets from: %s' % args.target_input outputscreen.success(msg) # save to conf for target in lists: conf.target.put(target) conf.target_nums = conf.target.qsize() # 文件读入入队 elif args.target_file: if not os.path.isfile(args.target_file): msg = '[-] TargetFile not found: %s' % args.target_file outputscreen.error(msg) sys.exit() msg = '[+] Load targets from: %s' % args.target_file outputscreen.success(msg) with open(args.target_file, 'r', encoding='utf-8') as f: targets = f.readlines() for target in targets: target = target.strip('\n') parsed_target = parseTarget(target) for i in parsed_target: conf.target.put(i) conf.target_nums = conf.target.qsize() #验证目标数量 if conf.target.qsize() == 0: errormsg = msg = '[!] No targets found.Please load targets with [-i|-iF]' outputscreen.error(errormsg) sys.exit()
def _give_it_a_try(self): """Every time this method is called it will request a random resource the target domain. Return value is a dictionary with values as HTTP response code, resquest size, md5 of the content and the content itself. If there were a redirection it will record the new url""" s = [] for n in range(0, 42): random.seed() s.append(chr(random.randrange(97, 122))) s = "".join(s) target = self.target + s outputscreen.success("[+] Checking with %s" % target) page = requests.get(target, headers=user_agent, verify=False) content = page.content result = {'code': str(page.status_code), 'size': len(content), 'md5': hashlib.md5(content).hexdigest(), 'content': content, 'location': None} if len(page.history) >= 1: result['location'] = page.url return result
def login(self): msg = '[+] Trying to login with credentials in config file: %s.' % paths.CONFIG_PATH outputscreen.success(msg) self.api_key = ConfigFileParser().shodan_apikey() if not self.api_key: msg = '[*] Automatic authorization failed.' outputscreen.warning(msg) msg = '[*] Please input your Shodan API Key (https://account.shodan.io/).' outputscreen.warning(msg) self.api_key = input('[*] API KEY > ').strip()
def initEngine(): # init control parameter th.result = [] th.thread_num = conf.thread_num th.target = conf.target #是否继续扫描标志位 th.is_continue = True #控制台宽度 th.console_width = getTerminalSize()[0] - 2 #记录开始时间 th.start_time = time.time() msg = '[+] Set the number of thread: %d' % th.thread_num outputscreen.success(msg)
def loadSingleDict(path): ''' @description: 添加单个字典文件 @param {path:字典文件路径} @return: ''' try: outputscreen.success('[+] Load dict:{}'.format(path)) #加载文件时,使用utf-8编码,防止出现编码问题 with open(path, encoding='UTF-8') as single_file: return single_file.read().splitlines() except Exception as e: outputscreen.error('[x] plz check file path!\n[x] error:{}'.format(e))
def account_info(self): try: if not self.api_key: outputscreen.error("[-] Shodan api cant not be Null") sys.exit() api = Shodan(self.api_key) account_info = api.info() msg = "[+] Available Shodan query credits: %d" % account_info.get( 'query_credits') outputscreen.success(msg) except APIError as e: outputscreen.error(e) sys.exit() return True
def run(): initEngine() if th.thread_mode: # set lock for multi_threaded mode setThreadLock() outputscreen.success('[+] Set working way Multi-Threaded mode') outputscreen.success('[+] Set the number of thread: %d' % th.concurrent_num) for i in range(th.concurrent_num): t = threading.Thread(target=scan, name=str(i)) t.setDaemon(True) t.start() # It can quit with Ctrl-C while th.concurrent_count > 0 and th.is_continue: time.sleep(0.01) # Coroutine mode else: outputscreen.success('[+] Set working way Coroutine mode') outputscreen.success('[+] Set the number of Coroutine: %d' % th.concurrent_num) gevent.joinall( [gevent.spawn(scan) for i in range(0, th.concurrent_num)]) # save result to output file if not th.no_output: output2file(th.result) printProgress() if 'errmsg' in th: outputscreen.error(th.errmsg)
def auto_login(self): msg = '[+] Trying to login with credentials in config file: %s.' % paths.CONFIG_PATH outputscreen.success(msg) try: self.username = ConfigFileParser().ZoomEyeEmail() self.password = ConfigFileParser().ZoomEyePassword() except: pass if bool(self.username and self.password): if self.get_token(): return msg = '[*] Automatic authorization failed.' outputscreen.warning(msg) self.manual_login()
def Output(args): if args.no_output and args.output_path: msg = '[-] Cannot use [-oF] and [-o] together, please read the usage with [-h].' outputscreen.error(msg) sys.exit() conf.no_output = args.no_output if not args.no_output: # if not define output, named it by time if not args.output_path: filename= time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) +'.txt' conf.output_path = os.path.join(paths.OUTPUT_PATH, filename) msg = '[+] Output: %s' % conf.output_path outputscreen.success(msg)
def initEngine(): # init control parameter th.result = [] th.thread_count = th.thread_num = conf.thread_num th.module_name = conf.module_name th.module_path = conf.module_path th.module_obj = conf.module_obj th.target = conf.target th.no_output = conf.no_output th.output_path = conf.output_path th.scan_count = th.found_count = 0 th.is_continue = True th.console_width = getTerminalSize()[0] - 2 th.start_time = time.time() setThreadLock() msg = '[+] Set the number of thread: %d' % th.thread_num outputscreen.success(msg)
def check_this(self): """Get the a request and decide what to do""" first_result = self._give_it_a_try() if first_result['code'] == '404': outputscreen.success("Got a nice 404, problems not expected") # Ok, resquest gave a 404 so we should not find problems return '', Inspector.TEST404_OK elif first_result['code'] == '302' or first_result['location']: location = first_result['location'] return location, Inspector.TEST404_URL else: return first_result['md5'], Inspector.TEST404_MD5 # We give up here :( return '', Inspector.TEST404_NONE
def handle_google(query, limit, offset=0): key = ConfigFileParser().google_developer_key() engine = ConfigFileParser().google_engine() if not key or not engine: msg = "[-] Please config your 'developer_key' and 'search_enging' at saucerfram.conf" outputscreen.error(msg) sys.exit() try: service = build("customsearch", "v1", http=_initHttpClient(), developerKey=key) result_info = service.cse().list(q=query, cx=engine).execute() msg = '[+] Max query results: %s' % str( result_info.get('searchInformation', {}).get('totalResults')) outputscreen.success(msg) ans = set() limit += offset for i in range(int(offset / 10), int((limit + 10 - 1) / 10)): result = service.cse().list(q=query, cx=engine, num=10, start=i * 10 + 1).execute() if 'items' in result: for url in result.get('items'): ans.add(url.get('link')) for t in ans: conf.target.put(t) except SocketError: outputscreen.error( '[-] Unable to connect Google, maybe agent/proxy error.') sys.exit() except ServerHttpDenied as e: outputscreen.warning( '[-] It seems like Google-Server denied this request.') outputscreen.error(e) sys.exit()
def handle_fofa(query, limit, offset=0): try: msg = '[+] Trying to login with credentials in config file: %s.' % paths.CONFIG_PATH outputscreen.success(msg) email = ConfigFileParser().fofa_email() key = ConfigFileParser().fofa_key() if check(email, key): pass else: raise # will go to except block except: msg = '[*] Automatic authorization failed.' outputscreen.warning(msg) msg = '[*] Please input your FoFa Email and API Key below.' outputscreen.warning(msg) email = input("[*] Fofa Email: ").strip() key = input('[*] Fofa API Key: ').strip() if not check(email, key): msg = '[-] Fofa API authorization failed, Please re-run it and enter a valid key.' outputscreen.error(msg) sys.exit() query = base64.b64encode(query) request = "https://fofa.so/api/v1/search/all?email={0}&key={1}&qbase64={2}".format( email, key, query) try: response = requests.get(request) resp = response.readlines()[0] resp = json.loads(resp) if resp["error"] is None: for item in resp.get('results'): cong.target.append(item[0]) if resp.get('size') >= 100: outputscreen.warning( "{0} items found! just 100 returned....".format( resp.get('size'))) except Exception as e: outputscreen.error(e) sys.exit()
def handle_zoomeye(query, limit=50, type='host', offset=0): z = ZoomEye() z.auto_login() info = z.resources_info().get('resources') if info: msg = '[+] Available ZoomEye search: (search:%s)' % (info.get( 'search', 'NO FOUND')) outputscreen.success(msg) else: msg = '[-] ZoomEye API authorization failed, Please re-run it and enter a new token.' outputscreen.error(msg) sys.exit() # 开始爬取 limit += offset for page_n in range(int(offset / 10), int((limit + 9) / 10)): data = z.dork_search(query, page=page_n, resource=type) if data: for i in data: ip_str = i.get('ip') if 'portinfo' in i: ip_str = ip_str + ':' + str(i.get('portinfo').get('port')) conf.target.put(ip_str) else: break
def checkShow(args): # if show scripts if args.show_scripts: module_name_list = os.listdir(paths.SCRIPT_PATH) outputscreen.success('[+] Script Name ') order = 1 for module in module_name_list: if module != '__init__.py' and os.path.splitext(module)[1] == '.py': outputscreen.success(str(order)+ '. ' +module) order += 1 msg = '\n' + ' ' * 20 + 'Total: %d' % (order-1) outputscreen.success(msg) sys.exit()
def bruter(url): ''' @description: 扫描插件入口函数 @param {url:目标} @return: ''' #全局target的url,给crawl、fuzz模块使用。FIXME conf.url = url #url初始化 conf.parsed_url = urllib.parse.urlparse(url) #填补协议 if conf.parsed_url.scheme != 'http' and conf.parsed_url.scheme != 'https': url = 'http://' + url conf.parsed_url = urllib.parse.urlparse(url) #填补url后的/ if not url.endswith('/'): url = url + '/' #自动识别404-预先获取404页面特征 if conf.auto_check_404_page: outputscreen.warning("[*] Launching auto check 404") # Autodiscriminator (probably deprecated by future diagnostic subsystem) i = Inspector(url) (result, notfound_type) = i.check_this() if notfound_type == Inspector.TEST404_URL: conf.autodiscriminator_location = result outputscreen.success("[+] 404 ---> 302 ----> {}".format( conf.autodiscriminator_location)) elif notfound_type == Inspector.TEST404_MD5: conf.autodiscriminator_md5 = result outputscreen.success("[+] 404 ---> PAGE_MD5 ----> {}".format( conf.autodiscriminator_md5)) #加载payloads #添加payloads是否加载成功判断 payloads.all_payloads = scanModeHandler() if payloads.all_payloads == None: outputscreen.error('[x] load payloads error!') if conf.dict_mode: outputscreen.error('[x] plz check dict mode config!') if conf.blast_mode: outputscreen.error('[x] plz check blast mode config!') if conf.crawl_mode: outputscreen.error('[x] plz check crawl mode config!') if conf.fuzz_mode: outputscreen.error('[x] plz check fuzz mode config!') sys.exit() #FIXME:设置后缀名。当前以拼接方式实现,遍历一遍payload。 try: if conf.file_extension: outputscreen.warning('[+] Use file extentsion: {}'.format( conf.file_extension)) for i in range(len(payloads.all_payloads)): payloads.all_payloads[i] += conf.file_extension except: outputscreen.error('[+] plz check extension!') #debug模式,打印所有payload,并退出 if conf.debug: outputscreen.blue('[+] all payloads:{}'.format(payloads.all_payloads)) sys.exit() #payload入队task队列 for payload in payloads.all_payloads: #FIXME:添加fuzz模式时,引入的url_payload构造判断 if conf.fuzz_mode: url_payload = conf.parsed_url.scheme + '://' + conf.parsed_url.netloc + payload else: url_payload = url + payload #payload入队,等待处理 tasks.all_task.put(url_payload) #设置进度条长度,若是递归模式,则不设置任务队列长度,即无法显示进度,仅显示耗时 if not conf.recursive_scan: tasks.task_length = tasks.all_task.qsize() bar.log.start(tasks.task_length) #FIXME:循环任务数不能一次性取完所有的task,暂时采用每次执行30个任务。这样写还能解决hub.LoopExit的bug while not tasks.all_task.empty(): all_task = [gevent.spawn(boss) for i in range(conf.request_limit)] gevent.joinall(all_task)
def bruter(url): ''' @description: 扫描插件入口函数 @param {url:目标} @return: ''' #url初始化 conf.parsed_url = urllib.parse.urlparse(url) #填补协议 if conf.parsed_url.scheme != 'http' and conf.parsed_url.scheme != 'https': url = 'http://' + url conf.parsed_url = urllib.parse.urlparse(url) #全局target的url,给crawl、fuzz模块使用。XXX:要放在填补url之前,否则fuzz模式会出现这样的问题:https://target.com/phpinfo.{dir}/ conf.url = url #填补url后的/ if not url.endswith('/'): url = url + '/' #打印当前target msg = '[+] Current target: {}'.format(url) outputscreen.success('\r' + msg + ' ' * (th.console_width - len(msg) + 1)) #自动识别404-预先获取404页面特征 if conf.auto_check_404_page: outputscreen.warning("[*] Launching auto check 404") # Autodiscriminator (probably deprecated by future diagnostic subsystem) i = Inspector(url) (result, notfound_type) = i.check_this() if notfound_type == Inspector.TEST404_MD5 or notfound_type == Inspector.TEST404_OK: conf.autodiscriminator_md5.add(result) #加载payloads payloads.all_payloads = scanModeHandler() #FIXME:设置后缀名。当前以拼接方式实现,遍历一遍payload。 try: if conf.file_extension: outputscreen.warning('[+] Use file extentsion: {}'.format( conf.file_extension)) for i in range(len(payloads.all_payloads)): payloads.all_payloads[i] += conf.file_extension except: outputscreen.error('[+] plz check extension!') sys.exit() #debug模式,打印所有payload,并退出 if conf.debug: outputscreen.blue('[+] all payloads:{}'.format(payloads.all_payloads)) sys.exit() #payload入队task队列 for payload in payloads.all_payloads: #FIXME:添加fuzz模式时,引入的url_payload构造判断 if conf.fuzz_mode: url_payload = conf.parsed_url.scheme + '://' + conf.parsed_url.netloc + payload else: url_payload = url + payload #print(url_payload) #payload入队,等待处理 tasks.all_task.put(url_payload) #设置进度条长度,若是递归模式或爬虫模式,则不设置任务队列长度,即无法显示进度,仅显示耗时 if not conf.recursive_scan: #NOTE:这里取所有payloads的长度*target数量计算任务总数,修复issue#2 tasks.task_length = len(payloads.all_payloads) * conf.target_nums bar.log.start(tasks.task_length) #FIXME:循环任务数不能一次性取完所有的task,暂时采用每次执行30个任务。这样写还能解决hub.LoopExit的bug while not tasks.all_task.empty(): all_task = [gevent.spawn(boss) for i in range(conf.request_limit)] gevent.joinall(all_task)
def TargetRegister(args): """ 加载目标模块 """ msg = '[*] Initialize targets...' outputscreen.warning(msg) #初始化目标队列 conf.target = queue.Queue() #单目标入队 if args.target_single: msg = '[+] Load target: %s' % args.target_single outputscreen.success(msg) conf.target.put(args.target_single) #多目标入队 elif args.target_file: if not os.path.isfile(args.target_file): msg = '[-] TargetFile not found: %s' % args.target_file outputscreen.error(msg) sys.exit() msg = '[+] Load targets from: %s' % args.target_file outputscreen.success(msg) with open(args.target_file, 'r', encoding='utf8') as f: targets = f.readlines() for target in targets: conf.target.put(target.strip('\n')) #ip范围目标入队.e.g. 192.168.1.1-192.168.1.100 elif args.target_range: try: lists = genIP(args.target_range) if (len(lists)) > 100000: warnmsg = "[*] Loading %d targets, Maybe it's too much, continue? [y/N]" % ( len(lists)) outputscreen.warning(warnmsg) flag = input() if flag in ('Y', 'y', 'yes', 'YES', 'Yes'): pass else: msg = '[-] User quit!' outputscreen.warning(msg) sys.exit() msg = '[+] Load targets from: %s' % args.target_range outputscreen.success(msg) # save to conf for target in lists: conf.target.put(target) except: helpmsg = "Invalid input in [-iR], Example: -iR 192.168.1.1-192.168.1.100" outputscreen.error(helpmsg) sys.exit() # ip/mask e.g. 192.168.1.2/24 elif args.target_network: try: # get 192.168.1.2 -->192.168.1.0 ip_format = args.target_network.split('/') ip_str = IP(ip_format[0]).strBin() ip_str = ip_str[0:int(ip_format[1])] + '0' * (32 - int(ip_format[1])) ip = "%s.%s.%s.%s" % ( str(int(ip_str[0:8], 2)), str(int(ip_str[8:16], 2)), str(int(ip_str[16:24], 2)), str(int(ip_str[24:32], 2))) ip_range = IP('%s/%s' % (ip, ip_format[1])) msg = '[+] Load targets from: %s' % args.target_network outputscreen.success(msg) for i in ip_range: conf.target.put(i) except: msg = "[-] Invalid input in [-iN], Example: -iN 192.168.1.0/24" outputscreen.error(msg) sys.exit() #验证目标数量 if conf.target.qsize() == 0: errormsg = msg = 'No targets found\nPlease load targets with [-iU|-iF|-iR|-iN] or use API with [-aZ|-aS|-aG|-aF]' outputscreen.error(errormsg) sys.exit()
def TargetRegister(args): msg = '[*] Initialize targets...' outputscreen.warning(msg) # init target queue conf.target = queue.Queue() # single target to queue if args.target_single: msg = '[+] Load target : %s' % args.target_single outputscreen.success(msg) conf.target.put(args.target_single) # file target to queue elif args.target_file: if not os.path.isfile(args.target_file): msg = '[-] TargetFile not found: %s' % args.target_file outputscreen.error(msg) sys.exit() msg = '[+] Load targets from : %s' % args.target_file outputscreen.success(msg) with open(args.target_file, 'r', encoding='utf8') as f: targets = f.readlines() for target in targets: conf.target.put(target.strip('\n')) # range of ip target to queue .e.g. 192.168.1.1-192.168.1.100 elif args.target_range: try: lists = gen_ip(args.target_range) if (len(lists)) > 100000: warnmsg = "[*] Loading %d targets, Maybe it's too much, continue? [y/N]" % ( len(lists)) outputscreen.warning(warnmsg) flag = input() if flag in ('Y', 'y', 'yes', 'YES', 'Yes'): pass else: msg = '[-] User quit!' outputscreen.warning(msg) sys.exit() msg = '[+] Load targets from : %s' % args.target_range outputscreen.success(msg) # save to conf for target in lists: conf.target.put(target) except: helpmsg = "Invalid input in [-iR], Example: -iR 192.168.1.1-192.168.1.100" outputscreen.error(helpmsg) sys.exit() # ip/mask e.g. 192.168.1.2/24 elif args.target_network: try: # get 192.168.1.2 -->192.168.1.0 ip_format = args.target_network.split('/') ip_str = IP(ip_format[0]).strBin() ip_str = ip_str[0:int(ip_format[1])] + '0' * (32 - int(ip_format[1])) ip = "%s.%s.%s.%s" % ( str(int(ip_str[0:8], 2)), str(int(ip_str[8:16], 2)), str(int(ip_str[16:24], 2)), str(int(ip_str[24:32], 2))) ip_range = IP('%s/%s' % (ip, ip_format[1])) msg = '[+] Load targets from : %s' % args.target_network outputscreen.success(msg) for i in ip_range: conf.target.put(i) except: msg = "[-] Invalid input in [-iN], Example: -iN 192.168.1.0/24" outputscreen.error(msg) sys.exit() else: # set search limit of api if args.api_limit <= 0: errormsg = 'Invalid input in [-limit] (can not be negative number)' outputscreen.error(errormsg) sys.exit() elif args.api_limit > 100000: warnmsg = "Loading %d targets, Maybe it's too much, continue? [y/N]" % ( len(lists)) outputscreen.warning(warnmsg) flag = input() if flag in ('Y', 'y', 'yes', 'YES', 'Yes'): pass else: msg = 'User quit!' outputscreen.warning(msg) sys.exit() conf.limit = args.api_limit # set search offset of api conf.offset = args.api_offset if args.zoomeye_dork: # verify search_type for zoomeye if args.search_type not in ['web', 'host']: msg = '[-] Invalid value in [--search-type], show usage with [-h]' outputscreen.error(msg) sys.exit() conf.search_type = args.search_type handle_zoomeye(query=args.zoomeye_dork, limit=conf.limit, type=conf.search_type, offset=conf.offset) elif args.fofa_dork: handle_fofa(query=args.fofa_dork, limit=conf.limit, offset=conf.offset) elif args.shodan_dork: handle_shodan(query=args.shodan_dork, limit=conf.limit, offset=conf.offset) elif args.google_dork: conf.google_proxy = args.google_proxy handle_google(query=args.google_dork, limit=conf.limit, offset=conf.offset) # verify targets number if conf.target.qsize() == 0: errormsg = msg = 'No targets found\nPlease load targets with [-iU|-iF|-iR|-iN] or use API with [-aZ|-aS|-aG|-aF]' outputscreen.error(errormsg) sys.exit()