模板注入攻击SSTI

概念

模板

参考 https://www.cnblogs.com/dojo-lzz/p/5518474.html
模板的诞生是为了将显示与数据分离,模板技术多种多样,但其本质是将模板文件和数据通过模板引擎生成最终的 HTML 代码。

模板引擎

模板引擎要做的事情主要分为 4 步

  1. 利用正则表达式分解出普通字符串和模板标识符
  2. 将模板标识符转换成普通的语言表达式
  3. 生成待执行语句
  4. 将数据填入执行,生成最终的字符串

模板注入

参考 https://www.cnblogs.com/bmjoker/p/13508538.html
SSTI 就是服务器端模板注入(Server-Side Template Injection)
服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。
凡是使用模板的地方都可能会出现 SSTI 的问题,SSTI 不属于任何一种语言

(python)沙盒逃逸

参考 https://www.freebuf.com/articles/web/326406.html

  • 沙盒
    沙盒,亦称沙箱,在早期主要用于对可疑文件进行测试以及对于病毒危害程度进行测试,现如今已经发展到对网站进行对物理机的隔离,类似于虚拟机的使用
  • 沙盒逃逸
    此处的沙箱逃逸 代表的意思是 从一个被做了严格限制的 python 执行环境中获取到更高的权限,甚至 getshell
    对于 python 的沙盒逃逸而言,我们想实现目的的最终想法有以下几个:
  1. 使用 os 包中的 popen,system 两个函数来直接执行 shell
  2. 使用 commands 模块中的方法
  3. 使用 subprocess
  4. 使用写文件到指定位置,再使用其他辅助手段

(PYTHON)中的(SSTI)

PYTHON-FLASK (Jinja2)

使用 Jinja2 作为渲染引擎

  • Jinja2 中变量
    控制结构 {% %}
    变量取值 {{ }}
    注释 {# #}
  • 路由
1
2
3
4
5
6
7
from flask import Flask, render_template_string, request
app = Flask(__name__)
@app.route('/index/')
def hello_word():
return 'hello word'
if __name__ == '__main__':
app.run()
  • 渲染
    渲染的方法有 render_template 和 render_template_string 两种
  1. render_template
1
2
3
@app.route('/')
def zhuye():
return render_template('主页.html')
  1. render_template_string
1
2
3
4
@app.route('/index/')
def hello_word():
html = '<h1>This is index page</h1>'
return render_template_string(html)
  • 模板使用
    结构
  • 使用
  1. 不安全使用
1
2
3
4
5
6
7
@app.route('/test/')
def test():
code = request.args.get('id')
html = '''
<h3>%s</h3>
'''%(code)
return render_template_string(html)

参数 id 传值会被解析,直接将用户输入作为了模板,不会经过转义和过滤的步骤

结合 xss

2. 安全使用

1
2
3
4
@app.route('/test/')
def test():
code = request.args.get('id')
return render_template_string('<h1>{{ code }}</h1>', code=code)

id 的值只会原样返回,不会被解析,模板引擎会对输入变量进行编码转义

利用方法

不正确的使用 flask 中的 render_template_string 方法会引发 SSTI

  • 什么是魔术方法
    参考 https://zhuanlan.zhihu.com/p/329962624
    在 Python 中,所有以双下划线 __ 包起来的方法,统称为 Magic Method(魔术方法),它是一种的特殊方法,普通方法需要调用,而魔术方法不需要调用就可以自动执行。
    魔术方法在类或对象的某些事件出发后会自动执行,让类具有神奇的 “魔力”。如果希望根据自己的程序定制自己特殊功能的类,那么就需要对这些方法进行重写。
  • 几种常用于 ssti 的魔术方法
返回类型所属的对象
1
2
3
4
5
6
7
__mro__   返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。  
__base__ 返回该对象所继承的基类
ps:__base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
__builtins__builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以可以直接调用引用的模块
  • 获取基类的几种方法
1
2
3
4
[].__class__.__base__
''.__class__.__mro__
().__class__.__base__
{}.__class__.__base__
  • 获取基本类的子类
1
[].__class__.__base__.__subclasses__()
  1. 文件读取

    只需要从这些类中寻找需要的类,用数组下标获取,然后执行该类中想要执行的函数即可。比如第 41 个类是 file 类,就可以构造利用
    ().__class__.__base__.__subclasses__()[41]('路径').read()
    如果没有 file 类,使用类 <class ‘_frozen_importlib_external.FileLoader’>,可以进行文件的读取
    (94, <class ‘_frozen_importlib_external.FileLoader’>)
    我这里是第 94
    [].__class__.__base__.__subclasses__()[94].get_data(0, "D:\post.txt")
  2. 命令执行
访问量 访客