Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

smtp_deliver.rb 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. # -*- coding: binary -*-
  2. module Msf
  3. require 'msf/core/exploit/tcp'
  4. require 'rex/mime'
  5. ###
  6. #
  7. # This module exposes methods that may be useful to exploits that send email
  8. # messages via SMTP.
  9. #
  10. ###
  11. module Exploit::Remote::SMTPDeliver
  12. include Exploit::Remote::Tcp
  13. #
  14. # Creates an instance of an exploit that delivers messages via SMTP
  15. #
  16. def initialize(info = {})
  17. super
  18. # Register our options, overriding the RHOST/RPORT from TCP
  19. register_options(
  20. [
  21. OptAddress.new("RHOST", [ true, "The SMTP server to send through" ]),
  22. OptPort.new("RPORT", [ true, "The SMTP server port (e.g. 25, 465, 587, 2525)", 25 ]),
  23. OptString.new('DATE', [false, 'Override the DATE: field with this value', '']),
  24. OptString.new('MAILFROM', [ true, 'The FROM address of the e-mail', 'random@example.com' ]),
  25. OptString.new('MAILTO', [ true, 'The TO address of the email' ]),
  26. OptString.new('SUBJECT', [ true, 'Subject line of the email' ]),
  27. OptString.new('USERNAME', [ false, 'SMTP Username for sending email', '' ]),
  28. OptString.new('PASSWORD', [ false, 'SMTP Password for sending email', '' ]),
  29. OptString.new('DOMAIN', [false, 'SMTP Domain to EHLO to', '']),
  30. OptString.new('VERBOSE', [ false, 'Display verbose information' ]),
  31. ], Msf::Exploit::Remote::SMTPDeliver)
  32. register_autofilter_ports([ 25, 465, 587, 2525, 25025, 25000])
  33. register_autofilter_services(%W{ smtp smtps })
  34. @connected = false
  35. end
  36. def connected?
  37. (@connected)
  38. end
  39. #
  40. # Establish an SMTP connection to host and port specified by the RHOST and
  41. # RPORT options, respectively. After connecting, the banner message is
  42. # read in and stored in the +banner+ attribute.
  43. #
  44. # This method does NOT perform an EHLO, it only connects.
  45. #
  46. def connect(global = true)
  47. fd = super
  48. if fd
  49. @connected = true
  50. # Wait for a banner to arrive...
  51. self.banner = fd.get_once(-1, 30)
  52. end
  53. fd
  54. end
  55. #
  56. # Connect to the remote SMTP server, send EHLO, start TLS if the server
  57. # asks for it, and authenticate if we've got creds (specified in +USERNAME+
  58. # and +PASSWORD+ datastore options).
  59. #
  60. # This method currently only knows about PLAIN authentication.
  61. #
  62. def connect_login(global = true)
  63. vprint_status("Connecting to SMTP server #{rhost}:#{rport}...")
  64. nsock = connect(global)
  65. if datastore['DOMAIN'] and not datastore['DOMAIN'] == ''
  66. domain = datastore['DOMAIN']
  67. else
  68. domain = Rex::Text.rand_text_alpha(rand(32)+1)
  69. end
  70. res = raw_send_recv("EHLO #{domain}\r\n", nsock)
  71. if res =~ /STARTTLS/
  72. print_status("Starting tls")
  73. raw_send_recv("STARTTLS\r\n", nsock)
  74. swap_sock_plain_to_ssl(nsock)
  75. res = raw_send_recv("EHLO #{domain}\r\n", nsock)
  76. end
  77. unless datastore['PASSWORD'].empty? and datastore["USERNAME"].empty?
  78. # TODO: other auth methods
  79. if res =~ /AUTH .*PLAIN/
  80. if datastore["USERNAME"] and not datastore["USERNAME"].empty?
  81. # Have to double the username. SMTP auth is weird
  82. user = "#{datastore["USERNAME"]}\0" * 2
  83. auth = Rex::Text.encode_base64("#{user}#{datastore["PASSWORD"]}")
  84. raw_send_recv("AUTH PLAIN #{auth}\r\n", nsock)
  85. else
  86. print_status("Server requested auth and no creds given, trying to continue anyway")
  87. end
  88. elsif res =~ /AUTH .*LOGIN/
  89. if datastore["USERNAME"] and not datastore["USERNAME"].empty?
  90. user = Rex::Text.encode_base64("#{datastore["USERNAME"]}")
  91. auth = Rex::Text.encode_base64("#{datastore["PASSWORD"]}")
  92. raw_send_recv("AUTH LOGIN\r\n",nsock)
  93. raw_send_recv("#{user}\r\n",nsock)
  94. raw_send_recv("#{auth}\r\n",nsock)
  95. else
  96. print_status("Server requested auth and no creds given, trying to continue anyway")
  97. end
  98. elsif res =~ /AUTH/
  99. print_error("Server doesn't accept any supported authentication, trying to continue anyway")
  100. else
  101. if datastore['PASSWORD'] and datastore["USERNAME"] and not datastore["USERNAME"].empty?
  102. # Let the user know their creds are going unused
  103. vprint_status("Server didn't ask for authentication, skipping")
  104. end
  105. end
  106. end
  107. return nsock
  108. end
  109. #
  110. # Sends an email message, connecting to the server first if a connection is
  111. # not already established.
  112. #
  113. def send_message(data)
  114. send_status = nil
  115. already_connected = connected?
  116. if already_connected
  117. print_status("Already connected, reusing")
  118. nsock = self.sock
  119. else
  120. nsock = connect_login(false)
  121. end
  122. raw_send_recv("MAIL FROM: <#{datastore['MAILFROM']}>\r\n", nsock)
  123. raw_send_recv("RCPT TO: <#{datastore['MAILTO']}>\r\n", nsock)
  124. resp = raw_send_recv("DATA\r\n", nsock)
  125. # If the user supplied a Date field, use that, else use the current
  126. # DateTime in the proper RFC2822 format.
  127. if datastore['DATE'].present?
  128. date = "Date: #{datastore['DATE']}\r\n"
  129. else
  130. date = "Date: #{DateTime.now.rfc2822}\r\n"
  131. end
  132. # If the user supplied a Subject field, use that
  133. subject = nil
  134. if datastore['SUBJECT'].present?
  135. subject = "Subject: #{datastore['SUBJECT']}\r\n"
  136. end
  137. # Avoid sending tons of data and killing the connection if the server
  138. # didn't like us.
  139. if not resp or not resp[0,3] == '354'
  140. print_error("Server refused our mail")
  141. else
  142. full_msg = ''
  143. full_msg << date unless data =~ /date: /i
  144. full_msg << subject unless subject.nil? || data =~ /subject: /i
  145. full_msg << data
  146. send_status = raw_send_recv("#{full_msg}\r\n.\r\n", nsock)
  147. end
  148. if not already_connected
  149. vprint_status("Closing the connection...")
  150. disconnect(nsock)
  151. end
  152. send_status
  153. end
  154. def disconnect(nsock=self.sock)
  155. raw_send_recv("QUIT\r\n", nsock)
  156. super
  157. @connected = false
  158. end
  159. def raw_send_recv(cmd, nsock=self.sock)
  160. return false if not nsock
  161. if cmd =~ /AUTH PLAIN/
  162. # Don't print the user's plaintext password
  163. vprint_status("C: AUTH PLAIN ...")
  164. else
  165. # Truncate because this will include a full email and we don't want
  166. # to dump it all.
  167. vprint_status("C: #{((cmd.length > 120) ? cmd[0,120] + "..." : cmd).strip}")
  168. end
  169. nsock.put(cmd)
  170. res = nsock.get_once
  171. # Don't truncate the server output because it might be helpful for
  172. # debugging.
  173. vprint_status("S: #{res.strip}") if res
  174. return res
  175. end
  176. # The banner received after the initial connection to the server. This should look something like:
  177. # 220 mx.google.com ESMTP s5sm3837150wak.12
  178. attr_reader :banner
  179. protected
  180. attr_writer :banner #:nodoc:
  181. #
  182. # Create a new SSL session on the existing socket. Used for STARTTLS
  183. # support.
  184. #
  185. def swap_sock_plain_to_ssl(nsock=self.sock)
  186. ctx = generate_ssl_context()
  187. ssl = OpenSSL::SSL::SSLSocket.new(nsock, ctx)
  188. ssl.connect
  189. nsock.extend(Rex::Socket::SslTcp)
  190. nsock.sslsock = ssl
  191. nsock.sslctx = ctx
  192. end
  193. def generate_ssl_context
  194. ctx = OpenSSL::SSL::SSLContext.new
  195. ctx.key = OpenSSL::PKey::RSA.new(1024){ }
  196. ctx.session_id_context = Rex::Text.rand_text(16)
  197. return ctx
  198. end
  199. end
  200. end