Land #10876, ibm_mq_enum: IBM WebSphere MQ Name and Version Enumeration

4.x
asoto-r7 2018-11-21 16:10:59 -06:00 committed by Metasploit
parent 42a8022fd1
commit 82abc7b76b
No known key found for this signature in database
GPG Key ID: CDFB5FA52007B954
2 changed files with 217 additions and 0 deletions

View File

@ -0,0 +1,36 @@
## Vulnerable Application
* IBM Downloads page: https://developer.ibm.com/messaging/mq-downloads/
* Tested on IBM MQ 7.5, 8 and 9
* Usage:
* Download and install MQ Server
* Create a new Queue Manager
* Create a new channel (without SSL)
* Run the module
## Verification Steps
Example steps in this format (is also in the PR):
1. Install IBM MQ Server 7.5, 8, or 9
2. Start msfconsole
3. Do: ```use auxiliary/scanner/misc/ibm_mq_enum```
4. Do: ```set channel <channel_name>```
5. Do: ```set rhosts <target_IP>```
6. Do: ```set rport <port>```
7. Do: ```run```
Example output:
```
msf auxiliary(scanner/misc/ibm_mq_enum) > run
[+] 10.1.1.144: - 10.1.1.144:1414 - Queue Manager Name: TESTQM - MQ Version: 9.1.0.0
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```
## Options
**The CHANNEL option**
This option should contain the name of a valid MQ channel. This can be obtained using the module ```auxiliary/scanner/misc/ibm_mq_channel_brute```
## Scenarios
This module can be used to obtain the Queue Manager name as well as the version of the MQ being used on the target host. When the Queue Manager name and a valid MQI channel name without SSL is known , the module ```auxiliary/scanner/misc/ibm_mq_login``` can be used to identify usernames that can authenticate to the Queue Manager.

View File

