前言
首先提出一个场景,目标使用了weblogic,存在t3反序列化漏洞,正常情况下回显都是使用rmi内存马,rmi内存马存在诸多缺点。首先容易被发现 貌似登录weblogic后台就能很轻易的看到,其次只能执行命令。当我们想传一个cs的马或者frp等进行后渗透时,还得用远程文件下载,如果目标有杀软就还需要考虑远程文件下载过杀软的问题,颇为麻烦,这时我们希望放一个功能性强的webshell上去,如:冰蝎、哥斯拉等,然而weblogic放jsp马问题也比较多,第一个需要找到本地目录,第二个需要找到外网可访问目录。设想如果我们直接访问这个weblogic是404,爆破又没找到任何目录,t3反序列化又打成了,那我们应该怎么放jsp上去。。。。。。这样就没得搞了。那么我们能不能在t3反序列化的时候直接给目标打一个http内存马呢,这就是我本文要讨论的问题。
分析
首先找一个t3漏洞的poc和搭建一个weblogic环境,首先按照常规的Filter、Servlet内存马的思路,先找request请求或者ApplicationContext,这里我是用的是c0ny1师傅的java-object-searcher项目。在weblogic中jsp情况下打内存马,可以参考:https://www.cnblogs.com/nice0e3/p/14956677.html,这个项目也是用java-object-searcher做辅助的,看完比较容易理解我下面的操作,当然如果你有调试内存马的基础也可以直接往下看。
T3的exp使用CVE-2020-14644的,首先需要将java-object-searcher放到weblogic的lib目录中或者jdk依赖中,然后直接打反序列化exp到服务器。
得到如下结果
1 | ############################################################# |
总体来说就是request请求没找到,但是WebAppServletContext找到了,这样也可以打内存马,但是这里存在一个问题,就是我怎么知道我打的内存马的ContextPath是多少呢?比如我打了一个内存马路径为/hello,其连接路径就是其WebApplicationContext的contextPath+/hello,ContextPath不知道就没办法连接内存马。而且在t3反序列化场景下,我们也没有response,没办法把contextPath输出到response中。
在这种情况下,常规思路是给所有找到的WebApplicationContext都打一个内存马,然后爆破weblogic的目录,如果找到运气好,找到了console或者其他应用的目录,那就可以连上,如果运气不好就连不了。
WebLogic 404冰蝎内存马
在思索后发现,我们在访问weblogic时,页面找不到都会弹出一个weblogic特有的404页面,如果我把404页面的访问逻辑给改成内存马的逻辑,那么我在连接内存马的时候不就不需要找路径了,随便一个404页面的路径都能连接。
想通之后剩下的就是实现方式,改变原有代码逻辑在我认知中用java agent可以做到。那么我只需要找到404页面得处理类,然后用java agent篡改他即可。
最后找到HttpConnectionHandler.resolveServletContext这么一个合适位置,下图标记得this.handleNoContext((ContextVersionManager)null)就是实际渲染出404页面得方法。我选择在其前面一段得地方插入我的恶意代码,也就是HttpConnectionHandler.resolveServletContext方法的最前面。
java agent的写法参考冰蝎的写法,只不过把要hook的类名和方法名分别改成了HttpConnectionHandler和resolveServletContext。
同时,因为这个类的这个方法是每个http请求都会调用到的,我设置了只有匹配某个路径的时候,才会进入冰蝎内存马的解析逻辑。
当我以为一切都没有问题的时候,将java agent打入weblogic服务器,使用冰蝎进行连接我设置好的路径,却发现请求一直处于连接状态,不返回状态码,不返回任何网页内容。而我访问其他内容却正常的很。
抱着不解,我仔细阅读了HttpConnectionHandler.resolveServletContext的源码,正常访问一个不存在的页面会进入this.handleNoContext((ContextVersionManager)null) 解析404逻辑
首先,我发现了第一个问题所在。就是我将resolveServletContext篡改了之后,当冰蝎连接我指定的path时,先发送Echo.class的字节码让weblogic服务器运行(这里涉及到冰蝎的原理,冰蝎每次的请求都是发送java字节码让目标服务器进行加载、初始化、执行)。而当服务器执行冰蝎逻辑时,执行完毕之后时直接就return了(见下图第二章),也就是说正常的resolveServletContext逻辑戛然而止了,因此缺失了正常http请求的为http请求设置404状态码等处理http请求的response的问题。
对此,我的解决方案就是,完善java agent修改的目标方法的代码,使其不仅包含冰蝎代码的逻辑,同时还包含正常情况下resolveServletContext方法的执行逻辑。
正常情况下,我们访问一个404页面,ctxManager都是空的,因此直接看this.handleNoContext((ContextVersionManager)null)的逻辑
首先设置this.response.setStatus(404)。然后设置输出流可写,最后调用this.sendError(this.response, 404)
进入this.sendError(this.response,404),我们发现其只要做了三件事,如下图,第一个箭头很好理解。第二个要跟进入看看具体做了什么。最后一个就是关闭了socket的连接(这个也是之前为什么连接一起挂起,因为socket一直没关)。
接下来跟进res.sendError(code);
比较关键的时下图的两行代码
this.sendContentError(code, msg) 其作用就是将404页面的内容写到response中
this.outputStream.commit() 提交outputStream的,这个很关键,如果不调用commit,直接把内容写到response中,然后关闭socket,会发现输出并没有任何内容,所以commit也必须被调用。
分析完毕,添加agent中shellcode的部分内容
注意this.httpSocket.closeConnection((java.lang.Throwable)null)要添加到最后,不然冰蝎逻辑没执行完socket就关了可不行。
剩下的就是需要调用outputStream.commit()了,由于冰蝎在执行完毕时会关闭response流,所以我们的commit肯定得在关闭之前执行,因此outputStream.commit()需要写在冰蝎的逻辑中,也就是需要二开冰蝎。在每个冰蝎每个java payload涉及到response的都添加下图中的if逻辑代码即可。
最后的最后,跟response的纠缠到此就结束了。不过还有一个问题就是由于我们这里的原理是404内存马,所有上下文中没有session,要知道冰蝎的key是存在session中的。因此这里也要改造,虽然request对象中没有session,但是有attributes啊,对应java agent中shellcode修改代码
冰蝎中将所有java payload设计到加密的地方增加下图的if逻辑
最终成果,连接hello路径
直接访问
WebLogic 404一句话内存马
只需要把冰蝎的代码改成使用ProcessBuilder执行命令并将输出通过response进行输出即可。最后调用scommit和this.httpSocket.closeConnection即可。
github地址:https://github.com/flowerwind/Weblogic404Memshell
结尾
知道了javaAgent的写法后后面的其实就简单了,无非就是通过t3协议落地一个java agent的jar包并将其attach到weblogic的jvm上就可以了,这个无需阐述。
在调试的过程中遇到了很多的坑,不过好在坚持下来了,并最终一一解决,因为实现这个我调试了很久,因此对各种缘由比较清楚,因此文章中有些复杂的思考可能我没在意就带过了。如果师傅们在阅读过程中有不了解的地方,请先搭建环境调试并仔细思考,如还无法解决可在文章下留言,可以联系我,定会回答。除此之外,师傅们有在t3协议下更完美的打多功能webshell(如冰蝎等)的方式,也可以和我讨论。