SSTI模板注入

前言

开局一张图,姿势全靠yy

模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这大大提升了开发效率,良好的设计也使得代码重用变得更加容易。与此同时,它也扩展了黑客的攻击面。除了常规的 XSS 外,注入到模板中的代码还有可能引发 RCE(远程代码执行)。通常来说,这类问题会在博客,CMS,wiki 中产生。虽然模板引擎会提供沙箱机制,攻击者依然有许多手段绕过它。

和常见Web注入的成因一样,也是服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

小课堂---模板渲染是what?

首先 模板渲染分解为前端渲染和后端渲染,还有浏览器渲染。

模板只是一种提供给程序来解析的一种语法,换句话说,模板是用于从数据(变量)到实际的视觉表现(HTML代码)这项工作的一种实现手段,而这种手段不论在前端还是后端都有应用。

通俗点理解:拿到数据,塞到模板里,然后让渲染引擎将赛进去的东西生成 html 的文本,返回给浏览器,这样做的好处展示数据快,大大提升效率。

后端渲染

后端渲染HTML的情况下,浏览器会直接接收到经过服务器计算之后的呈现给用户的最终的HTML字符串,这里的计算就是服务器经过解析存放在服务器端的模板文件来完成的,在这种情况下,浏览器只进行了HTML的解析,以及通过操作系统提供的操纵显示器显示内容的系统调用在显示器上把HTML所代表的图像显示给用户。

实现:后端拼字符串呗…… (理论上后端模板也是字符串)

好处:模板统一在后端。前端(相对)省事,不占用客户端运算资源(解析模板),只要不大改结构,文字啥的小修改后端改了就好了。

坏处:占用(部分、少部分)服务器运算资源、,response 出的数据量会(稍)大点,模板改了前端的交互和样式什么的一样得跟着联动修改。

前端渲染

前端渲染就是指浏览器会从后端得到一些信息,这些信息或许是适用于题主所说的angularjs的模板文件,亦或是JSON等各种数据交换格式所包装的数据,甚至是直接的合法的HTML字符串。这些形式都不重要,重要的是,将这些信息组织排列形成最终可读的HTML字符串是由浏览器来完成的,在形成了HTML字符串之后,再进行显示。

好处:不占用服务端运算资源(解析模板),模板在前端(很有可能仅部分在前端),改结构变交互都前端自己来了,改完自己调就行。不用麻烦后端再联调神马的。

坏处:占用(一部分、少部分)客户端运算资源(解析模板)。前端代码多点,毕竟包含模板代码了么。脚本是不是首次下就慢点了(看你在意不在意这个毕竟能304和CDN啥的)。可能造成前后两份模板的情况,总归要后端吐出个首屏啥的先让用户看见吧。那这部分页面模板不就是后端拼好了吐出来的么。

示例1:定义一个模板,例如

    <html>

    <div>{$what}</div>

    </html>

这只是一个模板。{$what}是数据。此时不知道数据是什么。

    如果我想html里面成为

    <div>peiqi</div>

    渲染到html代码里

渲染完成后

    <html>

    <div>peiqi</div>

    </html>

当然这只是最简答的例子;

一般来说,至少会提供分支,迭代。还有一些内置函数,如格式化等等

那么 {$what}这个数据如何代码传入。

比如我工作用的模板引擎是smarty

我定义了一个模板。

    <html>

    <div>{$what}</div>

    </html>    

js代码就是这么写就可以了

    var tpl= new jSmart(tplStr);//tplStr就是模板的字符串。

    var content = "Hello World";

    tpl.fetch({what:content});

这样就可以了

其余的就交给引擎去渲染执行了。

示例2:

模板:front.tpl

    <div>

    {%$a%}

    </div>

后端:

设置变量:$smarty->assign('a', 'give data');

展示模板:$smarty->display("front.tpl");

到前端时是渲染好的html串:

    <div>

    give data

    </div>

这种方式的特点是展示数据快,直接后端拼装好数据与模板,展现到用户面前。

为什么需要服务器模板

首先解释一下为什么HTML代码和应用程序逻辑混合在一起不好,看下面的例子你就知道了。假如你使用下面的代码为你的用户提供服务:

这不仅仅是静态HTML代码。用户名是从cookie里获取并且自动填写的。这样一来,只要你之前登录过该网站,你就无需再次输入。但是这有一个问题,那就是你必须通过某种形式将值插入到HTML文档中,有两种方法可以实现,一种是正确的,一种是有危害的。不过我们要先问一下,为什么要这么做。

下图展示了解决该问题的完全错误的方法:

这段代码有很多问题,作者不仅仅没有对用户的输入进行处理,HTML代码和PHP代码复杂的混合在一起,非常难以理解。部分HTML代码分布在多个函数中,这还不算什么,当你尝试修改HTML代码中任何内容时,你才发现有多难受,比如新增css类或修改HTML标签的顺序。

