Analysis of the reflective class loading of high-version JDK in practical network defense and attack

0 22
IntroductionStarting from JDK9, the Java Platform Module System (JPMS) was intro...

Introduction

Starting from JDK9, the Java Platform Module System (JPMS) was introduced, for detailed introduction, you can refer to Oracle's official description of the new features of JDK9: https://docs.oracle.com/javase/9/whatsnew/toc.htm

About the access permissions between modules:

Analysis of the reflective class loading of high-version JDK in practical network defense and attack

The access permissions of Java's class are usually divided into: public, protected, private, and default package access permissions. After JDK9 introduced the concept of modules, these concepts need to be distinguished from modules. The access permissions of these classes have not been invalidated, but they can only take effect within the module. If modules want to access our classes externally, they need to be explicitly exported, that is, usingexports

module hello.world {
    exports com.itranswarp.sample;

    requires java.base;
		requires java.xml;
}

demo as follows:

import javax.management.loading.MLet;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Base64;

public class Main {
    public static void main(String[] args)  {
        try {
            String evilClassBase64 = "xxxx";
            byte[] bytes = Base64.getDecoder().decode(evilClassBase64);
            Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
            method.setAccessible(true);
            Class cc = (Class) method.invoke(new MLet(new URL[0], Main.class.getClassLoader()), bytes, new Integer(0), new Integer(bytes.length));
            cc.newInstance();
        }

        }

    }
}

Run the above demo separately onJDK11andJDK21test in the environment

JDK 11

After running the above demo, it will prompt illegal reflection operation, and it will prompt that such unsafe reflection operations will be completely disabled in future versions, but it will not affect bytecode loading

image-20240713113628934.png

image-20240712180200779.png

JDK21

image-20240713112635059.png

After JDK17, strong encapsulation directly bans illegal reflection, and you can see from the error message that the java.base module's java.lang package does not open reflection to the unnamed module

Explanation given by the Oracle official document

https://docs.oracle.com/en/java/javase/17/migrate/migrating-jdk-8-later-jdk-releases.html#GUID-7BB28E4D-99B3-4078-BDC4-FC24180CE82B

Here we need to look at a module instruction after JDK9

open, opens, opens…to instructions

Before Java 9, we could use reflection technology to obtain information about all classes and their internal members under a certain package, even for private types, so class information is not really completely isolated from the outside. One of the main goals of the module system is to achieve strong encapsulation. By default, unless a class is explicitly exported or declared as public type, the classes in the module are not visible to the outside. Modularization requires us to expose the scope of packages to external modules to the minimum extent. The instructions related to open are used to limit which classes can be detected by reflection technology at runtime.

Let's first look at the opens instruction, the syntax is as follows:

opens package

The opens instruction is used to specify that all public classes under a certain package can only be reflected by other modules at runtime, and all classes and their members under the package can be accessed through reflection.

The syntax of the opens…to instruction is as follows:

opens package to modules

This instruction is used to specify that certain modules can perform reflection operations on public classes under specific packages of the module at runtime, followed by module names separated by commas after 'to'.

The open instruction syntax is as follows:

open module moduleName{
}

The instruction is used to specify that external modules can perform reflection operations on all classes under this module at runtime.

That is to say, JDK17+ did not include the instructions we need when developingjava.langOpen reflection permissions, causing us to be unable to perform reflection class loading, check the source code in the JDKmodule-info.classdefinition, I found that indeed it is not usedopeninstruction

image-20240713114211261.png

Afterwards, I found in Oracle's official documentation that the official reservedsun.miscandsun.reflectTwo packages can perform reflection calls

image-20240713124958677.png

Afterwards, I looked at the source code of JDK21 and found in the JDKjdk.unsupportedmodulemodule-infoThere is a declaration

image-20240713125318846.png

Using the opens instruction, classes under the two package names can be reflected.

Unsafe

For an introduction to the Unsafe class, see this article: https://javaguide.cn/java/basis/unsafe.html

The article mentioned that it hasdefineClassanddefineAnonymousClassTwo methods can load bytecode in two ways. However, the author found in the actual JDK8, JDK11, and JDK21 environments that there are two methods in JDK8, but onlydefineAnonymousClassOne method, even both methods of JDK21 have been removed......

Below are three versions of JDK'sUnsafeclass

JDK8

image-20240713130839765.png

JDK11

image-20240713130925459.png

JDK21

image-20240713131016878.png

Afterwards, I found through searching related articles that JDK 17 has removeddefineAnonymousClassmethod.

image-20240713131859556.png

That is to say, before <JDK17, you can directly usedefineAnonymousClassThis method is used for reflection class loading operations.

