Weblogic CVE-2023-21931 漏洞挖掘技巧:后反序列化利用

GobySec  369天前

2.png

0×01 概述

近些年,Weblogic 反序列化漏洞一直围绕着反序列化的触发点进行漏洞挖掘,事实上还有很多存在反序列化但无法实时利用的点,在大家平时的漏洞挖掘中容易忽略。在行业内也有一些关于“后反序列化”的进一步讨论,这些看似无法利用的漏洞,其实可以通过一些后续的技巧完成稳定的利用效果。例如,进行 bind() 或 rebind() 操作后,并没有触发漏洞,此时可以尝试其他方法如 lookup()lookupLink() 等触发漏洞。

通过这种思路我们发现了两个 Weblogic 的后反序列化漏洞(CVE-2023-21931、CVE-2023-21839),获得了 Oracle 的官方确认。本文以这两个 Weblogic 漏洞为例,分享"后反序列化漏洞"的利用思路。我们相信还有很多这类的漏洞在未来会逐渐被挖掘出来,希望本篇文章能够给大家一些启发。

0×02 后反序列化漏洞

1.png

Weblogic 反序列化漏洞挖掘思路是利用 readob ject()readResolve()readExternal() 等反序列化方法对恶意序列化数据进行操作,以达到攻击目的。常规的漏洞思路重点关注 Weblogic 在反序列化过程中进行恶意攻击,而忽略了反序列化完成后的操作。后反序列化漏洞挖掘的思路重点关注 Weblogic 完成反序列化过程后,在达到某个时机或执行操作后触发的漏洞攻击。

在 Weblogic 中,如果进行 bind() 或 rebind() 操作后,并没有触发漏洞,此时可以尝试其他方法如 lookup()lookupLink() 等触发漏洞。

本文将以 lookup() 方法作为漏洞触发点,对 Weblogic 后反序列化漏洞的攻击过程进行分析和漏洞实例展示。

0×03 lookup

通过跟踪调用堆栈,我们发现 lookup() 的流程如下:

  • Weblogic 在接收到请求后,通过 BasicServerRef 类中的 invoke() 方法解析传入数据。

  • 通过 _invoke() 方法,Weblogic 根据传入的方法名 resolve_any 执行的 resolve_any() 方法。
  • 在 resolve_any() 方法中,通过 resolveob ject() 方法对传入的绑定命名进行解析。
  • 在 resolveob ject() 方法中,根据上下文信息调用其中的 lookup() 方法。
  • 根据上下文中的信息,经过在 WLContextImplWLEventContextImpl、 WLEventContextImpl 、RootNamingNodeServerNamingNode 、BasicNamingNode 类中一系列的 lookup() 方法调用,实现 BasicNamingNode 类中的 resolveob ject() 方法调用。
  • 由于传入 resolveob ject() 方法中的 obj 不是 NamingNode 类的实例,且 mode 的值默认为 1,所以会调用 WLNamingManager 类中的 getob jectInstance() 方法。

2.png

最终,可以看到 WLNamingManager 类的 getob jectInstance() 方法根据传入的对象接口类型,调用对象中的 getReferent() 方法,完成漏洞触发点的 lookup() 方法调用。实际上这两个 CVE 漏洞都是通过 getob jectInstance() 的两个分支触发的。

0×04 CVE-2023-21931

CVE-2023-21931 的漏洞触发点在 WLNamingManager 类的 getob jectInstance() 方法中,当传入的 boundob ject 对象是 LinkRef 的实现类时,则调用传入对象 boundob ject 的 getLinkName() 方法,并通过 lookup() 方法对 getLinkName() 方法返回的 linkAddrType 地址进行远程 JNDI 加载。在实例化 LinkRef 类时,可以通过类中的构造方法给 linkAddrType 传入一个 JNDI 地址。这样,我们就可以调用 lookup() 方法对自定义的 JNDI 地址进行远程加载,达到攻击的目的。

package weblogic.jndi.internal;

public final class WLNamingManager {

    public static ob ject getob jectInstance(ob ject boundob ject, Name name, Context ctx, Hashtable env) throws NamingException {

        if (boundob ject instanceof ClassTypeOpaqueReference) {

            ......

        } else if (boundob ject instanceof LinkRef) {

            String linkName = ((LinkRef)boundob ject).getLinkName();

            InitialContext ic = null;

            try {

                ic = new InitialContext(env);

                boundob ject = ic.lookup(linkName);  // 漏洞触发点

            } catch (NamingException var15) {

              ......

            } finally {......}

        }

    }

}

漏洞 JNDI 地址构造在 LinkRef 这个类中,LinkRef 是 Java 的一个原生类。通过 LinkRef 类中的构造方法,我们可以控制变量 linkAddrType 的值, 再通过 getLinkName() 方法将 linkAddrType 作为字符串返回。

package javax.naming;

public class LinkRef extends Reference {

    static final String linkClassName = LinkRef.class.getName();

    static final String linkAddrType = "LinkAddress";

    public LinkRef(Name linkName) {

        super(linkClassName, new StringRefAddr(linkAddrType, linkName.toString()));

    }

    public LinkRef(String linkName) {

        super(linkClassName, new StringRefAddr(linkAddrType, linkName));

    }

    public String getLinkName() throws NamingException {

        if (className != null && className.equals(linkClassName)) {

            RefAddr addr = get(linkAddrType);

            if (addr != null && addr instanceof StringRefAddr) {

                return (String)((StringRefAddr)addr).getContent();

            }

        }

        throw new MalformedLinkException();

    }

}


3.png