上面这个例子显然是有意写的这么烂的,不过可以通过格式化进行优化。然而,在大型的代码库中,即使代码格式良好,也会很快变得无法管理。这就是为什么我们需要模板。

与上面混乱的代码相比,服务器端模板提供了一种更加简单的方法来管理动态生成的HTML代码。最大的优点就是你可以在服务器端动态生成HTML页面,看起来跟静态HTML页面一样。现在我们来看看,当我们使用服务器端模板时,复杂的代码看起来如何。

这对前面的代码做了一些优化,它依然是静态的。为了显示正确的信息而不是大括号占位符,我们需要一个替换它们的模板引擎。后端代码可能是这样的。

这段代码的意思非常清楚,首先加载login.tpl模板文件,然后对与模板中名称相同的变量赋值(大括号里的变量),然后调用show()函数,相应的替换它们的内容并输出HTML代码。然而,我们在模板中增加了新的功能,这将会向用户展示模板渲染的时间。

什么是服务端模板注入

通过模板,Web应用可以把输入转换成特定的HTML文件或者email格式。就拿一个销售软件来说,我们假设它会发送大量的邮件给客户,并在每封邮件前SKE插入问候语,它会通过Twig(一个模板引擎)做如下处理:

$output = $twig->render( $_GET['custom_email'] , array("first_name" => $user.first_name) );

有经验的读者可能迅速发现 XSS,但是问题不止如此。这行代码其实有更深层次的隐患,假设我们发送如下请求:

    custom_email={{7*7}} // GET 参数

    49  // $output 结果

    还有更神奇的结果:

    custom_email={{self}} // GET 参数

    Object of class

    __TwigTemplate_7ae62e582f8a35e5ea6cc639800ecf15b96c0d6f78db3538221c1145580ca4a5

    could not be converted to string // 错误

我们不难猜到服务器执行了我们传过去的数据。每当服务器用模板引擎解析用户的输入时,这类问题都有可能发生。除了常规的输入外,攻击者还可以通过 LFI(文件包含)触发它。模板注入和 SQL 注入的产生原因有几分相似——都是将未过滤的数据传给引擎解析。

为什么我们在模板注入前加“服务端”呢?这是为了和 jQuery,KnockoutJS 产生的客户端模板注入区别开来。通常的来讲,前者甚至可以让攻击者执行任意代码,而后者只能 XSS。

注入原理

    <?php

    require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php';

    Twig_Autoloader::register(true);

    $twig = new Twig_Environment(new Twig_Loader_String());

    $output = $twig->render("Hello {{name}}", array("name" => $_GET["name"]));  // 将用户输入作为模版变量的值

    echo $output;

    ?>

使用 Twig 模版引擎渲染页面,其中模版含有 {{name}} 变量,其模版变量值来自于 GET 请求参数 $_GET["name"]。

显然这段代码并没有什么问题,即使你想通过 name 参数传递一段 JavaScript 代码给服务端进行渲染,也许你会认为这里可以进行 XSS,

但是由于模版引擎一般都默认对渲染的变量值进行编码和转义,所以并不会造成跨站脚本攻击:

但是,如果渲染的模版内容受到用户的控制,情况就不一样了。修改代码为:

    <?php

    require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php';

    Twig_Autoloader::register(true);

    $twig = new Twig_Environment(new Twig_Loader_String());

    $output = $twig->render("Hello {$_GET['name']}");  // 将用户输入作为模版内容的一部分

    echo $output;    

    ?>

上面这段代码在构建模版时,拼接了用户输入作为模板的内容,现在如果再向服务端直接传递 JavaScript 代码,用户输入会原样输出,测试结果显而易见。

在 Twig 模板引擎里, {{var}}  除了可以输出传递的变量以外,还能执行一些基本的表达式然后将其结果作为该模板变量的值,例如这里用户输入 name={{2*10}} ,则在服务端拼接的模版内容为:

这里简单分析一下,由于 {# comment #}  作为 Twig 模板引擎的默认注释形式,所以在前端输出的时候并不会显示,而 {{2*8}}  作为模板变量最终会返回 16  作为其值进行显示,因此前端最终会返回内容 Hello IsVuln16OK ,如下图:

漏洞发掘

每一个(重)模板引擎都有着自己的语法(点),Payload 的构造需要针对各类模板引擎制定其不同的扫描规则,就如同 SQL 注入中有着不同的数据库类型一样。

简单来说,就是更改请求参数使之承载含有模板引擎语法的 Payload,通过页面渲染返回的内容检测承载的 Payload 是否有得到编译解析,有解析则可以判定含有 Payload 对应模板引擎注入,否则不存在 SSTI。

模板语言的语法和 HTML 语法相差甚大,因此我们可以用其独特的语法来探测漏洞。虽然各种模板的实现细节不大一样,不过它们的基本语法大致相同,我们可以发送如下 payload:

    smarty=Hello ${7*7}

    Hello 49

    freemarker=Hello ${7*7}

    Hello 49

来确认漏洞。

在一些环境下,用户的输入也会被当作模板的可执行代码。比如说变量名:

    personal_greeting=username

    Hello user01

这种情况下,XSS 的方法就无效了。但是我们可以通过破坏 template 语句,并附加注入的HTML标签以确认漏洞:

    personal_greeting=username<tag>

    Hello

    personal_greeting=username}}<tag>

    Hello user01 <tag>

