
Spring Kafka Deserialization भेद्यता CVE-2023-34040 के लिए POC
सबसे पहले Dockerized Kafka इंस्टेंस शुरू करें। यह Kafka को प्रारंभ करेगा और इसे पोर्ट 29092 पर उपलब्ध कराएगा।
docker-compose up
Consumer एप्लिकेशन शुरू करें।
cd spring-kafka-consumer
mvn clean install
mvn spring-boot:run
यह Consumer एप्लिकेशन को बिल्ड और प्रारंभ करेगा। Consumer बंद होने से पहले अधिकतम 10 मिनट तक एक संदेश की प्रतीक्षा करेगा।
Producer
cd spring-kafka-producer
mvn clean install
mvn spring-boot:run
यह producer एप्लिकेशन को बिल्ड और प्रारंभ करेगा। Producer kafka कतार में एक संदेश भेजेगा और फिर बंद हो जाएगा।
इस कमजोरी के सफल होने के लिए, Consumer पर निम्नलिखित में से एक या दोनों फ़्लैग सक्षम होने चाहिए।
CheckDeserExWhenValueNull
CheckDeserExWhenKeyNull
यह Consumer एप्लिकेशन में KafkaConsumerConfig.greetingKafkaListenerContainerFactory() के अंतर्गत किया जाता है।
दो पेलोड ट्रिगर किए जा सकते हैं, डिफ़ॉल्ट रूप से RCE पेलोड उपयोग किया जाता है।
DOS पेलोड सक्षम करने के लिए, KafkaApplication.sendGreetingMessage() मेथड को संशोधित करें।
header में जोड़े जाने वाले पेलोड को dosPayload में बदलें और producer को पुनः बिल्ड करके चलाएँ।
नोट: चूँकि DOS संदेश पढ़ते समय होता है, और पढ़ना कभी समाप्त नहीं होता, संदेश कतार में बना रहता है, जब तक कि उसे मैन्युअल रूप से हटाया न जाए या संदेश प्रतिधारण समय समाप्त न हो। यह DOS की प्रभावशीलता को बढ़ाता है क्योंकि यह उस कतार को तब तक अनुपयोगी बना देता है जब तक मैन्युअल हस्तक्षेप न हो या प्रतिधारण समय समाप्त न हो। DOS संदेश के तुरंत बाद भेजे गए संदेशों के लिए संभावित रूप से डेटा हानि हो सकती है।
DOS payload को सक्षम करने के लिए, KafkaApplication.sendGreetingMessage() मेथड को संशोधित करें।
header में जोड़े जाने वाले payload को rcePayload में बदलें और producer को पुनः बिल्ड करके चलाएँ। डिफ़ॉल्ट रूप से जो कमांड चलाई जाती है वह है
touch /tmp/newfile — यह देखने के लिए कि क्या आक्रमण सफल रहा, /tmp के अंतर्गत newfile नामक फ़ाइल देखें।
यदि Windows पर चला रहे हैं, तो आप कमांड स्ट्रिंग को कुछ अधिक उपयुक्त में संशोधित कर सकते हैं।
यह Gadget काफी हद तक केवल एक POC है। वास्तविक दुनिया में RCE होने के लिए, Consumer के class path पर एक gadget class उपलब्ध होना आवश्यक होगा।
Denial of Service के लिए Consumer के class path पर कोई विशेष gadget class उपलब्ध होना आवश्यक नहीं है। यह वर्ग के एक संशोधित संस्करण को उत्पन्न करने पर निर्भर करता है
org.springframework.kafka.support.serializer.DeserializationException जिसमें एक Object होता है।
इससे हमें पसंद के किसी भी payload को serialized object में जोड़ना आसान हो जाता है।
इस संशोधित class को xrg.springframework.kafka.support.serializer.DeserializationException कहा जाता है (पैकेज नाम की शुरुआत में x पर ध्यान दें)।
एक बार payload इंजेक्ट हो जाने के बाद, इस मामले में java.util.Set और java.lang.Object का उपयोग करके billion laughs शैली का आक्रमण, इसे serialized कर दिया जाता है।
फिर बाइनरी डेटा को संशोधित किया जाता है ताकि x को o में बदला जा सके, जो consumer की अपेक्षा से मेल खाता है।
इस serialized exception class को फिर springDeserializerExceptionValue header और springDeserializerExceptionKey header दोनों में message header के रूप में जोड़ा जाता है।
फिर यदि key या message null होता है तो इन्हें consumer द्वारा पढ़ा जाता है। उसके बाद केवल यह सुनिश्चित करें कि key या message null है और consumer उसे पढ़ लेगा।
Spring-Kafka में कुछ deserialization सुरक्षा होती है। ListenerUtils में।
public static DeserializationException byteArrayToDeserializationException(LogAccessor logger, byte[] value) {
try {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(value)) {
boolean first = true;
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (this.first) {
this.first = false;
Assert.state(desc.getName().equals(DeserializationException.class.getName()),
"Header does not contain a DeserializationException");
}
return super.resolveClass(desc);
}
};
return (DeserializationException) ois.readObject();
}
catch (IOException | ClassNotFoundException | ClassCastException e) {
logger.error(e, "Failed to deserialize a deserialization exception");
return null;
}
}