模板引擎在 Web 应用中被广泛使用,用于将 HTML 和数据组合起来生成显示内容。例如,在后端,模板引擎用于将用户名、帖子内容等嵌入到 HTML 中。
用户输入值直接包含在这样的模板处理过程中,则整个模板语法都可能被执行。这种漏洞被称为“SSTI(服务器端模板注入)”。
如果发生 SSTI,则根据模板引擎的类型,存在导致内部变量访问的风险,甚至在最坏的情况下导致任意操作系统命令执行 (RCE)。尤其是在基于 Python 的系统中,Jinja2
和Mako
实现存在漏洞,它们就可能成为攻击者的便捷“入口”。
本次验证的对象Mako
是Python中常用的模板引擎之一,但也容易误将用户输入直接嵌入
我们将看一个具体的例子,看看如何在实际应用程序中利用这个漏洞。
📹:您还可以在 YouTube 上看到实际操作方法!
- 电容式非接触式系统独有的清脆打字感觉!
- REALFORCE首款无线兼容设备!有线连接也可用!
- 与 HHKB 不同,日语键盘布局没有任何怪癖,任何人都可以轻松使用!
- 配有拇指轮,水平滚动非常容易!
- 还拥有出色的降噪性能,安静舒适!
- 滚动可以在高速模式和棘轮模式之间切换!
关于 HackTheBox
这次我们其实是HackTheBox(HTB)来验证漏洞。
HackTheBox 是一个实践型 CTF 平台,参赛者可以在各种安全领域进行练习,包括 Web 应用程序、服务器和网络。
其最大的特点是参赛者可以通过实际访问将被攻击的设备和应用程序并亲自动手来学习。
之前在 HackTheBox 上提供的挑战类别之一VIP 计划或更高级别的用户才能访问
还有各种类别的机器和挑战,包括 Web、逆向、Pwn 和取证,因此您可以根据自己的水平来应对它们。
如果您想认真磨练使用 HackTheBox 的技能,请务必VIP 计划并充分利用过去的机器和挑战。
👉 有关如何注册 HackTheBox 以及各个计划之间的差异的详细信息,请单击此处。

挑战概要:Spookifier
我们这次应对的挑战“ Spookifier 属于HackTheBox 的Web 类别设置为非常简单
从表面上看,该应用程序非常简单:一个 Web 服务,它会在您输入文本时简单地转换并以不同的字体样式显示。乍一看,它似乎没有任何安全漏洞。
但是,该应用程序Python 模板引擎“Mako”,并且用户输入的字符串直接在模板内进行评估。
此设计缺陷可能导致服务器端模板注入(SSTI)并最终导致任意命令执行(RCE)
👉 https://app.hackthebox.com/challenges/Spookifier
挑战点
- 使用的技术:Python、Flask、Mako
- 值得注意的行为:输入以多种字体显示
- 攻击媒介:模板中未经过处理的输入 → SSTI
- 目标:
/flag.txt
检索标志
这样,在表面上看似安全的功能背后模板评估过程中的弱点,并且这种结构会让你体验到典型的“从 SSTI 到 RCE 的模式”。
黑客攻击实践:从 SSTI 到 RCE
从这里开始,我们将实际操作该应用程序并寻找漏洞。
如果模板处理不当,它可能会成为攻击的入口点。
我们将验证 SSTI 是否有效,并最终瞄准 RCE。
侦察 ①:首次试用该应用程序
首先,访问目标应用程序并检查它具有什么功能以及它接受什么输入
该屏幕的输入形式很简单,看起来只有“输入文本并将其转换为装饰字体样式并输出”的功能。
外观和结构非常简单,没有身份验证功能或复杂的路由。

这里需要注意的是用户输入的字符串会经过某种处理,然后以模板的形式显示出来。
在“以多种字体显示”的过程中,模板引擎似乎在某个地方操纵着字符串。

我使用 devtools 检查了请求信息,但那里似乎没有任何重要的信息。

侦察2:从源代码识别模板引擎
在调查实际 Web 应用程序中的漏洞时,很少能够访问源代码。
渗透测试和漏洞赏金测试依赖于黑盒测试(从外部观察行为)。
因此我们通常通过以下流程来检查模板引擎是否存在以及是否存在漏洞。
- 在文本字段中,尝试一些常见的模板语法,例如
${7*7}
、{{7*7}}
、<%= 7*7 %>
- 结果显示
49
或7*7
- 任何不寻常的模板错误或评估结果都可以为模板引擎提供线索。
- 从其输出和行为推断所使用的模板引擎(Mako、Jinja2、Twig 等)
这次,源代码是作为一项旨在了解漏洞的特殊挑战提供的,
它让我能够直接检查内部结构并快速了解模板处理的工作原理。
在提供的源代码中,您可以找到以下模板渲染描述:
从 mako.template 导入 Template... 返回 Template(result).render()
这句话很清楚地表明了
我们使用了 Mako 作为模板引擎是一个 Python 模板引擎,${...}
语法会被直接当作 Python 表达式来执行,因此SSTI(服务器端模板注入)的风险
将尝试评估用户输入中的${}
既然我们知道了使用了 Mako 作为模板引擎,那么接下来要检查的是
用户输入的字符串中的${}
真的被求值为模板表达式,这是判断是否存在 SSTI(服务器端模板注入)的第一步。
尝试直接在输入表单中输入Mako 的基本模板语法${7*7}
${7*7}
如果模板引擎将此输入作为表达式进行评估,则它应该输出数字49。
相反,如果不对其进行评估并将其视为字符串,则它将简单地显示在屏幕上为${7*7}
当我发送时,屏幕上显示了以下消息:

