23 package org.sleuthkit.autopsy.recentactivity;
25 import com.google.common.collect.ImmutableMap;
26 import com.google.gson.JsonArray;
27 import com.google.gson.JsonElement;
28 import com.google.gson.JsonIOException;
29 import com.google.gson.JsonObject;
30 import com.google.gson.JsonParser;
31 import com.google.gson.JsonSyntaxException;
32 import org.openide.util.NbBundle;
34 import java.util.logging.Level;
36 import java.io.FileNotFoundException;
37 import java.io.FileReader;
38 import java.io.IOException;
39 import java.util.Collection;
40 import java.util.List;
42 import java.util.HashMap;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import org.apache.commons.io.FilenameUtils;
46 import org.openide.util.NbBundle.Messages;
57 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
58 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK;
60 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
62 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
66 import org.
sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
71 class Chromium
extends Extract {
73 private static final String HISTORY_QUERY =
"SELECT urls.url, urls.title, urls.visit_count, urls.typed_count, "
74 +
"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";
75 private static final String COOKIE_QUERY =
"SELECT name, value, host_key, expires_utc,last_access_utc, creation_utc FROM cookies";
76 private static final String DOWNLOAD_QUERY =
"SELECT full_path, url, start_time, received_bytes FROM downloads";
77 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";
78 private static final String LOGIN_QUERY =
"SELECT origin_url, username_value, date_created, signon_realm from logins";
79 private static final String AUTOFILL_QUERY =
"SELECT name, value, count, date_created "
80 +
" FROM autofill, autofill_dates "
81 +
" WHERE autofill.pair_id = autofill_dates.pair_id";
82 private static final String AUTOFILL_QUERY_V8X =
"SELECT name, value, count, date_created, date_last_used from autofill";
83 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 "
84 +
" FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones"
85 +
" WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid";
87 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"
88 +
" FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones"
89 +
" WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid";
90 private static final String HISTORY_FILE_NAME =
"History";
91 private static final String BOOKMARK_FILE_NAME =
"Bookmarks";
92 private static final String COOKIE_FILE_NAME =
"Cookies";
93 private static final String LOGIN_DATA_FILE_NAME =
"Login Data";
94 private static final String WEB_DATA_FILE_NAME =
"Web Data";
95 private static final String UC_BROWSER_NAME =
"UC Browser";
96 private static final String ENCRYPTED_FIELD_MESSAGE =
"The data was encrypted.";
98 private Boolean databaseEncrypted =
false;
99 private Boolean fieldEncrypted =
false;
101 private final Logger logger = Logger.getLogger(this.getClass().getName());
102 private Content dataSource;
103 private IngestJobContext context;
105 private static final Map<String, String> BROWSERS_MAP = ImmutableMap.<String, String>builder()
106 .put(
"Microsoft Edge",
"Microsoft/Edge/User Data/Default")
107 .put(
"Yandex",
"YandexBrowser/User Data/Default")
108 .put(
"Opera",
"Opera Software/Opera Stable")
109 .put(
"SalamWeb",
"SalamWeb/User Data/Default")
110 .put(
"UC Browser",
"UCBrowser/User Data%/Default")
111 .put(
"Brave",
"BraveSoftware/Brave-Browser/User Data/Default")
112 .put(
"Google Chrome",
"Chrome/User Data/Default")
115 @Messages({
"# {0} - browserName",
116 "Progress_Message_Chrome_History=Chrome History Browser {0}",
117 "# {0} - browserName",
118 "Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}",
119 "# {0} - browserName",
120 "Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}",
121 "# {0} - browserName",
122 "Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}",
123 "Progress_Message_Chrome_FormHistory=Chrome Form History",
124 "# {0} - browserName",
125 "Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}",
126 "# {0} - browserName",
127 "Progress_Message_Chrome_Logins=Chrome Logins Browser {0}",
128 "Progress_Message_Chrome_Cache=Chrome Cache",})
131 super(NbBundle.getMessage(Chromium.class,
"Chrome.moduleName"));
135 public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
136 this.dataSource = dataSource;
137 this.context = context;
139 long ingestJobId = context.getJobId();
141 for (Map.Entry<String, String> browser : BROWSERS_MAP.entrySet()) {
142 String browserName = browser.getKey();
143 String browserLocation = browser.getValue();
144 progressBar.progress(NbBundle.getMessage(
this.getClass(),
"Progress_Message_Chrome_History", browserName));
145 this.getHistory(browser.getKey(), browser.getValue(), ingestJobId);
146 if (context.dataSourceIngestIsCancelled()) {
150 progressBar.progress(NbBundle.getMessage(
this.getClass(),
"Progress_Message_Chrome_Bookmarks", browserName));
151 this.getBookmark(browser.getKey(), browser.getValue(), ingestJobId);
152 if (context.dataSourceIngestIsCancelled()) {
156 progressBar.progress(NbBundle.getMessage(
this.getClass(),
"Progress_Message_Chrome_Cookies", browserName));
157 this.getCookie(browser.getKey(), browser.getValue(), ingestJobId);
158 if (context.dataSourceIngestIsCancelled()) {
162 progressBar.progress(NbBundle.getMessage(
this.getClass(),
"Progress_Message_Chrome_Logins", browserName));
163 this.getLogins(browser.getKey(), browser.getValue(), ingestJobId);
164 if (context.dataSourceIngestIsCancelled()) {
168 progressBar.progress(NbBundle.getMessage(
this.getClass(),
"Progress_Message_Chrome_AutoFill", browserName));
169 this.getAutofill(browser.getKey(), browser.getValue(), ingestJobId);
170 if (context.dataSourceIngestIsCancelled()) {
174 progressBar.progress(NbBundle.getMessage(
this.getClass(),
"Progress_Message_Chrome_Downloads", browserName));
175 this.getDownload(browser.getKey(), browser.getValue(), ingestJobId);
176 if (context.dataSourceIngestIsCancelled()) {
181 progressBar.progress(Bundle.Progress_Message_Chrome_Cache());
182 ChromeCacheExtractor chromeCacheExtractor =
new ChromeCacheExtractor(dataSource, context, progressBar);
183 chromeCacheExtractor.processCaches();
193 private void getHistory(String browser, String browserLocation,
long ingestJobId) {
194 FileManager fileManager = currentCase.getServices().getFileManager();
195 List<AbstractFile> historyFiles;
196 String historyFileName = HISTORY_FILE_NAME;
197 if (browser.equals(UC_BROWSER_NAME)) {
198 historyFileName = HISTORY_FILE_NAME +
"%";
201 historyFiles = fileManager.findFiles(dataSource, historyFileName, browserLocation);
202 }
catch (TskCoreException ex) {
203 String msg = NbBundle.getMessage(this.getClass(),
"Chrome.getHistory.errMsg.errGettingFiles");
204 logger.log(Level.SEVERE, msg, ex);
205 this.addErrorMessage(this.getName() +
": " + msg);
210 List<AbstractFile> allocatedHistoryFiles =
new ArrayList<>();
211 for (AbstractFile historyFile : historyFiles) {
212 if (historyFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
213 allocatedHistoryFiles.add(historyFile);
218 if (allocatedHistoryFiles.isEmpty()) {
219 String msg = NbBundle.getMessage(this.getClass(),
"Chrome.getHistory.errMsg.couldntFindAnyFiles");
220 logger.log(Level.INFO, msg);
225 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
227 while (j < allocatedHistoryFiles.size()) {
228 String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + allocatedHistoryFiles.get(j).getName() + j +
".db";
229 final AbstractFile historyFile = allocatedHistoryFiles.get(j++);
230 if ((historyFile.getSize() == 0) || (historyFile.getName().toLowerCase().contains(
"-slack"))
231 || (historyFile.getName().toLowerCase().contains(
"cache")) || (historyFile.getName().toLowerCase().contains(
"media"))
232 || (historyFile.getName().toLowerCase().contains(
"index"))) {
236 ContentUtils.writeToFile(historyFile,
new File(temps), context::dataSourceIngestIsCancelled);
237 }
catch (ReadContentInputStreamException ex) {
238 logger.log(Level.WARNING, String.format(
"Error reading Chrome web history artifacts file '%s' (id=%d).",
239 historyFile.getName(), historyFile.getId()), ex);
240 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getHistory.errMsg.errAnalyzingFile",
241 this.getName(), historyFile.getName()));
243 }
catch (IOException ex) {
244 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Chrome web history artifacts file '%s' (id=%d).",
245 temps, historyFile.getName(), historyFile.getId()), ex);
246 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getHistory.errMsg.errAnalyzingFile",
247 this.getName(), historyFile.getName()));
250 File dbFile =
new File(temps);
251 if (context.dataSourceIngestIsCancelled()) {
255 List<HashMap<String, Object>> tempList;
256 tempList = this.dbConnect(temps, HISTORY_QUERY);
257 logger.log(Level.INFO,
"{0}- Now getting history from {1} with {2} artifacts identified.",
new Object[]{getName(), temps, tempList.size()});
258 for (HashMap<String, Object> result : tempList) {
259 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
260 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
261 RecentActivityExtracterModuleFactory.getModuleName(),
262 ((result.get(
"url").toString() != null) ? result.get(
"url").toString() :
"")));
263 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
264 RecentActivityExtracterModuleFactory.getModuleName(),
265 (Long.valueOf(result.get(
"last_visit_time").toString()) / 1000000) - Long.valueOf(
"11644473600")));
266 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
267 RecentActivityExtracterModuleFactory.getModuleName(),
268 ((result.get(
"from_visit").toString() != null) ? result.get(
"from_visit").toString() :
"")));
269 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
270 RecentActivityExtracterModuleFactory.getModuleName(),
271 ((result.get(
"title").toString() != null) ? result.get(
"title").toString() :
"")));
272 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
273 RecentActivityExtracterModuleFactory.getModuleName(), browser));
274 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
275 RecentActivityExtracterModuleFactory.getModuleName(),
276 (NetworkUtils.extractDomain((result.get(
"url").toString() != null) ? result.get(
"url").toString() :
""))));
279 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes));
280 }
catch (TskCoreException ex) {
281 logger.log(Level.SEVERE, String.format(
"Failed to create history artifact for file (%d)", historyFile.getId()), ex);
287 if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
288 postArtifacts(bbartifacts);
298 private void getBookmark(String browser, String browserLocation,
long ingestJobId) {
299 FileManager fileManager = currentCase.getServices().getFileManager();
300 List<AbstractFile> bookmarkFiles;
301 String bookmarkFileName = BOOKMARK_FILE_NAME;
302 if (browser.equals(UC_BROWSER_NAME)) {
303 bookmarkFileName = BOOKMARK_FILE_NAME +
"%";
306 bookmarkFiles = fileManager.findFiles(dataSource, bookmarkFileName, browserLocation);
307 }
catch (TskCoreException ex) {
308 String msg = NbBundle.getMessage(this.getClass(),
"Chrome.getBookmark.errMsg.errGettingFiles");
309 logger.log(Level.SEVERE, msg, ex);
310 this.addErrorMessage(this.getName() +
": " + msg);
314 if (bookmarkFiles.isEmpty()) {
315 logger.log(Level.INFO,
"Didn't find any Chrome bookmark files.");
320 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
323 while (j < bookmarkFiles.size()) {
324 AbstractFile bookmarkFile = bookmarkFiles.get(j++);
325 if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains(
"-slack"))
326 || (bookmarkFile.getName().toLowerCase().contains(
"extras")) || (bookmarkFile.getName().toLowerCase().contains(
"log"))
327 || (bookmarkFile.getName().toLowerCase().contains(
"backup")) || (bookmarkFile.getName().toLowerCase().contains(
"visualized"))
328 || (bookmarkFile.getName().toLowerCase().contains(
"bak")) || (bookmarkFile.getParentPath().toLowerCase().contains(
"backup"))) {
331 String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + bookmarkFile.getName() + j +
".db";
333 ContentUtils.writeToFile(bookmarkFile,
new File(temps), context::dataSourceIngestIsCancelled);
334 }
catch (ReadContentInputStreamException ex) {
335 logger.log(Level.WARNING, String.format(
"Error reading Chrome bookmark artifacts file '%s' (id=%d).",
336 bookmarkFile.getName(), bookmarkFile.getId()), ex);
337 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getBookmark.errMsg.errAnalyzingFile",
338 this.getName(), bookmarkFile.getName()));
340 }
catch (IOException ex) {
341 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Chrome bookmark artifacts file '%s' (id=%d).",
342 temps, bookmarkFile.getName(), bookmarkFile.getId()), ex);
343 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getBookmark.errMsg.errAnalyzingFile",
344 this.getName(), bookmarkFile.getName()));
348 logger.log(Level.INFO,
"{0}- Now getting Bookmarks from {1}",
new Object[]{getName(), temps});
349 File dbFile =
new File(temps);
350 if (context.dataSourceIngestIsCancelled()) {
355 FileReader tempReader;
357 tempReader =
new FileReader(temps);
358 }
catch (FileNotFoundException ex) {
359 logger.log(Level.WARNING,
"Error while trying to read into the Bookmarks for Chrome.", ex);
363 final JsonParser parser =
new JsonParser();
364 JsonElement jsonElement;
365 JsonObject jElement, jRoot, jBookmark;
366 JsonArray jBookmarkArray;
369 jsonElement = parser.parse(tempReader);
370 jElement = jsonElement.getAsJsonObject();
371 jRoot = jElement.get(
"roots").getAsJsonObject();
372 jBookmark = jRoot.get(
"bookmark_bar").getAsJsonObject();
373 jBookmarkArray = jBookmark.getAsJsonArray(
"children");
374 }
catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
375 logger.log(Level.WARNING,
"Error parsing Json from Chrome Bookmark.", ex);
376 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getBookmark.errMsg.errAnalyzingFile3",
377 this.getName(), bookmarkFile.getName()));
381 for (JsonElement result : jBookmarkArray) {
382 JsonObject address = result.getAsJsonObject();
383 if (address == null) {
386 JsonElement urlEl = address.get(
"url");
389 url = urlEl.getAsString();
394 JsonElement nameEl = address.get(
"name");
395 if (nameEl != null) {
396 name = nameEl.getAsString();
401 JsonElement dateEl = address.get(
"date_added");
402 if (dateEl != null) {
403 date = dateEl.getAsLong();
405 date = Long.valueOf(0);
407 String domain = NetworkUtils.extractDomain(url);
408 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
410 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
411 RecentActivityExtracterModuleFactory.getModuleName(), url));
412 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
413 RecentActivityExtracterModuleFactory.getModuleName(), name));
414 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
415 RecentActivityExtracterModuleFactory.getModuleName(), (date / 1000000) - Long.valueOf(
"11644473600")));
416 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
417 RecentActivityExtracterModuleFactory.getModuleName(), browser));
418 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
419 RecentActivityExtracterModuleFactory.getModuleName(), domain));
422 bbartifacts.add(createArtifactWithAttributes(TSK_WEB_BOOKMARK, bookmarkFile, bbattributes));
423 }
catch (TskCoreException ex) {
424 logger.log(Level.SEVERE, String.format(
"Failed to create bookmark artifact for file (%d)", bookmarkFile.getId()), ex);
429 if(!context.dataSourceIngestIsCancelled()) {
430 postArtifacts(bbartifacts);
443 private void getCookie(String browser, String browserLocation,
long ingestJobId) {
445 FileManager fileManager = currentCase.getServices().getFileManager();
446 List<AbstractFile> cookiesFiles;
447 String cookieFileName = COOKIE_FILE_NAME;
448 if (browser.equals(UC_BROWSER_NAME)) {
451 cookieFileName =
"%" + COOKIE_FILE_NAME +
"%";
454 cookiesFiles = fileManager.findFiles(dataSource, cookieFileName, browserLocation);
455 }
catch (TskCoreException ex) {
456 String msg = NbBundle.getMessage(this.getClass(),
"Chrome.getCookie.errMsg.errGettingFiles");
457 logger.log(Level.SEVERE, msg, ex);
458 this.addErrorMessage(this.getName() +
": " + msg);
462 if (cookiesFiles.isEmpty()) {
463 logger.log(Level.INFO,
"Didn't find any Chrome cookies files.");
468 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
470 while (j < cookiesFiles.size()) {
471 AbstractFile cookiesFile = cookiesFiles.get(j++);
472 if ((cookiesFile.getSize() == 0) || (cookiesFile.getName().toLowerCase().contains(
"-slack"))) {
475 String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + cookiesFile.getName() + j +
".db";
477 ContentUtils.writeToFile(cookiesFile,
new File(temps), context::dataSourceIngestIsCancelled);
478 }
catch (ReadContentInputStreamException ex) {
479 logger.log(Level.WARNING, String.format(
"Error reading Chrome cookie artifacts file '%s' (id=%d).",
480 cookiesFile.getName(), cookiesFile.getId()), ex);
481 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getCookie.errMsg.errAnalyzeFile",
482 this.getName(), cookiesFile.getName()));
484 }
catch (IOException ex) {
485 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Chrome cookie artifacts file '%s' (id=%d).",
486 temps, cookiesFile.getName(), cookiesFile.getId()), ex);
487 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getCookie.errMsg.errAnalyzeFile",
488 this.getName(), cookiesFile.getName()));
491 File dbFile =
new File(temps);
492 if (context.dataSourceIngestIsCancelled()) {
497 List<HashMap<String, Object>> tempList = this.dbConnect(temps, COOKIE_QUERY);
498 logger.log(Level.INFO,
"{0}- Now getting cookies from {1} with {2} artifacts identified.",
new Object[]{getName(), temps, tempList.size()});
499 for (HashMap<String, Object> result : tempList) {
500 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
501 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
502 RecentActivityExtracterModuleFactory.getModuleName(),
503 ((result.get(
"host_key").toString() != null) ? result.get(
"host_key").toString() :
"")));
504 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
505 RecentActivityExtracterModuleFactory.getModuleName(),
506 (Long.valueOf(result.get(
"last_access_utc").toString()) / 1000000) - Long.valueOf(
"11644473600")));
508 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
509 RecentActivityExtracterModuleFactory.getModuleName(),
510 ((result.get(
"name").toString() != null) ? result.get(
"name").toString() :
"")));
511 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
512 RecentActivityExtracterModuleFactory.getModuleName(),
513 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
514 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
515 RecentActivityExtracterModuleFactory.getModuleName(), browser));
516 String domain = result.get(
"host_key").toString();
517 domain = domain.replaceFirst(
"^\\.+(?!$)",
"");
518 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
519 RecentActivityExtracterModuleFactory.getModuleName(), domain));
522 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes));
523 }
catch (TskCoreException ex) {
524 logger.log(Level.SEVERE, String.format(
"Failed to create cookie artifact for file (%d)", cookiesFile.getId()), ex);
531 if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
532 postArtifacts(bbartifacts);
542 private void getDownload(String browser, String browserLocation,
long ingestJobId) {
543 FileManager fileManager = currentCase.getServices().getFileManager();
544 List<AbstractFile> downloadFiles;
545 String historyFileName = HISTORY_FILE_NAME;
546 if (browser.equals(UC_BROWSER_NAME)) {
547 historyFileName = HISTORY_FILE_NAME +
"%";
550 downloadFiles = fileManager.findFiles(dataSource, historyFileName, browserLocation);
551 }
catch (TskCoreException ex) {
552 String msg = NbBundle.getMessage(this.getClass(),
"Chrome.getDownload.errMsg.errGettingFiles");
553 logger.log(Level.SEVERE, msg, ex);
554 this.addErrorMessage(this.getName() +
": " + msg);
558 if (downloadFiles.isEmpty()) {
559 logger.log(Level.INFO,
"Didn't find any Chrome download files.");
564 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
566 while (j < downloadFiles.size()) {
567 AbstractFile downloadFile = downloadFiles.get(j++);
568 if ((downloadFile.getSize() == 0) || (downloadFile.getName().toLowerCase().contains(
"-slack"))
569 || (downloadFile.getName().toLowerCase().contains(
"cache")) || (downloadFile.getName().toLowerCase().contains(
"index"))) {
573 String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + downloadFile.getName() + j +
".db";
575 ContentUtils.writeToFile(downloadFile,
new File(temps), context::dataSourceIngestIsCancelled);
576 }
catch (ReadContentInputStreamException ex) {
577 logger.log(Level.WARNING, String.format(
"Error reading Chrome download artifacts file '%s' (id=%d).",
578 downloadFile.getName(), downloadFile.getId()), ex);
579 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getDownload.errMsg.errAnalyzeFiles1",
580 this.getName(), downloadFile.getName()));
582 }
catch (IOException ex) {
583 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Chrome download artifacts file '%s' (id=%d).",
584 temps, downloadFile.getName(), downloadFile.getId()), ex);
585 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getDownload.errMsg.errAnalyzeFiles1",
586 this.getName(), downloadFile.getName()));
589 File dbFile =
new File(temps);
590 if (context.dataSourceIngestIsCancelled()) {
595 List<HashMap<String, Object>> tempList;
597 if (isChromePreVersion30(temps)) {
598 tempList = this.dbConnect(temps, DOWNLOAD_QUERY);
600 tempList = this.dbConnect(temps, DOWNLOAD_QUERY_V30);
603 logger.log(Level.INFO,
"{0}- Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{getName(), temps, tempList.size()});
604 for (HashMap<String, Object> result : tempList) {
605 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
606 String fullPath = result.get(
"full_path").toString();
607 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
608 RecentActivityExtracterModuleFactory.getModuleName(), fullPath));
609 long pathID = Util.findID(dataSource, fullPath);
611 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
612 NbBundle.getMessage(
this.getClass(),
613 "Chrome.parentModuleName"), pathID));
615 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
616 RecentActivityExtracterModuleFactory.getModuleName(),
617 ((result.get(
"url").toString() != null) ? result.get(
"url").toString() :
"")));
619 Long time = (Long.valueOf(result.get(
"start_time").toString()) / 1000000) - Long.valueOf(
"11644473600");
623 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
624 RecentActivityExtracterModuleFactory.getModuleName(), time));
625 String domain = NetworkUtils.extractDomain((result.get(
"url").toString() != null) ? result.get(
"url").toString() :
"");
626 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
627 RecentActivityExtracterModuleFactory.getModuleName(), domain));
628 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
629 RecentActivityExtracterModuleFactory.getModuleName(), browser));
633 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
634 bbartifacts.add(webDownloadArtifact);
635 String normalizedFullPath = FilenameUtils.normalize(fullPath,
true);
636 for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource, FilenameUtils.getName(normalizedFullPath), FilenameUtils.getPath(normalizedFullPath))) {
637 bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
640 }
catch (TskCoreException ex) {
641 logger.log(Level.SEVERE, String.format(
"Error creating associated object artifact for file '%s'", fullPath), ex);
648 if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
649 postArtifacts(bbartifacts);
659 private void getLogins(String browser, String browserLocation,
long ingestJobId) {
661 FileManager fileManager = currentCase.getServices().getFileManager();
662 List<AbstractFile> loginDataFiles;
663 String loginDataFileName = LOGIN_DATA_FILE_NAME;
664 if (browser.equals(UC_BROWSER_NAME)) {
665 loginDataFileName = LOGIN_DATA_FILE_NAME +
"%";
669 loginDataFiles = fileManager.findFiles(dataSource, loginDataFileName, browserLocation);
670 }
catch (TskCoreException ex) {
671 String msg = NbBundle.getMessage(this.getClass(),
"Chrome.getLogin.errMsg.errGettingFiles");
672 logger.log(Level.SEVERE, msg, ex);
673 this.addErrorMessage(this.getName() +
": " + msg);
677 if (loginDataFiles.isEmpty()) {
678 logger.log(Level.INFO,
"Didn't find any Chrome Login Data files.");
683 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
685 while (j < loginDataFiles.size()) {
686 AbstractFile loginDataFile = loginDataFiles.get(j++);
687 if ((loginDataFile.getSize() == 0) || (loginDataFile.getName().toLowerCase().contains(
"-slack"))) {
690 String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + loginDataFile.getName() + j +
".db";
692 ContentUtils.writeToFile(loginDataFile,
new File(temps), context::dataSourceIngestIsCancelled);
693 }
catch (ReadContentInputStreamException ex) {
694 logger.log(Level.WARNING, String.format(
"Error reading Chrome login artifacts file '%s' (id=%d).",
695 loginDataFile.getName(), loginDataFile.getId()), ex);
696 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getLogin.errMsg.errAnalyzingFiles",
697 this.getName(), loginDataFile.getName()));
699 }
catch (IOException ex) {
700 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Chrome login artifacts file '%s' (id=%d).",
701 temps, loginDataFile.getName(), loginDataFile.getId()), ex);
702 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getLogin.errMsg.errAnalyzingFiles",
703 this.getName(), loginDataFile.getName()));
706 File dbFile =
new File(temps);
707 if (context.dataSourceIngestIsCancelled()) {
711 List<HashMap<String, Object>> tempList = this.dbConnect(temps, LOGIN_QUERY);
712 logger.log(Level.INFO,
"{0}- Now getting login information from {1} with {2} artifacts identified.",
new Object[]{getName(), temps, tempList.size()});
713 for (HashMap<String, Object> result : tempList) {
714 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
716 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
717 RecentActivityExtracterModuleFactory.getModuleName(),
718 ((result.get(
"origin_url").toString() != null) ? result.get(
"origin_url").toString() :
"")));
720 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
721 RecentActivityExtracterModuleFactory.getModuleName(),
722 (Long.valueOf(result.get(
"date_created").toString()) / 1000000) - Long.valueOf(
"11644473600")));
724 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED,
725 RecentActivityExtracterModuleFactory.getModuleName(),
726 (NetworkUtils.extractDomain((result.get(
"origin_url").toString() != null) ? result.get(
"origin_url").toString() :
""))));
728 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
729 RecentActivityExtracterModuleFactory.getModuleName(),
730 ((result.get(
"username_value").toString() != null) ? result.get(
"username_value").toString().replaceAll(
"'",
"''") :
"")));
732 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REALM,
733 RecentActivityExtracterModuleFactory.getModuleName(),
734 ((result.get(
"signon_realm") != null && result.get(
"signon_realm").toString() != null) ? result.get(
"signon_realm").toString() :
"")));
736 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
737 RecentActivityExtracterModuleFactory.getModuleName(),
738 result.containsKey(
"signon_realm") ? NetworkUtils.extractDomain(result.get(
"signon_realm").toString()) :
""));
740 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
741 RecentActivityExtracterModuleFactory.getModuleName(), browser));
744 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes));
745 }
catch (TskCoreException ex) {
746 logger.log(Level.SEVERE, String.format(
"Failed to create service account artifact for file (%d)", loginDataFile.getId()), ex);
753 if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
754 postArtifacts(bbartifacts);
765 private void getAutofill(String browser, String browserLocation,
long ingestJobId) {
767 FileManager fileManager = currentCase.getServices().getFileManager();
768 List<AbstractFile> webDataFiles;
769 String webDataFileName = WEB_DATA_FILE_NAME;
770 if (browser.equals(UC_BROWSER_NAME)) {
771 webDataFileName = WEB_DATA_FILE_NAME +
"%";
775 webDataFiles = fileManager.findFiles(dataSource, webDataFileName, browserLocation);
776 }
catch (TskCoreException ex) {
777 String msg = NbBundle.getMessage(this.getClass(),
"Chrome.getAutofills.errMsg.errGettingFiles");
778 logger.log(Level.SEVERE, msg, ex);
779 this.addErrorMessage(this.getName() +
": " + msg);
783 if (webDataFiles.isEmpty()) {
784 logger.log(Level.INFO,
"Didn't find any Chrome Web Data files.");
789 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
791 while (j < webDataFiles.size()) {
792 databaseEncrypted =
false;
793 AbstractFile webDataFile = webDataFiles.get(j++);
794 if ((webDataFile.getSize() == 0) || (webDataFile.getName().toLowerCase().contains(
"-slack"))) {
797 String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + webDataFile.getName() + j +
".db";
799 ContentUtils.writeToFile(webDataFile,
new File(tempFilePath), context::dataSourceIngestIsCancelled);
800 }
catch (ReadContentInputStreamException ex) {
801 logger.log(Level.WARNING, String.format(
"Error reading Chrome Autofill artifacts file '%s' (id=%d).",
802 webDataFile.getName(), webDataFile.getId()), ex);
803 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getAutofill.errMsg.errAnalyzingFiles",
804 this.getName(), webDataFile.getName()));
806 }
catch (IOException ex) {
807 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Chrome Web data file '%s' (id=%d).",
808 tempFilePath, webDataFile.getName(), webDataFile.getId()), ex);
809 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Chrome.getLogin.errMsg.errAnalyzingFiles",
810 this.getName(), webDataFile.getName()));
813 File dbFile =
new File(tempFilePath);
814 if (context.dataSourceIngestIsCancelled()) {
820 boolean isSchemaV8X = Util.checkColumn(
"date_created",
"autofill", tempFilePath);
823 bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, browser));
826 getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X);
827 if (databaseEncrypted) {
828 String comment = String.format(
"%s Autofill Database Encryption Detected", browser);
829 Collection<BlackboardAttribute> bbattributes = Arrays.asList(
830 new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT,
831 RecentActivityExtracterModuleFactory.getModuleName(), comment));
834 webDataFile.newAnalysisResult(
835 BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, Score.SCORE_NOTABLE,
836 null, null, comment, bbattributes).getAnalysisResult());
838 }
catch (NoCurrentCaseException | TskCoreException | Blackboard.BlackboardException ex) {
839 logger.log(Level.SEVERE, String.format(
"Error adding artifacts to the case database "
840 +
"for chrome file %s [objId=%d]", webDataFile.getName(), webDataFile.getId()), ex);
846 if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) {
847 postArtifacts(bbartifacts);
861 private Collection<BlackboardArtifact> getFormAutofillArtifacts(AbstractFile webDataFile, String dbFilePath,
boolean isSchemaV8X, String browser) {
863 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
866 String autoFillquery = (isSchemaV8X) ? AUTOFILL_QUERY_V8X
869 List<HashMap<String, Object>> autofills = this.dbConnect(dbFilePath, autoFillquery);
870 logger.log(Level.INFO,
"{0}- Now getting Autofill information from {1} with {2} artifacts identified.",
new Object[]{getName(), dbFilePath, autofills.size()});
871 for (HashMap<String, Object> result : autofills) {
872 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
875 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
876 NbBundle.getMessage(
this.getClass(),
"Chrome.parentModuleName"),
877 ((result.get(
"name").toString() != null) ? result.get(
"name").toString() :
"")));
879 fieldEncrypted =
false;
880 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
881 RecentActivityExtracterModuleFactory.getModuleName(),
882 processFields(result.get(
"value"))));
884 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
885 RecentActivityExtracterModuleFactory.getModuleName(),
886 (Integer.valueOf(result.get(
"count").toString()))));
888 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
889 RecentActivityExtracterModuleFactory.getModuleName(),
890 Long.valueOf(result.get(
"date_created").toString())));
894 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
895 RecentActivityExtracterModuleFactory.getModuleName(),
896 Long.valueOf(result.get(
"date_last_used").toString())));
899 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
900 RecentActivityExtracterModuleFactory.getModuleName(), browser));
901 if (fieldEncrypted) {
902 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT,
903 RecentActivityExtracterModuleFactory.getModuleName(), ENCRYPTED_FIELD_MESSAGE));
908 bbartifacts.add(createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes));
909 }
catch (TskCoreException ex) {
910 logger.log(Level.SEVERE, String.format(
"Failed to create web form autopfill artifact for file (%d)", webDataFile.getId()), ex);
929 private void getFormAddressArtifacts(AbstractFile webDataFile, String dbFilePath,
boolean isSchemaV8X)
throws NoCurrentCaseException,
930 TskCoreException, Blackboard.BlackboardException {
932 String webformAddressQuery = (isSchemaV8X) ? WEBFORM_ADDRESS_QUERY_V8X
933 : WEBFORM_ADDRESS_QUERY;
936 WebBrowserArtifactsHelper helper =
new WebBrowserArtifactsHelper(
937 Case.getCurrentCaseThrows().getSleuthkitCase(),
938 NbBundle.getMessage(this.getClass(),
"Chrome.parentModuleName"),
943 List<HashMap<String, Object>> addresses = this.dbConnect(dbFilePath, webformAddressQuery);
944 logger.log(Level.INFO,
"{0}- Now getting Web form addresses from {1} with {2} artifacts identified.",
new Object[]{getName(), dbFilePath, addresses.size()});
945 for (HashMap<String, Object> result : addresses) {
947 fieldEncrypted =
false;
949 String first_name = processFields(result.get(
"first_name"));
950 String middle_name = processFields(result.get(
"middle_name"));
951 String last_name = processFields(result.get(
"last_name"));
954 String email_Addr = processFields(result.get(
"email"));
955 String phone_number = processFields(result.get(
"number"));
958 String city = processFields(result.get(
"city"));
959 String state = processFields(result.get(
"state"));
960 String zipcode = processFields(result.get(
"zipcode"));
961 String country_code = processFields(result.get(
"country_code"));
964 String full_name =
"";
965 String street_address =
"";
966 long date_modified = 0;
972 full_name = processFields(result.get(
"full_name"));
973 street_address = processFields(result.get(
"street_address"));
974 date_modified = result.get(
"date_modified").toString() != null ? Long.valueOf(result.get(
"date_modified").toString()) : 0;
975 use_count = result.get(
"use_count").toString() != null ? Integer.valueOf(result.get(
"use_count").toString()) : 0;
976 use_date = result.get(
"use_date").toString() != null ? Long.valueOf(result.get(
"use_date").toString()) : 0;
978 String address_line_1 = processFields(result.get(
"address_line_1"));
979 String address_line_2 = processFields(result.get(
"address_line_2"));
980 street_address = String.join(
" ", address_line_1, address_line_2);
984 if (full_name == null || full_name.isEmpty()) {
985 full_name = String.join(
" ", first_name, middle_name, last_name);
988 String locationAddress = String.join(
", ", street_address, city, state, zipcode, country_code);
990 List<BlackboardAttribute> otherAttributes =
new ArrayList<>();
991 if (date_modified > 0) {
992 otherAttributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED,
993 RecentActivityExtracterModuleFactory.getModuleName(),
995 if (fieldEncrypted) {
996 otherAttributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT,
997 RecentActivityExtracterModuleFactory.getModuleName(), ENCRYPTED_FIELD_MESSAGE));
1002 helper.addWebFormAddress(
1003 full_name, email_Addr, phone_number,
1004 locationAddress, 0, use_date,
1005 use_count, otherAttributes);
1015 private String processFields(Object dataValue) {
1017 if (dataValue instanceof byte[]) {
1018 fieldEncrypted =
true;
1019 databaseEncrypted =
true;
1022 return dataValue.toString() != null ? dataValue.toString() :
"";
1026 private boolean isChromePreVersion30(String temps) {
1027 String query =
"PRAGMA table_info(downloads)";
1028 List<HashMap<String, Object>> columns = this.dbConnect(temps, query);
1029 for (HashMap<String, Object> col : columns) {
1030 if (col.get(
"name").equals(
"url")) {