ssti模板注入原理
ssti模板注入是一种基于服务器的模板引擎的特性和漏洞产生的一种漏洞,通过将而已代码注入模板中实现的服务器的攻击
模板引擎
为什么要有模板引擎
在web开发中,为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。在不同的技术领域下有很多模板引擎,比如php和c等等都有各自的模板引擎
模板定义
模板是一种包含占位符和静态内容的文本文件,占位符用于表示在运行时将被动态替换的数据或执行的逻辑。例如,在一个 HTML 模板中,可能会有{{ name }}
这样的占位符,用于表示用户的姓名。
模板的渲染和替换
当应用程序需要生成最终的页面或文档时,模板引擎会读取模板文件,并根据提供的数据将占位符替换为实际的值。同时,模板引擎还会执行模板中定义的各种逻辑,如循环、条件判断等,最终生成完整的 HTML 或其他格式的文档发送给客户端。以{{name}}为例在模板引擎渲染的过程中,name这个字符串就可能被动态数据内容替换,或者在进行一定逻辑运算之后产生新的字符串,例如在HTML文档中,如果提供了名为name
且值为 “张三” 的数据,那么渲染之后的效果就是{{张三}}
原理
模板引擎有很多种类型,例如最简单的置换型,从名称上我们也可以大致推断他的用处,还有复杂一点的解释型、编译型等等模板引擎。不同的模板引擎可以应用于不同的场景。
使用场景
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。还能实现其他类的功能。
ssti模板注入产生的原因
程序可能在没有经过完善的验证和过滤的情况下,将用户的输入直接嵌入到模板中或者作为参数嵌入到模板引擎中,如果用户输入包含恶意代码,可能造成意想不到的代码执行效果。
用大白话举个例子:网站程序可能给你提供了向网站输入数据的途径,但是对于你的输入,网站对他的处理不是很到位,导致你的输入可能经过模板引擎渲染之后执行了,产生了一些效果,或者是你的输入直接破坏了模板,导致之后没达到渲染效果或者是敏感信息泄露、getshell等等问题。
所以在了解完产生原因之后我们肯定也知道了,对于不同的模板,他的逻辑可能不一样,表达的字符串也可能不一样,各种方面都可能不一样;所以对于不同的模板,注入的代码可能也不一样,注入的形式可能也不一样……
ssti一般的注入过程
注入前
了解注入网站程序的模板信息、编程语言等等;在之后注入时构造payload时要根据这两个方面进行。
了解一些模板引擎的特点和性质等等,比如python的jinja2,php的Smarty、Twig和Blade,java的Thymeleaf 和Velocity 等等,后面用的flask框架用的就是python的jinja2
注入
根据搜集到的信息构造payload,例如,在 Jinja2 中,可以使用{{ }}
来包裹表达式,使用{% %}
来包裹控制语句。{{ config['SECRET_KEY'] }}
可以用于获取 Flask 应用程序的密钥。 {{ __import__('os').system('ls -la') }}
可以在服务器上执行ls -la
命令,列出当前目录下的文件和目录信息。
找到注入点,可能是登录注册的输入栏,也可能时评论的输入栏等等,如果有源码,可以进行代码审计找到注入点,或者通过其他办法找到注入点。
Flask
定义
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。
由于塔容易上手,我们使用这个来尝试一下ssti漏洞。
这是一个简单的py代码,调用了flask库
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '你好'
if __name__ == '__main__':
app.run()
代码的用途可以去了解一下,我们运行这个代码,在本地网址5000端口可以看到回显
我们尝试搭建一个用来测试ssti模板注入的网站
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/safe')
def safe():
name = request.args.get('name', 'World')
if any(char in name for char in ['{', '}', '%', '#']):
name = '非法输入'
template = '<h1>安全页面:你好,{{ name }}!</h1>'
return render_template_string(template, name=name)
@app.route('/unsafe')
def unsafe():
name = request.args.get('name', 'World')
template = f'<h1>不安全页面:你好,{name}!</h1>'
return render_template_string(template)
@app.route('/danger')
def danger():
expr = request.args.get('expr', '2+2')
try:
result = eval(expr)
except Exception as e:
result = f"错误: {str(e)}"
template = f'<h1>表达式结果: {result}</h1>'
return render_template_string(template)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=True)