
WebLogic CVE-2020-2883 का उपयोग करके Shiro rememberMe deserialization vulnerability का शोषण करता है, एक क्लिक में 蚁剑 filter memory shell पंजीकृत करता है।
इस बार साझा करने में निम्नलिखित बिंदु शामिल हैं:
जावा सीरियलाइज़ेशन से तात्पर्य जावा ऑब्जेक्ट को बाइट अनुक्रम में बदलने की प्रक्रिया से है, ताकि इसे मेमोरी, फ़ाइल या डेटाबेस में आसानी से संग्रहीत किया जा सके। ObjectOutputStream क्लास की writeObject() विधि सीरियलाइज़ेशन को लागू कर सकती है, जो जावा ऑब्जेक्ट को बाइट अनुक्रम में बदल देती है।
जावा डिसीरियलाइज़ेशन से तात्पर्य बाइट अनुक्रम को पुनः जावा ऑब्जेक्ट में बदलने की प्रक्रिया से है। ObjectInputStream क्लास की readObject() विधि डिसीरियलाइज़ेशन के लिए उपयोग की जाती है।
एक सरल उदाहरण लें, कोड SerializeAndDeserialize देखें ps: यहाँ कोड में फ़ोर्स्ड टाइप कास्टिंग पर विशेष ध्यान दें```java
package org.chabug.demo;
import org.chabug.entity.Dog; import org.chabug.entity.Person; import org.chabug.util.Serializables;
/* 这个例子是为了证明只要实现了Serializable接口的类都可以被序列化 并且Java内置的几大数据类型也可被序列化,因为他们都继承了Object类 */
public class SerializeAndDeserialize {
public static void main(String[] args) throws Exception {
byte[] bytes;
String s1 = "I'm a String Object....";
bytes = Serializables.serializeToBytes(s1);
Object o1 = Serializables.deserializeFromBytes(bytes);
System.out.println(o1);
String[] s2 = new String[]{"tom", "bob", "jack"};
bytes = Serializables.serializeToBytes(s2);
String[] o2 = (String[])Serializables.deserializeFromBytes(bytes);
System.out.println(o2);
int i = 123;
bytes = Serializables.serializeToBytes(i);
int o3 = (Integer) Serializables.deserializeFromBytes(bytes);
System.out.println(o3);
// 一只名叫woody的狗
Dog dog = new Dog();
dog.setName("woody");
// tom
Person tom = new Person();
tom.setAge(14);
tom.setName("tom");
tom.setSex("男");
tom.setDog(dog);
bytes = Serializables.serializeToBytes(tom);
Person o = (Person) Serializables.deserializeFromBytes(bytes);
System.out.println(o);
}
}
String、Integer、Array、Object जैसे Java के बिल्ट-इन डेटा प्रकार क्रमबद्ध किए जा सकते हैं। हमारे द्वारा लिखे गए Person और Dog क्लास को Serializable इंटरफ़ेस लागू करने पर क्रमबद्ध और डीक्रमबद्ध किया जा सकता है।
## डीक्रमबद्धीकरण के दौरान कमज़ोरी क्यों उत्पन्न होती है?
एक कोड स्निपेट देखें। अब एक दुर्भावनापूर्ण एंटिटी क्लास EvilClass है।```java
package org.chabug.entity;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class EvilClass implements Serializable {
String name;
public EvilClass() {
System.out.println(this.getClass() + "的EvilClass()构造方法被调用!!!!!!");
}
public EvilClass(String name) {
System.out.println(this.getClass() + "的EvilClass(String name)构造方法被调用!!!!!!");
this.name = name;
}
public String getName() {
System.out.println(this.getClass() + "的getName被调用!!!!!!");
return name;
}
public void setName(String name) {
System.out.println(this.getClass() + "的setName被调用!!!!!!");
this.name = name;
}
@Override
public String toString() {
System.out.println(this.getClass() + "的toString()被调用!!!!!!");
return "EvilClass{" +
"name='" + getName() + '\'' +
'}';
}
private void readObject(ObjectInputStream in) throws Exception {
//执行默认的readObject()方法
in.defaultReadObject();
System.out.println(this.getClass() + "readObject()被调用!!!!!!");
Runtime.getRuntime().exec(new String[]{"cmd", "/c", name});
}
}
इसके readObject में कमांड निष्पादित करने वाला कोड है Runtime.getRuntime().exec(new String[]{"cmd", "/c", name}), name पैरामीटर निष्पादित होने वाला कमांड है। तो हम एक दुर्भावनापूर्ण ऑब्जेक्ट बना सकते हैं, उसके name प्रॉपर्टी को निष्पादित होने वाले कमांड पर सेट कर सकते हैं, जब डीसीरियलाइज़ेशन readObject को ट्रिगर करेगा तो RCE होगा। इस प्रकार```java
package org.chabug.demo;
import org.chabug.entity.EvilClass; import org.chabug.util.Serializables;
public class EvilSerialize { public static void main(String[] args) throws Exception { EvilClass evilObj = new EvilClass(); evilObj.setName("calc"); byte[] bytes = Serializables.serializeToBytes(evilObj); EvilClass o = (EvilClass) Serializables.deserializeFromBytes(bytes); System.out.println(o); } }

अब हम जानते हैं कि डिसीरियलाइज़ेशन (deserialization) को RCE तक कैसे पहुँचाया जाता है, लेकिन विकास में इसे सीधे इस तरह से नहीं लिखा जाता है, इसलिए इसमें उपयोग श्रृंखला (exploit chain) की खोज शामिल है। डिसीरियलाइज़ेशन भेद्यता के लिए तीन चीजों की आवश्यकता होती है:
1. डिसीरियलाइज़ेशन प्रवेश (source)
2. लक्ष्य विधि (sink)
3. उपयोग श्रृंखला (gadget chain)
ध्यान से ऊपर की छवि में आउटपुट परिणाम को फिर से देखें, न केवल readObject विधि ट्रिगर हुई, बल्कि toString(), बिना पैरामीटर वाला कंस्ट्रक्टर, set, get विधियाँ भी ट्रिगर हुईं, इसलिए वास्तविक उपयोग श्रृंखला की खोज में केवल readObject() विधि पर ध्यान केंद्रित करने की आवश्यकता नहीं है।
फिर अब हमें **प्रतिबिंब (Reflection)** को समझने की आवश्यकता है, ऊपर हमने **अनिवार्य प्रकार रूपांतरण (Forced type conversion)** की समस्या का उल्लेख किया था, वास्तविक विकास में, readObject में तार्किक प्रसंस्करण किया जाता है, जब इनपुट ऑब्जेक्ट के विशिष्ट डेटा प्रकार का पता नहीं होता है तो प्रतिबिंब के माध्यम से निर्णय लिया जाता है, और प्रतिबिंब ही हमारा RCE तक पहुँचने का महत्वपूर्ण साधन है।
## Java प्रतिबिंब (Reflection)
प्रतिबिंब क्या है? "प्रतिबिंब" में एक "प्रति" शब्द है, तो प्रतिबिंब को समझाने के लिए "सीधे प्रतिबिंब" (direct reflection) से शुरू करना होता है, कोड देखें। यह मेरी एंटिटी क्लास है।```java
package org.chabug.entity;
import java.io.IOException;
public class ReflectionClass {
String name;
public ReflectionClass(String name) {
this.name = name;
}
public ReflectionClass() {
}
public String say() {
return this.name;
}
private void evil(String cmd) {
try {
Runtime.getRuntime().exec(new String[]{"cmd","/c",cmd});
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "ReflectionClass{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
सामान्य लेखन```java package org.chabug.demo;
import org.chabug.entity.ReflectionClass;
public class ReflectionDemo { public static void main(String[] args) { ReflectionClass demo = new ReflectionClass(); demo.setName("hello"); System.out.println(demo.say()); // demo.evil("calc"); // 不能够调用private方法 } }
सीधे शब्दों में, new के ज़रिए एक ReflectionClass इंस्टेंस बनाया जाता है, फिर उस इंस्टेंस के ज़रिए उसकी संबंधित विधि को कॉल किया जाता है, इसे "प्रत्यक्ष प्रतिबिंब" कहते हैं। लेकिन जब आप new का उपयोग कर रहे हों और क्लास का नाम नहीं जानते हों तो क्या करें? private द्वारा संरक्षित विधि को कैसे कॉल करें? प्रतिबिंब की भूमिका यहाँ स्पष्ट होती है। नीचे दिए गए कोड को देखें।```java
package org.chabug.demo;
import org.chabug.entity.ReflectionClass;
import java.lang.reflect.Method;