最近某微爆出RCE漏洞,本想着通过相对路径写入shell,但是不知是什么原因,在实际渗透中并没有成功,内存马这里也考虑了一下,但是由于这里的利用需要将class经过BCEL编码,由于无法将两个类进行BCEL编码,所以只能放弃了种内存马的想法,那目前最稳妥的方法就是获得命令执行的结果,让攻击者根据自己的需求去完成后续的利用。
Resin 4.x获取命令执行结果
有大佬已经给出了在Resin 4.x中获取回显的方法了,下面我们先学习一下他的思路。
1 | try { |
我们先看看在resin 4.X中,如何获取response对象。为此我写了一个servlet并打印出到达servlet的调用栈。
1 | doGet:98, HelloWorld |
Response对象主要来自于com.caucho.server.http.HttpRequest#handleRequest
com.caucho.server.http.AbstractHttpRequest#getResponseFacade
中返回了Response对象。所以如果我们能获取到AbstractHttpRequest对象并调用该对象的getResponseFacade方法,即可获取response对象。
现在的目标转到获取AbstractHttpRequest对象,再查看下类的继承关系,通过下面的继承关系可以得到,想要获取AbstractHttpRequest对象,也可以获取它的子类或者父类。当然获取父类不一定就能得到AbstractHttpRequest对象,这个和具体返回的对象是什么类型有关系。要想通过反射得到对应的对象,主要还是要寻找返回类型为该对象并且由static修饰的方法。
1 | public static ProtocolConnection |
经过一番搜索,只有下面的两个方法会返回ProtocolConnection对象。
先看看com.caucho.ejb.util.EjbUtil#createRequestContext
方法,这个方法要返回ProtocolConnection的条件是需要user的内容不为Null,但实际上我通过反射调用该方法user为Null,因此不会返回ProtocolConnection对象。
所以只能通过com.caucho.network.listen.TcpSocketLink#getCurrentRequest
来获取该对象,从_currentRequest
获取对象,并且_currentRequest
也是static类型的,所以可以通过反射拿到。
但是获取的ProtocolConnection
对象实际类型是否是我们想要的AbstractHttpRequest
类型呢?可以尝试获取下。可以看到实际上获取的是HttpRequest对象。而HttpRequest对象是我们想要的AbstractHttpRequest
的子类,所以是可以满足我们的要求的。
最后梳理一下Resin 4.x获取回显的思路。
1 | 1. 通过调用TcpSocketLink.getCurrentRequest()获取ProtocolConnection对象。 |
Resin 3.x获取命令执行结果
虽然上面的回显方案可以解决大部分问题,但是我本地搭建的泛微OA V8.0却不能使用上面的方案,经过分析在Resin 3.X系统中并没有com.caucho.network.listen.TcpSocketLink
。和上面的思路类似,我们先看看在正常的使用中,系统是如何获取Response对象的。执行任意一个请求并打断点,可以看到并不是通过TcpSocketLink类到Request请求的。
在com.caucho.server.connection.AbstractHttpRequest
中可以获取response对象。
同理,查看类的继承结构,可以通过获取ServletRequest间接获取AbstractHttpRequest对象。
经过搜索可以在com.caucho.server.dispatch.ServletInvocation#getContextRequest
中拿到ServletRequest对象。
测试代码如下
1 | Object currentRequest =Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation").getMethod("getContextRequest").invoke((Object)null); |
获取到ServletRequest对象后,通过_response字段的内容获取到response对象。
1 | Field f = currentRequest.getClass().getSuperclass().getDeclaredField("_response"); |
获取Writer并执行写入操作
1 | Method getWriterM = response.getClass().getMethod("getWriter"); |
整体的代码如下
1 | Object currentRequest =Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation").getMethod("getContextRequest").invoke((Object)null); |
通用回显
在Resin 4.X中查看是否可以通过getContextRequest获取request对象,通过TcpSocketLink.getCurrentRequest()
获取ProtocolConnection对象并返回,也就是说在Resin 4.X中也可以通过ServletInvocation的getContextRequest方法获取回显。通过ServletInvocation获取request对象是版本兼容的。
虽然在 Resin 3.x和4.x都获取了request对象,但实际上获取的request对象的实际类型是不同的。
Resin 3.x
在resin 3.x中获取的类型是HttpRequest对象,HttpRequest类中并没有保存_response对象,需要从父类AbstractHttpRequest中获取。
通过上面的分析在resin 3.x中获取response对象需要使用下面的代码。
1 | Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation").getMethod("getContextRequest").invoke((Object)null).getClass().getSuperclass().getDeclaredField("_response") |
Resin 4.x
在Resin 4中获取的实际类型是HttpServletRequestImpl类型。HttpServletRequestImpl中直接保存了_response对象,所以可以用HttpServletRequestImpl的类加载器直接获取response对象。
在resin 4.x中使用下面的代码
1 | Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation").getMethod("getContextRequest").invoke((Object)null).getClass().getDeclaredField("_response") |
代码整合
通过上面的分析,可以通过判断获取的request对象的类型来判断resin的版本,再根据版本的不同选择不同微调代码进行回显利用。根据获取request对象的不同,通过不同的方式获取response对象。下面是从请求头获取命令执行并回显的代码。
1 | try { |