log4j 漏洞一些特殊的利用方式
背景
正愁本周没主题可写,结果两天前就曝了一个核弹级的漏洞“log4j RCE”,这两天官方的修补方案也逐渐完善。所以本篇就拿 log4j 作为主题讲一下我的几个发现。
RCE
log4j RCE 原理已经有挺多人发过了,本文不过多赘述。简单说就是日志在打印时遇到 ${ 后 Interpolator 类按照 : 分割出第一部分作为 prefix 第二部分作为 key。通过 prefix 去找对应的 lookup,再通过对应的 lookup 实例调用 lookup 方法传入 key 作为参数。
log4j-core 自带的 lookup 有很多实例,其中就包括了此次存在漏洞的 JndiLookup 实例。JndiLookup 则是直接把传进来的 key 当做 JNDI URL 用 InitialContext.lookup 去访问,从而造成了 JNDI 代码执行漏洞。
WAF bypass
该漏洞曝光后各安全厂商也纷纷推出了解决方案,WAF、RASP、改源码、改配置文件、更新到rc2等。
在此次漏洞中最没有防御效果的就是 WAF 了。有提出 ${ 、jndi、ldap、rmi 等关键词规则的防护。但研究后发现都会存在被绕过问题。
首先是 jndi、ldap 简直太容易被绕过,只要用 lowerCase upperCase 就可以把关键词分割开。
如果是用了正则的话那还可以使用 upper 把 jndı 转成 jndi。
注意:这里的 ı(\u0131) 不是 i(\x69)和I(\x49),经过 toUpperCase 就会转变成 I。从而绕过了 jndi 关键词的拦截。
再就是 ${ 关键词的拦截了,虽然这个范围有点大可能会产生一些误报,但鉴于漏洞的严重性还是有很多人建议拦截 ${
但这样也未必能够真正的解决,因为漏洞的触发点是在打印日志的时候把可控内容携带进去了。那么可控内容从哪里来?
Header、URL、键值对参数、JSON参数、XML参数 ...
现在随着 JSON 数据格式的流行,很多系统都在使用 JSON 处理参数,JSON 处理库用的最多的就数 Jackson和fastjson。
而 Jackson 和 fastjson 又有 unicode 和 hex 的编码特性。
例如:
{"key":"\u0024\u007b"} {"key":"\x24\u007b"}
这样就避开了数据包中有 ${ 的条件,所以 WAF 的防护规则还要多考虑几种编码。
信息泄露
sys、env 这两个 lookup 的 payload 也在讨论中被频繁提起,实际上他们分别对应的是 System.getProperty() 和 System.getenv(),能够获取一些环境变量和系统属性。部分内容是可以被携带在 dnslog 传出去的。
除了 sys、env 以外我还发现 ResourceBundleLookup 也可以获取敏感信息,但没有看到有人讨论 Bundle,所以重点讲一下。
public String lookup(final LogEvent event, final String key) { if (key == null) { return null; } final String[] keys = key.split(":"); final int keyLen = keys.length; if (keyLen != 2) { LOGGER.warn(LOOKUP, "Bad ResourceBundle key format [{}]. Expected format is BundleName:KeyName.", key); return null; } final String bundleName = keys[0]; final String bundleKey = keys[1]; try { // The ResourceBundle class caches bundles, no need to cache here. return ResourceBundle.getBundle(bundleName).getString(bundleKey); } catch (final MissingResourceException e) { LOGGER.warn(LOOKUP, "Error looking up ResourceBundle [{}].", bundleName, e); return null; } }
从代码上来看就很好理解,把 key 按照 : 分割成两份,第一个是 bundleName 获取 ResourceBundle,第二个是 bundleKey 获取 Properties Value
ResourceBundle 在 Java 应用开发中经常被用来做国际化,网站通常会给一段表述的内容翻译成多种语言,比如中文简体、中文繁体、英文。
那开发者可能就会使用 ResourceBundle 来分别加载 classpath 下的 zh_CN.properties、en_US.properties。并按照唯一的 key 取出对应的那段文字。例如: zh_CN.properties
LOGIN_SUCCESS=登录成功
那 ResourceBundle.getBundle("zh_CN").getString("LOGIN_SUCCESS")
获取到的就是 登录成功
如果系统是 springboot 的话,它会有一个 application.properties 配置文件。里面存放着这个系统的各项配置,其中有可能就包含 redis、mysql 的配置项。当然也不止 springboot,很多其他类型的系统也会写一些类似 jdbc.properties 的文件来存放配置。
这些 properties 文件都可以通过 ResourceBundle 来获取到里面的配置项。所以在 log4j 中 Bundle 是比sys和env更严重的存在。
在不出网的环境下可以通过 dnslog 的方式来外带信息。
除了dnslog以外还可以通过这两种方法来获取信息。
ldap
dns
修复方案
log4j 更新到最新版本
[超站]友情链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
随时掌握互联网精彩
- 1 习近平乘专机离开巴西利亚 7908017
- 2 王宝强方回应涉嫌欺诈:无愧于心 7947303
- 3 王楚钦把对手拍子打掉了 7893436
- 4 建设网络强国 更好造福人民 7773479
- 5 湖南发现超40条金矿脉 7617449
- 6 王楚钦3-1淘汰德国名将晋级八强 7566908
- 7 黄执中在奇葩说都没有这么激动过 7490596
- 8 房贷利率下调月供反而多了 7340256
- 9 烧饼任德云社副总 7274107
- 10 “两新”政策支撑经济回升向好 7197895