@ -0,0 +1,181 @@
##
# 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(info = {})
super(update_info(info,
'Name' => 'Identify Queue Manager Name and MQ Version',
'Description' => 'Run this auxiliary against the listening port of an IBM MQ Queue Manager to identify its name and version. Any channel type can be used to get this information as long as the name of the channel is valid.',
'Author' => [ 'Petros Koutroumpis' ],
'License' => MSF_LICENSE
))
register_options(
[
OptString.new('CHANNEL', [ true, "Channel to use" ,"SYSTEM.DEF.SVRCONN"]),
OptInt.new('CONCURRENCY', [true, "The number of concurrent ports to check per host", 10]),
OptInt.new('TIMEOUT', [true, "The socket connect timeout in seconds", 10]),
OptString.new('PORTS', [true, 'Ports to probe', '1414']),
])
deregister_options('RPORT')
end
def create_packet(channel_type)
chan = datastore['CHANNEL'] + "\x20"*(20-datastore['CHANNEL'].length.to_i)
if channel_type == 0
chan_type = "\x26"
elsif channel_type == 1
chan_type = "\x07"
elsif channel_type == 2
chan_type = "\x08"
end
packet = "\x54\x53\x48\x20" + # StructID
"\x00\x00\x01\x0c" + # MQSegmLen
"\x02" + # ByteOrder
"\x01" + # SegmType
"\x01" + # CtlFlag1
"\x00" + # CtlFlag2
"\x00\x00\x00\x00\x00\x00\x00\x00" + # LUW Ident
"\x22\x02\x00\x00" + # Encoding
"\xb5\x01" + # CCSID
"\x00\x00" + # Reserved
"\x49\x44\x20\x20" + # StructId
"\x0d" + # FAP level
chan_type + # CapFlag1 - Message Type
"\x00" + # ECapFlag1
"\x00" + # IniErrFlg1
"\x00\x00" + # Reserved
"\x32\x00" + # MaxMsgBtch
"\xec\x7f\x00\x00" + # MaxTrSize
"\x00\x00\x40\x00" + # MaxMsgSize
"\xff\xc9\x9a\x3b" + # SeqWrapVal
chan + # Channel Name
"\x87" + # CapFlag2
"\x00" + # ECapFlag2
"\x5b\x01" + # ccsid
"QM1" + "\x20"*45 + # Queue Manager Name
"\x2c\x01\x00\x00" + # HBInterval
"\x8a\x00" + # EFLLength
"\x00" + # IniErrFlg2
"\x55" + # Reserved1
"\x00\xff" + # HdrCprsLst
"\x00\xff\xff\xff\xff\xff\xff\xff\xff" + # MsgCprsLst1
"\xff\xff\xff\xff\xff\xff\xff" + # MsgCprsLst2
"\x00\x00" + # Reserved2
"\x00\x00\x00\x00" + # SSLKeyRst
"\x00\x00\x00\x00" + # ConvBySkt
"\x05" + # CapFlag3
"\x00" + # ECapFlag3
"\x00\x00" + # Reserved3
"\x10\x13\x00\x00" + # ProcessId
"\x01\x00\x00\x00" + # ThreadId
"\x01\x00\x00\x00" + # TraceId
"MQMM09000000" + # ProdId
"MQMID" + "\x20"*43 + # MQM ID
"\x00\x00\xff\xff\xff\xff\xff\xff\xff" + # Unknown1
"\xff\xff\xff\xff\xff\xff\xff\xff\xff" + # Unknown2
"\xff\xff\x00\x00\x00\x00\x00\x00\x00" + # Unknown3
"\x00\x00\x00\x00\x00" # Unknown4
end
def run_host(ip)
chan = datastore['CHANNEL']
if chan.length > 20
print_error("Channel name must be less than 20 characters.")
raise Msf::OptionValidateError.new(['CHANNEL'])
end
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
while(ports.length > 0)
t = []
r = []
begin
1.upto(datastore['CONCURRENCY']) do
this_port = ports.shift
break if not this_port
t << framework.threads.spawn("Module(#{self.refname})-#{ip}:#{this_port}", false, this_port) do |port|
begin
data_recv = ""
3.times do |channel_type|
data_recv = send_packet(ip,port,channel_type)
if data_recv.nil?
next
end
# check if CHANNEL_WRONG_TYPE error received and retry with different type
if data_recv[data_recv.length-4...data_recv.length] != "\x02\x00\x00\x00"
break
end
end
if data_recv.nil?
print_status("No response received. Try increasing TIMEOUT value.")
print_line
next
end
status_code = data_recv[-4..-1]
if status_code == "\x18\x00\x00\x00"
print_status("Channel Requires SSL. Could not get more information.")
print_line
end
if not data_recv[0...3].include?('TSH')
next
end
if status_code == "\x01\x00\x00\x00"
print_error('Channel "' + chan + '" does not exist.')
print_line
end
if status_code == "\x02\x00\x00\x00" or status_code == "\x06\x00\x00\x00"
print_error('Unsupported channel type. Try a different channel.')
print_line
end
if data_recv.length < 180
next
end
qm_name = data_recv[76...124].delete(' ')
mq_version = data_recv[180...188].scan(/../).collect{|x| x.to_i}.join('.')
print_good("#{ip}:#{port} - Queue Manager Name: #{qm_name} - MQ Version: #{mq_version}")
print_line
end
end
end
t.each {|x| x.join }
end
end
end
def send_packet(ip,port,channel_type)
begin
timeout = datastore['TIMEOUT'].to_i
packet = create_packet(channel_type)
s = connect(false,
{
'RPORT' => port,
'RHOST' => ip,
}
)
s.put(packet)
data = s.get_once(-1,timeout)
return data
rescue ::Rex::ConnectionRefused
print_error("#{ip}:#{port} - TCP Port Closed.")
print_line
rescue ::Rex::ConnectionError, ::IOError, ::Timeout::Error, Errno::ECONNRESET
print_error("#{ip}:#{port} - Connection Failed.")
print_line
rescue ::Interrupt
raise $!
ensure
if s
disconnect(s) rescue nil
end
end
end
end