Merge branch 'dmaloney-r7-http/auth_methods' into rapid7

bug/bundler_fix
James Lee 2013-02-08 12:45:35 -06:00
commit 5b3b0a8b6d
24 changed files with 550 additions and 646 deletions

View File

@ -188,7 +188,9 @@ module Anemone
context,
url.scheme == "https",
'SSLv23',
@opts[:proxies]
@opts[:proxies],
@opts[:username],
@opts[:password]
)
conn.set_config(

View File

@ -22,7 +22,9 @@ module Auxiliary::HttpCrawler
Opt::Proxies,
OptInt.new('MAX_PAGES', [ true, 'The maximum number of pages to crawl per URL', 500]),
OptInt.new('MAX_MINUTES', [ true, 'The maximum number of minutes to spend on each URL', 5]),
OptInt.new('MAX_THREADS', [ true, 'The maximum number of concurrent requests', 4])
OptInt.new('MAX_THREADS', [ true, 'The maximum number of concurrent requests', 4]),
OptString.new('USERNAME', [false, 'The HTTP username to specify for authentication']),
OptString.new('PASSWORD', [false, 'The HTTP password to specify for authentication'])
], self.class
)
@ -34,8 +36,6 @@ module Auxiliary::HttpCrawler
OptString.new('UserAgent', [true, 'The User-Agent header to use for all requests',
"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
]),
OptString.new('BasicAuthUser', [false, 'The HTTP username to specify for basic authentication']),
OptString.new('BasicAuthPass', [false, 'The HTTP password to specify for basic authentication']),
OptString.new('HTTPAdditionalHeaders', [false, "A list of additional headers to send (separated by \\x01)"]),
OptString.new('HTTPCookie', [false, "A HTTP cookie header to send with each request"]),
OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]),
@ -118,8 +118,9 @@ module Auxiliary::HttpCrawler
:info => ""
})
if datastore['BasicAuthUser']
t[:http_basic_auth] = [ "#{datastore['BasicAuthUser']}:#{datastore['BasicAuthPass']}" ].pack("m*").gsub(/\s+/, '')
if datastore['USERNAME'] and datastore['USERNAME'] != ''
t[:username] = datastore['USERNAME'].to_s
t[:password] = datastore['PASSWORD'].to_s
end
if datastore['HTTPCookie']
@ -278,9 +279,8 @@ module Auxiliary::HttpCrawler
opts[:cookies] = t[:cookies]
end
if t[:http_basic_auth]
opts[:http_basic_auth] = t[:http_basic_auth]
end
opts[:username] = t[:username] || ''
opts[:password] =t[:password] || ''
opts
end

View File

