java reflection and vulnerability risk

Keywords: Java security

reflex

Reflection plays an irreplaceable role in java. Objects can get their classes through reflection. Classes can get all methods, properties including all private methods through reflection and can be used directly.
There are three main ways to get classes:

  • obj.getClass(): An instance of a class, obj, allows you to get its classes directly.
  • Test.class: Get its class property directly from a class that has already been loaded.
  • Class.forName: Get this class by its name.

Enumerate🌰

public void execute(String className, String methodName) throws Exception {
 Class clazz = Class.forName(className);
 clazz.getMethod(methodName).invoke(clazz.newInstance());
}

🌰Interpretations are as follows:

  • forName: Get the class
  • newInstance: Instantiate a class object
  • getMethod: Get the function
  • invoke: implements a function

Reflection of single profit mode

For singleton patterns, such as Runtime classes, Runtime objects can only be obtained from Runtime.getRuntime()

Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec",String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz),
"calc.exe");

Or break it down into:

Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe");

Reflection with a parametric constructor

For classes that only have a parametric constructor, a new reflection method, getConstructor(), is needed. Like getMethod, getConstructor receives a parameter of the constructor list type, because the constructor also supports overloading, it is necessary to use the parameter list type to uniquely determine a constructor.

Another common command execution class, ProcessBuilder, has two constructors:

  • public ProcessBuilder(List command)
  • public ProcessBuilder(String... command)

It executes the command by calling start().Reflecting through the first constructor of ProcessBuilder is implemented as follows:

Class clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));

Launching through the second constructor of ProcessBuilder is accomplished as follows

Class clazz = Class.forName("java.lang.ProcessBuilder");
        clazz.getMethod("start").invoke(clazz.getConstructor(String[].class).newInstance(new String[][]{{"open", "/System/Applications/Calculator.app"}}));

Reflection of private methods

If you need to reflect a private constructor or a private constructor for a class, you need to use getDeclaredMethod and getDeclaredConstructor, which have usages similar to getMethod and getDeclaredConstructor, such as:

  • The getMethod family method takes all the common methods in the current class, including those inherited from the parent class
  • The getDeclaredMethod family method takes the declarative method from the current class and is actually written in this class, including private methods, and does not contain methods inherited from the parent class

🌰
If the constructor of the Runtime class is private, we can instantiate the object by getting this private constructor, such as

Class clazz = Class.forName("java.lang.Runtime");
Constructor m = clazz.getDeclaredConstructor();
m.setAccessible(true);
clazz.getMethod("exec", String.class).invoke(m.newInstance(), "calc.exe");

Where setAccessible(true) suppresses access checks in the java language, allowing you to get the value of a private variable through reflection

Focus

It is important to note that static code blocks of classes are initialized with forname (the constructor of the class is not initialized), and if there are malicious static code blocks in the class, they are executed when forname is called, as follows:

package com.5wimming.Tools;

import java.lang.Runtime;
import java.lang.Process;

public class TestCMD {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"open", "/System/Applications/Calculator.app"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

test

public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("com.5wimming.Tools.TestCMD");

    }

Loophole🌰

Source download address: challenge
We can audit the source code, here we can use the source vulnerability scanning tool gadgetinsepctor , there is an introduction on how to use reademe, or look through the posts in front of me.

A gadgetinsepctor scan reveals a template injection vulnerability

io/tricking/challenge/MainController.admin(Ljava/lang/String;Ljavax/servlet/http/HttpSession;Lorg/springframework/ui/Model;)Ljava/lang/String; (0)
  io/tricking/challenge/MainController.getAdvanceValue(Ljava/lang/String;)Ljava/lang/String; (0)
  org/springframework/expression/ExpressionParser.parseExpression(Ljava/lang/String;Lorg/springframework/expression/ParserContext;)Lorg/springframework/expression/Expression; (0)

No more recurrence here, the process can be referred to Launch vulnerability example

Reference resources:
http://rui0.cn/archives/1015

Posted by emrys404 on Fri, 03 Sep 2021 19:53:52 -0700