Module: Msf::Exploit::Remote::ZeroMQ

Includes:
Tcp
Defined in:
lib/msf/core/exploit/zeromq.rb

Overview

This mixin is a simplistic implementation of ZeroMQ as used by SaltStack Salt

ZMTP 3.0 RFC: rfc.zeromq.org/spec/23/ Wireshark dissector: github.com/whitequark/zmtp-wireshark

TODO: Please iterate on this! I spent little time going over the protocol :(

Constant Summary collapse

ZEROMQ_SIGNATURE =
"\xff\x00\x00\x00\x00\x00\x00\x00\x01\x7f".freeze
ZEROMQ_VERSION =
"\x03".freeze

Instance Attribute Summary

Attributes included from Tcp

#sock

Instance Method Summary collapse

Methods included from Tcp

#chost, #cleanup, #connect, #connect_timeout, #cport, #deregister_tcp_options, #disconnect, #handler, #initialize, #lhost, #lport, #peer, #print_prefix, #proxies, #rhost, #rport, #set_tcp_evasions, #shutdown, #ssl, #ssl_cipher, #ssl_verify_mode, #ssl_version

Instance Method Details

#serialize_clear_load(load_hash = {}) ⇒ Object

NOTE: This is Salt-specific code to serialize a cleartext MessagePack “load”


129
130
131
132
133
134
135
136
137
138
139
# File 'lib/msf/core/exploit/zeromq.rb', line 129

def serialize_clear_load(load_hash = {})
  clear_load = {
    'enc' => 'clear',
    'load' => load_hash
  }

  # XXX: Strings NEED to be UTF-8 here, NOT binary!
  # rubocop:disable Security/Eval
  eval(clear_load.to_s.force_encoding(Encoding::UTF_8)).to_msgpack
  # rubocop:enable Security/Eval
end

#zmq_connectObject


19
20
21
22
# File 'lib/msf/core/exploit/zeromq.rb', line 19

def zmq_connect
  print_status("Connecting to ZeroMQ service at #{peer}")
  connect
end

#zmq_disconnectObject


24
25
26
27
# File 'lib/msf/core/exploit/zeromq.rb', line 24

def zmq_disconnect
  vprint_status("Disconnecting from #{peer}")
  disconnect
end

#zmq_negotiate(mechanism: 'NULL', client: 'REQ', server: 'ROUTER') ⇒ Object

Replay the ZeroMQ elbow bump


30
31
32
33
34
35
# File 'lib/msf/core/exploit/zeromq.rb', line 30

def zmq_negotiate(mechanism: 'NULL', client: 'REQ', server: 'ROUTER')
  zmq_negotiate_signature
  zmq_negotiate_version
  zmq_negotiate_security_mechanism(mechanism)
  zmq_negotiate_ready_command(client, server)
end

#zmq_negotiate_ready_command(client = 'REQ', server = 'ROUTER') ⇒ Object


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/msf/core/exploit/zeromq.rb', line 93

def zmq_negotiate_ready_command(client = 'REQ', server = 'ROUTER')
  print_status("Sending READY command of type #{client}")

  # 0x04 indicates an 8-bit command length
  sock.put(
    "\x04\x26" \
    "\x05READY" \
    "\x0bSocket-Type" \
    "\x00\x00\x00#{[client.length].pack('C')}#{client}" \
    "\x08Identity" \
    "\x00\x00\x00\x00"
  )

  unless (res = sock.get_once)
    fail_with(Failure::Unknown, 'Did not receive READY reply')
  end

  unless res.match(/READY.+Socket-Type.+#{server}.+Identity/m)
    fail_with(
      Failure::UnexpectedReply,
      "Did not receive READY reply of type #{server}: #{res.inspect}"
    )
  end

  vprint_good("Received READY reply of type #{server}")
end

#zmq_negotiate_security_mechanism(mechanism = 'NULL') ⇒ Object


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/msf/core/exploit/zeromq.rb', line 73

def zmq_negotiate_security_mechanism(mechanism = 'NULL')
  print_status("Negotiating #{mechanism} security mechanism")

  unless (res = sock.get_once)
    fail_with(Failure::Unknown, 'Did not receive security mechanism')
  end

  unless res.include?(mechanism)
    fail_with(
      Failure::UnexpectedReply,
      "Did not receive #{mechanism} security mechanism: #{res.inspect}"
    )
  end

  vprint_good("Received #{mechanism} security mechanism")

  vprint_status("Sending #{mechanism} security mechanism")
  sock.put(res)
end

#zmq_negotiate_signatureObject


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/msf/core/exploit/zeromq.rb', line 37

def zmq_negotiate_signature
  print_status('Negotiating signature')

  unless (res = sock.get_once)
    fail_with(Failure::Unknown, 'Did not receive signature')
  end

  unless res == ZEROMQ_SIGNATURE
    fail_with(Failure::UnexpectedReply,
              "Received invalid signature: #{res.inspect}")
  end

  vprint_good("Received valid signature: #{res.inspect}")

  vprint_status('Sending identical signature')
  sock.put(res)
end

#zmq_negotiate_versionObject


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/msf/core/exploit/zeromq.rb', line 55

def zmq_negotiate_version
  print_status('Negotiating version')

  unless (res = sock.get_once)
    fail_with(Failure::Unknown, 'Did not receive version')
  end

  unless res == ZEROMQ_VERSION
    fail_with(Failure::UnexpectedReply,
              "Received incompatible version: #{res.inspect}")
  end

  vprint_good("Received compatible version: #{res.inspect}")

  vprint_status('Sending identical version')
  sock.put(res)
end

#zmq_send_message(msg = '') ⇒ Object


120
121
122
123
124
125
126
# File 'lib/msf/core/exploit/zeromq.rb', line 120

def zmq_send_message(msg = '')
  # 0x02 indicates a 64-bit message length
  sock.put(
    "\x01\x00" \
    "\x02#{[msg.length].pack('Q>')}#{msg}"
  )
end