@ -10,6 +10,7 @@ require 'uri'
module Msf
class Auxiliary::Web::HTTP
class Request
attr_accessor :url
attr_reader :opts
@ -69,6 +70,7 @@ class Auxiliary::Web::HTTP
attr_reader :framework
attr_accessor :redirect_limit
attr_accessor :username , :password
def initialize( opts = {} )
@opts = opts.dup
@ -84,8 +86,8 @@ class Auxiliary::Web::HTTP
@request_opts = {}
if opts[:auth].is_a? Hash
@request_opts['basic_auth'] = [ opts[:auth][:user].to_s + ':' +
opts[:auth][:password] ]. pack( 'm*' ).gsub( /\s+/, '' )
@username = opts[:auth][:user].to_s
@password = opts[:auth][:password].to_s
end
self.redirect_limit = opts[:redirect_limit] || 20
@ -105,7 +107,9 @@ class Auxiliary::Web::HTTP
opts[:target].port,
{},
opts[:target].ssl,
'SSLv23'
'SSLv23',
username,
password
)
c.set_config({
@ -296,6 +300,10 @@ class Auxiliary::Web::HTTP
opts['data'] = body if body
c = connect
if opts['username'] and opts['username'] != ''
c.username = opts['username'].to_s
c.password = opts['password'].to_s
end
Response.from_rex_response c.send_recv( c.request_cgi( opts ), timeout )
rescue ::Timeout::Error
Response.timed_out

View File

@ -37,7 +37,9 @@ module Exploit::Remote::HttpClient
Opt::RHOST,
Opt::RPORT(80),
OptString.new('VHOST', [ false, "HTTP server virtual host" ]),
Opt::Proxies
Opt::Proxies,
OptString.new('USERNAME', [false, 'The HTTP username to specify for authentication', '']),
OptString.new('PASSWORD', [false, 'The HTTP password to specify for authentication', '']),
], self.class
)
@ -46,10 +48,6 @@ module Exploit::Remote::HttpClient
OptString.new('UserAgent', [false, 'The User-Agent header to use for all requests',
Rex::Proto::Http::Client::DefaultUserAgent
]),
OptString.new('BasicAuthUser', [false, 'The HTTP username to specify for basic authentication']),
OptString.new('BasicAuthPass', [false, 'The HTTP password to specify for basic authentication']),
OptString.new('DigestAuthUser', [false, 'The HTTP username to specify for digest authentication']),
OptString.new('DigestAuthPassword', [false, 'The HTTP password to specify for digest authentication']),
OptBool.new('DigestAuthIIS', [false, 'Conform to IIS, should work for most servers. Only set to false for non-IIS servers', true]),
OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]),
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL3', 'TLS1']]),
@ -156,7 +154,9 @@ module Exploit::Remote::HttpClient
},
dossl,
ssl_version,
proxies
proxies,
datastore['USERNAME'],
datastore['PASSWORD']
)
# Configure the HTTP client with the supplied parameter
@ -184,7 +184,15 @@ module Exploit::Remote::HttpClient
'pad_post_params_count' => datastore['HTTP::pad_post_params_count'],
'uri_fake_end' => datastore['HTTP::uri_fake_end'],
'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'],
'header_folding' => datastore['HTTP::header_folding']
'header_folding' => datastore['HTTP::header_folding'],
'usentlm2_session' => datastore['NTLM::UseNTLM2_session'],
'use_ntlmv2' => datastore['NTLM::UseNTLMv2'],
'send_lm' => datastore['NTLM::SendLM'],
'send_ntlm' => datastore['NTLM::SendNTLM'],
'SendSPN' => datastore['NTLM::SendSPN'],
'UseLMKey' => datastore['NTLM::UseLMKey'],
'domain' => datastore['DOMAIN'],
'DigestAuthIIS' => datastore['DigestAuthIIS']
)
# If this connection is global, persist it
@ -266,6 +274,10 @@ module Exploit::Remote::HttpClient
def send_request_cgi(opts={}, timeout = 20)
begin
c = connect(opts)
if opts['username'] and opts['username'] != ''
c.username = opts['username'].to_s
c.password = opts['password'].to_s
end
r = c.request_cgi(opts)
c.send_recv(r, opts[:timeout] ? opts[:timeout] : timeout)
rescue ::Errno::EPIPE, ::Timeout::Error
@ -277,241 +289,8 @@ module Exploit::Remote::HttpClient
# Combine the user/pass into an auth string for the HTTP Client
#
def basic_auth
return if not datastore['BasicAuthUser']
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="
ntlm_options = {
:signing => false,
:usentlm2_session => datastore['NTLM::UseNTLM2_session'],
:use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
:send_lm => datastore['NTLM::SendLM'],
:send_ntlm => datastore['NTLM::SendNTLM']
}
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
domain_name = datastore['DOMAIN']
ntlm_message_1 = "NTLM " + Rex::Text::encode_base64(NTLM_UTILS::make_ntlmssp_blob_init( domain_name,
workstation_name,
ntlmssp_flags))
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
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
#old and simplier method but not compatible with windows 7/2008r2
#ntlm_message_2 = Rex::Proto::NTLM::Message.decode64(ntlm_challenge)
#ntlm_message_3 = ntlm_message_2.response( {:user => opts['username'],:password => opts['password']}, {:ntlmv2 => true})
ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge)
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(ntlm_message_2)
challenge_key = blob_data[:challenge_key]
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
#netbios name
default_name = blob_data[:default_name] || ''
#netbios domain
default_domain = blob_data[:default_domain] || ''
#dns name
dns_host_name = blob_data[:dns_host_name] || ''
#dns domain
dns_domain_name = blob_data[:dns_domain_name] || ''
#Client time
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
spnopt = {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
resp_lm,
resp_ntlm,
client_challenge,
ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(opts['username'], opts['password'], challenge_key,
domain_name, default_name, default_domain,
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
spnopt, ntlm_options)
ntlm_message_3 = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, opts['username'],
resp_lm, resp_ntlm, '', ntlmssp_flags)
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
# Send the response
r = c.request_cgi(opts.merge({
'uri' => opts['uri'],
'method' => 'GET',
'headers' => { 'Authorization' => "NTLM #{ntlm_message_3}"}}))
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
return [resp,c]
rescue ::Errno::EPIPE, ::Timeout::Error
end
end
def send_digest_request_cgi(opts={}, timeout=20)
@nonce_count = 0
return [nil,nil] if not (datastore['DigestAuthUser'] or opts['DigestAuthUser'])
to = opts['timeout'] || timeout
digest_user = datastore['DigestAuthUser'] || opts['DigestAuthUser'] || ""
digest_password = datastore['DigestAuthPassword'] || opts['DigestAuthPassword'] || ""
method = opts['method']
path = opts['uri']
iis = true
if (opts['DigestAuthIIS'] == false or datastore['DigestAuthIIS'] == false)
iis = false
end
begin
@nonce_count += 1
resp = opts['response']
if not resp
# Get authentication-challenge from server, and read out parameters required
c = connect(opts)
r = c.request_cgi(opts.merge({
'uri' => path,
'method' => method }))
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
if resp.code != 401
return resp
end
return [nil,nil] unless resp.headers['WWW-Authenticate']
end
# Don't anchor this regex to the beginning of string because header
# folding makes it appear later when the server presents multiple
# WWW-Authentication options (such as is the case with IIS configured
# for Digest or NTLM).
resp['www-authenticate'] =~ /Digest (.*)/
parameters = {}
$1.split(/,[[:space:]]*/).each do |p|
k, v = p.split("=", 2)
parameters[k] = v.gsub('"', '')
end
qop = parameters['qop']
if parameters['algorithm'] =~ /(.*?)(-sess)?$/
algorithm = case $1
when 'MD5' then Digest::MD5
when 'SHA1' then Digest::SHA1
when 'SHA2' then Digest::SHA2
when 'SHA256' then Digest::SHA256
when 'SHA384' then Digest::SHA384
when 'SHA512' then Digest::SHA512
when 'RMD160' then Digest::RMD160
else raise Error, "unknown algorithm \"#{$1}\""
end
algstr = parameters["algorithm"]
sess = $2
else
algorithm = Digest::MD5
algstr = "MD5"
sess = false
end
a1 = if sess then
[
algorithm.hexdigest("#{digest_user}:#{parameters['realm']}:#{digest_password}"),
parameters['nonce'],
@cnonce
].join ':'
else
"#{digest_user}:#{parameters['realm']}:#{digest_password}"
end
ha1 = algorithm.hexdigest(a1)
ha2 = algorithm.hexdigest("#{method}:#{path}")
request_digest = [ha1, parameters['nonce']]
request_digest.push(('%08x' % @nonce_count), @cnonce, qop) if qop
request_digest << ha2
request_digest = request_digest.join ':'
# Same order as IE7
auth = [
"Digest username=\"#{digest_user}\"",
"realm=\"#{parameters['realm']}\"",
"nonce=\"#{parameters['nonce']}\"",
"uri=\"#{path}\"",
"cnonce=\"#{@cnonce}\"",
"nc=#{'%08x' % @nonce_count}",
"algorithm=#{algstr}",
"response=\"#{algorithm.hexdigest(request_digest)[0, 32]}\"",
# The spec says the qop value shouldn't be enclosed in quotes, but
# some versions of IIS require it and Apache accepts it. Chrome
# and Firefox both send it without quotes but IE does it this way.
# Use the non-compliant-but-everybody-does-it to be as compatible
# as possible by default. The user can override if they don't like
# it.
if qop.nil? then
elsif iis then
"qop=\"#{qop}\""
else
"qop=#{qop}"
end,
if parameters.key? 'opaque' then
"opaque=\"#{parameters['opaque']}\""
end
].compact
headers ={ 'Authorization' => auth.join(', ') }
headers.merge!(opts['headers']) if opts['headers']
# Send main request with authentication
r = c.request_cgi(opts.merge({
'uri' => path,
'method' => method,
'headers' => headers }))
resp = c.send_recv(r, to)
unless resp.kind_of? Rex::Proto::Http::Response
return [nil,nil]
end
return [resp,c]
rescue ::Errno::EPIPE, ::Timeout::Error
end
return if not datastore['USERNAME']
datastore['USERNAME'].to_s + ":" + (datastore['PASSWORD'].to_s || '')
end
##
@ -722,4 +501,4 @@ protected
end
end
end

View File

@ -94,3 +94,4 @@ require 'msf/core/exploit/winrm'
# WebApp
require 'msf/core/exploit/web'

View File

