使用Java的XXE

XXE和Java

XXE是什么指的是什么?

XXE(XML外部实体)是利用XML的外部引用功能来获取(泄露)服务器内部文件内容,或者访问内部网络中的文件进行非法行为的。

由于XXE(XML外部实体)可能在处理XML的应用程序中出现,所以在处理XML文档时需要注意。

Java 和 XML

要在Java中处理XML,可以考虑使用javax.xml.parsers.DocumentBuilder。

因此,接下来,我们可以尝试使用javax.xml.parsers.DocumentBuilder来重现或思考解决方案。

結果

在XXE基本編的链接中,也可以通过JavaVM的属性来控制,因此可以选择快捷的方式。但另一方面,作为Java程序员,自己编写解析器并进行控制也应该不是很困难。

使用Java触发XXE漏洞

请尝试使用以下的答案:

下面的源代码程序中,第一个参数要指定XML文件,第二个参数要指定外部引用的操作模式,所以我们可以使用默认的操作模式,读取XML文件(C:\z\xmlxxe\in1.xml)。

就是这样的感觉。

xxe-java-defo.png

在Java的情况下,通常会受到XXE攻击。

将外部参考对象设置为NULL

下面我们尝试使用javax.xml.parsers.DocumentBuilder#setEntityResolver()方法并传入“NULL”。

差不多这样的感觉。

xxe-java-null.png

与.NET Framework不同,Java中的Null被视为默认的解析器对象,因此即使是NULL,在Java的情况下也可能受到XXE攻击的影响。

自己制作外部参考对象

如果想要有限地使用外部引用功能但又想避免XXE,在需要修改解析器来防止外部引用功能的情况下,可以自行编写解析器。

换句话说,javax.xml.parsers.DocumentBuilder#setEntityResolver()方法的第一个参数是”org.xml.sax.EntityResolver”接口类型,因此可以自定义继承该接口的类,并将其分配给javax.xml.parsers.DocumentBuilder#setEntityResolver()方法的第一个参数,从而可以自由地控制外部引用。

必須覆写的方法只有一个:”org.xml.sax.InputSource.InputSource resolveEntity(String publicId,String systemId)”方法。

这里关键的部分是,第二个参数是一个String类型,用于指示外部引用的URL,所以只需要返回相应的”org.xml.sax.InputSource.InputSource”类型即可。

然后,只需要确保从该”org.xml.sax.InputSource.InputSource”类型正确返回”java.io.InputStream”类或”java.io.Reader”类即可。

例如,可以进行个别定制,如需要验证或仅允许特定的URI。

在源代码文件”exEntityResolver.java”的exEntityResolver类中,决定对于表示所有URI的systemId,最终返回一个空的java.io.ByteArrayInputStream的exInputSource类。

变成这样的感觉。

xxe-java-cust.png

源代码(XXEtest.java)

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import java.io.File;
import org.xml.sax.EntityResolver;

public class XXEtest{
 public static void main(String args[]){
  System.out.println("java.exe test <<inFile>> <<Resolver>>");
  if(0 < args.length){
   Boolean IsResolve = false;
   exEntityResolver myExEntityResolver = null;
   if(1 < args.length){
    if(args[1].equals("null") == true){
     IsResolve = true;
    }else{
     myExEntityResolver = new exEntityResolver();
    }
   }
   try{
    DocumentBuilderFactory tempDocumentBuilderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder tempDocumentBuilder = tempDocumentBuilderFactory.newDocumentBuilder();
    if(IsResolve == true){
     tempDocumentBuilder.setEntityResolver(null);
    }else if(myExEntityResolver != null){
     tempDocumentBuilder.setEntityResolver(myExEntityResolver);
    }
    Document tempDocument = tempDocumentBuilder.parse(new File(args[0]));
    System.out.println("getTextContent  : " + tempDocument.getTextContent());
    System.out.print("getXmlStandalone: ");
    tempDocument.setXmlStandalone(true);
    System.out.println(tempDocument.getXmlStandalone());
    System.out.println("========Detail=================");
    PrintNode(tempDocument.getChildNodes(), "");
   }catch(Exception e){
    e.printStackTrace();
   }
  }
 }
 static void PrintNode(NodeList nodeList, String spacer){
  Node node = null;
  for(int i=0; i< nodeList.getLength(); i++){
   node = nodeList.item(i);
   String typeStr = "unknown";
   switch(node.getNodeType()){
    case Node.ELEMENT_NODE:
     typeStr = "ELEMENT_NODE";
     break;
    case Node.ATTRIBUTE_NODE:
     typeStr = "ATTRIBUTE_NODE";
     break;
    case Node.TEXT_NODE:
     typeStr = "TEXT_NODE";
     break;
    case Node.CDATA_SECTION_NODE:
     typeStr = "CDATA_SECTION_NODE";
     break;
    case Node.ENTITY_REFERENCE_NODE:
     typeStr = "ENTITY_REFERENCE_NODE";
     break;
    case Node.ENTITY_NODE:
     typeStr = "ENTITY_NODE";
     break;
    case Node.PROCESSING_INSTRUCTION_NODE:
     typeStr = "PROCESSING_INSTRUCTION_NODE";
     break;
    case Node.COMMENT_NODE:
     typeStr = "COMMENT_NODE";
     break;
    case Node.DOCUMENT_NODE:
     typeStr = "DOCUMENT_NODE";
     break;
    case Node.DOCUMENT_TYPE_NODE:
     typeStr = "DOCUMENT_TYPE_NODE";
     break;
    case Node.NOTATION_NODE:
     typeStr = "NOTATION_NODE";
     break;
   }
   System.out.println(spacer + "name =" + node.getNodeName() + ", type=" + typeStr);
   System.out.println(spacer + "value=" + node.getNodeValue());
   System.out.println(spacer + "getTextContent=" + node.getTextContent());
   if(node.hasChildNodes() == true){
    PrintNode(node.getChildNodes(), spacer + " ");
   }
  }
 }
}

源代码(exEntityResolver.java)

返回一个仅返回空的 ByteArrayInputStream 的 InputSource 类的解析器。

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.IOException;

public class exEntityResolver implements EntityResolver{
 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException{
  System.out.println("publicId: " + publicId);
  System.out.println("systemId: " + systemId);
  return (InputSource)new exInputSource();
 }
}

源代码(exInputSource.java)

只返回一个空的 ByteArrayInputStream 的 InputSource 类。

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.Reader;
import java.io.InputStreamReader;

public class exInputSource extends InputSource{
 ByteArrayInputStream myStream;
 public exInputSource(){
  byte[] hako = new byte[0];
  this.myStream = new ByteArrayInputStream(hako);
 }
 public InputStream getByteStream(){
  return (InputStream)this.myStream;
 }
 public Reader getCharacterStream(){
  return (Reader)new InputStreamReader(this.myStream);
 }
}

返回

返回到基本编辑栏

广告
将在 10 秒后关闭
bannerAds