啟動並在配置好之后,巡風就i在后端開始的資產探測的掃描,先來看一下需要啟動的三個腳本:aider.py、nasscan.py和vulscan.py
0x01:aider.py
這個腳本主要作用有兩個,一是用作dns,建立socket連接,一個簡單的DNS log平台,啟動兩個線程,一個線程執行udp服務,一個執行http服務;二是用來判斷無返回類型的服務
import socket,thread,datetime,time query_history = [] url_history = [] def web_server(): # 創建http服務 web = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 監聽8088(http)端口 web.bind(('0.0.0.0',8088)) # 監聽端口 web.listen(10) while True: try: # 被動接受TCP客戶端連接,(阻塞式)等待連接的到來 # 連接成功返回非負值,失敗時返回-1 conn,addr = web.accept() # recv接受tcpp數據,最大為4096字節 data = conn.recv(4096) req_line = data.split("\r\n")[0] path = req_line.split()[1] route_list = path.split('/') html = "NO" if len(route_list) == 3: if route_list[1] == 'add': if route_list[2] not in url_history: url_history.append(route_list[2]) elif route_list[1] == 'check': if route_list[2] in url_history: url_history.remove(route_list[2]) html = 'YES' else: query_str = route_list[1] for query_raw in query_history: if query_str in query_raw: query_history.remove(query_raw) html = "YES" print datetime.datetime.now().strftime('%m-%d %H:%M:%S') + " " + str(addr[0]) +' web query: ' + path raw = "HTTP/1.0 200 OK\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: %d\r\nConnection: close\r\n\r\n%s" %(len(html),html) conn.send(raw) conn.close() except: pass if __name__=="__main__": # 創建一個socket對象,規定套接字家族和類型(非面向連接) dns = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 監聽53(udp)端口 dns.bind(('0.0.0.0', 53)) # start_new_thread創建一個新線程,返回線程標識符 thread.start_new_thread(web_server,()) while True: try: # 接受udp數據,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。 recv,addr = dns.recvfrom(1024) # 將請求添加到query_history數組中。 if recv not in query_history:query_history.append(recv) print datetime.datetime.now().strftime('%m-%d %H:%M:%S') + " " +str(addr[0]) +' Dns Query: ' + recv except Exception,e: print e
0x02:nasscan.py
大致的順一下這個腳本的功能順序:獲取前端配置-進行日志記錄-讀取統計信息-判斷是否應該掃描(包括兩種情況)-刪除失效記錄-開始掃描
這個腳本主要是用來進行網絡資產的掃描,包括探測存活主機、開放端口、服務等。
if __name__ == "__main__": try: # 讀取配置 CONFIG_INI = get_config() # 日志記錄,傳入參數scan_type, host, port, info log.write('info', None, 0, u'獲取配置成功') # 讀取統計信息 STATISTICS = get_statistics() print STATISTICS MASSCAN_AC = [0] # 標識符 masscan是否在使用 NACHANGE = [0] # 標識符 掃描列表是否被改變 thread.start_new_thread(monitor, (CONFIG_INI, STATISTICS, NACHANGE)) # 心跳線程,主要用於判斷掃描配置是否發生了變化 thread.start_new_thread(cruise, (STATISTICS, MASSCAN_AC)) # 失效記錄刪除線程 socket.setdefaulttimeout(int(CONFIG_INI['Timeout']) / 2) # 設置連接超時 ac_data = [] # 掃描循環 while True: # 獲取當前具體時間信息 now_time = time.localtime() # 獲取當前小時 now_hour = now_time.tm_hour # 獲取當前星期幾 now_day = now_time.tm_mday # 獲取年月日 now_date = str(now_time.tm_year) + str(now_time.tm_mon) + str(now_day) # 獲取資產探測周期 cy_day, ac_hour = CONFIG_INI['Cycle'].split('|') log.write('info', None, 0, u'掃描規則: ' + str(CONFIG_INI['Cycle'])) # 判斷是否進入掃描時段 # 判斷是否達到了一個掃描的周期,或者心跳線程是否檢測到掃描列表更新 # 在心跳線程中可以看到base64不同時會將NACHANGE[0]置於1 if (now_hour == int(ac_hour) and now_day % int(cy_day) == 0 and now_date not in ac_data) or NACHANGE[0]: # 判斷是否掃描過列表 ac_data.append(now_date) NACHANGE[0] = 0 log.write('info', None, 0, u'開始掃描') # 聲明一個start對象,並傳入配置參數 s = start(CONFIG_INI) # 標識masscan是否在使用,標識掃描列表是否被改變 s.masscan_ac = MASSCAN_AC s.statistics = STATISTICS # 開始掃描 s.run() time.sleep(60) except Exception, e: print e
這個方法用來獲取配置頁面中的各項配置
def get_config(): config = {} # 從mongodb中讀取`nascan`的配置,可以看到Config集合中有`vulscan`和`nascan`的掃描配置 config_info = mongo.na_db.Config.find_one({"type": "nascan"}) for name in config_info['config']: # 對於cms識別、組件容器、動態語言、服務 的配置存儲是使用`|`進行分割存儲的 # 所以在取出之前要進行簡單的格式化然后放到配置中 if name in ['Discern_cms', 'Discern_con', 'Discern_lang', 'Discern_server']: config[name] = format_config(name, config_info['config'][name]['value']) else: config[name] = config_info['config'][name]['value'] return config
用來進行日志記錄
import threading import time import sys reload(sys) sys.setdefaultencoding('utf8') # 線程互斥鎖 mutex = threading.Lock() def write(scan_type, host, port, info): # 上鎖,避免多個進程輸出,導致格式混亂 mutex.acquire() port = int(port) try: time_str = time.strftime('%X', time.localtime(time.time())) # 根據傳入的scan_type,判斷輸出內容 if scan_type == 'portscan': print "[%s] %s:%d open" % (time_str, host, port) elif scan_type == 'server': print "[%s] %s:%d is %s" % (time_str, host, port, str(info)) elif scan_type == 'web': print "[%s] %s:%d is web" % (time_str, host, port) print "[%s] %s:%d web info %s" % (time_str, host, port, info) elif scan_type == 'active': print "[%s] %s active" % (time_str, host) elif scan_type == 'info': print "[%s] %s" % (time_str, info) except Exception, e: print 'logerror',e pass # 釋放鎖 mutex.release()
讀取統計信息
def get_statistics(): # 獲取當日的統計信息 date_ = datetime.datetime.now().strftime('%Y-%m-%d') now_stati = mongo.na_db.Statistics.find_one({"date": date_}) if not now_stati: # 沒有當日的信息則返回一個初始統計信息 now_stati = {date_: {"add": 0, "update": 0, "delete": 0}} return now_stati else: # 有則返回 return {date_: now_stati['info']}
心跳線程,判斷是否到達掃描周期或資產探測列表已改變
# 心跳線程,主要用於判斷掃描配置是否發生了變化,如果改變,則立即觸發掃描 def monitor(CONFIG_INI, STATISTICS, NACHANGE): while True: try: time_ = datetime.datetime.now() # 記錄心跳 date_ = time_.strftime('%Y-%m-%d') mongo.na_db.Heartbeat.update({"name": "heartbeat"}, {"$set": {"up_time": time_}}) if date_ not in STATISTICS: STATISTICS[date_] = {"add": 0, "update": 0, "delete": 0} # 更新統計信息 mongo.na_db.Statistics.update({"date": date_}, {"$set": {"info": STATISTICS[date_]}}, upsert=True) new_config = get_config() # 獲取最新配置 # 比較配置掃描列表的base64是否相同,不同則置NACHANGE[0]為1,說明配置發生改名,立即出發掃描 if base64.b64encode(CONFIG_INI["Scan_list"]) != base64.b64encode(new_config["Scan_list"]):NACHANGE[0] = 1 CONFIG_INI.clear() # 更新新配置 CONFIG_INI.update(new_config) except Exception, e: print e # 每30秒檢測一次 time.sleep(30)
失效記錄刪除線程
# 失效記錄,刪除線程,STATISTICS統計信息,MASSCAN_AC判斷是否啟用masscan def cruise(STATISTICS,MASSCAN_AC): while True: # 獲取當前日期(年 月 日 時 分 秒) now_str = datetime.datetime.now() # 獲取當天是星期幾0-6 week = int(now_str.weekday()) # 獲取當前時間的整點數 hour = int(now_str.hour) if week >= 1 and week <= 5 and hour >= 9 and hour <= 18: # 非工作時間不刪除 try: # 獲取掃描信息記錄,根據time字段進行升序排列 data = mongo.NA_INFO.find().sort("time", 1) for history_info in data: while True: # 如果masscan正在掃描即不進行清理,在用masscan進行掃描的時候會置1 if MASSCAN_AC[0]: # 如果masscan正在掃描即不進行清理 time.sleep(10) else: break ip = history_info['ip'] port = history_info['port'] try: # 檢測端口是否存活 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, int(port))) sock.close() except Exception, e: time_ = datetime.datetime.now() date_ = time_.strftime('%Y-%m-%d') # 不存活則刪除改記錄 mongo.NA_INFO.remove({"ip": ip, "port": port}) # 日志記錄 log.write('info', None, 0, '%s:%s delete' % (ip, port)) STATISTICS[date_]['delete'] += 1 del history_info["_id"] history_info['del_time'] = time_ history_info['type'] = 'delete' # 添加一條操作歷史 mongo.NA_HISTORY.insert(history_info) except: pass # 每小時檢測一次 time.sleep(3600)
start類,掃描的主要方法,包括IP地址段的解析,端口的掃描,白名單的繞過等
class start: def __init__(self, config): # 默認配置 # 傳入CONFIG_INI 配置,然后設置類的屬性 self.config_ini = config self.queue = Queue.Queue() # 隊列對象 self.thread = int(self.config_ini['Thread']) # 最大線程數 self.scan_list = self.config_ini['Scan_list'].split('\n') # 掃描列表 self.mode = int(self.config_ini['Masscan'].split('|')[0]) # MASSCAN配置 self.icmp = int(self.config_ini['Port_list'].split('|')[0]) # 端口列表 self.white_list = self.config_ini.get('White_list', '').split('\n') # 白名單 # 啟動函數 def run(self): # 在start.py中定義的全局變量,端口列表 global AC_PORT_LIST all_ip_list = [] for ip in self.scan_list: # 處理CIDR格式的ip, eg:192.168.0.1/24 # 就不具體跟進看了,大約40行左右,涉及一些位運算格式轉換啥的 if "/" in ip: # 把CIDR格式的地址轉換成地址段 ip = cidr.CIDR(ip) if not ip: continue # 獲取IP列表 ip_list = self.get_ip_list(ip) # 對於白名單ip進行移除 for white_ip in self.white_list: if white_ip in ip_list: ip_list.remove(white_ip) # 是否開始了masscan掃描,開啟了mode置為1,否則為0 if self.mode == 1: # 獲取文件路徑 masscan_path = self.config_ini['Masscan'].split('|')[2] # 獲取掃描速率 masscan_rate = self.config_ini['Masscan'].split('|')[1] # 如果用戶在前台關閉了ICMP存活探測則進行全IP段掃描 # 獲取存活IP if self.icmp: ip_list = self.get_ac_ip(ip_list) self.masscan_ac[0] = 1 # 如果安裝了Masscan即使用Masscan進行全端口掃描 AC_PORT_LIST = self.masscan( ip_list, masscan_path, masscan_rate) if not AC_PORT_LIST: continue # 將self.masscan_ac[0]置0,表示結束使用 self.masscan_ac[0] = 0 for ip_str in AC_PORT_LIST.keys(): self.queue.put(ip_str) # 加入隊列 self.scan_start() # 開始掃描 else: all_ip_list.extend(ip_list) # 不使用masscan掃描 if self.mode == 0: if self.icmp: all_ip_list = self.get_ac_ip(all_ip_list) for ip_str in all_ip_list: self.queue.put(ip_str) # 加入隊列 self.scan_start() # TCP探測模式開始掃描 # 開始掃描 def scan_start(self): for i in range(self.thread): # 開始掃描 t = ThreadNum(self.queue) t.setDaemon(True) t.mode = self.mode t.config_ini = self.config_ini t.statistics = self.statistics t.start() self.queue.join() def masscan(self, ip, masscan_path, masscan_rate): try: if len(ip) == 0: return sys.path.append(sys.path[0] + "/plugin") # 導入masscan.py,並在下面調用它的run方法 m_scan = __import__("masscan") result = m_scan.run(ip, masscan_path, masscan_rate) return result except Exception, e: print e print 'No masscan plugin detected' # 接受cidr格式的ip,返回IP列表 def get_ip_list(self, ip): ip_list_tmp = [] def iptonum(x): return sum([256 ** j * int(i) for j, i in enumerate(x.split('.')[::-1])]) def numtoip(x): return '.'.join( [str(x / (256 ** i) % 256) for i in range(3, -1, -1)]) if '-' in ip: ip_range = ip.split('-') ip_start = long(iptonum(ip_range[0])) ip_end = long(iptonum(ip_range[1])) ip_count = ip_end - ip_start if ip_count >= 0 and ip_count <= 655360: for ip_num in range(ip_start, ip_end + 1): ip_list_tmp.append(numtoip(ip_num)) else: print 'IP format error' else: ip_split = ip.split('.') net = len(ip_split) if net == 2: for b in range(1, 255): for c in range(1, 255): ip = "%s.%s.%d.%d" % (ip_split[0], ip_split[1], b, c) ip_list_tmp.append(ip) elif net == 3: for c in range(1, 255): ip = "%s.%s.%s.%d" % ( ip_split[0], ip_split[1], ip_split[2], c) ip_list_tmp.append(ip) elif net == 4: ip_list_tmp.append(ip) else: print "IP format error" return ip_list_tmp # 通過ping請求來探測主機存活,后期只對存活主機進行掃描 def get_ac_ip(self, ip_list): try: s = icmp.Nscan() ipPool = set(ip_list) return s.mPing(ipPool) except Exception, e: print 'The current user permissions unable to send icmp packets' return ip_list
動態引入masscan.py腳本(m_scan = __import__("masscan"))
def run(ip_list,path,rate): try: ip_file = open('target.log','w') # 將存活的ip列表寫到target.log中 ip_file.write("\n".join(ip_list)) ip_file.close() # 進行過濾一些危險字符,translate用來轉換字符串字符 path = str(path).translate(None, ';|&`\n') rate = str(rate).translate(None, ';|&`\n') if not os.path.exists(path):return # 用系統命令進行masscan全端口掃描 os.system("%s -p1-65535 -iL target.log -oL tmp.log --randomize-hosts --rate=%s"%(path,rate)) # 讀取掃描結果 result_file = open('tmp.log', 'r') result_json = result_file.readlines() result_file.close() del result_json[0] del result_json[-1] open_list = {} # 對掃描結果進行格式化處理 for res in result_json: try: ip = res.split()[3] port = res.split()[2] if ip in open_list: open_list[ip].append(port) else: open_list[ip] = [port] except:pass os.remove('target.log') os.remove('tmp.log') # 返回掃描結果 return open_list except: pass
0x03:vulscan.py
用於對掃出的資產進行漏洞掃描,具體的掃描過程依賴於vuldb中的插件形式進行掃描,
main函數:腳本運行的開始函數,主要是啟動心跳線程、漏斗檢查線程、更新kunpeng插件線程、獲取任務、清理插件緩存等。
if __name__ == '__main__': init() # 密碼字典、線程數量、超時時間、白名單 PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST = get_config() # 開啟心跳線程 thread.start_new_thread(monitor, ()) # 開啟漏斗檢查線程 thread.start_new_thread(kp_check, ()) # 開啟更新kunpeng線程 thread.start_new_thread(kp_update, ()) while True: try: # 獲取未執行任務的任務id,、任務周期、任務目標、任務插件 task_id, task_plan, task_target, task_plugin = queue_get() if task_id == '': time.sleep(10) continue # 清理插件緩存 if PLUGIN_DB: del sys.modules[PLUGIN_DB.keys()[0]] PLUGIN_DB.clear() for task_netloc in task_target: while True: # thread._count返回正在運行的線程數量,包括主線程 if int(thread._count()) < THREAD_COUNT: # 繞過白名單 if task_netloc[0] in WHITE_LIST: break # 進行漏斗檢測 try: thread.start_new_thread( vulscan, (task_id, task_netloc, task_plugin)) except Exception as e: print e break else: time.sleep(2) # 更新status文檔 if task_plan == 0: na_task.update({"_id": task_id}, {"$set": {"status": 2}}) except Exception as e: print e
init函數:用來初始化,獲取插件列表,安裝kunpeng等。
def init(): time_ = datetime.datetime.now() if na_plugin.find().count() >= 1: return script_plugin = [] json_plugin = [] print 'init plugins' # 獲取插件列表 file_list = os.listdir(sys.path[0] + '/vuldb') # 把插件添加到對應的列表中 for filename in file_list: try: if filename.split('.')[1] == 'py': script_plugin.append(filename.split('.')[0]) if filename.split('.')[1] == 'json': json_plugin.append(filename) except: pass for plugin_name in script_plugin: try: # 動態加載 res_tmp = __import__(plugin_name) # 獲取插件信息 plugin_info = res_tmp.get_plugin_info() plugin_info['add_time'] = time_ plugin_info['filename'] = plugin_name plugin_info['count'] = 0 na_plugin.insert(plugin_info) except: pass for plugin_name in json_plugin: try: json_text = open(sys.path[0] + '/vuldb/' + plugin_name, 'r').read() plugin_info = json.loads(json_text) plugin_info['add_time'] = time_ plugin_info['filename'] = plugin_name plugin_info['count'] = 0 del plugin_info['plugin'] na_plugin.insert(plugin_info) except: pass # 安裝kunpeng install_kunpeng_plugin()
get_config:從數據庫里查找配置,白名單、線程數、超時時間等
def get_config(): try: # 在Config集合中,找到type=vulscan的文檔 config_info = na_config.find_one({"type": "vulscan"}) # 下面都是獲取這個文檔中的一些值,都是字典類型的 pass_row = config_info['config']['Password_dic'] thread_row = config_info['config']['Thread'] timeout_row = config_info['config']['Timeout'] white_row = config_info['config']['White_list'] password_dic = pass_row['value'].split('\n') thread_count = int(thread_row['value']) timeout = int(timeout_row['value']) white_list = white_row['value'].split('\n') return password_dic, thread_count, timeout, white_list except Exception, e: print e
monitor:心跳線程函數,看的不是很明白,希望大佬們可以支教
def monitor(): global PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST while True: # 從數據庫里找到相應的值 queue_count = na_task.find({"status": 0, "plan": 0}).count() if queue_count: load = 1 else: ac_count = thread._count() load = float(ac_count - 6) / THREAD_COUNT if load > 1: load = 1 if load < 0: load = 0 na_heart.update({"name": "load"}, { "$set": {"value": load, "up_time": datetime.datetime.now()}}) PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST = get_config() if load > 0: time.sleep(8) else: time.sleep(60)
kp_check:調用kunpeng,進行檢查
def kp_check(): while True: try: new_release = kp.check_version() print new_release if new_release: info = new_release['body'] if '###' in new_release['body']: info = new_release['body'].split('###')[1] row = { 'info': info, 'isInstall': 0, 'name': new_release['name'], 'author': new_release['author']['login'], 'pushtime': new_release['published_at'], 'location': "", 'unicode': new_release['tag_name'], 'coverage': 0, 'source': 'kunpeng' } na_update.insert(row) time.sleep(60 * 60 * 48) except Exception as e: print e time.sleep(60 * 30)
kp_update:更新kunpeng插件
def kp_update(): while True: try: # 找到相應的文檔刪除,並返回刪除的數量 row = na_update.find_one_and_delete( {'source': 'kunpeng', 'isInstall': 1}) if row: kp.update_version(row['unicode']) na_plugin.delete_many({'_id':re.compile('^KP')}) install_kunpeng_plugin() except Exception as e: print e time.sleep(10)
queue_get:獲取任務信息,id、周期、插件等
def queue_get(): # 全局字典 global TASK_DATE_DIC # 在Task這個集合中提取並更新數據,找到所有status等於0,plan等於0的文檔,返回后更新status=1,然后根據時間升序排列 # find_and_modify task_req = na_task.find_and_modify(query={"status": 0, "plan": 0}, update={ "$set": {"status": 1}}, sort={'time': 1}) # 返回未執行任務 if task_req: TASK_DATE_DIC[str(task_req['_id'])] = datetime.datetime.now() return task_req['_id'], task_req['plan'], task_req['target'], task_req['plugin'] # 不存在還未執行任務,返回空或返回定期執行任務 else: # $ne表示不等於 task_req_row = na_task.find({"plan": {"$ne": 0}}) if task_req_row: # 執行定期任務 for task_req in task_req_row: if (datetime.datetime.now() - task_req['time']).days / int(task_req['plan']) >= int(task_req['status']): if task_req['isupdate'] == 1: task_req['target'] = update_target( json.loads(task_req['query'])) na_task.update({"_id": task_req['_id']}, { "$set": {"target": task_req['target']}}) na_task.update({"_id": task_req['_id']}, { "$inc": {"status": 1}}) TASK_DATE_DIC[str(task_req['_id']) ] = datetime.datetime.now() return task_req['_id'], task_req['plan'], task_req['target'], task_req['plugin'] return '', '', '', ''
vulscan類:這是主要的,漏斗的檢測主要在這里面進行。
class vulscan(): def __init__(self, task_id, task_netloc, task_plugin): self.task_id = task_id # 任務id self.task_netloc = task_netloc # 任務目標 self.task_plugin = task_plugin # 任務插件 self.result_info = '' self.start() # 線程啟動函數,與run不同,會在一個新線程里開啟 def start(self): self.get_plugin_info() # 獲取插件信息 if '.json' in self.plugin_info['filename']: # 標示符檢測模式 self.load_json_plugin() # 讀取漏洞標示 self.set_request() # 標示符轉換為請求 self.poc_check() # 檢測 elif 'KP-' in self.plugin_info['filename']: self.log(str(self.task_netloc) + 'call kunpeng - ' + self.plugin_info['filename']) kp.set_config(TIMEOUT, PASSWORD_DIC) if self.task_netloc[1] != 80: self.result_info = kp.check('service', '{}:{}'.format( self.task_netloc[0], self.task_netloc[1]), self.plugin_info['filename']) if not self.result_info: scheme = 'http' if self.task_netloc[1] == 443: scheme = 'https' self.result_info = kp.check('web', '{}://{}:{}'.format( scheme, self.task_netloc[0], self.task_netloc[1]), self.plugin_info['filename']) else: # 腳本檢測模式 plugin_filename = self.plugin_info['filename'] self.log(str(self.task_netloc) + 'call ' + self.task_plugin) if task_plugin not in PLUGIN_DB: # 字典 plugin_res = __import__(plugin_filename) setattr(plugin_res, "PASSWORD_DIC", PASSWORD_DIC) # 給插件聲明密碼字典 PLUGIN_DB[plugin_filename] = plugin_res self.result_info = PLUGIN_DB[plugin_filename].check( str(self.task_netloc[0]), int(self.task_netloc[1]), TIMEOUT) self.save_request() # 保存結果 # 獲取插件信息 def get_plugin_info(self): # 從插件庫中找到插件 info = na_plugin.find_one({"name": self.task_plugin}) self.plugin_info = info def load_json_plugin(self): json_plugin = open(sys.path[0] + '/vuldb/' + self.plugin_info['filename']).read() self.plugin_info['plugin'] = json.loads(json_plugin)['plugin'] # 構造請求 def set_request(self): url = 'http://' + \ self.task_netloc[0] + ":" + \ str(self.task_netloc[1]) + self.plugin_info['plugin']['url'] if self.plugin_info['plugin']['method'] == 'GET': request = urllib2.Request(url) else: request = urllib2.Request(url, self.plugin_info['plugin']['data']) self.poc_request = request # 獲取代碼語言,主要是通過正則匹配 def get_code(self, header, html): try: m = re.search(r'<meta.*?charset=(.*?)"(>| |/)', html, flags=re.I) if m: return m.group(1).replace('"', '') except: pass try: if 'Content-Type' in header: Content_Type = header['Content-Type'] m = re.search(r'.*?charset=(.*?)(;|$)', Content_Type, flags=re.I) if m: return m.group(1) except: pass def poc_check(self): try: # 發送請求 res = urllib2.urlopen(self.poc_request, timeout=30) res_html = res.read(204800) # 獲取i請求頭 header = res.headers # res_code = res.code except urllib2.HTTPError, e: # res_code = e.code header = e.headers res_html = e.read(204800) except Exception, e: return try: # 獲取編碼語言 html_code = self.get_code(header, res_html).strip() if html_code and len(html_code) < 12: res_html = res_html.decode(html_code).encode('utf-8') except: pass an_type = self.plugin_info['plugin']['analyzing'] vul_tag = self.plugin_info['plugin']['tag'] analyzingdata = self.plugin_info['plugin']['analyzingdata'] if an_type == 'keyword': # print poc['analyzingdata'].encode("utf-8") if analyzingdata.encode("utf-8") in res_html: self.result_info = vul_tag elif an_type == 'regex': if re.search(analyzingdata, res_html, re.I): self.result_info = vul_tag elif an_type == 'md5': md5 = hashlib.md5() md5.update(res_html) if md5.hexdigest() == analyzingdata: self.result_info = vul_tag # def save_request(self): if self.result_info: time_ = datetime.datetime.now() self.log(str(self.task_netloc) + " " + self.result_info) v_count = na_result.find( {"ip": self.task_netloc[0], "port": self.task_netloc[1], "info": self.result_info}).count() if not v_count: na_plugin.update({"name": self.task_plugin}, {"$inc": {'count': 1}}) vulinfo = {"vul_name": self.plugin_info['name'], "vul_level": self.plugin_info['level'], "vul_type": self.plugin_info['type']} w_vul = {"task_id": self.task_id, "ip": self.task_netloc[0], "port": self.task_netloc[1], "vul_info": vulinfo, "info": self.result_info, "time": time_, "task_date": TASK_DATE_DIC[str(self.task_id)]} na_result.insert(w_vul) # self.wx_send(w_vul) # 自行定義漏洞提醒 def log(self, info): lock.acquire() try: time_str = time.strftime('%X', time.localtime(time.time())) print "[%s] %s" % (time_str, info) except: pass lock.release()
參考文章:https://xz.aliyun.com/t/4104#toc-9
*************不積跬步無以至千里*************