但是,如果渲染的模版内容受到用户的控制,情况就不一样了。修改代码为:

    <?php

    require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php';

    Twig_Autoloader::register(true);

    $twig = new Twig_Environment(new Twig_Loader_String());

    $output = $twig->render("Hello {$_GET['name']}");  // 将用户输入作为模版内容的一部分

    echo $output;    

    ?>

上面这段代码在构建模版时,拼接了用户输入作为模板的内容,现在如果再向服务端直接传递 JavaScript 代码,用户输入会原样输出,测试结果显而易见。

模板引擎注入

模板引擎

模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。

一些模板引擎:Smarty,Mako,Jinja2,Jade,Velocity,Freemaker和Twig,模板注入是一种注入攻击,可以产生一些特别有趣的影响。对于AngularJS的情况,这可能意味着XSS,并且在服务器端注入的情况下可能意味着远程代码执行。

重点来了,不同引擎有不同的测试以及注入方式!

flask/jinja2模板注入

Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2

Flask框架中提供的模版引擎可能会被一些无量开发者利用引入一个服务端模版注入漏洞,如果对此感到有些困惑可以看看James Kettle在黑帽大会中分享的议题(PDF),简而言之这个漏洞允许将语言/语法注入到模板中。在服务器的context中执行这个输入重现,根据应用的context可能导致任意远程代码执行(远端控制设备)

参考文章:https://www.freebuf.com/articles/web/88768.html

参考文章:https://www.freebuf.com/articles/web/98928.html

PHP/模版引擎Twig注入

可以参考本博客文章 Flask从零到无 。

这里给出一个漏洞环境代码,本地测试

    from flask import Flask

    from flask import render_template

    from flask import request

    from flask import render_template_string

    app = Flask(__name__)

    @app.route('/test',methods=['GET', 'POST'])

    def test():

        template = '''

            <div class="center-content error">

                <h1>Oops! That page doesn't exist.</h1>

                <h3>%s</h3>

            </div> 

        ''' %(request.url)

return render_template_string(template)

    if __name__ == '__main__':

        app.debug = True

        app.run()

代码简析: 我们自己简单写一个string类型的 html,html返回当前url,我们放入到渲染函数render_template_string进行渲染,然后页面会打印出当前url,如果url里含有{{}} 那么便可以进行模板注入。

测试url http://127.0.0.1:5000/test?{{config}}

测试结果如下:


而如果我们使用render_template函数,

    @app.route('/',methods=['GET', 'POST'])

    @app.route('/index',methods=['GET', 'POST'])#我们访问/或者/index都会跳转

    def index():

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

index.html

    <html>

      <head>

        <title>{{title}} - 小猪佩奇</title>

      </head>

      <body>

          <h1>Hello, {{user}}!</h1>

      </body>

    </html>

那么将不会有模板注入,因为render_template已经传入一个固定好了的模板,没法再去修改,在渲染之后传入数据,只有当第一种代码,我们模板可控的时候,先传入后渲染,这样才会导致ssti模板注入。

参考:https://www.freebuf.com/vuls/83999.html

CTF---模板注入

tornado的一道 ssti

https://blog.csdn.net/cccccfive/article/details/83145669

防御

为了防止此类漏洞,你应该像使用eval()函数一样处理字符串加载功能。尽可能加载静态模板文件。

注意:我们已经确定此功能类似于require()函数调用。因此,你也应该防止本地文件包含(LFI)漏洞。不要允许用户控制此类文件或其内容的路径。

另外,无论在何时,如果需要将动态数据传递给模板,不要直接在模板文件中执行,你可以使用模板引擎的内置功能来扩展表达式,实现同样的效果。

附表

---

参考:https://www.anquanke.com/post/id/82856

参考:https://zhuanlan.zhihu.com/p/28823933

参考:https://www.cnblogs.com/tyomcat/p/5440488.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    X先生_未知数的X阅读 15,934评论 3 118
  • 1. 昨夜下过的雨,给冬季的清晨又增添了一丝丝凉意,周围的事物都是那么肃静,窗外的树嘎然不动,只有树叶悄悄然飘零着...
    圆谨阅读 357评论 4 4
  • 然谷中医阅读 71评论 0 0
  • 今天看见做舞美的朋友的朋友圈晒了一组照片,大捧大捧的粉色、白色花插在造型特别的玻璃花瓶中,一看就很贵,不是廉价的红...
    路人甲一阅读 245评论 0 0
  • 今天我教大家的是 自制"百香果柠檬优格” 先准备冰块 百香果一个 柠檬一个 接着切开 先挖出百香果 因为我的比较小...
    鱼子桃酱阅读 488评论 2 5