专栏名称: 衡阳信安
船山院士网络安全团队唯一公众号,为国之安全而奋斗,为信息安全而发声!
目录
相关文章推荐
春城晚报  ·  昆明一小区突发火灾!起火原因正在调查 ·  昨天  
APPSO  ·  xAI 宣布 Grok 3 ... ·  昨天  
小众软件  ·  另外两件事[250220|被骗] ·  2 天前  
918云南交通台  ·  就在本周六!事关云南玉昆队中超首秀出行 ·  2 天前  
小众软件  ·  另外两件事[250219] ·  3 天前  
51好读  ›  专栏  ›  衡阳信安

jdk21下的jndi注入

衡阳信安  · 公众号  ·  · 2024-09-02 00:00

正文

jdk21bypass

限制点

对LDAP限制

我们知道打LDAP反序列化无论是远程类加载,还是直接传入序列化的数据或者是打工厂类都是在我们的decodeObject方法

jdk21

static Object decodeObject(Attributes attrs)
throws NamingException {

Attribute attr;

// Get codebase, which is used in all 3 cases.
String[] codebases = getCodebases(attrs.get(JAVA_ATTRIBUTES[CODEBASE]));
try {
if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) {
if (!VersionHelper.isSerialDataAllowed()) {
throw new NamingException("Object deserialization is not allowed");
}
ClassLoader cl = helper.getURLClassLoader(codebases);//打远程类的
return deserializeObject((byte[])attr.get(), cl);
} else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) {
// javaRemoteLocation attribute (RMI stub will be created)
if (!VersionHelper.isSerialDataAllowed()) {
throw new NamingException("Object deserialization is not allowed");
}
// For backward compatibility only
return decodeRmiObject(
(String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),
(String)attr.get(), codebases);
}

attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);
if (attr != null &&
(attr.contains(JAVA_OBJECT_CLASSES[REF_OBJECT]) ||
attr.contains(JAVA_OBJECT_CLASSES_LOWER[REF_OBJECT]))) {
return decodeReference(attrs, codebases);
}
return null;
} catch (IOException e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
}

jdk8u65

static Object decodeObject(Attributes var0) throws NamingException {
String[] var2 = getCodebases(var0.get(JAVA_ATTRIBUTES[4]));

try {
Attribute var1;
if ((var1 = var0.get(JAVA_ATTRIBUTES[1])) != null) {
ClassLoader var3 = helper.getURLClassLoader(var2);
return deserializeObject((byte[])((byte[])var1.get()), var3);
} else if ((var1 = var0.get(JAVA_ATTRIBUTES[7])) != null) {
return decodeRmiObject((String)var0.get(JAVA_ATTRIBUTES[2]).get(), (String)var1.get(), var2);
} else {
var1 = var0.get(JAVA_ATTRIBUTES[0]);
return var1 == null || !var1.contains(JAVA_OBJECT_CLASSES[2]) && !var1.contains(JAVA_OBJECT_CLASSES_LOWER[2]) ? null : decodeReference(var0, var2);
}
} catch (IOException var5) {
NamingException var4 = new NamingException();
var4.setRootCause(var5);
throw var4;
}
}

可以看到21是多了

VersionHelper.isSerialDataAllowed()
public static boolean isSerialDataAllowed() {
return trustSerialData;
}

而trustSerialData默认就是false

所以打ldap反序列化是很难进行的了

对rmi的限制

我们知道高版本打rmi一般都是打的本地工厂类,我们看看21又对这个做了什么限制

打工厂类,我们都是利用

NamingManager.getObjectInstance()

去触发工厂类的getObjectInstance()方法

在我们的21是NamingManagerHelper

我们看到getObjectInstance()方法

看到对比了,我们看一下这个新参数是什么

return NamingManagerHelper.getObjectInstance(obj, name, this,
environment, ObjectFactoriesFilter::checkRmiFilter);

我们看到checkRmiFilter

public static boolean checkRmiFilter(Class> serialClass) {
return checkInput(RMI_FILTER, () -> serialClass);
}

RMI_FILTER

private static final ConfiguredFilter RMI_FILTER =
initializeFilter(RMI_FACTORIES_FILTER_PROPNAME, DEFAULT_RMI_SP_VALUE);

可以看到是只能是我们这个jdk.naming.rmi/com.sun.jndi.rmi.* ;! "包下的

private static final String DEFAULT_RMI_SP_VALUE =
"jdk.naming.rmi/com.sun.jndi.rmi.**;!*";

突破

既然在我们两个点上给我们限制了,我们只能找其他可以反序列化的地方了

StreamRemoteCall#executeCall()

分析

我们先看到这个方法

public void executeCall() throws Exception {
byte returnType;

// read result header
DGCAckHandler ackHandler = null;
try {
if (out != null) {
ackHandler = out.getDGCAckHandler();
}
releaseOutputStream();
DataInputStream rd = new DataInputStream(conn.getInputStream());
byte op = rd.readByte();
if (op != TransportConstants.Return) {
if (Transport.transportLog.isLoggable(Log.BRIEF)) {
Transport.transportLog.log(Log.BRIEF,
"transport return code invalid: " + op);
}
throw new UnmarshalException("Transport return code invalid");
}
getInputStream();
returnType = in.readByte();
in.readID(); // id for DGC acknowledgement
} catch (UnmarshalException e) {
throw e;
} catch (IOException e) {
throw new UnmarshalException("Error unmarshaling return header",
e);
} finally {
if (ackHandler != null) {
ackHandler.release();
}
}

// read return value
switch (returnType) {
case TransportConstants.NormalReturn:
break;

case TransportConstants.ExceptionalReturn:
Object ex;
try {
ex = in.readObject();
} catch (Exception e) {
discardPendingRefs();
throw new UnmarshalException("Error unmarshaling return", e);
}

// An exception should have been received,
// if so throw it, else flag error
if (ex instanceof Exception) {
exceptionReceivedFromServer((Exception) ex);
} else {
discardPendingRefs();
throw new UnmarshalException("Return type not Exception");
}
// Exception is thrown before fallthrough can occur
default:
if (Transport.transportLog.






请到「今天看啥」查看全文