Joven hacker sonriendo

Evitar Ataques de XPATH Injection

Necesidad

Evitar ataques de XPATH Injection

Contexto

A continuación se describen las circunstancias bajo las cuales la siguiente solución tiene sentido:

  1. Se está desarrollando una aplicación con Java.

  2. Se está desarrollando una aplicación que utiliza el API de interpretación de XML XPATH.

Solución

De manera similar a las SQL Injection, los ataques de XPATH Injection se producen cuando un sitio web utiliza la información suministrada por el usuario para construir una consulta XPATH para datos XML. Mediante el envío de sentencias intencionalmente mal formadas, un atacante puede descubrir cómo se estructuran los datos XML, incluso acceder a todos sus datos. El atacante incluso puede ser capaz de elevar sus privilegios en el sitio web si los datos XML se utilizan para la autenticación.

Las consultas XML se realizan con XPATH, un tipo de declaración descriptiva simple que permite la consulta XML para localizar información. Al igual que SQL, puede especificar ciertos atributos para encontrar y mostrar patrones para que coincidan. Cuando se hace uso de XML en un sitio web es común aceptar algún tipo de entrada en la cadena de consulta para identificar el contenido a localizar y mostrar en la página.

Por último, hay que saber que XPATH es un lenguaje estándar, y su notación y/o sintaxis es siempre independiente de la implementación, lo que significa que el ataque puede ser automatizado. No hay dialectos diferentes, ya que se lleva a cabo en las solicitudes de las bases de datos SQL. Debido a que no existe un control de acceso de nivel, es posible obtener el documento completo. [1]

