游客发表
2021年12月9日深夜,漏洞很多运维工程师是修复被电话叫醒的。阿里云安全团队率先公开了Log4j2的漏洞一个远程代码执行漏洞,编号CVE-2021-44228。修复攻击者只需要向目标系统发送一段精心构造的漏洞字符串,就能在服务器上执行任意代码。修复这个漏洞影响面之大,漏洞用“核弹级”来形容并不夸张——Log4j2作为Java生态中最流行的修复日志组件,被几乎所有主流框架、漏洞中间件、修复Web应用间接或直接引用。漏洞
问题出在Log4j2的修复Lookup功能上。它支持一种叫${ jndi:ldap://...}的漏洞语法,本意是修复方便从JNDI(Java命名和目录接口)加载外部资源。但设计时没做严格校验,漏洞导致日志里一旦打印了恶意字符串,Log4j2就会主动去连接攻击者控制的LDAP服务器,加载并执行远程class文件。整个过程不需要用户交互,也不需要目标系统有任何额外配置。我印象很深的是,当时有个测试环境只因为打印了一条包含用户输入的错误日志,就被弹了shell。
很多人第一反应是升级Log4j2版本。官方很快发布了2.15.0,但随后发现这个版本在特定配置下仍可被绕过,于是又紧急推出2.16.0、2.17.0。真正稳妥的做法是至少升到2.17.1(Java 8环境)或2.12.4(Java 7环境)。但升级本身就有坑:老项目可能依赖了不兼容的API,或者被其他框架锁定了Log4j2版本。我见过一个Spring Boot项目,明明在pom.xml里把log4j-core改成了2.17.1,运行时却还是加载了旧版——因为某个starter间接引入了2.14.1,Maven的依赖调解没生效。
更麻烦的是,有些系统根本没法立刻升级。比如某个金融客户的核心交易系统,跑在WebLogic上,而WebLogic自身就打包了存在漏洞的Log4j2。Oracle后来发了补丁,但客户不敢轻易重启生产环境。这时候只能靠临时缓解措施:设置JVM参数-Dlog4j2.formatMsgNoLookups=true,或者删掉log4j-core里的JndiLookup.class文件。后者更彻底,一条zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class命令就能搞定。但注意,这只是止血,不是治愈。
漏洞公开后一周,我帮三个团队做过应急响应。最花时间的不是打补丁,而是搞清楚“到底哪些资产受影响”。一个典型的Java应用可能依赖几十个jar包,其中某个不起眼的工具库引用了Log4j2。用mvn dependency:tree能看个大概,但容器镜像里的fat jar、老式WAR包、甚至某些中间件的插件目录,都得手动翻。有个团队用find / -name "log4j-core*.jar"扫了整个服务器,结果在/tmp下发现了一个两年前测试留下的旧包——它不在classpath里,但万一被加载了呢?
修复完成后,验证也很关键。别只看版本号,要实际发一条${ jndi:ldap://127.0.0.1:1389/a}到日志里,观察是否触发DNS请求或报错。如果系统有WAF,可以临时加规则拦截${ jndi、${ lower:j}等变形 payload,但这只是争取时间。真正安全的做法是彻底移除JndiLookup类,或者升级到2.17.1以上并确认没有其他Lookup滥用路径。
这件事给我的教训是:基础组件的安全不能只靠“默认信任”。Log4j2的Lookup功能本可以默认关闭,或者至少限制协议白名单。现在很多团队在CI流程里加了SCA(软件成分分析)扫描,但扫描库本身也可能漏报。更务实的做法是:对任何打印用户输入的地方保持警惕,日志脱敏不是可选项。另外,别把“升级到最新版”当成万能药——2.17.1之后Log4j2又出过几个CVE,只是没这么严重。安全是一个持续过程,不是一次打补丁就完事。
友情链接