环境搭建
官网下载 Oracle WebLogic Server 12.2.1.4
https://www.oracle.com/middleware/technologies/weblogic-server-installers-downloads.html
下载 oracle jdk,这里使用 8u331
执行 install 下的 bat 安装即可
安装完后需要执行 C:\Oracle\Middleware\Oracle_Home\oracle_common\common\bin\config.cmd
来配置域
weblogic 启动脚本位于 C:\Oracle\Middleware\Oracle_Home\user_projects\domains\base_domain\startWebLogic.cmd
我们添加调试参数,由于这里是 jdk 高版本,需要修改一下 trustURLCodebase 便于测试 jndi
set JAVA_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Dcom.sun.jndi.ldap.object.trustURLCodebase=true
-Dlog4j2.formatMsgNoLookups=true
接着用 everything 搜索所有 jar 包添加进依赖,使用 idea 远程调试
1 2 3
| cd C:\Oracle\Middleware\Oracle_Home\wlserver\server\lib java -jar ../../modules/com.bea.core.jarbuilder.jar -javaagent:EasyRasp.jar
|
打补丁
参照这个设置一下环境变量,不用关闭服务
https://blog.csdn.net/u011145571/article/details/104878696
首先使用管理员 cmd
先升级 opatch 程序
1
| java -jar opatch_generic.jar -silent oracle_home=C:\Oracle\Middleware\Oracle_Home
|
opatch 命令
1 2 3 4
| opatch version opatch apply opatch lspatches opatch lsinventory
|
https://blog.csdn.net/cnskylee/article/details/142984161
远程绑定对象
weblogic的t3与iiop协议都支持使用JNDI来远程绑定对象并lookup查询,代码如下:
1 2 3 4 5 6 7 8 9 10 11
| MyRemoteObject remoteObject = new MyRemoteObject();
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL, "t3://<server_ip>:<iiop_port>"); Context ctx = new InitialContext(env);
ctx.rebind("myRemoteObject", remoteObject);
MyRemoteObject remoteObj = (MyRemoteObject) ctx.lookup("myRemoteObject");
|
如果想通过iiop协议绑定则把代码中的t3换成iiop即可,需要注意的是,由于在绑定的过程中,数据是序列化传输的,所以这里的MyRemoteObject需要实现Serializable接口。
CVE-2020-2551就是利用这种方式,客户端向服务端发送remoteObject之后,服务端对其进行反序列化,导致造成反序列化漏洞。
CVE-2023-21839
当远程对象继承自OpaqueReference时,lookup查看远程对象,服务端会调用远程对象getReferent方法。weblogic.deployment.jms.ForeignOpaqueReference继承自OpaqueReference并且实现了getReferent方法,并且存在retVal = context.lookup(this.remoteJNDIName)实现,故可以通过rmi/ldap远程协议进行远程命令执行。
在 lookup 操作时,我们首先定位到 weblogic.jndi.internal.RootNamingNode#lookup 中
![[_resources/Weblogic JNDI注入/64186c9c49265345cdfc83203d2c2071_MD5.jpeg]]
继续跟进
![[_resources/Weblogic JNDI注入/75fe071f65aebcf653cf429ba1da0979_MD5.jpeg]]
在this.lookupHere(prefix, env, restOfName)会恢复 bind 的对象出来,此处是一个 weblogic.deployment.jms.ForeignOpaqueReference 对象,继续进入resolveObject 中,在t3/iiop协议解析查找对象的过程中会调用 weblogic.jndi.internal.WLNamingManager
的 getObjectInstance
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public static Object getObjectInstance(Object boundObject, Name name, Context ctx, Hashtable env) throws NamingException { if (boundObject instanceof ClassTypeOpaqueReference) { Hashtable jndiEnv = ThreadLocalMap.get(); if (jndiEnv != null && Boolean.parseBoolean((String)jndiEnv.get("weblogic.jndi.onlyGetClassType"))) { boundObject = ((ClassTypeOpaqueReference)boundObject).getObjectClass(); } else { boundObject = ((OpaqueReference)boundObject).getReferent(name, ctx); } } else if (boundObject instanceof OpaqueReference) { boundObject = ((OpaqueReference)boundObject).getReferent(name, ctx); } 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) { NamingException e = var15; LinkException le = new LinkException(""); le.setLinkRemainingName(new CompositeName(linkName)); le.setLinkResolvedName(name); le.setLinkResolvedObj(boundObject); le.setRootCause(e); throw le; } finally { try { ic.close(); } catch (Exception var14) { } } } return boundObject; }
|
在这个方法中包含关键流程,大致的分支有三种 ClassTypeOpaqueReference,OpaqueReference,LinkRef,这个漏洞就出现在 OpaqueReference 当中,我们只要继承了 OpaqueReference 的类,就会触发 getReferent 方法
我们只用设置jndiEnvironment 为 null,并设置remoteJNDIName 就可以进行 jndi 注入
![[_resources/Weblogic JNDI注入/c3aa7435ecfbf00e68a0ba93536b47dd_MD5.jpeg]]
这里给出完整流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| public Object getReferent(Name name, Context ctx) throws NamingException { AbstractSubject originalSubject = SubjectManager.getSubjectManager().getCurrentSubject(KERNEL_ID); InitialContext context; if (this.jndiEnvironment == null) { context = new InitialContext(); } else { if (this.jndiEnvironment.get("java.naming.factory.initial") == null) { this.jndiEnvironment.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory"); } context = new InitialContext(this.jndiEnvironment); } AbstractSubject subject = SubjectManager.getSubjectManager().getCurrentSubject(KERNEL_ID); if (KernelStatus.isServer()) { String subjectUserName = SubjectUtils.getUsername(subject.getSubject()); String originalSubjectUserName = SubjectUtils.getUsername(originalSubject.getSubject()); if (WLSPrincipals.isKernelUsername(subjectUserName)) { if (WLSPrincipals.isKernelUsername(originalSubjectUserName)) { subject = SubjectManager.getSubjectManager().getAnonymousSubject(); } else { subject = originalSubject; } } String url = null; if (this.jndiEnvironment != null && this.jndiEnvironment.get("java.naming.security.principal") == null) { url = (String)this.jndiEnvironment.get("java.naming.provider.url"); if (url != null && this.jndiEnvironment.get("java.naming.factory.initial") != null && ((String)this.jndiEnvironment.get("java.naming.factory.initial")).indexOf("weblogic") != -1) { try { if (RMIEnvironment.getEnvironment().isRemoteDomain(url)) { subject = SubjectManager.getSubjectManager().getAnonymousSubject(); } } catch (IOException var16) { IOException ioe = var16; throw new NamingException(ioe.getMessage()); } } } } SubjectManager.getSubjectManager().pushSubject(KERNEL_ID, subject); Object retVal; try { if (this.jndiEnvironment == null || !AQJMS_ICF.equals(this.jndiEnvironment.get("java.naming.factory.initial")) || this.remoteJNDIName == null || !this.remoteJNDIName.startsWith(AQJMS_QPREFIX) && !this.remoteJNDIName.startsWith(AQJMS_TPREFIX)) { retVal = context.lookup(evalMacros(this.remoteJNDIName)); } else { synchronized(this) { if (this.cachedReferent == null) { this.cachedReferent = context.lookup(evalMacros(this.remoteJNDIName)); } } retVal = this.cachedReferent; } } finally { SubjectManager.getSubjectManager().popSubject(KERNEL_ID); context.close(); } if (retVal instanceof ObjectBasedSecurityAware && ((ObjectBasedSecurityAware)retVal).isOBSEnabled()) { throw new NamingException(JMSPoolLogger.logUnsupportedConnectionFactoryReferencedByForeignSeverLoggable().getMessage()); } else { if (retVal instanceof ForeignJMSServerAware) { ((ForeignJMSServerAware)retVal).setReferencedByFS(true); } return retVal; } }
|
在230328 补丁中加入了对 remoteJndiname 的校验
1 2 3 4 5 6 7 8
| if (this.jndiEnvironment == null || !AQJMS_ICF.equals(this.jndiEnvironment.get("java.naming.factory.initial")) || this.remoteJNDIName == null || !this.remoteJNDIName.startsWith(AQJMS_QPREFIX) && !this.remoteJNDIName.startsWith(AQJMS_TPREFIX)) { providerURL = this.jndiEnvironment != null ? (String)this.jndiEnvironment.get("java.naming.provider.url") : null; if (providerURL == null && this.remoteJNDIName != null && !JNDIUtils.isValidJndiScheme(new CompositeName(this.remoteJNDIName))) { throw new NamingException("JNDI name is invalid - " + this.remoteJNDIName); } retVal = context.lookup(evalMacros(this.remoteJNDIName)); }
|
CVE-2023-21931
和上述相同,也添加了校验
![[_resources/Weblogic JNDI注入/3f87b05b4baca2e9b1d718fc93301f15_MD5.jpeg]]
修复后的代码,使用 isValidJndiScheme 来验证
1 2 3 4 5 6 7 8 9 10 11
| String linkName = ((LinkRef)boundObject).getLinkName(); if (!JNDIUtils.isValidJndiScheme(new CompositeName(linkName), true)) { throw new NamingException("JNDI name is invalid - " + linkName); } InitialContext ic = null; try { ic = new InitialContext(env); boundObject = ic.lookup(linkName); } catch (NamingException var15) {
|
CVE-2024-20931
在 2023.4 月补丁后,对上述两个漏洞都进行了修复,对java.naming.provider.url 做了校验,并且对this.remoteJNDIName 做了校验,导致我们无法走到 lookup 这一步,但我们转而可以在 context = new InitialContext(this.jndiEnvironment);
初始化的时候做文章,利用其它的 jndiEnvironment 来触发 sink
![[_resources/Weblogic JNDI注入/20d167446524717a50c11ef283e813ee_MD5.jpeg]]
最终找到的类是oracle.jms.AQjmsInitialContextFactory
![[_resources/Weblogic JNDI注入/99941f1458876602695356c82a572f90_MD5.jpeg]]
最终在oracle.jms.AQjmsContext#getDataSource 触发 jndi
而在下一个补丁中,添加了对java.naming.factory.initial 的校验
![[_resources/Weblogic JNDI注入/0138a5ea509e4b465ea986613eaf255b_MD5.jpeg]]
CVE-2024-21006