python—request库/SSTI模板注入

python—request库/SSTI模板注入

XXXXXXXXXXXXXXX

2025-09-24 发布16 浏览 · 0 点赞 · 0 收藏




SSTI模板注入

1.SSTI模板注入介绍

SSTI模板注入是一种特殊的代码注入攻击,在使用模板引擎的Web应用程序中广泛存在。攻击者通过输入恶意数据,触发模板引擎解析执行攻击代码,从而控制应用程序并获取敏感信息。

以下是一个基于Jinja2模板引擎的SSTI模板注入攻击案例:

1、首先,攻击者访问目标网站并找到一个可以利用的输入点,如搜索框或评论框等。

2、攻击者在输入框中输入以下Jinja2代码:

该代码将显示应用程序的配置项。

3、当应用程序解析该输入时,恶意代码将被执行,显示应用程序的配置项,如下所示:

[('SECRET_KEY', '123456789'), ('SQLALCHEMY_DATABASE_URI', 'mysql://root:password@localhost/test'), ('DEBUG', 'True')]

通过SSTI模板注入攻击,攻击者可以轻松地获取应用程序的敏感信息并最终控制服务器。因此,开发人员应该采取必要的安全措施,防止SSTI模板注入攻击。

二、为什么需要服务器模板

简单来说,页面上的数据需要不断更新,即为渲染。后台语言通过一些模板引擎生成HTML的过程。(常见的模板渲染引擎Jade, YAML )

因为Web Application 最终是要落实到HTML、CSS、JavaScript等用户界面上的,有一种情况是,每一个页面都需要特殊的逻辑,随着应用功能的增加,而且彼此之间没有同步,比如你改了站点的布局风格,那么随之就要修改成百上千的HTML文件。谁能忍?

既然如此多的HTML具有一定的逻辑联系,何不使用代码生成代码?于是后端模板语言诞生了。分别有前端渲染和后端(服务器)渲染,区别是:

后端渲染是将一些模板规范语言翻译成如上三种语言回传给前端;而前端渲染则是将整个生成逻辑代码全部回传前端,再由客户端生成用户界面。

所以服务器模板提供了一种更加简单的方法来管理动态生成HTML,一个demo演示

前端:

<head>

    <title>{{title}}</title>

</head>

<body>

    <form method="{{method}}" action="{{action}}">

        <input type="text" name="user" value="{{username}}">

        <input type="password" name="pass" value="">

        <button type="submit">submit</button>

    </form>

    <p>Used {{mikrotime(true) - time}}</p>

</body>

后端:

$template Engine=new TempLate Engine() ;

​template Engine-load File('login.tpl') ;

$template->assign('title', 'login') ;

$template->assign('method', 'post') ;

$template->assign('action', 'login.php') ;

$template->assign('username', get Username From Cookie() ) ;

$template->assign('time', microtime(true) ) ;

$template->show() ;

首先加载login.tpl模板文件,然后对与模板中名称相同的变量赋值(大括号里的变量),然后调用show()函数,相应的替换它们的内容并输出HTML代码。

三、漏洞原理

当用户直接输入数据到模板不作任何过滤的时,可能会发生服务端模板注入攻击。

仅仅提供了占位符,并在其中呈现动态内容的静态模板通常不会受到SSTI攻击。经典的示例是一封电子邮件,其中用每个用户的名字向他们致意,例如以下Twig模板:

twig->render("Dear {first_name},", array("first_name" => $user.first_name) );

此时无法进行SSTI攻击,因为用户的名字仅仅是作为数据传入模板中。

然而,由于模板只是字符串,web开发者有时会在生成HTML之前,直接将用户的输入呈现到模板中。简单的以上述demo为例,此处用户能够在发送邮件之前自定义部分邮件。例如,他们也许能够选择使用的名称:

twig->render("Dear " . $_GET['name']);

在此示例中,不是使用静态值传递到模板中,而是使用GET参数名称动态生成模板本身的一部分。 在服务器端对模板语法进行动态生成时,这可能使攻击者可以将服务器端模板注入有效负载放置在name参数内,如下所示:

http://vulnerable-website.com/?name={{payload}}

诸如此类的漏洞有时是由不熟悉安全隐患的人由于不良模板设计导致的意外所致。 就像上面的示例一样,可能会看到不同的组件,其中一些包含用户输入,这些用户输入已直接输出到并嵌入到模板中。 在某些方面,这类似于编写不当的预编译语句中发生的SQL注入漏洞。

但是,有时这种行为实际上是有意实施的。 例如,某些网站故意允许某些特权用户(例如内容编辑器)通过设计来编辑或提交自定义模板。 如果攻击者能够利用这种特权来破坏帐户,则显然会带来巨大的安全风险。

四、利用过程

发现漏洞

寻找SSTI漏洞需要代码审计或者直接黑盒引发报错,最简单的方法是通过注入模板表达式中常用的特殊字符来Fuzz,例如:$ {{<%[%'“}}%\。如果引发了报错,则表明服务器模板可能存在漏洞。SSTI漏洞通常发生在两个不同的上下文中,所以要根据特定的上下文来进行Fuzz。

大多数模板语言都允许直接使用HTML标记或使用模板的语法自由输入内容,这些模板将在发送HTTP响应之前在后端呈现为HTML。例如,在Freemarker中,render('Hello'+ username)行将呈现为Hello Carlos之类的东西。

有时可以将其用于XSS,实际上经常被误认为是简单的XSS漏洞。但是,通过上述的方法注入模板表达式的特殊字符,我们可以测试这是否也是服务器端模板注入攻击的潜在入口点。

例如,如下就是一个有漏洞的模板:

render('Hello' + username);

攻击者可以通过请求URL来验证SSTI攻击:

http://vulnerable-website.com/?username=${7*7}

如果结果输出包含Hello 49,则表明该数学运算表达式正在服务器端进行渲染生成。这是服务器端模板注入漏洞的典型证明。

另外,漏洞可能将用户的输入放置在模板表达式中来暴露的,例如:

greeting = getQueryParameter('greeting') engine.render("Hello {{"+greeting+"}}", data)

对应的URL:

http://vulnerable-website.com/?greeting=data.username

这将输出 Hello Carlos

在验证漏洞的时候很容易错过此上下文,因为它不会导致明显的XSS,并且与简单的哈希映射查找几乎没有区别。在这种情况下,测试服务器端模板注入的一种方法是:通过将任意HTML注入到值中。

首先确定该参数不包含直接XSS漏洞:

http://vulnerable-website.com/?greeting=data.username<tag></tag>

在没有XSS的情况下,这通常会导致输出中出现空白页面(只是Hello,没有用户名),编码标签或错误消息。下一步是尝试使用通用模板语法闭合出该语句,并尝试在其后注入任意HTML:

http://vulnerable-website.com/?greeting=data.username}}<tag></tag>

如果输出和任意HTML一起正确呈现出 Hello Carlos<tag>,则表明存在服务器端模板注入漏洞;如果还是现空白页面(只是Hello,没有用户名),编码标签或错误消息,就是不存在SSTI漏洞。</tag>

确定模板

一旦发现SSTI漏洞,下一步就是要确定模板引擎。

尽管有大量的模板语言,但是其中许多模板使用非常相似的语法,而这些语法是专门为不与HTML字符冲突而选择的。因此创建探测有效载荷以测试正在使用哪个模板引擎可能相对简单。

通常只需提交非法可报错的语法就足够了,因为产生的错误消息将准确告诉您模板引擎是什么,有时甚至是哪个版本。例如,非法表达式<%= foobar%>会触发来自基于Ruby的ERB引擎的以下响应:

(erb):1:in

<main>': undefined local variable or method​ foobar' for main:Object (NameError) from /usr/lib/ruby/2.5.0/erb.rb:876:in eval' from /usr/lib/ruby/2.5.0/erb.rb:876:in​ result' from -e:4:in `<main>'

否则,就需要手动测试特定于语言的不同有效负载,并研究模板引擎如何编译它们。常用的方法是使用来自不同模板引擎的语法注入任意数学运算。然后,您可以观察它们是否被成功执行。为了更好的验证,可以使用类似于以下内容的决策树:

相同的payload有时可能会通用在一种以上的模板语言中。

例如,有效载荷{{7 *'7'}}在Twig中返回49;在Jinja2中返回7777777。因此,决定的因数是多方面的。

通常来说,这类问题会在博客,CMS,wiki 中产生。虽然模板引擎会提供沙箱机制,攻击者依然有许多手段绕过它。

</main></main>
请前往 登录/注册 即可发表您的看法…