@ -42,7 +42,7 @@ module Exploit::Remote::WinRM
c = connect(opts)
to = opts[:timeout] || timeout
ctype = "application/soap+xml;charset=UTF-8"
resp, c = send_request_cgi(opts.merge({
resp, c = send_winrm_request(opts.merge({
'uri' => opts['uri'],
'method' => 'POST',
'ctype' => ctype,
@ -61,7 +61,7 @@ module Exploit::Remote::WinRM
end
def winrm_run_cmd(cmd, timeout=20)
resp,c = send_request_ntlm(winrm_open_shell_msg,timeout)
resp = send_winrm_request(winrm_open_shell_msg,timeout)
if resp.nil?
print_error "Recieved no reply from server"
return nil
@ -76,17 +76,17 @@ module Exploit::Remote::WinRM
return retval
end
shell_id = winrm_get_shell_id(resp)
resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id),timeout)
resp = send_winrm_request(winrm_cmd_msg(cmd, shell_id),timeout)
cmd_id = winrm_get_cmd_id(resp)
resp,c = send_request_ntlm(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
resp = send_winrm_request(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
streams = winrm_get_cmd_streams(resp)
resp,c = send_request_ntlm(winrm_terminate_cmd_msg(shell_id,cmd_id),timeout)
resp,c = send_request_ntlm(winrm_delete_shell_msg(shell_id))
resp = send_winrm_request(winrm_terminate_cmd_msg(shell_id,cmd_id),timeout)
resp = send_winrm_request(winrm_delete_shell_msg(shell_id))
return streams
end
def winrm_run_cmd_hanging(cmd, timeout=20)
resp,c = send_request_ntlm(winrm_open_shell_msg,timeout)
resp = send_winrm_request(winrm_open_shell_msg,timeout)
if resp.nil?
print_error "Recieved no reply from server"
return nil
@ -101,9 +101,9 @@ module Exploit::Remote::WinRM
return retval
end
shell_id = winrm_get_shell_id(resp)
resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id),timeout)
resp = send_winrm_request(winrm_cmd_msg(cmd, shell_id),timeout)
cmd_id = winrm_get_cmd_id(resp)
resp,c = send_request_ntlm(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
resp = send_winrm_request(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
streams = winrm_get_cmd_streams(resp)
return streams
end
@ -219,98 +219,6 @@ module Exploit::Remote::WinRM
::Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16))
end
def send_request_ntlm(data, timeout = 20)
opts = {
'uri' => datastore['URI'],
'data' => data,
'username' => datastore['USERNAME'],
'password' => datastore['PASSWORD']
}
ntlm_options = {
:signing => false,
:usentlm2_session => datastore['NTLM::UseNTLM2_session'],
:use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
:send_lm => datastore['NTLM::SendLM'],
:send_ntlm => datastore['NTLM::SendNTLM']
}
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
domain_name = datastore['DOMAIN']
ntlm_message_1 = "NEGOTIATE " + Rex::Text::encode_base64(NTLM_UTILS::make_ntlmssp_blob_init( domain_name,
workstation_name,
ntlmssp_flags))
to = opts[:timeout] || timeout
begin
c = connect(opts)
ctype = "application/soap+xml;charset=UTF-8"
# First request to get the challenge
r = c.request_cgi(opts.merge({
'uri' => opts['uri'],
'method' => 'POST',
'ctype' => ctype,
'headers' => { 'Authorization' => ntlm_message_1},
'data' => opts['data']
}))
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
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(/NEGOTIATE ([A-Z0-9\x2b\x2f=]+)/i)[1]
return [nil,nil] unless ntlm_challenge
#old and simplier method but not compatible with windows 7/2008r2
#ntlm_message_2 = Rex::Proto::NTLM::Message.decode64(ntlm_challenge)
#ntlm_message_3 = ntlm_message_2.response( {:user => opts['username'],:password => opts['password']}, {:ntlmv2 => true})
ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge)
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(ntlm_message_2)
challenge_key = blob_data[:challenge_key]
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
#netbios name
default_name = blob_data[:default_name] || ''
#netbios domain
default_domain = blob_data[:default_domain] || ''
#dns name
dns_host_name = blob_data[:dns_host_name] || ''
#dns domain
dns_domain_name = blob_data[:dns_domain_name] || ''
#Client time
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
spnopt = {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
resp_lm,
resp_ntlm,
client_challenge,
ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(opts['username'], opts['password'], challenge_key,
domain_name, default_name, default_domain,
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
spnopt, ntlm_options)
ntlm_message_3 = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, opts['username'],
resp_lm, resp_ntlm, '', ntlmssp_flags)
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
# Send the response
r = c.request_cgi(opts.merge({
'uri' => opts['uri'],
'method' => 'POST',
'ctype' => ctype,
'headers' => { 'Authorization' => "NEGOTIATE #{ntlm_message_3}"},
'data' => opts['data']
}))
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
return [resp,c]
rescue ::Errno::EPIPE, ::Timeout::Error
end
end
def accepts_ntlm_auth
parse_auth_methods(winrm_poke).include? "Negotiate"
end
def target_url
proto = "http"
if rport == 5986 or datastore['SSL']
@ -329,6 +237,18 @@ module Exploit::Remote::WinRM
return "/root/cimv2/"
end
def send_winrm_request(data, timeout=20)
opts = {
'uri' => datastore['URI'],
'method' => 'POST',
'data' => data,
'username' => datastore['USERNAME'],
'password' => datastore['PASSWORD'],
'ctype' => "application/soap+xml;charset=UTF-8"
}
send_request_cgi(opts,timeout)
end
private

View File

@ -2,6 +2,11 @@
require 'rex/socket'
require 'rex/proto/http'
require 'rex/text'
require 'digest'
require 'rex/proto/ntlm/crypt'
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/utils'
require 'rex/proto/ntlm/exceptions'
module Rex
module Proto
@ -21,13 +26,15 @@ class Client
#
# Creates a new client instance
#
def initialize(host, port = 80, context = {}, ssl = nil, ssl_version = nil, proxies = nil)
def initialize(host, port = 80, context = {}, ssl = nil, ssl_version = nil, proxies = nil, username = '', password = '')
self.hostname = host
self.port = port.to_i
self.context = context
self.ssl = ssl
self.ssl_version = ssl_version
self.proxies = proxies
self.username = username
self.password = password
self.config = {
'read_max_data' => (1024*1024*1),
'vhost' => self.hostname,
@ -61,7 +68,21 @@ class Client
'uri_fake_end' => false, # bool
'uri_fake_params_start' => false, # bool
'header_folding' => false, # bool
'chunked_size' => 0 # integer
'chunked_size' => 0, # integer
#
# NTLM Options
#
'usentlm2_session' => true,
'use_ntlmv2' => true,
'send_lm' => true,
'send_ntlm' => true,
'SendSPN' => true,
'UseLMKey' => false,
'domain' => 'WORKSTATION',
#
# Digest Options
#
'DigestAuthIIS' => true
}
# This is not used right now...
@ -130,27 +151,44 @@ class Client
#
# Create an arbitrary HTTP request
#
# @param opts [Hash]
# @option opts 'agent' [String] User-Agent header value
# @option opts 'basic_auth' [String] Basic-Auth header value
# @option opts 'connection' [String] Connection header value
# @option opts 'cookie' [String] Cookie header value
# @option opts 'data' [String] HTTP data (only useful with some methods, see rfc2616)
# @option opts 'encode' [Bool] URI encode the supplied URI, default: false
# @option opts 'headers' [Hash] HTTP headers, e.g. <code>{ "X-MyHeader" => "value" }</code>
# @option opts 'method' [String] HTTP method to use in the request, not limited to standard methods defined by rfc2616, default: GET
# @option opts 'proto' [String] protocol, default: HTTP
# @option opts 'query' [String] raw query string
# @option opts 'raw_headers' [Hash] HTTP headers
# @option opts 'uri' [String] the URI to request
# @option opts 'version' [String] version of the protocol, default: 1.1
# @option opts 'vhost' [String] Host header value
#
# @return [Request]
def request_raw(opts={})
c_enc = opts['encode'] || false
c_uri = opts['uri'] || '/'
c_ag = opts['agent'] || config['agent']
c_auth = opts['basic_auth'] || config['basic_auth'] || ''
c_body = opts['data'] || ''
c_conn = opts['connection']
c_cook = opts['cookie'] || config['cookie']
c_enc = opts['encode'] || false
c_head = opts['headers'] || config['headers'] || {}
c_host = opts['vhost'] || config['vhost'] || self.hostname
c_meth = opts['method'] || 'GET'
c_prot = opts['proto'] || 'HTTP'
c_vers = opts['version'] || config['version'] || '1.1'
c_qs = opts['query']
c_ag = opts['agent'] || config['agent']
c_cook = opts['cookie'] || config['cookie']
c_host = opts['vhost'] || config['vhost'] || self.hostname
c_head = opts['headers'] || config['headers'] || {}
c_rawh = opts['raw_headers']|| config['raw_headers'] || ''
c_conn = opts['connection']
c_auth = opts['basic_auth'] || config['basic_auth'] || ''
c_uri = opts['uri'] || '/'
c_vers = opts['version'] || config['version'] || '1.1'
# An agent parameter was specified, but so was a header, prefer the header
if c_ag and c_head.keys.map{|x| x.downcase }.include?('user-agent')
c_ag = nil
end
uri = set_uri(c_uri)
req = ''
@ -170,7 +208,6 @@ class Client
req << set_host_header(c_host)
req << set_agent_header(c_ag)
if (c_auth.length > 0)
req << set_basic_auth_header(c_auth)
end
@ -181,53 +218,45 @@ class Client
req << set_raw_headers(c_rawh)
req << set_body(c_body)
req
request = Request.new
request.parse(req)
request.options = opts
request
end
#
# Create a CGI compatible request
#
# Options:
# - agent: User-Agent header value
# - basic_auth: Basic-Auth header value
# - connection: Connection header value
# - cookie: Cookie header value
# - ctype: Content-Type header value, default: +application/x-www-form-urlencoded+
# - data: HTTP data (only useful with some methods, see rfc2616)
# - encode: URI encode the supplied URI, default: false
# - encode_params: URI encode the GET or POST variables (names and values), default: true
# - headers: HTTP headers as a hash, e.g. <code>{ "X-MyHeader" => "value" }</code>
# - method: HTTP method to use in the request, not limited to standard methods defined by rfc2616, default: GET
# - proto: protocol, default: HTTP
# - query: raw query string
# - raw_headers: HTTP headers as a hash
# - uri: the URI to request
# - vars_get: GET variables as a hash to be translated into a query string
# - vars_post: POST variables as a hash to be translated into POST data
# - version: version of the protocol, default: 1.1
# - vhost: Host header value
# @param (see #request_raw)
# @option opts (see #request_raw)
# @option opts 'ctype' [String] Content-Type header value, default: +application/x-www-form-urlencoded+
# @option opts 'encode_params' [Bool] URI encode the GET or POST variables (names and values), default: true
# @option opts 'vars_get' [Hash] GET variables as a hash to be translated into a query string
# @option opts 'vars_post' [Hash] POST variables as a hash to be translated into POST data
#
# @return [Request]
def request_cgi(opts={})
c_ag = opts['agent'] || config['agent']
c_body = opts['data'] || ''
c_cgi = opts['uri'] || '/'
c_conn = opts['connection']
c_cook = opts['cookie'] || config['cookie']
c_enc = opts['encode'] || false
c_enc_p = (opts['encode_params'] == true or opts['encode_params'].nil? ? true : false)
c_cgi = opts['uri'] || '/'
c_body = opts['data'] || ''
c_meth = opts['method'] || 'GET'
c_prot = opts['proto'] || 'HTTP'
c_vers = opts['version'] || config['version'] || '1.1'
c_qs = opts['query'] || ''
c_varg = opts['vars_get'] || {}
c_varp = opts['vars_post'] || {}
c_head = opts['headers'] || config['headers'] || {}
c_host = opts['vhost'] || config['vhost']
c_meth = opts['method'] || 'GET'
c_path = opts['path_info']
c_prot = opts['proto'] || 'HTTP'
c_qs = opts['query'] || ''
c_rawh = opts['raw_headers'] || config['raw_headers'] || ''
c_type = opts['ctype'] || 'application/x-www-form-urlencoded'
c_ag = opts['agent'] || config['agent']
c_cook = opts['cookie'] || config['cookie']
c_host = opts['vhost'] || config['vhost']
c_conn = opts['connection']
c_path = opts['path_info']
c_auth = opts['basic_auth'] || config['basic_auth'] || ''
c_varg = opts['vars_get'] || {}
c_varp = opts['vars_post'] || {}
c_vers = opts['version'] || config['version'] || '1.1'
uri = set_cgi(c_cgi)
qstr = c_qs
pstr = c_body
@ -243,7 +272,7 @@ class Client
c_varg.each_pair do |var,val|
qstr << '&' if qstr.length > 0
qstr << (c_enc_p ? set_encode_uri(var) : var)
qstr << (c_enc_p ? set_encode_uri(var) : var)
qstr << '='
qstr << (c_enc_p ? set_encode_uri(val) : val)
end
@ -284,10 +313,6 @@ class Client
req << set_host_header(c_host)
req << set_agent_header(c_ag)
if (c_auth.length > 0)
req << set_basic_auth_header(c_auth)
end
req << set_cookie_header(c_cook)
req << set_connection_header(c_conn)
req << set_extra_headers(c_head)
@ -298,12 +323,19 @@ class Client
req << set_raw_headers(c_rawh)
req << set_body(pstr)
req
request = Request.new
request.parse(req)
request.options = opts
request
end
#
# Connects to the remote server if possible.
#
# @param t [Fixnum] Timeout
# @see Rex::Socket::Tcp.create
# @return [Rex::Socket::Tcp]
def connect(t = -1)
# If we already have a connection and we aren't pipelining, close it.
if (self.conn)
@ -342,11 +374,30 @@ class Client
end
#
# Transmit an HTTP request and receive the response
# If persist is set, then the request will attempt
# to reuse an existing connection.
# Sends a request and gets a response back
#
# If the request is a 401, and we have creds, it will attempt to complete
# authentication and return the final response
#
def send_recv(req, t = -1, persist=false)
res = _send_recv(req,t,persist)
if res and res.code == 401 and res.headers['WWW-Authenticate'] and have_creds?
res = send_auth(res, req.options, t, persist)
end
res
end
#
# Transmit an HTTP request and receive the response
#
# If persist is set, then the request will attempt to reuse an existing
# connection.
#
# Call this directly instead of {#send_recv} if you don't want automatic
# authentication handling.
#
# @return [Response]
def _send_recv(req, t = -1, persist=false)
@pipeline = persist
send_request(req, t)
res = read_response(t)
@ -357,11 +408,332 @@ class Client
#
# Send an HTTP request to the server
#
# @param req [Request,#to_s] The request to send
# @param t (see #connect)
def send_request(req, t = -1)
connect(t)
conn.put(req.to_s)
end
# Validates that the client has creds
def have_creds?
!(self.username.nil?) && self.username != ''
end
#
# Params -
# res = The 401 response we need to auth from
# opts = the opts used to generate the request that created this response
# t = the timeout for the http requests
# persist = whether to persist the tcp connection for HTTP Pipelining
#
# Parses the response for what Authentication methods are supported.
# Sets the corect authorization options and passes them on to the correct
# method for sending the next request.
def send_auth(res, opts, t, persist)
supported_auths = res.headers['WWW-Authenticate']
if supported_auths.include? 'Basic'
if opts['headers']
opts['headers']['Authorization'] = basic_auth_header(self.username,self.password)
else
opts['headers'] = { 'Authorization' => basic_auth_header(self.username,self.password)}
end
req = request_cgi(opts)
res = _send_recv(req,t,persist)
return res
elsif supported_auths.include? "Digest"
opts['DigestAuthUser'] = self.username.to_s
opts['DigestAuthPassword'] = self.password.to_s
temp_response = digest_auth(opts)
if temp_response.kind_of? Rex::Proto::Http::Response
res = temp_response
end
return res
elsif supported_auths.include? "NTLM"
opts['provider'] = 'NTLM'
temp_response = negotiate_auth(opts)
if temp_response.kind_of? Rex::Proto::Http::Response
res = temp_response
end
return res
elsif supported_auths.include? "Negotiate"
opts['provider'] = 'Negotiate'
temp_response = negotiate_auth(opts)
if temp_response.kind_of? Rex::Proto::Http::Response
res = temp_response
end
return res
end
return res
end
# Converts username and password into the HTTP Basic
# authorization string.
def basic_auth_header(username,password)
auth_str = username.to_s + ":" + password.to_s
auth_str = "Basic " + Rex::Text.encode_base64(auth_str)
end
#
# Opts -
# Inherits all the same options as send_request_cgi
# Also expects some specific opts
# DigestAuthUser - The username for DigestAuth
# DigestAuthPass - The password for DigestAuth
# DigestAuthIIS - IIS uses a slighlty different implementation, set this for IIS support
#
# This method builds new request to complete a Digest Authentication cycle.
# We do not persist the original connection , to clear state in preparation for our auth
# We do persist the rest of the connection stream because Digest is a tcp session
# based authentication method.
#
def digest_auth(opts={})
@nonce_count = 0
to = opts['timeout'] || 20
digest_user = opts['DigestAuthUser'] || ""
digest_password = opts['DigestAuthPassword'] || ""
method = opts['method']
path = opts['uri']
iis = true
if (opts['DigestAuthIIS'] == false or self.config['DigestAuthIIS'] == false)
iis = false
end
begin
@nonce_count += 1
resp = opts['response']
if not resp
# Get authentication-challenge from server, and read out parameters required
r = request_cgi(opts.merge({
'uri' => path,
'method' => method }))
resp = _send_recv(r, to)
unless resp.kind_of? Rex::Proto::Http::Response
return nil
end
if resp.code != 401
return resp
end
return resp unless resp.headers['WWW-Authenticate']
end
# Don't anchor this regex to the beginning of string because header
# folding makes it appear later when the server presents multiple
# WWW-Authentication options (such as is the case with IIS configured
# for Digest or NTLM).
resp['www-authenticate'] =~ /Digest (.*)/
parameters = {}
$1.split(/,[[:space:]]*/).each do |p|
k, v = p.split("=", 2)
parameters[k] = v.gsub('"', '')
end
qop = parameters['qop']
if parameters['algorithm'] =~ /(.*?)(-sess)?$/
algorithm = case $1
when 'MD5' then Digest::MD5
when 'SHA1' then Digest::SHA1
when 'SHA2' then Digest::SHA2
when 'SHA256' then Digest::SHA256
when 'SHA384' then Digest::SHA384
when 'SHA512' then Digest::SHA512
when 'RMD160' then Digest::RMD160
else raise Error, "unknown algorithm \"#{$1}\""
end
algstr = parameters["algorithm"]
sess = $2
else
algorithm = Digest::MD5
algstr = "MD5"
sess = false
end
a1 = if sess then
[
algorithm.hexdigest("#{digest_user}:#{parameters['realm']}:#{digest_password}"),
parameters['nonce'],
@cnonce
].join ':'
else
"#{digest_user}:#{parameters['realm']}:#{digest_password}"
end
ha1 = algorithm.hexdigest(a1)
ha2 = algorithm.hexdigest("#{method}:#{path}")
request_digest = [ha1, parameters['nonce']]
request_digest.push(('%08x' % @nonce_count), @cnonce, qop) if qop
request_digest << ha2
request_digest = request_digest.join ':'
# Same order as IE7
auth = [
"Digest username=\"#{digest_user}\"",
"realm=\"#{parameters['realm']}\"",
"nonce=\"#{parameters['nonce']}\"",
"uri=\"#{path}\"",
"cnonce=\"#{@cnonce}\"",
"nc=#{'%08x' % @nonce_count}",
"algorithm=#{algstr}",
"response=\"#{algorithm.hexdigest(request_digest)[0, 32]}\"",
# The spec says the qop value shouldn't be enclosed in quotes, but
# some versions of IIS require it and Apache accepts it. Chrome
# and Firefox both send it without quotes but IE does it this way.
# Use the non-compliant-but-everybody-does-it to be as compatible
# as possible by default. The user can override if they don't like
# it.
if qop.nil? then
elsif iis then
"qop=\"#{qop}\""
else
"qop=#{qop}"
end,
if parameters.key? 'opaque' then
"opaque=\"#{parameters['opaque']}\""
end
].compact
headers ={ 'Authorization' => auth.join(', ') }
headers.merge!(opts['headers']) if opts['headers']
# Send main request with authentication
r = request_cgi(opts.merge({
'uri' => path,
'method' => method,
'headers' => headers }))
resp = _send_recv(r, to, true)
unless resp.kind_of? Rex::Proto::Http::Response
return nil
end
return resp
rescue ::Errno::EPIPE, ::Timeout::Error
end
end
#
# Opts -
# Inherits all the same options as send_request_cgi
# provider - What Negotiate Provider to use (supports NTLM and Negotiate)
#
# Builds a series of requests to complete Negotiate Auth. Works essentially
# the same way as Digest auth. Same pipelining concerns exist.
#
def negotiate_auth(opts={})
ntlm_options = {
:signing => false,
:usentlm2_session => self.config['usentlm2_session'],
:use_ntlmv2 => self.config['use_ntlmv2'],
:send_lm => self.config['send_lm'],
:send_ntlm => self.config['send_ntlm']
}
to = opts['timeout'] || 20
opts['username'] ||= self.username.to_s
opts['password'] ||= self.password.to_s
if opts['provider'] and opts['provider'].include? 'Negotiate'
provider = "Negotiate "
else
provider = 'NTLM '
end
opts['method']||= 'GET'
opts['headers']||= {}
ntlmssp_flags = ::Rex::Proto::NTLM::Utils.make_ntlm_flags(ntlm_options)
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
domain_name = self.config['domain']
b64_blob = Rex::Text::encode_base64(
::Rex::Proto::NTLM::Utils::make_ntlmssp_blob_init(
domain_name,
workstation_name,
ntlmssp_flags
))
ntlm_message_1 = provider + b64_blob
begin
# First request to get the challenge
opts['headers']['Authorization'] = ntlm_message_1
r = request_cgi(opts)
resp = _send_recv(r, to)
unless resp.kind_of? Rex::Proto::Http::Response
return nil
end
return resp unless resp.code == 401 && resp.headers['WWW-Authenticate']
# Get the challenge and craft the response
ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/i).flatten[0]
return resp unless ntlm_challenge
ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge)
blob_data = ::Rex::Proto::NTLM::Utils.parse_ntlm_type_2_blob(ntlm_message_2)
challenge_key = blob_data[:challenge_key]
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
default_name = blob_data[:default_name] || '' #netbios name
default_domain = blob_data[:default_domain] || '' #netbios domain
dns_host_name = blob_data[:dns_host_name] || '' #dns name
dns_domain_name = blob_data[:dns_domain_name] || '' #dns domain
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || '' #Client time
spnopt = {:use_spn => self.config['SendSPN'], :name => self.hostname}
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = ::Rex::Proto::NTLM::Utils.create_lm_ntlm_responses(
opts['username'],
opts['password'],
challenge_key,
domain_name,
default_name,
default_domain,
dns_host_name,
dns_domain_name,
chall_MsvAvTimestamp,
spnopt,
ntlm_options
)
ntlm_message_3 = ::Rex::Proto::NTLM::Utils.make_ntlmssp_blob_auth(
domain_name,
workstation_name,
opts['username'],
resp_lm,
resp_ntlm,
'',
ntlmssp_flags
)
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
# Send the response
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3}"
r = request_cgi(opts)
resp = _send_recv(r, to, true)
unless resp.kind_of? Rex::Proto::Http::Response
return nil
end
return resp
rescue ::Errno::EPIPE, ::Timeout::Error
return nil
end
end
#
# Read a response from the server
#
@ -839,6 +1211,9 @@ class Client
#
attr_accessor :proxies
# Auth
attr_accessor :username, :password
# When parsing the request, thunk off the first response from the server, since junk
attr_accessor :junk_pipeline

