Unsafe 类不是一个 ClassLoader, 但是为什么要在本篇文章提起, 其实是因为该类可以进行注入恶意类到 JVM 中.
Unsafe 类简介
sun.misc.Unsafe 类是一个提供底层、不安全的操作,比如直接内存访问、线程调度、原子操作等功能的工具类。
这个类主要被Java内部库使用,比如Java的NIO、并发包等,因为它允许绕过Java的内存管理模型,直接进行内存操作,这可能导致程序崩溃、数据损坏等严重后果。因此,它被认为是"不安全"的,并且不建议在常规的应用程序开发中使用。
Unsafe 类详解
我们可以看到图中的定义, theUnsafe
成员属性的定义在static
代码块中进行初始化了。 所以我们可以通过Unsafe.getUnsafe
来得到该对象, 但是这里有一个限制。
根据下图进行代码分析:
Unsafe
的构造方法为private
修饰符, 所以我们无法在程序中直接new Unsafe()
进行实例化生成, 否则程序将报错:
这里的话我们可以通过反射进行暴破获取该类的theUnsafe
属性, 当然也可以通过反射暴破该类的构造方法, 都可以进行获取到Unsafe
类, 代码测试如下:
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName("sun.misc.Unsafe");
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Unsafe unsafe = (Unsafe) declaredConstructor.newInstance();
System.out.println(unsafe); // sun.misc.Unsafe@4554617c
}
}
以及:
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> clazz = Class.forName("sun.misc.Unsafe");
Field theUnsafe = clazz.getDeclaredField("theUnsafe"); // 因为 theUnsafe 使用 static 进行修饰, 在 static 代码块中进行初始化, 所以这里无需创建 Unsafe 对象, 静态调用就可以得到.
theUnsafe.setAccessible(true);
Unsafe o = (Unsafe) theUnsafe.get(null);
System.out.println(o); // sun.misc.Unsafe@74a14482
}
}
Unsafe 类利用
我们可以通过反射得到 Unsafe 对象, 那么我们如何利用呢?
我们在Unsafe 类详解
中图中已经看到了, 该类定义了三个native
定义的方法, 这些方法都是由C/C++
底层实现的, 如下:
public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);
// 可以加载字节码
public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3); // 可以加载字节码
public native Object allocateInstance(Class<?> var1) throws InstantiationException; // 实例化任意类
defineClass 案例
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
Class<?> clazz = Class.forName("sun.misc.Unsafe");
Field theUnsafe = clazz.getDeclaredField("theUnsafe"); // 因为 theUnsafe 使用 static 进行修饰, 在 static 代码块中进行初始化,
// 所以这里无需创建 Unsafe 对象, 静态调用就可以得到.
theUnsafe.setAccessible(true);
Unsafe o = (Unsafe) theUnsafe.get(null);
System.out.println(o); // sun.misc.Unsafe@74a14482
byte[] poc = new BASE64Decoder().decodeBuffer(
"yv66vgAAADQAKAoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAVMQ01EOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAB0BAApTb3VyY2VGaWxlAQAIQ01ELmphdmEMAAoACwcAIgwAIwAkAQAEY2FsYwwAJQAmAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uDAAKACcBAANDTUQBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAAAgABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAAgADgAAAAwAAQAAAAUADwAQAAAACAARAAsAAQAMAAAAZgADAAEAAAAXuAACEgO2AARXpwANS7sABlkqtwAHv7EAAQAAAAkADAAFAAMADQAAABYABQAAAAsACQAOAAwADAANAA0AFgAPAA4AAAAMAAEADQAJABIAEwAAABQAAAAHAAJMBwAVCQABABYAAAACABc=");
/*
该 Base64 值是如下类的字节码 Base64 后的值
public class CMD {
static {
try {
Process exec = Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} */
Class<?> evilClazz = o.defineClass("CMD", poc, 0, poc.length, ClassLoader.getSystemClassLoader(),
new ProtectionDomain(
new CodeSource(null, (Certificate[]) null), null, ClassLoader.getSystemClassLoader(), null
));
System.out.println(evilClazz); // class CMD
evilClazz.newInstance();
}
}
运行完毕后, 将弹出计算器。
Java 11
开始Unsafe
类已经把defineClass
方法移除了(defineAnonymousClass
方法还在)。
defineAnonymousClass 案例
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
Class<?> clazz = Class.forName("sun.misc.Unsafe");
Field theUnsafe = clazz.getDeclaredField("theUnsafe"); // 因为 theUnsafe 使用 static 进行修饰, 在 static 代码块中进行初始化,
// 所以这里无需创建 Unsafe 对象, 静态调用就可以得到.
theUnsafe.setAccessible(true);
Unsafe o = (Unsafe) theUnsafe.get(null);
System.out.println(o); // sun.misc.Unsafe@74a14482
byte[] poc = new BASE64Decoder().decodeBuffer(
"yv66vgAAADQAKAoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAVMQ01EOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAB0BAApTb3VyY2VGaWxlAQAIQ01ELmphdmEMAAoACwcAIgwAIwAkAQAEY2FsYwwAJQAmAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uDAAKACcBAANDTUQBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAAAgABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAAgADgAAAAwAAQAAAAUADwAQAAAACAARAAsAAQAMAAAAZgADAAEAAAAXuAACEgO2AARXpwANS7sABlkqtwAHv7EAAQAAAAkADAAFAAMADQAAABYABQAAAAsACQAOAAwADAANAA0AFgAPAA4AAAAMAAEADQAJABIAEwAAABQAAAAHAAJMBwAVCQABABYAAAACABc=");
/*
该 Base64 值是如下类的字节码 Base64 后的值
public class CMD {
static {
try {
Process exec = Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} */
Class<?> evilClazz = o.defineAnonymousClass(Class.class, poc, null);
System.out.println(evilClazz); // class CMD/356573597
evilClazz.newInstance();
}
}
运行弹出计算器.
allocateInstance 案例
定义如下类:
public class Cat {
private Cat(){}
}
测试程序:
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
Class<?> clazz = Class.forName("sun.misc.Unsafe");
Field theUnsafe = clazz.getDeclaredField("theUnsafe"); // 因为 theUnsafe 使用 static 进行修饰, 在 static 代码块中进行初始化,
// 所以这里无需创建 Unsafe 对象, 静态调用就可以得到.
theUnsafe.setAccessible(true);
Unsafe o = (Unsafe) theUnsafe.get(null);
System.out.println(o); // sun.misc.Unsafe@74a14482
Cat cat = (Cat) o.allocateInstance(Cat.class);
System.out.println(cat); // com.heihu577.Cat@1540e19d
}
}
最终可以实例化Cat类。
标签:java,theUnsafe,编程,Unsafe,public,class,static,Class From: https://www.cnblogs.com/o-O-oO/p/18575306