diff --git a/Gemfile.lock b/Gemfile.lock index 5e74ca08f3..3fdd329350 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,6 +20,7 @@ PATH metasploit-payloads (= 1.3.19) metasploit_data_models metasploit_payloads-mettle (= 0.3.2) + mqtt msgpack nessus_rest net-ssh @@ -192,6 +193,7 @@ GEM method_source (0.9.0) mini_portile2 (2.3.0) minitest (5.10.3) + mqtt (0.5.0) msgpack (1.2.0) multi_json (1.12.2) multipart-post (2.0.0) diff --git a/lib/msf/core/auxiliary/mixins.rb b/lib/msf/core/auxiliary/mixins.rb index 296a48d438..f5744ea480 100644 --- a/lib/msf/core/auxiliary/mixins.rb +++ b/lib/msf/core/auxiliary/mixins.rb @@ -23,6 +23,7 @@ require 'msf/core/auxiliary/cisco' require 'msf/core/auxiliary/kademlia' require 'msf/core/auxiliary/llmnr' require 'msf/core/auxiliary/mdns' +require 'msf/core/auxiliary/mqtt' require 'msf/core/auxiliary/nmap' require 'msf/core/auxiliary/natpmp' require 'msf/core/auxiliary/iax2' diff --git a/lib/msf/core/auxiliary/mqtt.rb b/lib/msf/core/auxiliary/mqtt.rb new file mode 100644 index 0000000000..0094d7879f --- /dev/null +++ b/lib/msf/core/auxiliary/mqtt.rb @@ -0,0 +1,69 @@ +# -*- coding: binary -*- + +require 'msf/core/exploit' +require 'rex/proto/mqtt' + +module Msf + module Auxiliary::MQTT + include Exploit::Remote::Tcp + + def initialize(info = {}) + super + + register_options( + [ + Opt::RPORT(Rex::Proto::MQTT::DEFAULT_PORT), + OptString.new('USERNAME', [false, 'The user to authenticate as']), + OptString.new('PASSWORD', [false, 'The password to authenticate with']) + ] + ) + + register_advanced_options( + [ + OptString.new('CLIENT_ID', [false, 'The client ID to send if necessary for bypassing clientid_prefixes']), + OptInt.new('READ_TIMEOUT', [true, 'Seconds to wait while reading MQTT responses', 5]) + ] + ) + + register_autofilter_ports([Rex::Proto::MQTT::DEFAULT_PORT]) + end + + def setup + fail_with(Failure::BadConfig, 'READ_TIMEOUT must be > 0') if read_timeout <= 0 + client_id_arg = datastore['CLIENT_ID'] + if client_id_arg + fail_with(Failure::BadConfig, 'CLIENT_ID must be a non-empty string') if client_id_arg.blank? + end + end + + def read_timeout + datastore['READ_TIMEOUT'] + end + + def client_id + datastore['CLIENT_ID'] || Rex::Text.rand_text_alpha(1 + rand(10)) + end + + def mqtt_client + client_opts = { + client_id: client_id().to_s, + username: datastore['USERNAME'], + password: datastore['PASSWORD'], + read_timeout: read_timeout + } + Rex::Proto::MQTT::Client.new(sock, client_opts) + end + + def mqtt_connect(client) + client.connect + end + + def mqtt_connect?(client) + mqtt_connect(client).return_code == 0 + end + + def mqtt_disconnect(client) + client.disconnect + end + end +end diff --git a/lib/rex/proto/mqtt.rb b/lib/rex/proto/mqtt.rb new file mode 100644 index 0000000000..c10e4e88cb --- /dev/null +++ b/lib/rex/proto/mqtt.rb @@ -0,0 +1,14 @@ +# -*- coding: binary -*- +# +# Support for MQTT + +require 'rex/proto/mqtt/client' + +module Rex + module Proto + module MQTT + DEFAULT_PORT = 1883 + DEFAULT_SSL_PORT = 8883 + end + end +end diff --git a/lib/rex/proto/mqtt/client.rb b/lib/rex/proto/mqtt/client.rb new file mode 100644 index 0000000000..79ef88c217 --- /dev/null +++ b/lib/rex/proto/mqtt/client.rb @@ -0,0 +1,42 @@ +# -*- coding: binary -*- + +require 'mqtt' + +## +# MQTT protocol support +## + +module Rex + module Proto + module MQTT + class Client + + def initialize(sock, opts = {}) + @sock = sock + @opts = opts + end + + def connect + connect_opts = { + client_id: @opts[:client_id], + username: @opts[:username], + password: @opts[:password] + } + connect = ::MQTT::Packet::Connect.new(connect_opts).to_s + @sock.put(connect) + res = @sock.get_once(-1, @opts[:read_timeout]) + ::MQTT::Packet.parse(res) + end + + def connect? + connect.return_code == 0 + end + + def disconnect + disconnect = ::MQTT::Packet::Disconnect.new().to_s + @sock.put(disconnect) + end + end + end + end +end diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 00dfecfd41..d9df75c633 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -123,6 +123,7 @@ Gem::Specification.new do |spec| # Protocol Libraries # spec.add_runtime_dependency 'dnsruby' + spec.add_runtime_dependency 'mqtt' spec.add_runtime_dependency 'net-ssh' spec.add_runtime_dependency 'rbnacl', ['< 5.0.0'] spec.add_runtime_dependency 'bcrypt_pbkdf'