Land #10875, IBM WebSphere MQ Channel Name Bruteforce auxiliary module
parent
de9c57408c
commit
53a6354a45
|
@ -0,0 +1,48 @@
|
|||
|
||||
## 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_channel_brute```
|
||||
4. Do: ```set channels_file <channel_list_file>```
|
||||
5. Do: ```set rhosts <target_IP>```
|
||||
6. Do: ```set rport <port>```
|
||||
7. Do: ```run```
|
||||
|
||||
Example output:
|
||||
```
|
||||
msf auxiliary(scanner/misc/ibm_mq_channel_brute) > run
|
||||
|
||||
[*] 10.1.1.144:1414 - Found channel: TEST.CHANNEL, IsEncrypted: False, IsMQI: True
|
||||
[*] 10.1.1.144:1414 - Found channel: SYSTEM.ADMIN.SVRCONN, IsEncrypted: False, IsMQI: True
|
||||
|
||||
[+] 10.1.1.144:1414 - Channels found: ["TEST.CHANNEL", "SYSTEM.ADMIN.SVRCONN"]
|
||||
[+] 10.1.1.144:1414 - Unencrypted MQI Channels found: ["TEST.CHANNEL", "SYSTEM.ADMIN.SVRCONN"]
|
||||
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
**The CHANNELS_FILE option**
|
||||
|
||||
This option should contain the path to a text file which contains a list of channel names that will be checked. One channel name per line.
|
||||
|
||||
## Scenarios
|
||||
|
||||
This module can be used to identify a list of channel names that are configured on the Queue Manager. Additionally, the module will return whether each identified channel uses SSL and if it MQI type.
|
||||
After obtaining a list of valid channel names, these can be used to further enumerate the MQ installation. For example, the ibm_mq_enum module can be executed using a valid channel name in order to obtain information regarding the Queue Manager.
|
|
@ -0,0 +1,169 @@
|
|||
##
|
||||
# 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 Channel Name Bruteforce',
|
||||
'Description' => 'This module uses a dictionary to bruteforce MQ channel names. For all identified channels it also returns if SSL is used and whether it is a server-connection channel.',
|
||||
'Author' => 'Petros Koutroumpis',
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
register_options([
|
||||
Opt::RPORT(1414),
|
||||
OptInt.new('TIMEOUT', [true, "The socket connect timeout in seconds", 10]),
|
||||
OptInt.new('CONCURRENCY', [true, "The number of concurrent channel names to check", 10]),
|
||||
OptPath.new('CHANNELS_FILE',
|
||||
[ true, "The file that contains a list of channel names"]
|
||||
)])
|
||||
end
|
||||
|
||||
def create_packet(chan)
|
||||
packet = "\x54\x53\x48\x20"+ # StructID
|
||||
"\x00\x00\x01\x0c"+ # MQSegmLen
|
||||
"\x02" + # Byte Order
|
||||
"\x01" + # SegmType
|
||||
"\x01" + # CtlFlag1
|
||||
"\x00" + # CtlFlag2
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"+ # LUWIdent
|
||||
"\x22\x02\x00\x00"+ # Encoding
|
||||
"\xb5\x01" + # CCSID
|
||||
"\x00\x00" + # Reserved
|
||||
"\x49\x44\x20\x20" + # StructID
|
||||
"\x0d" + # FAP Level
|
||||
"\x26" + # CapFlag1 - Channel 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" + # SegWrapVal
|
||||
+ chan + # Channel name
|
||||
"\x20" + # CapFlag2
|
||||
"\x20" + # ECapFlag2
|
||||
"\x20\x20" + # ccsid
|
||||
"QM1" + "\x20"*45 + # Queue Manager Name
|
||||
"\x20\x20\x20\x20" + # HBInterval
|
||||
"\x20\x20" + # EFLLength
|
||||
"\x20" + # IniErrFlg2
|
||||
"\x20" + # Reserved1
|
||||
"\x20\x20" + # HdrCprLst
|
||||
"\x20\x20\x20\x20\x2c\x01\x00\x00"+ # MSGCprLst1
|
||||
"\x8a\x00\x00\x55\x00\xff\x00\xff"+ # MsgCprLst2
|
||||
"\xff\xff" + # Reserved2
|
||||
"\xff\xff\xff\xff" + # SSLKeyRst
|
||||
"\xff\xff\xff\xff" + # ConvBySKt
|
||||
"\xff" + # CapFlag3
|
||||
"\xff" + # ECapFlag3
|
||||
"\xff\xff" + # Reserved3
|
||||
"\x00\x00\x00\x00" + # ProcessId
|
||||
"\x00\x00\x00\x00" + # ThreadId
|
||||
"\x00\x00\x05\x00" + # TraceId
|
||||
"\x00\x00\x10\x13\x00\x00" + # ProdId
|
||||
"\x01\x00\x00\x00\x01\x00" + # ProdId
|
||||
"MQMID" + "\x20"*43 + # MQM Id
|
||||
"\x20\x20\x20\x20\x20\x20\x20\x20"+ # Unknown
|
||||
"\x20\x20\x20\x20\x20\x20\x00\x00"+ # Unknown
|
||||
"\xff\xff\xff\xff\xff\xff\xff\xff"+ # Unknown
|
||||
"\xff\xff\xff\xff\xff\xff\xff\xff"+ # Unknown
|
||||
"\xff\xff\x00\x00\x00\x00\x00\x00"+ # Unknown
|
||||
"\x00\x00\x00\x00\x00\x00" # Unknown
|
||||
end
|
||||
|
||||
|
||||
def run_host(ip)
|
||||
@channels = []
|
||||
@unencrypted_mqi_channels = []
|
||||
begin
|
||||
channel_list
|
||||
rescue ::Rex::ConnectionRefused
|
||||
fail_with(Failure::Unreachable, "TCP Port closed.")
|
||||
rescue ::Rex::ConnectionError, ::IOError, ::Timeout::Error, Errno::ECONNRESET
|
||||
fail_with(Failure::Unreachable, "Connection Failed.")
|
||||
rescue ::Exception => e
|
||||
fail_with(Failure::Unknown, e)
|
||||
end
|
||||
if(@channels.empty?)
|
||||
print_status("#{ip}:#{rport} No channels found.")
|
||||
else
|
||||
print_good("Channels found: #{@channels}")
|
||||
print_good("Unencrypted MQI Channels found: #{@unencrypted_mqi_channels}")
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:type => 'mq.channels'
|
||||
)
|
||||
print_line
|
||||
end
|
||||
end
|
||||
|
||||
def channel_list
|
||||
channel_data = get_channel_names
|
||||
while (channel_data.length > 0)
|
||||
t = []
|
||||
r = []
|
||||
begin
|
||||
1.upto(datastore['CONCURRENCY']) do
|
||||
this_channel = channel_data.shift
|
||||
if this_channel.nil?
|
||||
next
|
||||
end
|
||||
t << framework.threads.spawn("Module(#{self.refname})-#{rhost}:#{rport}", false, this_channel) do |channel|
|
||||
connect
|
||||
vprint_status "#{rhost}:#{rport} - Sending request for #{channel}..."
|
||||
chan = channel + "\x20"*(20-channel.length.to_i)
|
||||
timeout = datastore['TIMEOUT'].to_i
|
||||
s = connect(false,
|
||||
{
|
||||
'RPORT' => rport,
|
||||
'RHOST' => rhost,
|
||||
}
|
||||
)
|
||||
s.put(create_packet(chan))
|
||||
data = s.get_once(-1,timeout)
|
||||
if data.nil?
|
||||
print_status("No response received. Try increasing timeout.")
|
||||
next
|
||||
end
|
||||
if not data[0...3].include? 'TSH'
|
||||
next
|
||||
end
|
||||
if data[-4..-1] == "\x01\x00\x00\x00" # NO_CHANNEL code
|
||||
next
|
||||
end
|
||||
if data[-4..-1] == "\x18\x00\x00\x00" # CIPHER_SPEC code
|
||||
print_status("Found channel: #{channel}, IsEncrypted: True, IsMQI: N/A")
|
||||
elsif data[-4..-1] == "\x02\x00\x00\x00" # CHANNEL_WRONG_TYPE code
|
||||
print_status("Found channel: #{channel}, IsEncrypted: False, IsMQI: False")
|
||||
else
|
||||
print_status("Found channel: #{channel}, IsEncrypted: False, IsMQI: True")
|
||||
@unencrypted_mqi_channels << channel
|
||||
end
|
||||
@channels << channel
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
t.each {|x| x.join }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_channel_names
|
||||
if(! @common)
|
||||
File.open(datastore['CHANNELS_FILE'], "rb") do |fd|
|
||||
data = fd.read(fd.stat.size)
|
||||
@common = data.split(/\n/).compact.uniq
|
||||
end
|
||||
end
|
||||
@common
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue