non_RCE复现
non_RCE复现
首先放出官方WP
https://mp.weixin.qq.com/s/yQ-00YaykUe41S0DdlgoiQ
面向官方WP复现一下
认证绕过
首先直接访问admin的话会401。我们来看一下代码
因为LoginFilter
和AdminServlet
的urlPatterns
是相同的。所以经过AdminServlet
的时候一定会经过LoginFilter
。
在这里,必须知道password才能通过认证,但是题目又说,不可能得到密码。所以就需要绕了。
所以接下来的思路也很明确,就是如何能绕过LoginFilter。在AntiUrlAttackFilter中,有一段代码比较可疑:
这里吧./
和;
替换成了空,然后使用forward进行了转发。通过forward就能绕过LoginFilter了。
因为这里只拦截/admin/请求,所以我们从AntiUrlAttackFilter
过来的请i去就不会拦截
http://127.0.0.1:8080/;admin/importData
这里把;
替换为空后转发。
现在能访问admin了。
里面是一段mysql连接。
黑名单检测绕过
这里预期解是利用mysql jdbc的反序列化
jdbcUrl是我们可控的。但是有过滤
搜一下autoDeserialize就能发现一些文章。
https://www.anquanke.com/post/id/203086
过滤之后dbc url包含%
或者autoDeserialize
关键字都无法通过校验,导致doGet
中直接return
而无法进入下面的jdbc连接部分。
这里的预期解是利用黑名单检测逻辑存在的条件竞争问题,来绕过黑名单检测机制,黑名单检测几个关键的逻辑如下:
可以看到,这里生成了一个单例BlackListChecker对象,也就是说,在整个程序生命周期中,最多只会生成1个BlackListChecker对象,而tomcat在同时处理多个http请求时,会起多个线程,每个线程都会调用Servlet的处理逻辑,因此,在这里会有多个线程同时调用servlet.AdminServlet#doGet
方法。
而如之前所说,整个程序生命周期中最多只会生成1个BlackListChecker对象,因此,这里存在多个线程对同一个对象做操作的情况。而在黑名单的检测逻辑checker.BlackListChecker#check
方法中,会把待检测的字符串设置到BlackListChecker对象的toBeChecked
成员变量中,再从toBeChecked
中拿出来做字符串contains
检测,因此,这里存在多个线程对同一个对象做写操作的情况,存在条件竞争问题。
这样一来,当恶意的jdbcUrl被绑定后。本来要进行check
但接着马上有线程来一个正常的jdbcUrl,而只有一个实例,这时候会过了check,实例并不会进行拦截,因此恶意的jdbcUrl也得以继续执行。
然后就是自己搭一个恶意mysql服务器来打。
寻找利用链
为了方便调试,我们自己先写个exp框架用于本地调试
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
public class exp {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args){
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(handler);
oos.close();
// 本地测试触发
// System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}
}
我们可以通过pom.xml发现使用了aspectjweaver。
所以确定是用这个打的。
这道题和ysoserial不同的地方就是这道题是没有cc链的。而他自己写了一个DataMap
类。是出题人为了防止不懂原理,直接用工具梭出来弄的。
在ysoserial中已经由gadget链了
从这个链,我们可以得知漏洞触发点在SimpleCache$StorableCachingMap.writeToPath()
漏洞点:可以写文件
所以我们需要找一个调用put函数,且参数可控的地方来触发这个漏洞。
我们从DataMap里寻找,发现了
这里使用了put方法。所以我们要想办法往这里靠。
继续看DataMap源码。发现DataMap#Entry
中有hashcode
方法,其中调用getValue
然后调用了get
方法。
而根据gadget chain。
只需要将key设置为DataMap#Entry
类即可
所以利用链
HashSet.readObject()
HashMap.put()
HashMap.hash()
DataMap$Entry.hashcode
DataMap$Entry.getValue()
DataMap.get()
SimpleCache$StorableCachingMap.put()
SimpleCache$StorableCachingMap.writeToPath()
FileOutputStream.write()
参考