原文:
https://cyc10n3.medium.com/rce-via-server-side-template-injection-ad46f8e0c2ae
在本文中,我们将看到如何识别远程代码执行漏洞并绕过Akamai WAF规则。在进行安全扫描时,我注意到一个端点,该端点将用户可控制的数据合并到一个字符串中,并将其反映回响应中。注意到文本的返回,我尝试了一些XSS有效负载,但由于Content-Type的响应为application / json,因此无法成功执行JavaScript 。但是,当输入诸如$ {191 * 7}之类的有效负载时,我很惊讶地看到该算术表达式已在响应中成功求值为
… getApprovalGroupByContext.contextType:1337 …

注意: “ RCE_”不是有效负载的一部分,仅用于查找反射的文本。
在这种情况下,用户控制context_type查询参数的内容。在检测到模板注入后,下一步是识别使用中的模板引擎。在网上进行了更多研究之后,我对此应用程序可能使用的模板引擎有一些猜测。
为了评估该漏洞的影响,我进行了更多研究以找到可用于远程执行命令的有效负载,并且我发现以下有效负载可用于在远程服务器上执行ls命令:
${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("ls")}
执行后,我没有看到任何命令导致响应,因为它是盲注,但是我确实收到了Unix进程参考(在上面的响应正文中突出显示)。它确认命令已成功执行。
现在,它变得更加有趣,因为我想上传一个反向shell脚本。我执行了另外两个命令,以查看是否已经安装了python和wget模块,以便下载并执行反向Shell脚本。作为执行的结果,我收到了一个进程引用号,指出该实用程序已预先安装。

因此,我使用python在端口80上启动了HTTP服务器,并托管了一个反向Shell脚本,如下所示:

首先,我将反向shell脚本下载到易受攻击的服务器上。

响应中的Unix进程引用足以表明反向Shell脚本已上传到远程计算机上。下一步是在VPS上用nc监听443并在目标服务器上执行脚本。如下面的屏幕快照所示,我能够获得目标服务器的反向shell。

下一个任务是绕过Akamai的WAF在目标服务器上造成远程代码执行。
对于基本的数学运算(例如乘法和除法),我从生产服务器收到了相同的响应,这确认了该错误也存在于生产服务器上。但是,最终的payload无法执行,并导致403错误页面显示“访问被拒绝”消息。
我决定将Payload分解为多个部分,以根据WAF规则检查什么是安全的还是不安全的。这种反复试验的方法帮助我弄清楚了以下两个关键字是WAF的不安全字符串:
"java.lang.Runtime"
().
要执行的有效负载为:
${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("wget")}
我开始研究Java语言文档,以查看是否存在返回“ java.lang.Runtime”字符串的另一种方法。花了一些时间后,我发现了多种实现方法,但只有concat方法适用于这种情况。


"java.lang.Runtime"==="java.lang".concat(".Runtime")
这就是我能够绕过第一次检查的方式。到目前为止的有效负载:
${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("wget")}
下一个挑战是绕过()。关键字检查。经过一些分析,我注意到如果在()和之间放置任何字符。防火墙不会阻止它,但这会破坏我们的有效负载,因为方法链接会失败。
有没有办法在不破坏链接方法的情况下做到这一点?在回顾JavaScript基础时,我可以想到“自动调用函数”。我从来没有用Java编程过,所以我不确定这是否还能用,但是我想尝试一下。
对于JavaScript console.log("hello"),(console.log(" hello"))和(console.log)("hello")的含义相同。

我用(getClass())代替了getClass(),因为没有检查()。关键字,我也能够绕过第二项检查。
最终有效载荷为:
${("".getClass()).forName("java.lang".concat("Runtime")).getMethods()[6].invoke(("".getClass()).forName("java.lang".concat("Runtime"))).exec("wget")}
我执行了有效负载,并在响应中看到了进程引用号,这让我知道能够在生产服务器上造成远程代码执行 。

由于众所周知的原因,我没有尝试将任何shell或恶意代码上传到生产服务器上,但是,我验证了是否有可能从生产服务器向Burp collaborator发出出站请求。
我发送了请求,以查看生产服务器是否进行DNS查询。

几秒钟后,我收到一个查找请求,其中显示了生产(原始)服务器的实际IP地址。

译者按:
作者首先发现了一个模板注入,然后通过报错,确定模板引擎。又通过字符串拼接绕过了和()绕过了Akamai的waf,最终成功远程代码执行。但是很少在国内的漏洞报告里看到SSTI注入,如果有小伙伴看到了,还希望推荐给我学习学习。
本文迁移自知识星球“火线Zone”