在利用第三方庫解析excel文件時,由於excel文件本身是一個壓縮包,因此在解析壓縮包中的xml文件時,會引入xxe問題。
一、poc驗證文件准備:
新建xlsx文件,修改后綴為zip,在 [Content_Types].xml、/xl/workbook.xml、/xl/worksheets/shee1.xml 中插入xxe的poc。
二、常見excel解析庫漏洞復現
1、Apache POI
受影響版本:poi-ooxml-3.10-FINAL 及其以下版本
示例代碼:
public static void main(String[] args) throws IOException, EncryptedDocumentException, InvalidFormatException { Workbook wb1 = WorkbookFactory.create(new FileInputStream("/Users/iyiyang/Desktop/test.xlsx")); Sheet sheet = wb1.getSheetAt(0); System.out.println(sheet.getLastRowNum()); }
2、excel-streaming-reader
受影響版本:2.0.0及其以下版本
示例代碼:
public class App { public static void main( String[] args ) throws EncryptedDocumentException, InvalidFormatException, IOException { Workbook wb1 = StreamingReader.builder().rowCacheSize(10).bufferSize(1024).open(new FileInputStream("/Users/iyiyang/Desktop/test1.xlsx")); Sheet sheet = wb1.getSheetAt(0); for (Row r : sheet) { for (Cell c : r) { System.out.println(c.getStringCellValue()); } } }
3、tika
受影響版本:tika-app-1.12 及其以下版本
示例代碼:
public static void main(String[] args) { Parser parser = new AutoDetectParser(); File f = new File("/Users/iyiyang/Desktop/test.xlsx"); InputStream is = null; try { Metadata metadata = new Metadata(); metadata.set(Metadata.RESOURCE_NAME_KEY, f.getName()); is = new FileInputStream(f); ContentHandler handler = new BodyContentHandler(); ParseContext context = new ParseContext(); context.set(Parser.class,parser); //2、執行parser的parse()方法。 parser.parse(is,handler, metadata,context); for(String name:metadata.names()) { System.out.println(name+":"+metadata.get(name)); } System.out.println(handler.toString()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (TikaException e) { e.printStackTrace(); } finally { try { if(is!=null) is.close(); } catch (IOException e) { e.printStackTrace(); } } }
4、php-excel-reader(已不在維護,需自行修復)
示例代碼:
<?php require('php-excel-reader/excel_reader2.php'); require('SpreadsheetReader.php'); $Reader = new SpreadsheetReader('test.xlsx'); $Sheets = $Reader -> Sheets(); foreach ($Sheets as $Index => $Name) { echo 'Sheet #'.$Index.': '.$Name; $Reader -> ChangeSheet($Index); foreach ($Reader as $Row) { print_r($Row); } } ?>
三、漏洞分析
1、Apache POI
在執行 workbook = new XSSFWorkbook(FileInputStream) 時,執行 init 初始化方法,以zip方式將文件信息傳入 OPCPackage.open() 方法,
跟進 pack.getParts() -> this.getPartsImpl() -> new ZipContentTypeManager(getZipArchive().getInputStream(entry), this) -> parseContentTypesFile(in) ,發現直接通過 SAXReader 來解析xml文檔,導致xxe漏洞產生
而修復方案,則是在此處增加了一個SAXHelper類來進行防御處理(位置在 poi-ooxml-3.10.1.jar!/org/apache/poi/util/SAXHelper.class ,可以去研究下有沒有繞過方式~)
2、excel-streaming-reader
在執行 com.monitorjbl.xlsx.StreamingReader.Builder.open(InputStream) 方法時會對 workbook 進行初始,跟進
跟進 OPCPackage.open(f) ,發現是對讀取的 excel 文件當做的 zip 包處理
然后接下來將我們從excel文件中讀取到的 xml 文件傳入 document() 方法,觸發漏洞
跟進 document() 方法,發現是直接通過 dom 方式解析 xml 文件導致了xxe漏洞(位置在 xlsx-streamer-2.0.0.jar!/com.monitorjbl.xlsx.XmlUtils.class)
而修復方式采用的直接禁用外部實體引用,如下:
3、tika
首先跟進 org.apache.tika.parser.AutoDetectParser.parse() 方法
跟進發現實際被繼承的是 org.apache.tika.parser.microsoft.ooxml.OOXMLExtractorFactory.parse() 方法,跟進 extractor.getXHTML(baseHandler, metadata, context) , 最后定位到執行的在 org.apache.tika.parser.microsoft.ooxml.AbstractOOXMLExtractor.getXHTML()
跟進 buildXHTML(xhtml) -> processSheet(sheetExtractor, comments, styles, strings, stream),發現直接通過 SAXParser 解析xml,觸發漏洞
而修復防范類似poi,采用了 ParseContext 這個類來進行防御處理(位置在 tika-app-1.13.jar!/org/apache/tika/parser/ParseContext.class)
java,且行且遠/