View File

@ -48,6 +48,8 @@ class Request < Packet
end
end
attr_accessor :options
#
# Initializes an instance of an HTTP request with the supplied method, URI,
# and protocol.

View File

@ -38,10 +38,10 @@ class Metasploit4 < Msf::Auxiliary
))
# disabling all the unnecessary options that someone might set to break our query
deregister_options('RPORT','RHOST', 'BasicAuthPass', 'BasicAuthUser', 'DOMAIN',
deregister_options('RPORT','RHOST', 'DOMAIN',
'DigestAuthIIS', 'SSLVersion', 'NTLM::SendLM', 'NTLM::SendNTLM',
'NTLM::SendSPN', 'NTLM::UseLMKey', 'NTLM::UseNTLM2_session',
'NTLM::UseNTLMv2', 'DigestAuthPassword', 'DigestAuthUser', 'SSL')
'NTLM::UseNTLMv2', 'SSL')
register_options(
[

View File

@ -26,7 +26,7 @@ class Metasploit3 < Msf::Auxiliary
'Name' => 'Cisco Device HTTP Device Manager Access',
'Description' => %q{
This module gathers data from a Cisco device (router or switch) with the device manager
web interface exposed. The BasicAuthUser and BasicAuthPass options can be used to specify
web interface exposed. The USERNAME and PASSWORD options can be used to specify
authentication.
},
'Author' => [ 'hdm' ],
@ -61,7 +61,7 @@ class Metasploit3 < Msf::Auxiliary
print_good("#{rhost}:#{rport} Successfully authenticated to this device")
# Report a vulnerability only if no password was specified
if datastore['BasicAuthPass'].to_s.length == 0
if datastore['PASSWORD'].to_s.length == 0
report_vuln(
{

View File

@ -26,7 +26,7 @@ class Metasploit3 < Msf::Auxiliary
[
],
'Author' => [ 'hdm' ],
'Author' => [ 'hdm' , 'thelightcosine'],
'References' =>
[
[ 'CVE', '1999-0502'] # Weak password
@ -48,9 +48,7 @@ class Metasploit3 < Msf::Auxiliary
register_autofilter_ports([ 80, 443, 8080, 8081, 8000, 8008, 8443, 8444, 8880, 8888 ])
end
def find_auth_uri_and_scheme
path_and_scheme = []
def find_auth_uri
if datastore['AUTH_URI'] and datastore['AUTH_URI'].length > 0
paths = [datastore['AUTH_URI']]
else
@ -80,21 +78,9 @@ class Metasploit3 < Msf::Auxiliary
next if not res
end
next if not res.code == 401
next if not res.headers['WWW-Authenticate']
path_and_scheme << path
case res.headers['WWW-Authenticate']
when /Basic/i
path_and_scheme << "Basic"
when /NTLM/i
path_and_scheme << "NTLM"
when /Digest/i
path_and_scheme << "Digest"
end
return path_and_scheme
return path
end
return path_and_scheme
end
def target_url
@ -111,7 +97,7 @@ class Metasploit3 < Msf::Auxiliary
print_error("You need need to set AUTH_URI when using PUT Method !")
return
end
@uri, @scheme = find_auth_uri_and_scheme()
@uri = find_auth_uri()
if ! @uri
print_error("#{target_url} No URI found that asks for HTTP authentication")
return
@ -119,12 +105,7 @@ class Metasploit3 < Msf::Auxiliary
@uri = "/#{@uri}" if @uri[0,1] != "/"
if ! @scheme
print_error("#{target_url} Incompatible authentication scheme")
return
end
print_status("Attempting to login to #{target_url} with #{@scheme} authentication")
print_status("Attempting to login to #{target_url}")
each_user_pass { |user, pass|
do_login(user, pass)
@ -133,27 +114,23 @@ class Metasploit3 < Msf::Auxiliary
def do_login(user='admin', pass='admin')
vprint_status("#{target_url} - Trying username:'#{user}' with password:'#{pass}'")
success = false
proof = ""
ret = do_http_login(user,pass,@scheme)
return :abort if ret == :abort
if ret == :success
proof = @proof.dup
success = true
end
response = do_http_login(user,pass)
result = determine_result(response)
if success
return :abort if result == :abort
if result == :success
print_good("#{target_url} - Successful login '#{user}' : '#{pass}'")
any_user = false
any_pass = false
vprint_status("#{target_url} - Trying random username with password:'#{pass}'")
any_user = do_http_login(Rex::Text.rand_text_alpha(8), pass, @scheme)
any_user = determine_result(do_http_login(Rex::Text.rand_text_alpha(8), pass))
vprint_status("#{target_url} - Trying username:'#{user}' with random password")
any_pass = do_http_login(user, Rex::Text.rand_text_alpha(8), @scheme)
any_pass = determine_result(do_http_login(user, Rex::Text.rand_text_alpha(8)))
if any_user == :success
user = "anyuser"
@ -175,7 +152,7 @@ class Metasploit3 < Msf::Auxiliary
:sname => (ssl ? 'https' : 'http'),
:user => user,
:pass => pass,
:proof => "WEBAPP=\"Generic\", PROOF=#{proof}",
:proof => "WEBAPP=\"Generic\", PROOF=#{response.to_s}",
:source_type => "user_supplied",
:active => true
)
@ -188,143 +165,28 @@ class Metasploit3 < Msf::Auxiliary
end
end
def do_http_login(user,pass,scheme)
case scheme
when /NTLM/i
do_http_auth_ntlm(user,pass)
when /Digest/i
do_http_auth_digest(user,pass,datastore['REQUESTTYPE'])
when /Basic/i
do_http_auth_basic(user,pass)
else
vprint_error("#{target_url}: Unknown authentication scheme")
return :abort
end
end
def do_http_auth_ntlm(user,pass)
def do_http_login(user,pass)
begin
resp,c = send_http_auth_ntlm(
response = send_request_cgi({
'uri' => @uri,
'method' => datastore['REQUESTTYPE'],
'username' => user,
'password' => pass
)
c.close
return :abort if (resp.code == 404)
if [200, 301, 302].include?(resp.code)
@proof = resp
return :success
end
})
return response
rescue ::Rex::ConnectionError
vprint_error("#{target_url} - Failed to connect to the web server")
return :abort
return nil
end
end
def determine_result(response)
return :abort unless response.kind_of? Rex::Proto::Http::Response
return :abort unless response.code
return :success if [200, 301, 302].include?(response.code)
return :fail
end
def do_http_auth_basic(user,pass)
user_pass = Rex::Text.encode_base64(user + ":" + pass)
begin
res = send_request_cgi({
'uri' => @uri,
'method' => 'GET',
'headers' =>
{
'Authorization' => "Basic #{user_pass}",
}
}, 25)
unless (res.kind_of? Rex::Proto::Http::Response)
vprint_error("#{target_url} not responding")
return :abort
end
return :abort if (res.code == 404)
if [200, 301, 302].include?(res.code)
@proof = res
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_digest(user,pass,requesttype)
path = datastore['AUTH_URI'] || "/"
begin
if requesttype == "PUT"
res,c = send_digest_request_cgi({
'uri' => path,
'method' => requesttype,
'data' => 'Test123\r\n',
#'DigestAuthIIS' => false,
'DigestAuthUser' => user,
'DigestAuthPassword' => pass
}, 25)
elsif requesttype == "PROPFIND"
res,c = send_digest_request_cgi({
'uri' => path,
'method' => requesttype,
'data' => '<?xml version="1.0" encoding="utf-8"?><D:propfind xmlns:D="DAV:"><D:allprop/></D:propfind>',
#'DigestAuthIIS' => false,
'DigestAuthUser' => user,
'DigestAuthPassword' => pass,
'headers' => { 'Depth' => '0'}
}, 25)
else
res,c = send_digest_request_cgi({
'uri' => path,
'method' => requesttype,
#'DigestAuthIIS' => false,
'DigestAuthUser' => user,
'DigestAuthPassword' => pass
}, 25)
end
unless (res.kind_of? Rex::Proto::Http::Response)
vprint_error("#{target_url} not responding")
return :abort
end
return :abort if (res.code == 404)
if ( [200, 301, 302].include?(res.code) ) or (res.code == 201)
if ((res.code == 201) and (requesttype == "PUT"))
print_good("Trying to delete #{path}")
del_res,c = send_digest_request_cgi({
'uri' => path,
'method' => 'DELETE',
'DigestAuthUser' => user,
'DigestAuthPassword' => pass
}, 25)
if not (del_res.code == 204)
print_error("#{path} could be created, but not deleted again. This may have been noisy ...")
end
end
@proof = res
return :success
end
if (res.code == 207) and (requesttype == "PROPFIND")
@proof = res
return :success
end
rescue ::Rex::ConnectionError
vprint_error("#{target_url} - Failed to connect to the web server")
return :abort
end
return :fail
end
end

View File

@ -40,10 +40,6 @@ class Metasploit3 < Msf::Auxiliary
def run_host(ip)
unless accepts_ntlm_auth
print_error "The Remote WinRM server (#{ip} does not appear to allow Negotiate(NTLM) auth"
return
end
streams = winrm_run_cmd(datastore['CMD'])
return unless streams.class == Hash
print_error streams['stderr'] unless streams['stderr'] == ''

View File

@ -39,12 +39,8 @@ class Metasploit3 < Msf::Auxiliary
def run_host(ip)
unless accepts_ntlm_auth
print_error "The Remote WinRM server (#{ip} does not appear to allow Negotiate(NTLM) auth"
return
end
each_user_pass do |user, pass|
resp,c = send_request_ntlm(test_request)
resp = send_winrm_request(test_request)
if resp.nil?
print_error "#{ip}:#{rport}: Got no reply from the server, connection may have timed out"
return

View File

@ -42,12 +42,7 @@ class Metasploit3 < Msf::Auxiliary
def run_host(ip)
unless accepts_ntlm_auth
print_error "The Remote WinRM server (#{ip} does not appear to allow Negotiate(NTLM) auth"
return
end
resp,c = send_request_ntlm(winrm_wql_msg(datastore['WQL']))
resp = send_winrm_request(winrm_wql_msg(datastore['WQL']))
if resp.nil?
print_error "Got no reply from the server"
return

View File

@ -84,8 +84,7 @@ class Metasploit3 < Msf::Auxiliary
'IPC$,ADMIN$,C$,D$,CCMLOGS$,ccmsetup$,share,netlogon,sysvol'])
], self.class)
deregister_options('BasicAuthPass', 'BasicAuthUser', 'DOMAIN', 'DigestAuthPassword',
'DigestAuthUser', 'NTLM::SendLM', 'NTLM::SendSPN', 'NTLM::SendNTLM', 'NTLM::UseLMKey',
deregister_options('DOMAIN', 'NTLM::SendLM', 'NTLM::SendSPN', 'NTLM::SendNTLM', 'NTLM::UseLMKey',
'NTLM::UseNTLM2_session', 'NTLM::UseNTLMv2')
end

View File

@ -72,8 +72,8 @@ class Metasploit3 < Msf::Exploit::Remote
register_options(
[
OptString.new('BasicAuthUser', [true, 'The HTTP username to specify for basic authentication', 'piranha']),
OptString.new('BasicAuthPass', [true, 'The HTTP password to specify for basic authentication', 'q']),
OptString.new('USERNAME', [true, 'The HTTP username to specify for basic authentication', 'piranha']),
OptString.new('PASSWORD', [true, 'The HTTP password to specify for basic authentication', 'q']),
], self.class)
end
@ -96,7 +96,7 @@ class Metasploit3 < Msf::Exploit::Remote
end
if res.code == 401
print_error("401 Authorization Required! Our BasicAuthUser and BasicAuthPass credentials not accepted!")
print_error("401 Authorization Required! Our credentials were not accepted!")
elsif (res.code == 200 and res.body =~ /The passwords you supplied match/)
print_status("Command successfully executed (according to the server).")
end

View File

@ -227,9 +227,7 @@ class Metasploit3 < Msf::Exploit::Remote
authmsg = res.headers['WWW-Authenticate']
end
print_error("The remote server responded expecting authentication")
if datastore['BasicAuthUser'] and datastore['BasicAuthPass']
print_error("BasicAuthUser \"%s\" failed to authenticate" % datastore['BasicAuthUser'])
elsif authmsg
if authmsg
print_error("WWW-Authenticate: %s" % authmsg)
end
cleanup_instructions(rpath, name) # display cleanup info

View File

@ -96,9 +96,6 @@ class Metasploit3 < Msf::Exploit::Remote
def exploit
datastore['BasicAuthUser'] = datastore['USERNAME']
datastore['BasicAuthPass'] = datastore['PASSWORD']
jsp_name = datastore['JSP'] || rand_text_alpha(8+rand(8))
app_base = datastore['APPBASE'] || rand_text_alpha(8+rand(8))

View File

@ -123,9 +123,6 @@ class Metasploit3 < Msf::Exploit::Remote
def exploit
datastore['BasicAuthUser'] = datastore['USERNAME']
datastore['BasicAuthPass'] = datastore['PASSWORD']
jsp_name = datastore['JSP'] || rand_text_alpha(8+rand(8))
app_base = datastore['APPBASE'] || rand_text_alpha(8+rand(8))

View File

@ -112,9 +112,6 @@ class Metasploit3 < Msf::Exploit::Remote
end
def check
datastore['BasicAuthUser'] = datastore['USERNAME']
datastore['BasicAuthPass'] = datastore['PASSWORD']
res = query_serverinfo
disconnect
return CheckCode::Unknown if res.nil?
@ -127,8 +124,8 @@ class Metasploit3 < Msf::Exploit::Remote
:host => rhost,
:port => rport,
:sname => (ssl ? "https" : "http"),
:user => datastore['BasicAuthUser'],
:pass => datastore['BasicAuthPass'],
:user => datastore['USERNAME'],
:pass => datastore['PASSWORD'],
:proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}",
:active => true
)
@ -164,9 +161,6 @@ class Metasploit3 < Msf::Exploit::Remote
def exploit
datastore['BasicAuthUser'] = datastore['USERNAME']
datastore['BasicAuthPass'] = datastore['PASSWORD']
mytarget = target
if (target.name =~ /Automatic/)
mytarget = auto_target
@ -221,8 +215,8 @@ class Metasploit3 < Msf::Exploit::Remote
:host => rhost,
:port => rport,
:sname => (ssl ? "https" : "http"),
:user => datastore['BasicAuthUser'],
:pass => datastore['BasicAuthPass'],
:user => datastore['USERNAME'],
:pass => datastore['PASSWORD'],
:proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}",
:active => true
)

