选择你喜欢的标签
我们会为你匹配适合你的网址导航

    确认 跳过

    跳过将删除所有初始化信息

    log4j 漏洞一些特殊的利用方式

    安全 2021-12-11 19:52

    声明:该文章来自(安全档案)版权由原作者所有,K2OS渲染引擎提供网页加速服务。

    背景

    正愁本周没主题可写,结果两天前就曝了一个核弹级的漏洞“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 了。有提出 ${ 、jndildaprmi 等关键词规则的防护。但研究后发现都会存在被绕过问题。

    首先是 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/

    图库