RWCTF2022_DesperateCat复现
摘要:RealWorld CTF 2022 DesperateCat复现。学习了一些tricks,记录下。
0x01复现过程
1、放在前面
@voidfyoo
师傅在解决这个问题的过程中主要顺着以下思路走:
1、可以上传任意文件,但由于构造JSP Webshell的关键字符被过滤因此无法直接上传可以利用的JSP Webshell
2、EL表达式特性+Tomcat Session持久化
a. 通过EL表达式能够修改Tomcat相关配置,这里可以修改Session文件的位置实现任意位置写入JSP马绕过过滤限制
b. Tomcat Session持久化机制会在Tomcat关闭时将未到期的用户Session写入到本地文件中
3、找到方法在不重启Tomcat的情况下触发Session持久化并写入Webshell
2、具体复现
- 复现平台:BUUOJ
- 参考文章:
@voidfyoo
师傅在安全客上的文章
1、能拿到一个war包,直接拖到JD-GUI中进行反编译
就一个/export路由
2、先审计各功能类:
StringUtil
类用于对字符串做替换完成过滤以及生成作文件名用的UUID,有以下核心几个方法:
1 | public static String randomStr() |
ParamUtil
类用于对用户参数做过滤处理内容如下:
1 |
|
可见对jsp马中的关键字符尖角括号<和圆括号(都做了过滤。
3、再往后自己没能力做下去了,直接用了voidfyoo
师傅的反弹Shell脚本打了下,并对原脚本做了些注释:
1 | #!/usr/bin/env python3 |
0x02 通过本题学到的
1、EL表达式
什么是EL表达式
EL(Expression Language)提供了在 JSP 中简化表达式的方法,让Jsp的代码更加简化。具有以下的功能:
- 获取数据:EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的Web域中检索Java对象、获取数据(某个Web域中的对象,访问JavaBean的属性、访问List集合、访问Map集合、访问数组);
- 执行运算:利用EL表达式可以在JSP页面中执行一些基本的关系运算、逻辑运算和算术运算,以在JSP页面中完成一些简单的逻辑运算,例如
${user==null}
; - 获取Web开发常用对象:EL表达式定义了一些隐式对象,利用这些隐式对象,Web开发人员可以很轻松获得对Web常用对象的引用,从而获得这些对象中的数据,这篇文章中对这些隐式对象有总结;
- 调用Java方法:EL表达式允许用户开发自定义EL函数,以在JSP页面中通过EL表达式调用Java类的方法。比如这样写一个JSP一句话:
${Runtime.getRuntime().exec(param.cmd)}
;
语法
EL表达式的语法为${ expression }
存/取值
[]
运算:普适的做法,支持动态取值(如${sessionScope.user[data]}
,data
是一个变量名)和包含特殊字符的属性名(如${user["My-Name"]}
,不能写成${user.My-Name}
)
.
运算:可以用来存取值但是不支持动态取值和包含特殊字符的属性名
变量
${ 变量名 }
表示从某一范围取出对应变量名的变量值。范围如下:
属性范围在EL中的名称 | |
---|---|
Page | PageScope |
Request | RequestScope |
Session | SessionScope |
Application | ApplicationScope |
若指定范围,仅在该范围内查找,找不到就返回空字符串:””;
若不指定范围按照上表格从上到下的顺序查找,找到后直接返回,找不熬返回空字符串。
EL表达式中可以使用以下变量类型:
文字 | 文字的值 |
---|---|
Boolean | true 和 false |
Integer | 与 Java 类似。可以包含任何整数,例如 24、-45、567 |
Floating Point | 与 Java 类似。可以包含任何正的或负的浮点数,例如 -1.8E-45、4.567 |
String | 任何由单引号或双引号限定的字符串。对于单引号、双引号和反斜杠,使用反斜杠字符作为转义序列。必须注意,如果在字符串两端使用双引号,则单引号不需要转义。 |
Null | null |
操作符
术语 | 定义 |
---|---|
算术型 | +、-(二元)、*、/、div、%、mod、-(一元) |
逻辑型 | and、&&、or、双管道符、!、not |
关系型 | ==、eq、!=、ne、<、lt、>、gt、<=、le、>=、ge。可以与其他值进行比较,或与布尔型、字符串型、整型或浮点型文字进行比较。 |
空 | empty 空操作符是前缀操作,可用于确定值是否为空。 |
条件型 | A ?B :C。根据 A 赋值的结果来赋值 B 或 C。 |
隐式对象
通过隐式对象可以方便地对JSP页面的上下文、Web上下文、会话、请求参数、页面等对象的参数进行访问
术语 | 定义 |
---|---|
pageContext | JSP页的上下文,可以用于访问 JSP 隐式对象,如请求、响应、会话、输出、servletContext 等。例如,${pageContext.response} 为页面的响应对象赋值。 |
术语 | 定义 |
---|---|
param | 将请求参数名称映射到单个字符串参数值(通过调用 ServletRequest.getParameter (String name) 获得)。getParameter (String) 方法返回带有特定名称的参数。表达式${param . name} 相当于 request.getParameter (name)。 |
paramValues | 将请求参数名称映射到一个数值数组(通过调用 ServletRequest.getParameter (String name) 获得)。它与 param 隐式对象非常类似,但它检索一个字符串数组而不是单个值。表达式 ${paramvalues. name} 相当于 request.getParamterValues(name)。 |
header | 将请求头名称映射到单个字符串头值(通过调用 ServletRequest.getHeader(String name) 获得)。表达式 ${header. name} 相当于 request.getHeader(name)。 |
headerValues | 将请求头名称映射到一个数值数组(通过调用 ServletRequest.getHeaders(String) 获得)。它与头隐式对象非常类似。表达式${headerValues. name} 相当于 request.getHeaderValues(name)。 |
cookie | 将 cookie 名称映射到单个 cookie 对象。向服务器发出的客户端请求可以获得一个或多个 cookie。表达式${cookie. name .value} 返回带有特定名称的第一个 cookie 值。如果请求包含多个同名的 cookie,则应该使用${headerValues. name} 表达式。 |
initParam | 将上下文初始化参数名称映射到单个值(通过调用 ServletContext.getInitparameter(String name) 获得)。 |
术语 | 定义 |
---|---|
pageScope | 将页面范围的变量名称映射到其值。例如,EL 表达式可以使用${pageScope.objectName} 访问一个 JSP 中页面范围的对象,还可以使用${pageScope .objectName. attributeName} 访问对象的属性。 |
requestScope | 将请求范围的变量名称映射到其值。该对象允许访问请求对象的属性。例如,EL 表达式可以使用${requestScope. objectName} 访问一个 JSP 请求范围的对象,还可以使用${requestScope. objectName. attributeName} 访问对象的属性。 |
sessionScope | 将会话范围的变量名称映射到其值。该对象允许访问会话对象的属性。例如:${sessionScope. name} |
applicationScope | 将应用程序范围的变量名称映射到其值。该隐式对象允许访问应用程序范围的对象。 |
EL表达式调用函数和方法
语法:
1 | ${ns:func(param1, param2, ...)} |
ns为命名空间,func是调用的函数
使用方式:
- 服务器上需要安装有对应函数的库
- 需要在JSP文件的开头shiyong
<taglib>
标签指定以包含这些库
2、Tricks 1:不重启Tomcat情况下利用Session持久化机制
@voidfyoo
师傅在翻看Tomcat文档时发现通过程序reload能够实现该点,而该点又可以通过翻看Tomcat源码找到reload的需要满足的两个条件:
- Context reloadable 配置为 true
- /WEB-INF/classes/ 或者 /WEB-INF/lib/ 目录下的文件发生变化
第一点可以通过EL表达式实现:
1 | ${pageContext.servletContext.classLoader.resources.context.reloadable=true} |
第二点由于可以任意写入文件所以可以轻松在lib目录下写入jar文件解决,这个jar文件内容可以是非法的不会影响reload。
3、Tricks 2:通过修改Tomcat appBase目录映射整个Linux根目录
由于Tricks1中写入的jar包不合法,因此会导致当前的Web应用崩溃从而无法访问到写入的Shell,@voidfyoo
师傅想到的办法是直接修改appBase,其表示存放了所有webapp所在的目录,默认值是webapps,在将其修改为/
后,整个系统盘都被映射到了Tomcat上从而实现资源的任意访问:
1 | ${pageContext.servletContext.classLoader.resources.context.parent.appBase=param.d} |
4、其他思路
现场比赛中有两位选手使用了ASCII ZIP Jar
的方式进行exploit,没弄明白这种攻击方式的原理,mark下先。
- 本文标题:RWCTF2022_DesperateCat复现
- 本文作者:Hn13
- 本文链接:https://www.hn13.top/2022/02/06/RWCTF2022-DesperateCat复现/>
- 发布时间:2022-02-06
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!