View File

@ -67,9 +67,6 @@ class Metasploit3 < Msf::Exploit::Remote
end
def go(command)
datastore['BasicAuthUser'] = datastore['USERNAME']
datastore['BasicAuthPass'] = datastore['PASSWORD']
xml = <<-EOS
<?xml version="1.0"?>
<methodCall>

View File

@ -72,8 +72,8 @@ class Metasploit3 < Msf::Exploit::Remote
register_options(
[
Opt::RPORT(8080),
OptString.new('BasicAuthUser', [true, 'The HTTP username to specify for basic authentication', 'anonymous']),
OptString.new('BasicAuthPass', [true, 'The HTTP password to specify for basic authentication', 'mozilla@example.com']),
OptString.new('USERNAME', [true, 'The HTTP username to specify for basic authentication', 'anonymous']),
OptString.new('PASSWORD', [true, 'The HTTP password to specify for basic authentication', 'mozilla@example.com']),
], self.class)
end

View File

@ -36,8 +36,8 @@ class Metasploit3 < Msf::Exploit::Remote
[
OptString.new('PATH', [ true, "The path to attempt to upload", '/webdav/']),
OptString.new('FILENAME', [ false , "The filename to give the payload. (Leave Blank for Random)"]),
OptString.new('RUSER', [ true, "The Username to use for Authentication", 'wampp']),
OptString.new('RPASS', [ true, "The Password to use for Authentication", 'xampp'])
OptString.new('USERNAME', [false, 'The HTTP username to specify for authentication', 'wampp']),
OptString.new('PASSWORD', [false, 'The HTTP password to specify for authentication', 'xampp'])
], self.class)
end
@ -46,12 +46,10 @@ class Metasploit3 < Msf::Exploit::Remote
def exploit
uri = build_path
print_status "Uploading Payload to #{uri}"
res,c = send_digest_request_cgi({
res = send_request_cgi({
'uri' => uri,
'method' => 'PUT',
'data' => payload.raw,
'DigestAuthUser' => datastore['RUSER'],
'DigestAuthPassword' => datastore['RPASS']
'data' => payload.raw
}, 25)
unless (res and res.code == 201)
print_error "Failed to upload file!"

View File

@ -66,20 +66,8 @@ class Metasploit3 < Msf::Exploit::Remote
@compat_mode = false
end
def check
unless accepts_ntlm_auth
print_error "The Remote WinRM server does not appear to allow Negotiate (NTLM) auth"
return Msf::Exploit::CheckCode::Safe
end
return Msf::Exploit::CheckCode::Vulnerable
end
def exploit
unless check == Msf::Exploit::CheckCode::Vulnerable
return
end
unless valid_login?
print_error "Login Failure. Recheck your credentials"
return
@ -141,7 +129,7 @@ class Metasploit3 < Msf::Exploit::Remote
def temp_dir
print_status "Grabbing %TEMP%"
resp,c = send_request_ntlm(winrm_open_shell_msg)
resp = send_winrm_request(winrm_open_shell_msg)
if resp.nil?
print_error "Got no reply from the server"
return nil
@ -152,16 +140,16 @@ class Metasploit3 < Msf::Exploit::Remote
end
shell_id = winrm_get_shell_id(resp)
cmd = "echo %TEMP%"
resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id))
resp= send_winrm_request(winrm_cmd_msg(cmd, shell_id))
cmd_id = winrm_get_cmd_id(resp)
resp,c = send_request_ntlm(winrm_cmd_recv_msg(shell_id,cmd_id))
resp = send_winrm_request(winrm_cmd_recv_msg(shell_id,cmd_id))
streams = winrm_get_cmd_streams(resp)
return streams['stdout'].chomp
end
def check_remote_arch
wql = %q{select AddressWidth from Win32_Processor where DeviceID="CPU0"}
resp,c = send_request_ntlm(winrm_wql_msg(wql))
resp = send_winrm_request(winrm_wql_msg(wql))
#Default to x86 if we can't be sure
return "x86" if resp.nil? or resp.code != 200
resp_tbl = parse_wql_response(resp)
@ -247,7 +235,7 @@ class Metasploit3 < Msf::Exploit::Remote
def valid_login?
data = winrm_wql_msg("Select Name,Status from Win32_Service")
resp,c = send_request_ntlm(data)
resp = send_winrm_request(data)
unless resp.code == 200
return false
end