En esa solución se pretende mostrar un ataque XPATH y como evitarlo en una aplicación desarrollada en java.

  1. Primero considere el siguiente archivo XML.

    test.xml
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?xml version="1.0" encoding="UTF-8"?>
     <users>
       <user>
         <firstname>Ben</firstname>
         <lastname>Elmore</lastname>
         <loginID>abc</loginID>
         <password>test123</password>
       </user>
       <user>
         <firstname>Shlomy</firstname>
         <lastname>Gantz</lastname>
         <loginID>xyz</loginID>
         <password>123test</password>
       </user>
       <user>
         <firstname>Jeghis</firstname>
         <lastname>Katz</lastname>
         <loginID>mrj</loginID>
         <password>jk2468</password>
       </user>
       <user>
         <firstname>Darien</firstname>
         <lastname>Heap</lastname>
         <loginID>drano</loginID>
         <password>2mne8s</password>
        </user>
     </users>
    
  2. Ahora bien, es posible fabricar la siguiente consulta de XPATH.

    1
    /users/user[loginID/text()='abc' and password/text()='test123']
    
  3. Si un atacante pudiera controlar las entradas de la consulta, de manera similar a los ataques de SQL Injection, podría fabricar una consulta de este tipo.

    1
    /users/user[LoginID/text()='' or '1'='1' and password/text()='' or '1'='1']
    
  4. En el siguiente código, si un atacante puede manipular la entrada a doLogin, es posible realizar tautologías que permitan evadir el mecanismo de autenticación.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    private boolean doLogin(String loginID, String password)
     throws ParserConfigurationException, SAXException,
      IOException, XPathExpressionException {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.parse("users.xml");
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();
        String query = "//users/user[loginID/text()='"
          + loginID + "' and password/text()='"+password+"' ]/firstname/text()";
        XPathExpression expr = xpath.compile(query);
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
        NodeList nodes = (NodeList) result;
        //print first names to the console
        for (int i = 0; i < nodes.getLength(); i++) {
          System.out.println(nodes.item(i).getNodeValue()+" accede al sistema");}
          if (nodes.getLength() >= 1) {
            return true;}
          else
           {return false;}
        }
    
  5. Para prevenir estos ataques, se procede del mismo modo que con los ataques de SQL Injection. Es decir, realizando validación de las entradas, sistemas de prevención como WAF, inspección de datos en las diferentes capas. Por ejemplo, en el servidor Web Apache se puede usar el Mod_Security SecFilterSelective THE_REQUEST "(\'|\")"). Ademas, es importante usar parametrización en las consultas.

    1
    2
    3
    declare variable $loginID as xs:string external;
    declare variable $password as xs:string external;
    //users/user[@loginID=$loginID and @password=$password]
    
  6. Para utilizar consultas parametrizadas se requiere emplear XQuery, que es un super conjunto de XPATH. XQuery básicamente permite utilizar archivos XML como si fuesen bases de datos. Java no soporta de modo nativo XQuery, pero existen parseadores gratuitos como el referenciado en [3].

  7. El código completo usando AWT sería:

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    import java.awt.*;
    import java.awt.event.*;
    import java.io.IOException;
    import org.w3c.dom.*;
    import org.xml.sax.SAXException;
    import javax.xml.parsers.*;
    import javax.xml.xpath.*;
    
    public class XpathInjectionExample {
      public static void main(String[] args) {
        System.out.println("Running");
        self= new XpathInjectionExample();
        self.printForm();
      }
    
      public static void doLogin() {
        boolean auth = false;
        try {
          auth = self.doLogin(self.lUserName.getText(), self.lPass.getText());
        } catch (ParserConfigurationException e1) {
            System.out.println("ParserConfigurationException");
        } catch (SAXException e1) {
            System.out.println("SAXException");
        } catch (IOException e1) {
            System.out.println("IOException");
        } catch (XPathExpressionException e1) {
            System.out.println("XPathExpressionException");
        }
        if (auth)
                    self.lbl2.setText("Autenticacion exitosa");
        else
                    self.lbl2.setText("Usuario y/o Contrasena invalida");
      }
    
      private boolean doLogin(String loginID, String password)
         throws ParserConfigurationException,
           SAXException, IOException, XPathExpressionException
      {
         DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
         domFactory.setNamespaceAware(true);
         DocumentBuilder builder = domFactory.newDocumentBuilder();
         Document doc = builder.parse("users.xml");
         XPathFactory factory = XPathFactory.newInstance();
         XPath xpath = factory.newXPath();
         String query = "//users/user[loginID/text()='"
           + loginID + "' and password/text()='"+password+"' ]/firstname/text()";
         XPathExpression expr = xpath.compile(query);
         Object result = expr.evaluate(doc, XPathConstants.NODESET);
         NodeList nodes = (NodeList) result;
         //print first names to the console
         for (int i = 0; i < nodes.getLength(); i++) {
           System.out.println(nodes.item(i).getNodeValue()+" accede al sistema");}
           if (nodes.getLength() >= 1) {
             return true;}
           else
             {return false;}
      }
    
      private void printForm() {
        al = new myActionListener();
        frm=new Frame("Autenticacion");
        lbl = new Label("Bienvenido al sistema mas seguro..."
          + "sientase tranquilo, nosotros no usamos SQL!");
        frm.add(lbl);
        frm.setSize(600,200);
        frm.setVisible(true);
        frm.addWindowListener(new WindowAdapter(){
          public void windowClosing(WindowEvent e){
            System.exit(0);}
          });
        p = new Panel();
        p1 = new Panel();
        jUserName = new Label("Nombre de usuario");
        lUserName = new TextField(20);
        jPass =new Label("Last Name");
        lPass=new TextField(20);
        lPass.setEchoChar('*');
        p.setLayout(new GridLayout(3,1));
        p.add(jUserName);
        p.add(lUserName);
        p.add(jPass);
        p.add(lPass);
        Submit=new Button("Hecho");
        Submit.setActionCommand("Hecho");
        Submit.addActionListener(al);
        p.add(Submit);
        p1.add(p);
        lbl2 = new Label("Presione Hecho para continuar");
        p1.add(lbl2);
        frm.add(p1,BorderLayout.NORTH);
      }
    
      // miembros del GUI
      private Frame frm;
      private Label lbl;
      private Label lbl2;
      private Panel p;
      private Panel p1;
      private Label jUserName;
      private TextField lUserName;
      private Label jPass;
      private TextField lPass;
      private Button Submit;
      private ActionListener al;
      public static XpathInjectionExample self;
    }
    
    class myActionListener implements ActionListener {
      public void actionPerformed(ActionEvent ae) {
        String s = ae.getActionCommand();
        if (s.equals("Hecho")) {
          XpathInjectionExample.doLogin() ;
        }
      }
    }
    

Referencias

  1. XPATH Injection

  2. XPath injection

  3. Saxon Home Edition (HE) (open source)

  4. REQ.0168: El sistema debe descartar toda la información potencialmente insegura que sea recibida por entradas de datos.




Haz un comentario