环境搭建

官网下载 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);
// 绑定对象到JNDI
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.WLNamingManagergetObjectInstance 方法

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