结果49
用户输入的${7*7}
已经由模板引擎(Mako)评估。
也就是说,Mako将用户输入作为模板来处理,并且已确认 SSTI 有效。
入侵:检查 RCE ⇒ 尝试看看你能从模板公式执行到什么程度
确认 SSTI 后,我们的下一个目标就很明确了:
看看我们是否可以利用模板表达式在服务器端执行任意代码(RCE)
确认命令执行
首先,让我们测试一下是否真的可以通过模板表达式执行操作系统命令。
为了验证,我们将使用简单的whoami
命令。
${__import__('os').popen('whoami').read()}
当我提交这个公式时,我得到了以下结果:

whoami
的执行结果显示为root
这证实了可以通过模板表达式执行任何 OS 命令,并且该命令是以 root 权限执行的
此时,很明显存在远程代码执行(RCE)漏洞,允许攻击者以提升的权限执行命令
检查文件读取
现在我们知道可以执行命令了,我们可以检查
是否可以读取任何文件一个典型的例子/etc/passwd
文件,我们在 Linux 环境中很熟悉它。
${open('/etc/passwd').read()}
输出包括用户信息,例如:

这个结果表明,
可以通过模板表达式调用Python的open()
,从而完全控制服务器上的读取操作。
执行:搜索并获取标志
到目前为止,通过模板注入
- 运行 Python 代码
- 执行操作系统命令
- 任意文件读取
这意味着已经实现了完整的远程代码执行(RCE) 。
下一步找到目标标志文件并检索其内容。
找到标志文件的位置
/
里面的内容,看看里面有哪些目录和文件。
${__import__('os').listdir('/')}

如您所见,如果您可以找到所需的文件( flag.txt
),那就没问题,但是如果您找不到它,则需要深入挖掘home
或root
读国旗
如果搜索/flag.txt
之类的文件open()
检索其内容:
${open('/flag.txt').read()}
当我运行这个表达式时,我得到了以下输出:

我们成功拿到了
flag,这也是本次HackTheBox挑战赛的通过这个过程,我们也通过实际测试,更加深入地了解了SSTI漏洞的可利用程度。
此漏洞为何发生?如何安全地使用模板
该漏洞(SSTI→RCE)的主要原因模板引擎使用不当
具体来说, Python端动态构造包含用户输入的模板字符串,导致模板注入(SSTI)并最终导致远程代码执行(RCE)。
解决方案——不要将动态字符串传递给模板
发生 SSTI(服务器端模板注入)的典型模式之一
在 Python 中构造一个字符串,然后将其作为模板进行评估。
str.format()
创建模板字符串(如下面的代码所示)
def generate_render(converted_fonts):结果='''<tr><td> {0}</td></tr><tr><td> {1}</td></tr><tr><td> {2}</td></tr><tr><td> {3}</td></tr> '''.format(*converted_fonts) 返回模板(result).render()
例如,converted_fonts
包含以下用户输入:
${__import__('os').popen('id').read()}
将此模板传递给 Mako会产生严重的漏洞,Mako${...}
评估
安全使用:将模板结构与数据分离
为了避免这个问题,必须
将模板结构与用户输入(变量)分开你可以通过将变量显式传递给render()
,如下所示:
def generate_render(converted_fonts):模板='''<tr><td> ${font1}</td></tr><tr><td> ${font2}</td></tr><tr><td> ${font3}</td></tr><tr><td> ${font4}</td></tr> ''' 返回模板(template).render(font1=converted_fonts[0],font2=converted_fonts[1],font3=converted_fonts[2],font4=converted_fonts[3])
通过这种方法,font1
到font4
包含${}
,Mako 也会将它们视为普通字符串,而不会将它们作为模板表达式进行评估。

Mako 的${...}
语法会评估模板内的表达式,
如果逐字传递用户输入,则存在代码执行的风险。
另一方面,Template(...).render(var=value)
显式传递变量
在这种情况下, var }
可以安全地用作 template 中的占位符,但不会作为代码执行。
摘要:SSTI 是由“使用习惯”导致的漏洞
在这个“Spookifier”挑战中,对 Mako 模板的不当处理
使我们能够利用漏洞,从而导致服务器端模板注入 (SSTI) 并最终导致远程代码执行 (RCE)。
这些漏洞是开发人员的实现错误引起的,
当模板字符串是动态构造的时,它们尤其危险。
要安全地使用模板引擎:
- 始终将模板和数据(变量)分开
- 将变量作为占位符传递,并且不评估表达式
- 不要将不受信任的输入直接传递给模板
只要遵循这些基本原则,就可以预防许多 SSTI。
👉 有关如何注册 HackTheBox 以及各个计划之间的差异的详细信息,请单击此处。
