1 /*
2 *
3 * Autopsy Forensic Browser
4 *
5 * Copyright 2012-2020 Basis Technology Corp.
6 *
7 * Copyright 2012 42six Solutions.
8 *
9 * Project Contact/Architect: carrier <at> sleuthkit <dot> org
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23 package org.sleuthkit.autopsy.recentactivity;
24
25 import com.google.gson.JsonArray;
26 import com.google.gson.JsonElement;
27 import com.google.gson.JsonIOException;
28 import com.google.gson.JsonObject;
29 import com.google.gson.JsonParser;
30 import com.google.gson.JsonSyntaxException;
31 import org.openide.util.NbBundle;
33 import java.util.logging.Level;
34 import java.util.*;
35 import java.io.File;
36 import java.io.FileNotFoundException;
37 import java.io.FileReader;
38 import java.io.IOException;
39 import org.apache.commons.io.FilenameUtils;
40 import org.openide.util.NbBundle.Messages;
51 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
53 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
55 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
58 import org.
sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
59
63 class Chrome extends Extract {
64
65 private static final String HISTORY_QUERY = "SELECT urls.url, urls.title, urls.visit_count, urls.typed_count, " //NON-NLS
66 + "last_visit_time, urls.hidden, visits.visit_time, (SELECT urls.url FROM urls WHERE urls.id=visits.url) AS from_visit, visits.transition FROM urls, visits WHERE urls.id = visits.url"; //NON-NLS
67 private static final String COOKIE_QUERY = "SELECT name, value, host_key, expires_utc,last_access_utc, creation_utc FROM cookies"; //NON-NLS
68 private static final String DOWNLOAD_QUERY = "SELECT full_path, url, start_time, received_bytes FROM downloads"; //NON-NLS
69 private static final String DOWNLOAD_QUERY_V30 = "SELECT current_path AS full_path, url, start_time, received_bytes FROM downloads, downloads_url_chains WHERE downloads.id=downloads_url_chains.id"; //NON-NLS
70 private static final String LOGIN_QUERY = "SELECT origin_url, username_value, date_created, signon_realm from logins"; //NON-NLS
71 private static final String AUTOFILL_QUERY = "SELECT name, value, count, date_created " +
72 " FROM autofill, autofill_dates " +
73 " WHERE autofill.pair_id = autofill_dates.pair_id"
74 ; //NON-NLS
75 private static final String AUTOFILL_QUERY_V8X = "SELECT name, value, count, date_created, date_last_used from autofill"; //NON-NLS
76 private static final String WEBFORM_ADDRESS_QUERY = "SELECT first_name, middle_name, last_name, address_line_1, address_line_2, city, state, zipcode, country_code, number, email, date_modified " +
77 " FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones" +
78 " WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid";
79
80 private static final String WEBFORM_ADDRESS_QUERY_V8X = "SELECT first_name, middle_name, last_name, full_name, street_address, city, state, zipcode, country_code, number, email, date_modified, use_date, use_count" +
81 " FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones" +
82 " WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid";
83 private final Logger logger = Logger.getLogger(this.getClass().getName());
84 private Content dataSource;
85 private IngestJobContext context;
86
87 @Messages({
88 "Progress_Message_Chrome_History=Chrome History",
89 "Progress_Message_Chrome_Bookmarks=Chrome Bookmarks",
90 "Progress_Message_Chrome_Cookies=Chrome Cookies",
91 "Progress_Message_Chrome_Downloads=Chrome Downloads",
92 "Progress_Message_Chrome_FormHistory=Chrome Form History",
93 "Progress_Message_Chrome_AutoFill=Chrome Auto Fill",
94 "Progress_Message_Chrome_Logins=Chrome Logins",
95 "Progress_Message_Chrome_Cache=Chrome Cache",
96 })
97
98 Chrome() {
99 moduleName = NbBundle.getMessage(Chrome.class, "Chrome.moduleName");
100 }
101
102 @Override
103 public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
104 this.dataSource = dataSource;
105 this.context = context;
106 dataFound = false;
107
108 progressBar.progress(Bundle.Progress_Message_Chrome_History());
109 this.getHistory();
110 if (context.dataSourceIngestIsCancelled()) {
111 return;
112 }
113
114 progressBar.progress(Bundle.Progress_Message_Chrome_Bookmarks());
115 this.getBookmark();
116 if (context.dataSourceIngestIsCancelled()) {
117 return;
118 }
119
120 progressBar.progress(Bundle.Progress_Message_Chrome_Cookies());
121 this.getCookie();
122 if (context.dataSourceIngestIsCancelled()) {
123 return;
124 }
125
126 progressBar.progress(Bundle.Progress_Message_Chrome_Logins());
127 this.getLogins();
128 if (context.dataSourceIngestIsCancelled()) {
129 return;
130 }
131
132 progressBar.progress(Bundle.Progress_Message_Chrome_AutoFill());
133 this.getAutofill();
134 if (context.dataSourceIngestIsCancelled()) {
135 return;
136 }
137
138 progressBar.progress(Bundle.Progress_Message_Chrome_Downloads());
139 this.getDownload();
140 if (context.dataSourceIngestIsCancelled()) {
141 return;
142 }
143
144 progressBar.progress(Bundle.Progress_Message_Chrome_Cache());
145 ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar);
146 chromeCacheExtractor.processCaches();
147 }
148
152 private void getHistory() {
153 FileManager fileManager = currentCase.getServices().getFileManager();
154 List<AbstractFile> historyFiles;
155 try {
156 historyFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS
157 } catch (TskCoreException ex) {
158 String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles");
159 logger.log(Level.SEVERE, msg, ex);
160 this.addErrorMessage(this.getName() + ": " + msg);
161 return;
162 }
163
164 // get only the allocated ones, for now
165 List<AbstractFile> allocatedHistoryFiles = new ArrayList<>();
166 for (AbstractFile historyFile : historyFiles) {
167 if (historyFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
168 allocatedHistoryFiles.add(historyFile);
169 }
170 }
171
172 // log a message if we don't have any allocated history files
173 if (allocatedHistoryFiles.isEmpty()) {
174 String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.couldntFindAnyFiles");
175 logger.log(Level.INFO, msg);
176 return;
177 }
178
179 dataFound = true;
180 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
181 int j = 0;
182 while (j < historyFiles.size()) {
183 String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + historyFiles.get(j).getName() + j + ".db"; //NON-NLS
184 final AbstractFile historyFile = historyFiles.get(j++);
185 if (historyFile.getSize() == 0) {
186 continue;
187 }
188 try {
189 ContentUtils.writeToFile(historyFile, new File(temps), context::dataSourceIngestIsCancelled);
190 } catch (ReadContentInputStreamException ex) {
191 logger.log(Level.WARNING, String.format("Error reading Chrome web history artifacts file '%s' (id=%d).",
192 historyFile.getName(), historyFile.getId()), ex); //NON-NLS
193 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errAnalyzingFile",
194 this.getName(), historyFile.getName()));
195 continue;
196 } catch (IOException ex) {
197 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome web history artifacts file '%s' (id=%d).",
198 temps, historyFile.getName(), historyFile.getId()), ex); //NON-NLS
199 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errAnalyzingFile",
200 this.getName(), historyFile.getName()));
201 continue;
202 }
203 File dbFile = new File(temps);
204 if (context.dataSourceIngestIsCancelled()) {
205 dbFile.delete();
206 break;
207 }
208 List<HashMap<String, Object>> tempList;
209 tempList = this.dbConnect(temps, HISTORY_QUERY);
210 logger.log(Level.INFO, "{0}- Now getting history from {1} with {2}artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
211 for (HashMap<String, Object> result : tempList) {
212 Collection<BlackboardAttribute> bbattributes = new ArrayList<BlackboardAttribute>();
213 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
214 RecentActivityExtracterModuleFactory.getModuleName(),
215 ((result.get("url").toString() != null) ? result.get("url").toString() : ""))); //NON-NLS
216 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
217 RecentActivityExtracterModuleFactory.getModuleName(),
218 (Long.valueOf(result.get("last_visit_time").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
219 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
220 RecentActivityExtracterModuleFactory.getModuleName(),
221 ((result.get("from_visit").toString() != null) ? result.get("from_visit").toString() : ""))); //NON-NLS
222 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
223 RecentActivityExtracterModuleFactory.getModuleName(),
224 ((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS
225 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
226 RecentActivityExtracterModuleFactory.getModuleName(),
227 NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
228 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
229 RecentActivityExtracterModuleFactory.getModuleName(),
230 (NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
231
232 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes);
233 if (bbart != null) {
234 bbartifacts.add(bbart);
235 }
236 }
237 dbFile.delete();
238 }
239
240 if( !bbartifacts.isEmpty() ){
241 postArtifacts(bbartifacts);
242 }
243 }
244
248 private void getBookmark() {
249 FileManager fileManager = currentCase.getServices().getFileManager();
250 List<AbstractFile> bookmarkFiles;
251 try {
252 bookmarkFiles = fileManager.findFiles(dataSource, "Bookmarks", "Chrome"); //NON-NLS
253 } catch (TskCoreException ex) {
254 String msg = NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errGettingFiles");
255 logger.log(Level.SEVERE, msg, ex);
256 this.addErrorMessage(this.getName() + ": " + msg);
257 return;
258 }
259
260 if (bookmarkFiles.isEmpty()) {
261 logger.log(Level.INFO, "Didn't find any Chrome bookmark files."); //NON-NLS
262 return;
263 }
264
265 dataFound = true;
266 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
267 int j = 0;
268
269 while (j < bookmarkFiles.size()) {
270 AbstractFile bookmarkFile = bookmarkFiles.get(j++);
271 if (bookmarkFile.getSize() == 0) {
272 continue;
273 }
274 String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS
275 try {
276 ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled);
277 } catch (ReadContentInputStreamException ex) {
278 logger.log(Level.WARNING, String.format("Error reading Chrome bookmark artifacts file '%s' (id=%d).",
279 bookmarkFile.getName(), bookmarkFile.getId()), ex); //NON-NLS
280 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile",
281 this.getName(), bookmarkFile.getName()));
282 continue;
283 } catch (IOException ex) {
284 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome bookmark artifacts file '%s' (id=%d).",
285 temps, bookmarkFile.getName(), bookmarkFile.getId()), ex); //NON-NLS
286 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile",
287 this.getName(), bookmarkFile.getName()));
288 continue;
289 }
290
291 logger.log(Level.INFO, "{0}- Now getting Bookmarks from {1}", new Object[]{moduleName, temps}); //NON-NLS
292 File dbFile = new File(temps);
293 if (context.dataSourceIngestIsCancelled()) {
294 dbFile.delete();
295 break;
296 }
297
298 FileReader tempReader;
299 try {
300 tempReader = new FileReader(temps);
301 } catch (FileNotFoundException ex) {
302 logger.log(Level.WARNING, "Error while trying to read into the Bookmarks for Chrome.", ex); //NON-NLS
303 continue;
304 }
305
306 final JsonParser parser = new JsonParser();
307 JsonElement jsonElement;
308 JsonObject jElement, jRoot, jBookmark;
309 JsonArray jBookmarkArray;
310
311 try {
312 jsonElement = parser.parse(tempReader);
313 jElement = jsonElement.getAsJsonObject();
314 jRoot = jElement.get("roots").getAsJsonObject(); //NON-NLS
315 jBookmark = jRoot.get("bookmark_bar").getAsJsonObject(); //NON-NLS
316 jBookmarkArray = jBookmark.getAsJsonArray("children"); //NON-NLS
317 } catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
318 logger.log(Level.WARNING, "Error parsing Json from Chrome Bookmark.", ex); //NON-NLS
319 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile3",
320 this.getName(), bookmarkFile.getName()));
321 continue;
322 }
323
324 for (JsonElement result : jBookmarkArray) {
325 JsonObject address = result.getAsJsonObject();
326 if (address == null) {
327 continue;
328 }
329 JsonElement urlEl = address.get("url"); //NON-NLS
330 String url;
331 if (urlEl != null) {
332 url = urlEl.getAsString();
333 } else {
334 url = "";
335 }
336 String name;
337 JsonElement nameEl = address.get("name"); //NON-NLS
338 if (nameEl != null) {
339 name = nameEl.getAsString();
340 } else {
341 name = "";
342 }
343 Long date;
344 JsonElement dateEl = address.get("date_added"); //NON-NLS
345 if (dateEl != null) {
346 date = dateEl.getAsLong();
347 } else {
348 date = Long.valueOf(0);
349 }
350 String domain = NetworkUtils.extractDomain(url);
351 try {
352 BlackboardArtifact bbart = bookmarkFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK);
353 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
354 //TODO Revisit usage of deprecated constructor as per TSK-583
355 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
356 RecentActivityExtracterModuleFactory.getModuleName(), url));
357 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
358 RecentActivityExtracterModuleFactory.getModuleName(), name));
359 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
360 RecentActivityExtracterModuleFactory.getModuleName(), (date / 1000000) - Long.valueOf("11644473600")));
361 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
362 RecentActivityExtracterModuleFactory.getModuleName(),
363 NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
364 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
365 RecentActivityExtracterModuleFactory.getModuleName(), domain));
366 bbart.addAttributes(bbattributes);
367
368 bbartifacts.add(bbart);
369 } catch (TskCoreException ex) {
370 logger.log(Level.SEVERE, "Error while trying to insert Chrome bookmark artifact{0}", ex); //NON-NLS
371 this.addErrorMessage(
372 NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile4",
373 this.getName(), bookmarkFile.getName()));
374 }
375 }
376 postArtifacts(bbartifacts);
377 dbFile.delete();
378 }
379 }
380
384 private void getCookie() {
385
386 FileManager fileManager = currentCase.getServices().getFileManager();
387 List<AbstractFile> cookiesFiles;
388 try {
389 cookiesFiles = fileManager.findFiles(dataSource, "Cookies", "Chrome"); //NON-NLS
390 } catch (TskCoreException ex) {
391 String msg = NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errGettingFiles");
392 logger.log(Level.SEVERE, msg, ex);
393 this.addErrorMessage(this.getName() + ": " + msg);
394 return;
395 }
396
397 if (cookiesFiles.isEmpty()) {
398 logger.log(Level.INFO, "Didn't find any Chrome cookies files."); //NON-NLS
399 return;
400 }
401
402 dataFound = true;
403 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
404 int j = 0;
405 while (j < cookiesFiles.size()) {
406 AbstractFile cookiesFile = cookiesFiles.get(j++);
407 if (cookiesFile.getSize() == 0) {
408 continue;
409 }
410 String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS
411 try {
412 ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled);
413 } catch (ReadContentInputStreamException ex) {
414 logger.log(Level.WARNING, String.format("Error reading Chrome cookie artifacts file '%s' (id=%d).",
415 cookiesFile.getName(), cookiesFile.getId()), ex); //NON-NLS
416 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errAnalyzeFile",
417 this.getName(), cookiesFile.getName()));
418 continue;
419 } catch (IOException ex) {
420 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome cookie artifacts file '%s' (id=%d).",
421 temps, cookiesFile.getName(), cookiesFile.getId()), ex); //NON-NLS
422 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errAnalyzeFile",
423 this.getName(), cookiesFile.getName()));
424 continue;
425 }
426 File dbFile = new File(temps);
427 if (context.dataSourceIngestIsCancelled()) {
428 dbFile.delete();
429 break;
430 }
431
432 List<HashMap<String, Object>> tempList = this.dbConnect(temps, COOKIE_QUERY);
433 logger.log(Level.INFO, "{0}- Now getting cookies from {1} with {2}artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
434 for (HashMap<String, Object> result : tempList) {
435 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
436 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
437 RecentActivityExtracterModuleFactory.getModuleName(),
438 ((result.get("host_key").toString() != null) ? result.get("host_key").toString() : ""))); //NON-NLS
439 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
440 RecentActivityExtracterModuleFactory.getModuleName(),
441 (Long.valueOf(result.get("last_access_utc").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
442
443 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
444 RecentActivityExtracterModuleFactory.getModuleName(),
445 ((result.get("name").toString() != null) ? result.get("name").toString() : ""))); //NON-NLS
446 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
447 RecentActivityExtracterModuleFactory.getModuleName(),
448 ((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
449 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
450 RecentActivityExtracterModuleFactory.getModuleName(),
451 NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
452 String domain = result.get("host_key").toString(); //NON-NLS
453 domain = domain.replaceFirst("^\\.+(?!$)", "");
454 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
455 RecentActivityExtracterModuleFactory.getModuleName(), domain));
456
457 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes);
458 if (bbart != null) {
459 bbartifacts.add(bbart);
460 }
461 }
462
463 dbFile.delete();
464 }
465
466 if( !bbartifacts.isEmpty() ) {
467 postArtifacts(bbartifacts);
468 }
469 }
470
474 private void getDownload() {
475 FileManager fileManager = currentCase.getServices().getFileManager();
476 List<AbstractFile> downloadFiles;
477 try {
478 downloadFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS
479 } catch (TskCoreException ex) {
480 String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles");
481 logger.log(Level.SEVERE, msg, ex);
482 this.addErrorMessage(this.getName() + ": " + msg);
483 return;
484 }
485
486 if (downloadFiles.isEmpty()) {
487 logger.log(Level.INFO, "Didn't find any Chrome download files."); //NON-NLS
488 return;
489 }
490
491 dataFound = true;
492 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
493 int j = 0;
494 while (j < downloadFiles.size()) {
495 AbstractFile downloadFile = downloadFiles.get(j++);
496 if (downloadFile.getSize() == 0) {
497 continue;
498 }
499 String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS
500 try {
501 ContentUtils.writeToFile(downloadFile, new File(temps), context::dataSourceIngestIsCancelled);
502 } catch (ReadContentInputStreamException ex) {
503 logger.log(Level.WARNING, String.format("Error reading Chrome download artifacts file '%s' (id=%d).",
504 downloadFile.getName(), downloadFile.getId()), ex); //NON-NLS
505 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errAnalyzeFiles1",
506 this.getName(), downloadFile.getName()));
507 continue;
508 } catch (IOException ex) {
509 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome download artifacts file '%s' (id=%d).",
510 temps, downloadFile.getName(), downloadFile.getId()), ex); //NON-NLS
511 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errAnalyzeFiles1",
512 this.getName(), downloadFile.getName()));
513 continue;
514 }
515 File dbFile = new File(temps);
516 if (context.dataSourceIngestIsCancelled()) {
517 dbFile.delete();
518 break;
519 }
520
521 List<HashMap<String, Object>> tempList;
522
523 if (isChromePreVersion30(temps)) {
524 tempList = this.dbConnect(temps, DOWNLOAD_QUERY);
525 } else {
526 tempList = this.dbConnect(temps, DOWNLOAD_QUERY_V30);
527 }
528
529 logger.log(Level.INFO, "{0}- Now getting downloads from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
530 for (HashMap<String, Object> result : tempList) {
531 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
532 String fullPath = result.get("full_path").toString(); //NON-NLS
533 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
534 RecentActivityExtracterModuleFactory.getModuleName(), fullPath));
535 long pathID = Util.findID(dataSource, fullPath);
536 if (pathID != -1) {
537 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
538 NbBundle.getMessage(this.getClass(),
539 "Chrome.parentModuleName"), pathID));
540 }
541 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
542 RecentActivityExtracterModuleFactory.getModuleName(),
543 ((result.get("url").toString() != null) ? result.get("url").toString() : ""))); //NON-NLS
544 //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
545 Long time = (Long.valueOf(result.get("start_time").toString()) / 1000000) - Long.valueOf("11644473600"); //NON-NLS
546
547 //TODO Revisit usage of deprecated constructor as per TSK-583
548 //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "Recent Activity", "Last Visited", time));
549 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
550 RecentActivityExtracterModuleFactory.getModuleName(), time));
551 String domain = NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : ""); //NON-NLS
552 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
553 RecentActivityExtracterModuleFactory.getModuleName(), domain));
554 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
555 RecentActivityExtracterModuleFactory.getModuleName(),
556 NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
557
558 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
559 if (webDownloadArtifact != null) {
560 bbartifacts.add(webDownloadArtifact);
561
562 // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
563 try {
564 String normalizedFullPath = FilenameUtils.normalize(fullPath, true);
565 for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(normalizedFullPath), FilenameUtils.getPath(normalizedFullPath))) {
566 BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
567 associatedObjectArtifact.addAttribute(
568 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
569 RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
570
571 bbartifacts.add(associatedObjectArtifact);
572 break;
573 }
574 } catch (TskCoreException ex) {
575 logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", fullPath), ex); //NON-NLS
576 }
577 }
578 }
579
580 dbFile.delete();
581 }
582
583 if( !bbartifacts.isEmpty() ) {
584 postArtifacts(bbartifacts);
585 }
586 }
587
591 private void getLogins() {
592
593 FileManager fileManager = currentCase.getServices().getFileManager();
594 List<AbstractFile> loginDataFiles;
595 try {
596 loginDataFiles = fileManager.findFiles(dataSource, "Login Data", "Chrome"); //NON-NLS
597 } catch (TskCoreException ex) {
598 String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles");
599 logger.log(Level.SEVERE, msg, ex);
600 this.addErrorMessage(this.getName() + ": " + msg);
601 return;
602 }
603
604 if (loginDataFiles.isEmpty()) {
605 logger.log(Level.INFO, "Didn't find any Chrome Login Data files."); //NON-NLS
606 return;
607 }
608
609 dataFound = true;
610 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
611 int j = 0;
612 while (j < loginDataFiles.size()) {
613 AbstractFile loginDataFile = loginDataFiles.get(j++);
614 if (loginDataFile.getSize() == 0) {
615 continue;
616 }
617 String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS
618 try {
619 ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled);
620 } catch (ReadContentInputStreamException ex) {
621 logger.log(Level.WARNING, String.format("Error reading Chrome login artifacts file '%s' (id=%d).",
622 loginDataFile.getName(), loginDataFile.getId()), ex); //NON-NLS
623 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
624 this.getName(), loginDataFile.getName()));
625 continue;
626 } catch (IOException ex) {
627 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome login artifacts file '%s' (id=%d).",
628 temps, loginDataFile.getName(), loginDataFile.getId()), ex); //NON-NLS
629 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
630 this.getName(), loginDataFile.getName()));
631 continue;
632 }
633 File dbFile = new File(temps);
634 if (context.dataSourceIngestIsCancelled()) {
635 dbFile.delete();
636 break;
637 }
638 List<HashMap<String, Object>> tempList = this.dbConnect(temps, LOGIN_QUERY);
639 logger.log(Level.INFO, "{0}- Now getting login information from {1} with {2}artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
640 for (HashMap<String, Object> result : tempList) {
641 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
642
643 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
644 RecentActivityExtracterModuleFactory.getModuleName(),
645 ((result.get("origin_url").toString() != null) ? result.get("origin_url").toString() : ""))); //NON-NLS
646
647
648 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
649 RecentActivityExtracterModuleFactory.getModuleName(),
650 (Long.valueOf(result.get("date_created").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
651
652 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED,
653 RecentActivityExtracterModuleFactory.getModuleName(),
654 (NetworkUtils.extractDomain((result.get("origin_url").toString() != null) ? result.get("origin_url").toString() : "")))); //NON-NLS
655
656 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
657 RecentActivityExtracterModuleFactory.getModuleName(),
658 ((result.get("username_value").toString() != null) ? result.get("username_value").toString().replaceAll("'", "''") : ""))); //NON-NLS
659
660 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
661 RecentActivityExtracterModuleFactory.getModuleName(),
662 ((result.get("signon_realm").toString() != null) ? result.get("signon_realm").toString() : ""))); //NON-NLS
663
664 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes);
665 if (bbart != null) {
666 bbartifacts.add(bbart);
667 }
668 }
669
670 dbFile.delete();
671 }
672
673 if( !bbartifacts.isEmpty() ) {
674 postArtifacts(bbartifacts);
675 }
676 }
677
682 private void getAutofill() {
683
684 FileManager fileManager = currentCase.getServices().getFileManager();
685 List<AbstractFile> webDataFiles;
686 try {
687 webDataFiles = fileManager.findFiles(dataSource, "Web Data", "Chrome"); //NON-NLS
688 } catch (TskCoreException ex) {
689 String msg = NbBundle.getMessage(this.getClass(), "Chrome.getAutofills.errMsg.errGettingFiles");
690 logger.log(Level.SEVERE, msg, ex);
691 this.addErrorMessage(this.getName() + ": " + msg);
692 return;
693 }
694
695 if (webDataFiles.isEmpty()) {
696 logger.log(Level.INFO, "Didn't find any Chrome Web Data files."); //NON-NLS
697 return;
698 }
699
700 dataFound = true;
701 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
702 int j = 0;
703 while (j < webDataFiles.size()) {
704 AbstractFile webDataFile = webDataFiles.get(j++);
705 if (webDataFile.getSize() == 0) {
706 continue;
707 }
708 String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS
709 try {
710 ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled);
711 } catch (ReadContentInputStreamException ex) {
712 logger.log(Level.WARNING, String.format("Error reading Chrome Autofill artifacts file '%s' (id=%d).",
713 webDataFile.getName(), webDataFile.getId()), ex); //NON-NLS
714 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getAutofill.errMsg.errAnalyzingFiles",
715 this.getName(), webDataFile.getName()));
716 continue;
717 } catch (IOException ex) {
718 logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome Web data file '%s' (id=%d).",
719 tempFilePath, webDataFile.getName(), webDataFile.getId()), ex); //NON-NLS
720 this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
721 this.getName(), webDataFile.getName()));
722 continue;
723 }
724 File dbFile = new File(tempFilePath);
725 if (context.dataSourceIngestIsCancelled()) {
726 dbFile.delete();
727 break;
728 }
729
730 // The DB schema is little different in schema version 8x vs older versions
731 boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath);
732
733 // get form autofill artifacts
734 bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X));
735 try {
736 // get form address atifacts
737 getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X);
738 } catch (NoCurrentCaseException | TskCoreException | Blackboard.BlackboardException ex) {
739 logger.log(Level.SEVERE, String.format("Error adding artifacts to the case database "
740 + "for chrome file %s [objId=%d]", webDataFile.getName(), webDataFile.getId()), ex);
741 }
742
743 dbFile.delete();
744 }
745
746 if( !bbartifacts.isEmpty() ){
747 postArtifacts(bbartifacts);
748 }
749 }
750
760 private Collection<BlackboardArtifact> getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) {
761
762 Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
763
764 // The DB Schema is little different in version 8x vs older versions
765 String autoFillquery = (isSchemaV8X) ? AUTOFILL_QUERY_V8X
766 : AUTOFILL_QUERY;
767
768 List<HashMap<String, Object>> autofills = this.dbConnect(dbFilePath, autoFillquery);
769 logger.log(Level.INFO, "{0}- Now getting Autofill information from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, autofills.size()}); //NON-NLS
770 for (HashMap<String, Object> result : autofills) {
771 Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
772
773 // extract all common attributes
774 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
775 NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
776 ((result.get("name").toString() != null) ? result.get("name").toString() : ""))); //NON-NLS
777
778 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
779 RecentActivityExtracterModuleFactory.getModuleName(),
780 ((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
781
782 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
783 RecentActivityExtracterModuleFactory.getModuleName(),
784 (Integer.valueOf(result.get("count").toString())))); //NON-NLS
785
786 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
787 RecentActivityExtracterModuleFactory.getModuleName(),
788 Long.valueOf(result.get("date_created").toString()))); //NON-NLS
789
790 // get schema version specific attributes
791 if (isSchemaV8X) {
792 bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
793 RecentActivityExtracterModuleFactory.getModuleName(),
794 Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS
795 }
796
797 // Add an artifact
798 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes);
799 if (bbart != null) {
800 bbartifacts.add(bbart);
801 }
802 }
803
804 // return all extracted artifacts
805 return bbartifacts;
806 }
807
817 private void getFormAddressArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) throws NoCurrentCaseException,
818 TskCoreException, Blackboard.BlackboardException {
819
820 String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X
821 : WEBFORM_ADDRESS_QUERY;
822
823 // Helper to create web form address artifacts.
824 WebBrowserArtifactsHelper helper = new WebBrowserArtifactsHelper(
825 Case.getCurrentCaseThrows().getSleuthkitCase(),
826 NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
827 webDataFile
828 );
829
830 // Get Web form addresses
831 List<HashMap<String, Object>> addresses = this.dbConnect(dbFilePath, webformAddressQuery);
832 logger.log(Level.INFO, "{0}- Now getting Web form addresses from {1} with {2}artifacts identified.", new Object[]{moduleName, dbFilePath, addresses.size()}); //NON-NLS
833 for (HashMap<String, Object> result : addresses) {
834
835 // get name fields
836 String first_name = result.get("first_name").toString() != null ? result.get("first_name").toString() : "";
837 String middle_name = result.get("middle_name").toString() != null ? result.get("middle_name").toString() : "";
838 String last_name = result.get("last_name").toString() != null ? result.get("last_name").toString() : "";
839
840 // get email and phone
841 String email_Addr = result.get("email").toString() != null ? result.get("email").toString() : "";
842 String phone_number = result.get("number").toString() != null ? result.get("number").toString() : "";
843
844 // Get the address fields
845 String city = result.get("city").toString() != null ? result.get("city").toString() : "";
846 String state = result.get("state").toString() != null ? result.get("state").toString() : "";
847 String zipcode = result.get("zipcode").toString() != null ? result.get("zipcode").toString() : "";
848 String country_code = result.get("country_code").toString() != null ? result.get("country_code").toString() : "";
849
850 // schema version specific fields
851 String full_name = "";
852 String street_address = "";
853 long date_modified = 0;
854 int use_count = 0;
855 long use_date = 0;
856
857 if (isSchemaV8X) {
858 full_name = result.get("full_name").toString() != null ? result.get("full_name").toString() : "";
859 street_address = result.get("street_address").toString() != null ? result.get("street_address").toString() : "";
860 date_modified = result.get("date_modified").toString() != null ? Long.valueOf(result.get("date_modified").toString()) : 0;
861 use_count = result.get("use_count").toString() != null ? Integer.valueOf(result.get("use_count").toString()) : 0;
862 use_date = result.get("use_date").toString() != null ? Long.valueOf(result.get("use_date").toString()) : 0;
863 } else {
864 String address_line_1 = result.get("address_line_1").toString() != null ? result.get("street_address").toString() : "";
865 String address_line_2 = result.get("address_line_2").toString() != null ? result.get("address_line_2").toString() : "";
866 street_address = String.join(" ", address_line_1, address_line_2);
867 }
868
869 // Create atrributes from extracted fields
870 if (full_name == null || full_name.isEmpty()) {
871 full_name = String.join(" ", first_name, middle_name, last_name);
872 }
873
874 String locationAddress = String.join(", ", street_address, city, state, zipcode, country_code);
875
876 List<BlackboardAttribute> otherAttributes = new ArrayList<>();
877 if (date_modified > 0) {
878 otherAttributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
879 RecentActivityExtracterModuleFactory.getModuleName(),
880 date_modified)); //NON-NLS
881 }
882
883 helper.addWebFormAddress(
884 full_name, email_Addr, phone_number,
885 locationAddress, 0, use_date,
886 use_count, otherAttributes);
887 }
888 }
889
890 private boolean isChromePreVersion30(String temps) {
891 String query = "PRAGMA table_info(downloads)"; //NON-NLS
892 List<HashMap<String, Object>> columns = this.dbConnect(temps, query);
893 for (HashMap<String, Object> col : columns) {
894 if (col.get("name").equals("url")) { //NON-NLS
895 return true;
896 }
897 }
898
899 return false;
900 }
901 }