Field field = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);
            unsafe.defineAnonymousClass(Class.class, bytes, null).newInstance();

JDK17+ bytecode loading

Although JDK modularization officially reservedsun.miscandsun.reflectTwo packages can perform reflection calls, but JDK17+ also removedUnsafeofdefineAnonymousClassmethod. This causes the previous loading method to fail.

Afterwards, I saw @Aiwin master share through modifying the current class's module tojava.baseMaintain andjava.lang.ClassLoaderUnder the same module, it can break the limitations of modularization, thus enabling the loading of bytecode files.

private boolean checkCanSetAccessible(Class<?> caller,
                                          Class<?> declaringClass,
                                          boolean throwExceptionIfDenied) {
        if (caller == MethodHandle.class) {
            throw new IllegalCallerException();   // should not happen
        }

        Module callerModule = caller.getModule();
        Module declaringModule = declaringClass.getModule();

        if (callerModule == declaringModule) return true;
        if (callerModule == Object.class.getModule()) return true;
        if (!declaringModule.isNamed()) return true;

        String pn = declaringClass.getPackageName();
        int modifiers;
        if (this instanceof Executable) {
            modifiers = ((Executable) this).getModifiers();
        } else {
            modifiers = ((Field) this).getModifiers();
        }

        // class is public and package is exported to caller
        boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
        if (isClassPublic && declaringModule.isExported(pn, callerModule)) {
            // member is public
            if (Modifier.isPublic(modifiers)) {
                logIfExportedForIllegalAccess(caller, declaringClass);
                return true;
            }

            // member is protected-static
            if (Modifier.isProtected(modifiers))
                && Modifier.isStatic(modifiers)}
                && isSubclassOf(caller, declaringClass)) {
                logIfExportedForIllegalAccess(caller, declaringClass);
                return true;
            }
        }

        // package is open to caller
        if (declaringModule.isOpen(pn, callerModule)) {
            logIfOpenedForIllegalAccess(caller, declaringClass);
            return true;
        }

        if (throwExceptionIfDenied) {
            // not accessible
            String msg = "Unable to make ";
            if (this instanceof Field)
                msg += "field ";
            msg += this + " accessible: " + declaringModule + " does not \""
            if (isClassPublic && Modifier.isPublic(modifiers))
                msg += "exports";
            else
                msg += "opens";
            msg += " " + pn + "\" to " + callerModule;
            InaccessibleObjectException e = new InaccessibleObjectException(msg);
            if (printStackTraceWhenAccessFails()) {
                e.printStackTrace(System.err);
            }
            throw e;
        }
        return false;
    }

From the above method, it can be seen that it mainly checks the class's module. The Unsafe class can modify the offset, and by modifying our class's module to the base module, we can bypass the reflection restrictions of JDK17+ versions.

String evilClassBase64 = "xxxx";
        byte[] bytes = Base64.getDecoder().decode(evilClassBase64);
        Class unsafeClass = Class.forName("sun.misc.Unsafe");
        Field field = unsafeClass.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        Module baseModule = Object.class.getModule();
        Class currentClass = Main.class;
        long offset = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
        unsafe.putObject(currentClass, offset, baseModule);
        Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
        method.setAccessible(true);
        ((Class)method.invoke(ClassLoader.getSystemClassLoader(), bytes, 0, bytes.length)).newInstance();

image-20240714223046919.png

你可能想看:

Internal and external cultivation | Under the high-confrontation offensive and defensive, internal network security cannot be ignored

d) Adopt identification technologies such as passwords, password technologies, biometric technologies, and combinations of two or more to identify users, and at least one identification technology sho

Article 2 of the Cryptography Law clearly defines the term 'cryptography', which does not include commonly known terms such as 'bank card password', 'login password', as well as facial recognition, fi

Analysis and reflection on some practical issues of network intrusion detection system based on traffic

In today's rapidly developing digital economy, data has become an important engine driving social progress and enterprise development. From being initially regarded as part of intangible assets to now

How to implement cloud workload protection in the production network? A practice sharing from ByteDance

Interpretation and Practice of the Requirements for the Registration and Declaration of Medical Device Network Security

It is possible to perform credible verification on the system boot program, system program, important configuration parameters, and application programs of computing devices based on a credible root,

Cloud Migration Security (Part Two): Understanding AWS Cloud Security Strategies from the Perspective of Buying and Decorating a House

(3) Is the national secret OTP simply replacing the SHA series hash algorithms with the SM3 algorithm, and becoming the national secret version of HOTP and TOTP according to the adopted dynamic factor

最后修改时间:
admin
上一篇 2025年03月27日 01:52
下一篇 2025年03月27日 02:14

评论已关闭