Fixes #2002. Needed to work with some pipelining to get this all to work right, but it seems to function now pretty well -- if the target takes Basic, do basic, if the target takes NTLM, do NTLM. Should implement Digest too, but I don't think hardly anyone uses that.
git-svn-id: file:///home/svn/framework3/trunk@9346 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
df5ce05fa1
commit
1a2be34a63
|
@ -224,6 +224,66 @@ module Exploit::Remote::HttpClient
|
|||
datastore['BasicAuthUser'] + ":" + (datastore['BasicAuthPass'] || '')
|
||||
end
|
||||
|
||||
#
|
||||
# Connect to the server, and perform NTLM authentication for this session.
|
||||
# Note the return value is [resp,c], so the caller can have access to both
|
||||
# the last response, and the connection itself -- this is important since
|
||||
# NTLM auth is bound to this particular TCP session.
|
||||
#
|
||||
# TODO: Fix up error messaging a lot more -- right now it's pretty hard
|
||||
# to tell what all went wrong.
|
||||
#
|
||||
def send_http_auth_ntlm(opts={}, timeout = 20)
|
||||
ntlm_message_1 = "NTLM TlRMTVNTUAABAAAAB4IIAAAAAAAAAAAAAAAAAAAAAAA="
|
||||
to = opts[:timeout] || timeout
|
||||
begin
|
||||
c = connect(opts)
|
||||
|
||||
# First request to get the challenge
|
||||
r = c.request_cgi(opts.merge({
|
||||
'uri' => opts['uri'],
|
||||
'method' => 'GET',
|
||||
'headers' => { 'Authorization' => ntlm_message_1 }}))
|
||||
resp = c.send_recv(r, to)
|
||||
unless resp.kind_of? Rex::Proto::Http::Response
|
||||
return [nil,nil]
|
||||
end
|
||||
return [nil,nil] if resp.code == 404
|
||||
while(resp and resp.code == 100)
|
||||
resp = c.reread_response(resp, to)
|
||||
end
|
||||
return [nil,nil] unless resp.code == 401 && resp.headers['WWW-Authenticate']
|
||||
|
||||
# Get the challenge and craft the response
|
||||
ntlm_challenge = resp.headers['WWW-Authenticate'].match(/NTLM ([A-Z0-9\x2b\x2f=]+)/i)[1]
|
||||
return [nil,nil] unless ntlm_challenge
|
||||
ntlm_message_2 = Net::NTLM::Message.decode64(ntlm_challenge)
|
||||
ntlm_message_3 = ntlm_message_2.response(
|
||||
{
|
||||
:user => opts['username'],
|
||||
:password => opts['password']
|
||||
}, {:ntlmv2 => true}
|
||||
)
|
||||
|
||||
# Send the response
|
||||
r = c.request_cgi(opts.merge({
|
||||
'uri' => opts['uri'],
|
||||
'method' => 'GET',
|
||||
'headers' => { 'Authorization' => "NTLM #{ntlm_message_3.encode64}"}}))
|
||||
resp = c.send_recv(r, to, true)
|
||||
unless resp.kind_of? Rex::Proto::Http::Response
|
||||
return [nil,nil]
|
||||
end
|
||||
return [nil,nil] if resp.code == 404
|
||||
while(resp and resp.code == 100)
|
||||
resp = c.reread_response(resp, to)
|
||||
end
|
||||
return [resp,c]
|
||||
|
||||
rescue ::Errno::EPIPE, ::Timeout::Error
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Wrappers for getters
|
||||
|
|
|
@ -302,8 +302,11 @@ class Client
|
|||
|
||||
#
|
||||
# Transmit a HTTP request and receive the response
|
||||
# If persist is set, then the request will attempt
|
||||
# to reuse an existing connection.
|
||||
#
|
||||
def send_recv(req, t = -1)
|
||||
def send_recv(req, t = -1, persist=false)
|
||||
@pipeline = persist
|
||||
send_request(req)
|
||||
read_response(t)
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
|
||||
require 'msf/core'
|
||||
require 'net/ntlm'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
@ -50,10 +51,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
register_autofilter_ports([ 80, 443, 8080, 8081, 8000, 8008, 8443, 8444, 8880, 8888 ])
|
||||
end
|
||||
|
||||
def find_auth_uri
|
||||
def find_auth_uri_and_scheme
|
||||
path_and_scheme = []
|
||||
if datastore['AUTH_URI'] and datastore['AUTH_URI'].length > 0
|
||||
path_and_scheme << datastore['AUTH_URI']
|
||||
paths = [datastore['AUTH_URI']]
|
||||
else
|
||||
paths = %W{
|
||||
|
@ -69,9 +69,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
res = send_request_cgi({
|
||||
'uri' => path,
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'User-Agent' => datastore['UserAgent']
|
||||
}
|
||||
'headers' => { }
|
||||
}, 10)
|
||||
|
||||
next if not res
|
||||
|
@ -89,10 +87,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
next if not res.headers['WWW-Authenticate']
|
||||
path_and_scheme << path
|
||||
case res.headers['WWW-Authenticate']
|
||||
when /NTLMSSP/i
|
||||
path_and_scheme << nil # "NTLMSSP"
|
||||
when /Basic/i
|
||||
path_and_scheme << "Basic"
|
||||
when /NTLM/i
|
||||
path_and_scheme << "NTLM"
|
||||
end
|
||||
return path_and_scheme
|
||||
end
|
||||
|
@ -101,15 +99,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
next if not res.headers['WWW-Authenticate']
|
||||
path_and_scheme << path
|
||||
case res.headers['WWW-Authenticate']
|
||||
when /NTLMSSP/i
|
||||
path_and_scheme << nil # "NTLMSSP"
|
||||
when /Basic/i
|
||||
path_and_scheme << "Basic"
|
||||
when /NTLM/i
|
||||
path_and_scheme << "NTLM"
|
||||
end
|
||||
return path_and_scheme
|
||||
end
|
||||
|
||||
return nil
|
||||
return path_and_scheme
|
||||
end
|
||||
|
||||
def target_url
|
||||
|
@ -122,7 +120,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def run_host(ip)
|
||||
|
||||
@uri = find_auth_uri()[0]
|
||||
@uri = find_auth_uri_and_scheme()[0]
|
||||
if ! @uri
|
||||
print_error("#{target_url} No URI found that asks for HTTP authentication")
|
||||
return
|
||||
|
@ -130,7 +128,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
@uri = "/#{@uri}" if @uri[0,1] != "/"
|
||||
|
||||
@scheme = find_auth_uri()[1]
|
||||
@scheme = find_auth_uri_and_scheme()[1]
|
||||
if ! @scheme
|
||||
print_error("#{target_url} Incompatible authentication scheme")
|
||||
return
|
||||
|
@ -149,7 +147,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
success = false
|
||||
proof = ""
|
||||
|
||||
ret = do_http_login(user,pass)
|
||||
ret = do_http_login(user,pass,@scheme)
|
||||
return :abort if ret == :abort
|
||||
if ret == :success
|
||||
proof = @proof.dup
|
||||
|
@ -162,8 +160,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
any_user = false
|
||||
any_pass = false
|
||||
|
||||
any_user = do_http_login(Rex::Text.rand_text_alpha(8), pass)
|
||||
any_pass = do_http_login(user, Rex::Text.rand_text_alpha(8))
|
||||
any_user = do_http_login(Rex::Text.rand_text_alpha(8), pass, @scheme)
|
||||
any_pass = do_http_login(user, Rex::Text.rand_text_alpha(8), @scheme)
|
||||
|
||||
if any_user == :success
|
||||
user = "anyuser"
|
||||
|
@ -194,7 +192,42 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
def do_http_login(user,pass)
|
||||
def do_http_login(user,pass,scheme)
|
||||
case scheme
|
||||
when /Basic/i
|
||||
do_http_auth_basic(user,pass)
|
||||
when /NTLM/i
|
||||
do_http_auth_ntlm(user,pass)
|
||||
else
|
||||
vprint_error("#{target_url}: Unknown authentication scheme")
|
||||
return :abort
|
||||
end
|
||||
end
|
||||
|
||||
def do_http_auth_ntlm(user,pass)
|
||||
begin
|
||||
resp,c = send_http_auth_ntlm(
|
||||
'uri' => @uri,
|
||||
'username' => user,
|
||||
'password' => pass
|
||||
)
|
||||
c.close
|
||||
return :abort if (resp.code == 404)
|
||||
|
||||
if resp.code == 200
|
||||
@proof = resp
|
||||
return :success
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{target_url} - Failed to connect to the web server")
|
||||
return :abort
|
||||
end
|
||||
|
||||
return :fail
|
||||
end
|
||||
|
||||
def do_http_auth_basic(user,pass)
|
||||
user_pass = Rex::Text.encode_base64(user + ":" + pass)
|
||||
|
||||
begin
|
||||
|
|
Loading…
Reference in New Issue