Module: Msf::Exploit::Remote::SMTPDeliver

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

Overview

This module exposes methods that may be useful to exploits that send email messages via SMTP.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Tcp

#chost, #cleanup, #connect_timeout, #cport, #handler, #lhost, #lport, #proxies, #rhost, #rport, #set_tcp_evasions, #ssl, #ssl_version

Instance Attribute Details

The banner received after the initial connection to the server. This should look something like:

220 mx.google.com ESMTP s5sm3837150wak.12

197
198
199
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 197

def banner
  @banner
end

Instance Method Details

#connect(global = true) ⇒ Object

Establish an SMTP connection to host and port specified by the RHOST and RPORT options, respectively. After connecting, the banner message is read in and stored in the banner attribute.

This method does NOT perform an EHLO, it only connects.


54
55
56
57
58
59
60
61
62
63
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 54

def connect(global = true)
  fd = super

  if fd
    @connected = true
    # Wait for a banner to arrive...
    self.banner = fd.get_once(-1, 30)
  end
  fd
end

#connect_login(global = true) ⇒ Object

Connect to the remote SMTP server, send EHLO, start TLS if the server asks for it, and authenticate if we've got creds (specified in USERNAME and PASSWORD datastore options).

This method currently only knows about PLAIN authentication.


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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
119
120
121
122
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 72

def (global = true)
  print_verbose("Connecting to SMTP server #{rhost}:#{rport}...")
  nsock = connect(global)

  if datastore['DOMAIN'] and not datastore['DOMAIN'] == ''
    domain = datastore['DOMAIN']
  else
    domain = Rex::Text.rand_text_alpha(rand(32)+1)
  end

  res = raw_send_recv("EHLO #{domain}\r\n", nsock)
  if res =~ /STARTTLS/
    print_status("Starting tls")
    raw_send_recv("STARTTLS\r\n", nsock)
    swap_sock_plain_to_ssl
    res = raw_send_recv("EHLO #{domain}\r\n", nsock)
  end

  unless datastore['PASSWORD'].empty? and datastore["USERNAME"].empty?
    # TODO: other auth methods
    if res =~ /AUTH .*PLAIN/
      if datastore["USERNAME"] and not datastore["USERNAME"].empty?
        # Have to double the username.  SMTP auth is weird
        user = "#{datastore["USERNAME"]}\0" * 2
        auth = Rex::Text.encode_base64("#{user}#{datastore["PASSWORD"]}")
        raw_send_recv("AUTH PLAIN #{auth}\r\n", nsock)
      else
        print_status("Server requested auth and no creds given, trying to continue anyway")
      end
    elsif res =~ /AUTH .*LOGIN/
      if datastore["USERNAME"] and not datastore["USERNAME"].empty?
        user = Rex::Text.encode_base64("#{datastore["USERNAME"]}")
        auth = Rex::Text.encode_base64("#{datastore["PASSWORD"]}")
        raw_send_recv("AUTH LOGIN\r\n",nsock)
        raw_send_recv("#{user}\r\n",nsock)
        raw_send_recv("#{auth}\r\n",nsock)
      else
        print_status("Server requested auth and no creds given, trying to continue anyway")
      end
    elsif res =~ /AUTH/
      print_error("Server doesn't accept any supported authentication, trying to continue anyway")
    else
      if datastore['PASSWORD'] and datastore["USERNAME"] and not datastore["USERNAME"].empty?
        # Let the user know their creds are going unused
        print_verbose("Server didn't ask for authentication, skipping")
      end
    end
  end

  return nsock
end

#connected?Boolean


43
44
45
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 43

def connected?
  (@connected)
end

#disconnect(nsock = self.sock) ⇒ Object


161
162
163
164
165
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 161

def disconnect(nsock=self.sock)
  raw_send_recv("QUIT\r\n", nsock)
  super
  @connected = false
end

#initialize(info = {}) ⇒ Object

Creates an instance of an exploit that delivers messages via SMTP


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 21

def initialize(info = {})
  super

  # Register our options, overriding the RHOST/RPORT from TCP
  register_options(
    [
      OptAddress.new("RHOST", [ true, "The SMTP server to send through" ]),
      OptPort.new("RPORT", [ true, "The SMTP server port (e.g. 25, 465, 587, 2525)", 25 ]),
      OptString.new('MAILFROM', [ true, 'The FROM address of the e-mail', '[email protected]' ]),
      OptString.new('MAILTO', [ true, 'The TO address of the email' ]),
      OptString.new('SUBJECT', [ true, 'Subject line of the email' ]),
      OptString.new('USERNAME', [ false, 'SMTP Username for sending email', '' ]),
      OptString.new('PASSWORD', [ false, 'SMTP Password for sending email', '' ]),
      OptString.new('DOMAIN', [false, 'SMTP Domain to EHLO to', '']),
      OptString.new('VERBOSE', [ false, 'Display verbose information' ]),
    ], Msf::Exploit::Remote::SMTPDeliver)
  register_autofilter_ports([ 25, 465, 587, 2525, 25025, 25000])
  register_autofilter_services(%W{ smtp smtps })

  @connected = false
end

188
189
190
191
192
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 188

def print_verbose(msg)
  if datastore['VERBOSE']
    print_status(msg)
  end
end

#raw_send_recv(cmd, nsock = self.sock) ⇒ Object


167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 167

def raw_send_recv(cmd, nsock=self.sock)
  return false if not nsock
  if cmd =~ /AUTH PLAIN/
    # Don't print the user's plaintext password
    print_verbose("C: AUTH PLAIN ...")
  else
    # Truncate because this will include a full email and we don't want
    # to dump it all.
    print_verbose("C: #{((cmd.length > 120) ? cmd[0,120] + "..." : cmd).strip}")
  end

  nsock.put(cmd)
  res = nsock.get_once

  # Don't truncate the server output because it might be helpful for
  # debugging.
  print_verbose("S: #{res.strip}") if res

  return res
end

#send_message(data) ⇒ Object

Sends an email message, connecting to the server first if a connection is not already established.


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/msf/core/exploit/smtp_deliver.rb', line 129

def send_message(data)
  send_status = nil

  already_connected = connected?
  if already_connected
    print_status("Already connected, reusing")
    nsock = self.sock
  else
    nsock = (false)
  end

  raw_send_recv("MAIL FROM: <#{datastore['MAILFROM']}>\r\n", nsock)
  raw_send_recv("RCPT TO: <#{datastore['MAILTO']}>\r\n", nsock)

  resp = raw_send_recv("DATA\r\n", nsock)

  # Avoid sending tons of data and killing the connection if the server
  # didn't like us.
  if not resp or not resp[0,3] == '354'
    print_error("Server refused our mail")
  else
    send_status = raw_send_recv("#{data}\r\n.\r\n", nsock)
  end

  if not already_connected
    print_verbose("Closing the connection...")
    disconnect(nsock)
  end

  send_status
end