网络安全检测|网络安全服务|网络安全扫描-香港墨客投资移动版

主页 > 业界资讯 > 网络渗透测试

21931 漏洞挖掘技巧:后反序列化利用》

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

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

0 × 02 后反序列化漏洞

Weblogic 反序列化漏洞挖掘思路是利用 readObject ( ) 、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 ( ) 方法中,通过 resolveObject ( ) 方法对传入的绑定命名进行解析。

在 resolveObject ( ) 方法中,根据上下文信息调用其中的 lookup ( ) 方法。

根据上下文中的信息,经过在 WLContextImpl、WLEventContextImpl、 WLEventContextImpl、RootNamingNode、ServerNamingNode 、BasicNamingNode 类中一系列的 lookup ( ) 方法调用,实现 BasicNamingNode 类中的 resolveObject ( ) 方法调用。

由于传入 resolveObject ( ) 方法中的 obj 不是 NamingNode 类的实例,且 mode 的值默认为 1,所以会调用 WLNamingManager 类中的 getObjectInstance ( ) 方法。

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

0 × 04   CVE-2023-21931

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

package   weblogic.jndi.internal;public   final   class   WLNamingManager   {

     public   static   Object   getObjectInstance ( Object   boundObject,   Name   name,   Context   ctx,   Hashtable   env )   throws   NamingException   {

         if   ( boundObject   instanceof   ClassTypeOpaqueReference )   {

             ......

         }   else   if   ( boundObject   instanceof   LinkRef )   {

             String   linkName   =   ( ( LinkRef ) boundObject ) .getLinkName ( ) ;

             InitialContext   ic   =   null;

             try   {

                 ic   =   new   InitialContext ( env ) ;

                 boundObject   =   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 ( ) ;

     }}

(责任编辑:admin)