在上述过程中,rebind() 和 lookup() 方法的反序列化过程并未执行恶意操作,而是在完成反序列化之后,通过调用类 WLNamingManager 中 getob jectInstance() 方法的 lookup() 才触发漏洞,进行远程恶意加载 JNDI 地址操作的。

我们在 Goby 中已经集成了 CVE-2023-21931 漏洞,并加入了回显和反弹 shell 的功能。演示效果如下:

Image


0×05 CVE-2023-21839

ForeignOpaqueReference 是 OpaqueReference 接口的实现类。在 ForeignOpaqueReference 类中声明了两个私有变量:jndiEnvironment 和 remoteJNDIName,同时声明了两个构造方法,在有参构造方法中接收 env 和 remoteJNDIName,并分别赋值给了上面的两个私有类变量。

ForeignOpaqueReference 类的 getReferent() 方法是 OpaqueReference 接口的实现方法,在 getReferent() 方法中,retVal = context.lookup(this.remoteJNDIName); 对本类 remoteJNDIName 变量中的 JNDI 地址进行远程加载,导致了反序列化漏洞。

package weblogic.jndi.internal;

public class ForeignOpaqueReference implements OpaqueReference, Serializable {

    private Hashtable jndiEnvironment;

    private String remoteJNDIName;

        ......

    public ForeignOpaqueReference(String remoteJNDIName, Hashtable env) {

        this.remoteJNDIName = remoteJNDIName;

        this.jndiEnvironment = env;

    }

    public ob ject getReferent(Name name, Context ctx) throws NamingException {

        InitialContext context;

        if (this.jndiEnvironment == null) {

            context = new InitialContext();

        } else {

            Hashtable properties = this.decrypt();

            context = new InitialContext(properties);

        }

        ob ject retVal;

        try {

            retVal = context.lookup(this.remoteJNDIName);   // 漏洞点

        } finally {

            context.close();

        }

        return retVal;

    }

    ......

}

getReferent() 调用分析

package weblogic.jndi;

public interface OpaqueReference {

    ob ject getReferent(Name var1, Context var2) throws NamingException;

    String toString();

}

OpaqueReference 接口有两个抽象方法:getReferent() 和 toString();

ForeignOpaqueReference 类的 getReferent() 方法调用在 WLNamingManager 类中。

在 WLNamingManager 类的 getob jectInstance() 方法中,当传入的 boundob ject 对象实现了 OpaqueReference 接口时,则会调用该对象的 getReferent() 方法,即 boundob ject = ((OpaqueReference)boundob ject).getReferent(name, ctx);。

正如上方提到的 ForeignOpaqueReference 类实现了 OpaqueReference 接口,因此会调用该类中的 getReferent() 方法,导致反序列化代码执行漏洞。

package weblogic.jndi.internal;

public final class WLNamingManager {

  public static ob ject getob jectInstance(ob ject boundob ject, Name name, Context ctx, Hashtable env) throws NamingException {

        if (boundob ject instanceof ClassTypeOpaqueReference) {

            ......

        } else if (boundob ject instanceof OpaqueReference) {

            boundob ject = ((OpaqueReference)boundob ject).getReferent(name, ctx);

        } else if (boundob ject instanceof LinkRef) {

      ...

        }

    }

}


5.png

CVE-2023-21931 漏洞原理相同,CVE-2023-21839 也是在反序列化过程中没有进行恶意操作,而是完成反序列化过程后执行了漏洞类 ForeignOpaqueReference 中 getReferent() 方法中的 lookup() 才触发的漏洞。

在 Goby 中,我们已经集成了 CVE-2023-21839 漏洞,并添加了回显以及反弹 Shell 的功能。以下是演示效果:

Image

0×06 时间线

CVE-2023-21931

  • 2022 年 8 月 12 日 漏洞提交官方
  • 2022 年 8 月 19 日 漏洞官方确认
  • 2023 年 4 月 18 日 漏洞官方修复

CVE-2023-21839

  • 2022 年 7 月 31 日 漏洞提交官方
  • 2022 年 8 月 5 日 漏洞官方确认
  • 2023 年 1 月 16 日 漏洞官方修复

0×07 研究环境

Vulfocus Weblogic 环境

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:12.2.1.2.0-jdk-release

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:12.2.1.1.0-jdk-release

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:12.2.1.3.0-jdk-release

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:12.2.1.4.0-jdk-release

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:12.2.1.0.0-jdk-release

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:14.1.1.0.0-jdk-release

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:12.1.2.0.0-jdk-release

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:12.1.3.0.0-jdk-release

docker pull vulfocus/vcpe-1.0-a-oracle-weblogic:10.3.6.0-jdk-release

0×08 参考

1. Java“后反序列化漏洞”利用思路 - Ruilin

2. Ruil1n/after-deserialization-attack: Java After-Deserialization Attack 


本文中演示的漏洞与功能适配 Goby 版本:Beta 2.4.7,已支持Goby红队版、漏扫版扫描验证。最新版本下载体验:https://gobysec.net/

Goby 欢迎表哥/表姐们加入我们的社区大家庭,一起交流技术、生活趣事、奇闻八卦,结交无数白帽好友。

也欢迎投稿到 Goby(Goby 介绍/扫描/口令爆破/漏洞利用/插件开发/ PoC 编写/ IP 库使用场景/ Webshell /漏洞分析 等文章均可),审核通过后可奖励 Goby 红队版,快来加入微信群体验吧~~~

  • 文章来自Goby社区成员:14m3ta7k@白帽汇安全研究院,转载请注明出处。
  • 微信群:公众号发暗号“加群”,参与积分商城、抽奖等众多有趣的活动

  • 获取版本:https://gobysec.net/sale

最新评论

昵称
邮箱
提交评论