3

Java反射

 2 years ago
source link: https://yq1ng.github.io/2021/12/01/java-fan-she/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

title: Java 反序列化漏洞(三)–Java 反射
date: 2021-06-22 17:04:08
categories: Java 安全

Java 反射概述

  • 功能:动态获取信息以及动态调用对象方法
  • 描述:Java 的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。 这种动态获取程序信息以及动态调用对象的功能称为 Java 语言的反射机制。 反射被视为动态语言的关键。

Java 是一个静态语言,它在编译期间就会检查变量的类型,所以在在写程序的时候需要声明所有变量的数据类型,否则编译失败。正是因为是静态语言,所以它无法像动态语言一样通过少量代码实现多数功能;而程序一旦确定后再去改变一些方法(例如实例化对象的更改)还要重新编译,但反射的存在(Class.forName(className))可以使 Java 通过读取配置信息(类的全限定名)动态改变实例。

  1. 动态加载:上个篇章说的 JVM 会动态加载所需要的类就是这个
  2. 动态代理: 在切面编程(AOP)中,需要拦截特定的方法,通常,会选择动态代理方式,这也是反射来实现的
  3. 各种通用框架:例如 Spring、Mybatis 等等都是通过读取配置文件信息来动态加载不同对象
  4. ide 的代码补全:idea 代码补全也是反射的实现
  1. 性能开销:这个不必多说吧
  2. 破坏封装性:Java 封装可谓是一大特性了,但是反射会把类内部的属性和方法暴露出来,而且反射调用方法时可以忽略权限检查,这是很危险的

反射漏洞示例

java.lang.Runtime

直接看代码

package com.yq1ng;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author ying
 * @Description
 * @create 2021-06-23 4:33 PM
 */

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        // 1. 先找到java.lang.Runtime类
        Class<?> runtime = Class.forName("java.lang.Runtime");
        System.out.println("通过Class.forName()找到 " + runtime);
        // 2. 找exec方法
        Method[] runtimeMethods = runtime.getMethods();
        for (Method runtimeMethod : runtimeMethods) {
            System.out.println(runtimeMethod);
        }
        // 3. invoke
        Object execObj = runtime.getMethod("exec", String.class).invoke(runtime.newInstance(),"pwd");
    }
}

报错:Exception in thread "main" java.lang.IllegalAccessException: Class com.yq1ng.Test can not access a member of class java.lang.Runtime with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102),私有方法不允许访问,去java.lang.Runtime看看,可以用getRuntime()

20210623171839.png#id=z2hh6&originHeight=361&originWidth=774&originalType=binary%E2%88%B6=1&status=done&style=none
package com.yq1ng;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author ying
 * @Description
 * @create 2021-06-23 5:21 PM
 */

public class exec {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        Class<?> runTime = Class.forName("java.lang.Runtime");
        Method getRuntime= runTime.getMethod("getRuntime");
        Method exec = runTime.getMethod("exec", String.class);
        Object obj = getRuntime.invoke(null);
        //  使用Process获取子进程的各种流
        //  win平台不能直接执行命令,需要 cmd.exe /c 命令
        Process p = (Process) exec.invoke(obj, "cmd.exe /c dir");
        InputStream inputStream = p.getInputStream();
        InputStreamReader isr = new InputStreamReader(inputStream);
        BufferedReader br = new BufferedReader(isr);
        String line = null;
        while ((line=br.readLine())!=null){
            System.out.println(line);
        }
    }
}
20210623174103.png#id=AQkqJ&originHeight=178&originWidth=669&originalType=binary%E2%88%B6=1&status=done&style=none

java.lang.ProcessBuilder

跟进java.lang.Runtime.exec()方法看怎么实现的

20210623212648.png#id=EvzMi&originHeight=333&originWidth=682&originalType=binary%E2%88%B6=1&status=done&style=none
20210623211623.png#id=KVMAo&originHeight=178&originWidth=635&originalType=binary%E2%88%B6=1&status=done&style=none

直接调用java.lang.ProcessBuilder.start()也可

package com .yq1ng.ProcessBuilder;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * @author ying
 * @Description
 * @create 2021-06-23 8:01 PM
 */

public class noParameter {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
        Class<?> processBuilder = Class.forName("java.lang.ProcessBuilder");
        //  由于 ProcessBuilder 构造函数均有参,且 className.newInstance() 没有参数 , 只能调用无参构造函数
        //  所以此处用了 getConstructor() 方法来调用有参函数
        //  参数类型为数组
//        Object obj = processBuilder.getConstructor(List.class).newInstance(Arrays.asList("id"));
        //  有参数的话需要将参数装到一个数组实例中
        Object arg[] = new Object[]{new String[]{"ls","-al"}};
        Object obj = processBuilder.getConstructor(String[].class).newInstance(arg);

        Method start = processBuilder.getMethod("start");
        Process p = (Process) start.invoke(obj);
        InputStream inputStream = p.getInputStream();
        InputStreamReader isr = new InputStreamReader(inputStream);
        BufferedReader br = new BufferedReader(isr);
        String line = null;
        while ((line=br.readLine())!=null){
            System.out.println(line);
        }
    }
}

在 win 下比较麻烦,这里直接用 kali 运行了

20210623215803.png#id=rhDk6&originHeight=121&originWidth=835&originalType=binary%E2%88%B6=1&status=done&style=none
20210623222159.png#id=NhEbM&originHeight=195&originWidth=863&originalType=binary%E2%88%B6=1&status=done&style=none

反射调用私有方法

上面也提到通过反射可以绕过安全检查,调用私有方法。这里就要使用getDeclaredConstructor(),这个方法能够返回指定参数类型的所有构造方法 . 包括 public , protected 以及 private 修饰符修饰的

package com.yq1ng;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author ying
 * @Description
 * @create 2021-06-23 10:50 PM
 */

public class BypassSecurityChecks {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
        Class<?> runtime = Class.forName("java.lang.Runtime");
        Constructor<?> runtimeDeclaredConstructor = runtime.getDeclaredConstructor();
        //  setAccessible 设置为 true 会取消Java安全检查,即可以访问到私有方法
        runtimeDeclaredConstructor.setAccessible(true);
        Method exec = runtime.getMethod("exec", String.class);
        Object obj = runtimeDeclaredConstructor.newInstance();
        Process p = (Process) exec.invoke(obj, "uname -a");
        InputStream inputStream = p.getInputStream();
        InputStreamReader isr = new InputStreamReader(inputStream);
        BufferedReader br = new BufferedReader(isr);
        String line;
        while ((line=br.readLine())!=null){
            System.out.println(line);
        }
    }
}
20210623225951.png#id=GPeJm&originHeight=145&originWidth=968&originalType=binary%E2%88%B6=1&status=done&style=none

可以看到,提示了非法操作,而且未来版本反射的非法访问要被修复了


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK