CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/misc/ibm_mq_channel_brute.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
8
include Msf::Exploit::Remote::Tcp
9
include Msf::Auxiliary::Scanner
10
include Msf::Auxiliary::Report
11
12
def initialize
13
super(
14
'Name' => 'IBM WebSphere MQ Channel Name Bruteforce',
15
'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.',
16
'Author' => 'Petros Koutroumpis',
17
'License' => MSF_LICENSE
18
)
19
register_options([
20
Opt::RPORT(1414),
21
OptInt.new('TIMEOUT', [true, "The socket connect timeout in seconds", 10]),
22
OptInt.new('CONCURRENCY', [true, "The number of concurrent channel names to check", 10]),
23
OptPath.new('CHANNELS_FILE',
24
[ true, "The file that contains a list of channel names"]
25
)])
26
end
27
28
def create_packet(chan)
29
packet = "\x54\x53\x48\x20"+ # StructID
30
"\x00\x00\x01\x0c"+ # MQSegmLen
31
"\x02" + # Byte Order
32
"\x01" + # SegmType
33
"\x01" + # CtlFlag1
34
"\x00" + # CtlFlag2
35
"\x00\x00\x00\x00\x00\x00\x00\x00"+ # LUWIdent
36
"\x22\x02\x00\x00"+ # Encoding
37
"\xb5\x01" + # CCSID
38
"\x00\x00" + # Reserved
39
"\x49\x44\x20\x20" + # StructID
40
"\x0d" + # FAP Level
41
"\x26" + # CapFlag1 - Channel Type
42
"\x00" + # ECapFlag1
43
"\x00" + # IniErrFlg1
44
"\x00\x00" + # Reserved
45
"\x32\x00" + # MaxMsgBtch
46
"\xec\x7f\x00\x00" + # MaxTrSize
47
"\x00\x00\x40\x00" + # MaxMsgSize
48
"\xff\xc9\x9a\x3b" + # SegWrapVal
49
+ chan + # Channel name
50
"\x20" + # CapFlag2
51
"\x20" + # ECapFlag2
52
"\x20\x20" + # ccsid
53
"QM1" + "\x20"*45 + # Queue Manager Name
54
"\x20\x20\x20\x20" + # HBInterval
55
"\x20\x20" + # EFLLength
56
"\x20" + # IniErrFlg2
57
"\x20" + # Reserved1
58
"\x20\x20" + # HdrCprLst
59
"\x20\x20\x20\x20\x2c\x01\x00\x00"+ # MSGCprLst1
60
"\x8a\x00\x00\x55\x00\xff\x00\xff"+ # MsgCprLst2
61
"\xff\xff" + # Reserved2
62
"\xff\xff\xff\xff" + # SSLKeyRst
63
"\xff\xff\xff\xff" + # ConvBySKt
64
"\xff" + # CapFlag3
65
"\xff" + # ECapFlag3
66
"\xff\xff" + # Reserved3
67
"\x00\x00\x00\x00" + # ProcessId
68
"\x00\x00\x00\x00" + # ThreadId
69
"\x00\x00\x05\x00" + # TraceId
70
"\x00\x00\x10\x13\x00\x00" + # ProdId
71
"\x01\x00\x00\x00\x01\x00" + # ProdId
72
"MQMID" + "\x20"*43 + # MQM Id
73
"\x20\x20\x20\x20\x20\x20\x20\x20"+ # Unknown
74
"\x20\x20\x20\x20\x20\x20\x00\x00"+ # Unknown
75
"\xff\xff\xff\xff\xff\xff\xff\xff"+ # Unknown
76
"\xff\xff\xff\xff\xff\xff\xff\xff"+ # Unknown
77
"\xff\xff\x00\x00\x00\x00\x00\x00"+ # Unknown
78
"\x00\x00\x00\x00\x00\x00" # Unknown
79
end
80
81
82
def run_host(ip)
83
@channels = []
84
@unencrypted_mqi_channels = []
85
begin
86
channel_list
87
rescue ::Rex::ConnectionRefused
88
fail_with(Failure::Unreachable, "TCP Port closed.")
89
rescue ::Rex::ConnectionError, ::IOError, ::Timeout::Error, Errno::ECONNRESET
90
fail_with(Failure::Unreachable, "Connection Failed.")
91
rescue ::Exception => e
92
fail_with(Failure::Unknown, e)
93
end
94
if(@channels.empty?)
95
print_status("#{ip}:#{rport} No channels found.")
96
else
97
print_good("Channels found: #{@channels}")
98
print_good("Unencrypted MQI Channels found: #{@unencrypted_mqi_channels}")
99
report_note(
100
:host => rhost,
101
:port => rport,
102
:type => 'mq.channels'
103
)
104
print_line
105
end
106
end
107
108
def channel_list
109
channel_data = get_channel_names
110
while (channel_data.length > 0)
111
t = []
112
r = []
113
begin
114
1.upto(datastore['CONCURRENCY']) do
115
this_channel = channel_data.shift
116
if this_channel.nil?
117
next
118
end
119
t << framework.threads.spawn("Module(#{self.refname})-#{rhost}:#{rport}", false, this_channel) do |channel|
120
connect
121
vprint_status "#{rhost}:#{rport} - Sending request for #{channel}..."
122
if channel.length.to_i > 20
123
print_error("Channel names cannot exceed 20 characters. Skipping.")
124
next
125
end
126
chan = channel + "\x20"*(20-channel.length.to_i)
127
timeout = datastore['TIMEOUT'].to_i
128
s = connect(false,
129
{
130
'RPORT' => rport,
131
'RHOST' => rhost,
132
}
133
)
134
s.put(create_packet(chan))
135
data = s.get_once(-1,timeout)
136
if data.nil?
137
print_status("No response received. Try increasing timeout.")
138
next
139
end
140
if not data[0...3].include? 'TSH'
141
next
142
end
143
if data[-4..-1] == "\x01\x00\x00\x00" # NO_CHANNEL code
144
next
145
end
146
if data[-4..-1] == "\x18\x00\x00\x00" # CIPHER_SPEC code
147
print_status("Found channel: #{channel}, IsEncrypted: True, IsMQI: N/A")
148
elsif data[-4..-1] == "\x02\x00\x00\x00" # CHANNEL_WRONG_TYPE code
149
print_status("Found channel: #{channel}, IsEncrypted: False, IsMQI: False")
150
else
151
print_status("Found channel: #{channel}, IsEncrypted: False, IsMQI: True")
152
@unencrypted_mqi_channels << channel
153
end
154
@channels << channel
155
disconnect
156
end
157
end
158
t.each {|x| x.join }
159
end
160
end
161
end
162
163
def get_channel_names
164
if(! @common)
165
File.open(datastore['CHANNELS_FILE'], "rb") do |fd|
166
data = fd.read(fd.stat.size)
167
@common = data.split(/\n/).compact.uniq
168
end
169
end
170
@common
171
end
172
173
end
174
175