导语:SST看起来并没有什么危害,但是你仔细研究的话,会发现它能在模板中执行本机函数,这就意味着如果攻击者能够向模板文件中写入这种表达式的话,他们就能够执行任意函数。

为什么需要服务器端模板(SST)?

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

1.jpg

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

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

2.jpg

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

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

什么是服务器端模板?

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

3.jpg

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

4.jpg

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

为什么SST是危险的?

SST看起来并没有什么危害,但是你仔细研究的话,会发现它能在模板中执行本机函数,这就意味着如果攻击者能够向模板文件中写入这种表达式的话,他们就能够执行任意函数。但是这个文件不一定非要包含你的模板。这种情况下,模板引擎无论如何都会将文件转换为字符串,以便用它们的结果代替表达式。这也是为什么模板引擎允许你对它们进行传递字符串,而不用直接传递文件位置。

你可以将其和require()和eval()函数进行比较。require()函数会包含一个文件并执行,eval()函数不是执行文件,而是将字符串当成代码来执行。你应该知道将未经处理的输入传递给eval()函数是极其危险的。每一本优秀的编程书都会反复的提到这一点。但是当涉及到处理模板引擎时,人们却通常忽略了这一点。所以,有时候,你看到的代码是下面这样的:

5.jpg

这段代码显示,在模板中,有一处输入是用户可控的,这就意味着用户可以执行模板表达式。

举个例子,恶意的表达式可能非常简单,比如[[system(‘whoami’)]],这就会执行系统命令whoami。因此模板注入很容易导致远程代码执行(RCE),就像未经过处理的输入直接传递为eval()函数一样。这就是我们所说的服务器端模板注入(SSTI)。这个例子非常明显,而在实际中,漏洞会非常隐蔽,难以发现。比如将许多不同的组件连接在一起传递为模板引擎,但是忽视了其中的某些组件可能包含用户可控的输入。

web应用如何防御SSTI?

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

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

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

源链接

Hacking more

...