/** * Create a tag for an artifact with TSK_TAG_NAME as tagName. * * @param artifact to create tag for * @param tagName TSK_TAG_NAME * @param comment the tag comment or null if not present */ public static void createTag(BlackboardArtifact artifact, String tagName, String comment) { try { Case currentCase = Case.getCurrentCase(); SleuthkitCase skCase = currentCase.getSleuthkitCase(); AbstractFile file = skCase.getAbstractFileById(artifact.getObjectID()); final BlackboardArtifact bookArt = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT); List<BlackboardAttribute> attrs = new ArrayList<BlackboardAttribute>(); BlackboardAttribute attr1 = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID(), "", tagName); if (comment != null && !comment.isEmpty()) { BlackboardAttribute attr2 = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID(), "", comment); attrs.add(attr2); } BlackboardAttribute attr3 = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID(), "", artifact.getArtifactID()); attrs.add(attr1); attrs.add(attr3); bookArt.addAttributes(attrs); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Failed to create tag for artifact " + artifact.getArtifactID()); } }
@Override public boolean isPreferred(Node node, boolean isSupported) { BlackboardArtifact art = node.getLookup().lookup(BlackboardArtifact.class); return isSupported && (art == null || art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()); }
/** * Generic method for adding a blackboard artifact to the blackboard * * @param type is a blackboard.artifact_type enum to determine which type the artifact should be * @param content is the FsContent object that needs to have the artifact added for it * @param bbattributes is the collection of blackboard attributes that need to be added to the * artifact after the artifact has been created */ public void addArtifact( BlackboardArtifact.ARTIFACT_TYPE type, FsContent content, Collection<BlackboardAttribute> bbattributes) { try { BlackboardArtifact bbart = content.newArtifact(type); bbart.addAttributes(bbattributes); } catch (TskException ex) { logger.log(Level.SEVERE, "Error while trying to add an artifact: " + ex); } }
/** * Generic method for adding a blackboard artifact to the blackboard * * @param type is a blackboard.artifact_type enum to determine which type the artifact should be * @param content is the FsContent object that needs to have the artifact added for it * @param bbattributes is the collection of blackboard attributes that need to be added to the * artifact after the artifact has been created */ public void addArtifact( BlackboardArtifact.ARTIFACT_TYPE type, FsContent content, Collection<BlackboardAttribute> bbattributes) { try { BlackboardArtifact bbart = content.newArtifact(type); bbart.addAttributes(bbattributes); } catch (TskException ex) { logger.log(Level.WARNING, "Error while trying to add an artifact: " + ex); this.addErrorMessage( this.getName() + ": Error while trying to add artifact to case for file:" + content.getName()); } }
private void initData() { try { // Get all file and artifact tags // init data tags = new EnumMap<BlackboardArtifact.ARTIFACT_TYPE, Map<String, List<BlackboardArtifact>>>( BlackboardArtifact.ARTIFACT_TYPE.class); tags.put( BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE, new HashMap<String, List<BlackboardArtifact>>()); tags.put( BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT, new HashMap<String, List<BlackboardArtifact>>()); // populate for (BlackboardArtifact.ARTIFACT_TYPE artType : tags.keySet()) { final Map<String, List<BlackboardArtifact>> artTags = tags.get(artType); for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(artType)) { for (BlackboardAttribute attribute : artifact.getAttributes()) { if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID()) { String tagName = attribute.getValueString(); if (artTags.containsKey(tagName)) { List<BlackboardArtifact> artifacts = artTags.get(tagName); artifacts.add(artifact); } else { List<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); artifacts.add(artifact); artTags.put(tagName, artifacts); } break; } } } } } catch (TskCoreException ex) { logger.log(Level.WARNING, "Count not initialize tag nodes, ", ex); } }
/** * Create a tag for a file with TSK_TAG_NAME as tagName. * * @param file to create tag for * @param tagName TSK_TAG_NAME * @param comment the tag comment, or null if not present */ public static void createTag(AbstractFile file, String tagName, String comment) { try { final BlackboardArtifact bookArt = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE); List<BlackboardAttribute> attrs = new ArrayList<BlackboardAttribute>(); BlackboardAttribute attr1 = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID(), "", tagName); attrs.add(attr1); if (comment != null && !comment.isEmpty()) { BlackboardAttribute attr2 = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID(), "", comment); attrs.add(attr2); } bookArt.addAttributes(attrs); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Failed to create tag for " + file.getName()); } }
/** * Get the artifact for a result tag. * * @param tagArtifactId artifact id of the tag * @return the tag's artifact */ static BlackboardArtifact getArtifactFromTag(long tagArtifactId) { try { Case currentCase = Case.getCurrentCase(); SleuthkitCase skCase = currentCase.getSleuthkitCase(); BlackboardArtifact artifact = skCase.getBlackboardArtifact(tagArtifactId); if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() || artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { List<BlackboardAttribute> attributes = artifact.getAttributes(); for (BlackboardAttribute att : attributes) { if (att.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()) { return skCase.getBlackboardArtifact(att.getValueLong()); } } } } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Failed to get artifact " + tagArtifactId + " from case."); } return null; }
/** * Looks up the tag names associated with either a tagged artifact or a tag artifact. * * @param artifactID The ID of the artifact * @param artifactTypeID The ID of the artifact type * @return A set of unique tag names */ public static HashSet<String> getUniqueTagNames(long artifactID, int artifactTypeID) { HashSet<String> tagNames = new HashSet<>(); try { ArrayList<Long> tagArtifactIDs = new ArrayList<>(); if (artifactTypeID == ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { tagArtifactIDs.add(artifactID); } else { List<BlackboardArtifact> tags = Case.getCurrentCase() .getSleuthkitCase() .getBlackboardArtifacts(ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT, artifactID); for (BlackboardArtifact tag : tags) { tagArtifactIDs.add(tag.getArtifactID()); } } for (Long tagArtifactID : tagArtifactIDs) { String whereClause = "WHERE artifact_id = " + tagArtifactID + " AND attribute_type_id = " + ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID(); List<BlackboardAttribute> attributes = Case.getCurrentCase().getSleuthkitCase().getMatchingAttributes(whereClause); for (BlackboardAttribute attr : attributes) { tagNames.add(attr.getValueString()); } } } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Failed to get tags for artifact " + artifactID, ex); } return tagNames; }
/** Search for bookmark files and make artifacts. */ private void getBookmark() { FileManager fileManager = currentCase.getServices().getFileManager(); List<AbstractFile> bookmarkFiles = null; try { bookmarkFiles = fileManager.findFiles(dataSource, "Bookmarks", "Chrome"); // NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); this.addErrorMessage(this.getName() + ": " + msg); return; } if (bookmarkFiles.isEmpty()) { logger.log(Level.INFO, "Didn't find any Chrome bookmark files."); // NON-NLS return; } dataFound = true; int j = 0; while (j < bookmarkFiles.size()) { AbstractFile bookmarkFile = bookmarkFiles.get(j++); if (bookmarkFile.getSize() == 0) { continue; } String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + bookmarkFile.getName().toString() + j + ".db"; // NON-NLS try { ContentUtils.writeToFile(bookmarkFile, new File(temps)); } catch (IOException ex) { logger.log( Level.SEVERE, "Error writing temp sqlite db for Chrome bookmark artifacts.{0}", ex); // NON-NLS this.addErrorMessage( NbBundle.getMessage( this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile", this.getName(), bookmarkFile.getName())); continue; } logger.log( Level.INFO, "{0}- Now getting Bookmarks from {1}", new Object[] {moduleName, temps}); // NON-NLS File dbFile = new File(temps); if (context.dataSourceIngestIsCancelled()) { dbFile.delete(); break; } FileReader tempReader; try { tempReader = new FileReader(temps); } catch (FileNotFoundException ex) { logger.log( Level.SEVERE, "Error while trying to read into the Bookmarks for Chrome.", ex); // NON-NLS this.addErrorMessage( NbBundle.getMessage( this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzeFile", this.getName(), bookmarkFile.getName())); continue; } final JsonParser parser = new JsonParser(); JsonElement jsonElement; JsonObject jElement, jRoot, jBookmark; JsonArray jBookmarkArray; try { jsonElement = parser.parse(tempReader); jElement = jsonElement.getAsJsonObject(); jRoot = jElement.get("roots").getAsJsonObject(); // NON-NLS jBookmark = jRoot.get("bookmark_bar").getAsJsonObject(); // NON-NLS jBookmarkArray = jBookmark.getAsJsonArray("children"); // NON-NLS } catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) { logger.log(Level.WARNING, "Error parsing Json from Chrome Bookmark.", ex); // NON-NLS this.addErrorMessage( NbBundle.getMessage( this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile3", this.getName(), bookmarkFile.getName())); continue; } for (JsonElement result : jBookmarkArray) { JsonObject address = result.getAsJsonObject(); if (address == null) { continue; } JsonElement urlEl = address.get("url"); // NON-NLS String url; if (urlEl != null) { url = urlEl.getAsString(); } else { url = ""; } String name; JsonElement nameEl = address.get("name"); // NON-NLS if (nameEl != null) { name = nameEl.getAsString(); } else { name = ""; } Long date; JsonElement dateEl = address.get("date_added"); // NON-NLS if (dateEl != null) { date = dateEl.getAsLong(); } else { date = Long.valueOf(0); } String domain = Util.extractDomain(url); try { BlackboardArtifact bbart = bookmarkFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK); Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); // TODO Revisit usage of deprecated constructor as per TSK-583 bbattributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_URL.getTypeID(), NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), url)); bbattributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_TITLE.getTypeID(), NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), name)); bbattributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(), NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), (date / 1000000) - Long.valueOf("11644473600"))); bbattributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); bbattributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_DOMAIN.getTypeID(), NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), domain)); bbart.addAttributes(bbattributes); } catch (TskCoreException ex) { logger.log( Level.SEVERE, "Error while trying to insert Chrome bookmark artifact{0}", ex); // NON-NLS this.addErrorMessage( NbBundle.getMessage( this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile4", this.getName(), bookmarkFile.getName())); } } dbFile.delete(); } IngestServices.getInstance() .fireModuleDataEvent( new ModuleDataEvent( NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK)); }
/** * Looks up the tag names associated with either a tagged artifact or a tag artifact. * * @param artifact The artifact * @return A set of unique tag names */ public static HashSet<String> getUniqueTagNames(BlackboardArtifact artifact) { return getUniqueTagNames(artifact.getArtifactID(), artifact.getArtifactTypeID()); }
@Override public KeywordCachedArtifact writeSingleFileHitsToBlackBoard( String termHit, KeywordHit hit, String snippet, String listName) { BlackboardArtifact newArtifact; Collection<BlackboardAttribute> attributes = new ArrayList<>(); if (keyword.getType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { attributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, MODULE_NAME, Account.Type.CREDIT_CARD.name())); Map<BlackboardAttribute.Type, BlackboardAttribute> parsedTrackAttributeMap = new HashMap<>(); // try to match it against the track 1 regex Matcher matcher = TRACK1_PATTERN.matcher(hit.getSnippet()); if (matcher.find()) { parseTrack1Data(parsedTrackAttributeMap, matcher); } // then try to match it against the track 2 regex matcher = TRACK2_PATTERN.matcher(hit.getSnippet()); if (matcher.find()) { parseTrack2Data(parsedTrackAttributeMap, matcher); } // if we couldn't parse the CCN abort this artifact final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { if (hit.isArtifactHit()) { LOGGER.log( Level.SEVERE, String.format( "Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", termHit, hit.getSnippet(), hit.getArtifact().getArtifactID())); } else { LOGGER.log( Level.SEVERE, String.format( "Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", termHit, hit.getSnippet(), hit.getContent().getId())); } return null; } attributes.addAll(parsedTrackAttributeMap.values()); // look up the bank name, schem, etc from the BIN final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8)); CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin); if (binInfo != null) { binInfo .getScheme() .ifPresent( scheme -> attributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme))); binInfo .getCardType() .ifPresent( cardType -> attributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType))); binInfo .getBrand() .ifPresent( brand -> attributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand))); binInfo .getBankName() .ifPresent( bankName -> attributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName))); binInfo .getBankPhoneNumber() .ifPresent( phoneNumber -> attributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber))); binInfo .getBankURL() .ifPresent( url -> attributes.add( new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url))); binInfo .getCountry() .ifPresent( country -> attributes.add( new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country))); binInfo .getBankCity() .ifPresent( city -> attributes.add( new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city))); } /* if the hit is from unused or unalocated blocks, record the * KEYWORD_SEARCH_DOCUMENT_ID, so we can show just that chunk in the * UI */ if (hit.getContent() instanceof AbstractFile) { AbstractFile file = (AbstractFile) hit.getContent(); if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { attributes.add( new BlackboardAttribute( KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); } } // make account artifact try { newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT); } catch (TskCoreException tskCoreException) { LOGGER.log( Level.SEVERE, "Error adding bb artifact for account", tskCoreException); // NON-NLS return null; } } else { // regex match attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, termHit)); // regex keyword attributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, keyword.getQuery())); // make keyword hit artifact try { newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); } catch (TskCoreException tskCoreException) { LOGGER.log( Level.SEVERE, "Error adding bb artifact for keyword hit", tskCoreException); // NON-NLS return null; } } if (StringUtils.isNotBlank(listName)) { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); } // preview if (snippet != null) { attributes.add( new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); } if (hit.isArtifactHit()) { attributes.add( new BlackboardAttribute( ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, hit.getArtifact().getArtifactID())); } try { // TODO: do we still/really need this KeywordCachedArtifact class? newArtifact.addAttributes(attributes); KeywordCachedArtifact writeResult = new KeywordCachedArtifact(newArtifact); writeResult.add(attributes); return writeResult; } catch (TskCoreException e) { LOGGER.log( Level.SEVERE, "Error adding bb attributes for terms search artifact", e); // NON-NLS return null; } }
@Override protected Node createNodeForKey(final BlackboardArtifact artifact) { // create node with action BlackboardArtifactNode tagNode = null; String iconPath; if (tagName.equals(BOOKMARK_TAG_NAME)) { iconPath = BOOKMARK_ICON_PATH; } else { iconPath = TAG_ICON_PATH; } // create actions here where Tag logic belongs // instead of DataResultFilterNode w/visitors, which is much less pluggable and cluttered if (tagType.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT)) { // in case of result tag, add a action by sublcassing bb art node // this action will be merged with other actions set DataResultFIlterNode // otherwise in case of tagNode = new BlackboardArtifactNode(artifact, iconPath) { @Override public Action[] getActions(boolean bln) { // Action [] actions = super.getActions(bln); //To change body of generated methods, // choose Tools | Templates. Action[] actions = new Action[1]; actions[0] = new AbstractAction("View Source Result") { @Override public void actionPerformed(ActionEvent e) { // open the source artifact in dir tree BlackboardArtifact sourceArt = Tags.getArtifactFromTag(artifact.getArtifactID()); if (sourceArt != null) { BlackboardResultViewer v = Lookup.getDefault().lookup(BlackboardResultViewer.class); v.viewArtifact(sourceArt); } } }; return actions; } }; } else { // for file tag, don't subclass to add the additional actions tagNode = new BlackboardArtifactNode(artifact, iconPath); } // add some additional node properties int artifactTypeID = artifact.getArtifactTypeID(); final String NO_DESCR = "no description"; if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { BlackboardArtifact sourceResult = Tags.getArtifactFromTag(artifact.getArtifactID()); String resultType = sourceResult.getDisplayName(); NodeProperty resultTypeProp = new NodeProperty("Source Result Type", "Result Type", NO_DESCR, resultType); tagNode.addNodeProperty(resultTypeProp); } try { // add source path property final AbstractFile sourceFile = skCase.getAbstractFileById(artifact.getObjectID()); final String sourcePath = sourceFile.getUniquePath(); NodeProperty sourcePathProp = new NodeProperty("Source File Path", "Source File Path", NO_DESCR, sourcePath); tagNode.addNodeProperty(sourcePathProp); } catch (TskCoreException ex) { logger.log( Level.SEVERE, "Error getting a file from artifact to get source file path for a tag, ", ex); } return tagNode; }
/** * Unpack the file to local folder and return a list of derived files * * @param pipelineContext current ingest context * @param archiveFile file to unpack * @return list of unpacked derived files */ void unpack(AbstractFile archiveFile) { String archiveFilePath; try { archiveFilePath = archiveFile.getUniquePath(); } catch (TskCoreException ex) { archiveFilePath = archiveFile.getParentPath() + archiveFile.getName(); } // check if already has derived files, skip try { if (archiveFile.hasChildren()) { // check if local unpacked dir exists if (new File(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) { logger.log( Level.INFO, "File already has been processed as it has children and local unpacked file, skipping: {0}", archiveFilePath); // NON-NLS return; } } } catch (TskCoreException e) { logger.log( Level.INFO, "Error checking if file already has been processed, skipping: {0}", archiveFilePath); // NON-NLS return; } List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList(); // recursion depth check for zip bomb final long archiveId = archiveFile.getId(); SevenZipExtractor.ArchiveDepthCountTree.Archive parentAr = archiveDepthCountTree.findArchive(archiveId); if (parentAr == null) { parentAr = archiveDepthCountTree.addArchive(null, archiveId); } else if (parentAr.getDepth() == MAX_DEPTH) { String msg = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb", archiveFile.getName()); String details = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb", parentAr.getDepth(), archiveFilePath); // MessageNotifyUtil.Notify.error(msg, details); services.postMessage( IngestMessage.createWarningMessage( EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); return; } boolean hasEncrypted = false; boolean fullEncryption = true; ISevenZipInArchive inArchive = null; SevenZipContentReadStream stream = null; final ProgressHandle progress = ProgressHandleFactory.createHandle( NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.moduleName")); int processedItems = 0; boolean progressStarted = false; try { stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile)); // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive // inside RAR archive // it will be opened incorrectly when using 7zip's built-in auto-detect functionality. // All other archive formats are still opened using 7zip built-in auto-detect functionality. ArchiveFormat options = get7ZipOptions(archiveFile); inArchive = SevenZip.openInArchive(options, stream); int numItems = inArchive.getNumberOfItems(); logger.log( Level.INFO, "Count of items in archive: {0}: {1}", new Object[] {archiveFilePath, numItems}); // NON-NLS progress.start(numItems); progressStarted = true; final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); // setup the archive local root folder final String uniqueArchiveFileName = EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile); final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName); final File localRoot = new File(localRootAbsPath); if (!localRoot.exists()) { try { localRoot.mkdirs(); } catch (SecurityException e) { logger.log( Level.SEVERE, "Error setting up output path for archive root: {0}", localRootAbsPath); // NON-NLS // bail return; } } // initialize tree hierarchy to keep track of unpacked file structure SevenZipExtractor.UnpackedTree unpackedTree = new SevenZipExtractor.UnpackedTree( moduleDirRelative + "/" + uniqueArchiveFileName, archiveFile); long freeDiskSpace = services.getFreeDiskSpace(); // unpack and process every item in archive int itemNumber = 0; for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { String pathInArchive = item.getPath(); if (pathInArchive == null || pathInArchive.isEmpty()) { // some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path // handle this for .tar.gz and tgz but assuming the child is tar, // otherwise, unpack using itemNumber as name // TODO this should really be signature based, not extension based String archName = archiveFile.getName(); int dotI = archName.lastIndexOf("."); String useName = null; if (dotI != -1) { String base = archName.substring(0, dotI); String ext = archName.substring(dotI); switch (ext) { case ".gz": // NON-NLS useName = base; break; case ".tgz": // NON-NLS useName = base + ".tar"; // NON-NLS break; } } if (useName == null) { pathInArchive = "/" + archName + "/" + Integer.toString(itemNumber); } else { pathInArchive = "/" + useName; } String msg = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg", archiveFilePath, pathInArchive); logger.log(Level.WARNING, msg); } ++itemNumber; logger.log(Level.INFO, "Extracted item path: {0}", pathInArchive); // NON-NLS // check if possible zip bomb if (isZipBombArchiveItemCheck(archiveFile, item)) { continue; // skip the item } // find this node in the hierarchy, create if needed SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive); String fileName = unpackedNode.getFileName(); // update progress bar progress.progress(archiveFile.getName() + ": " + fileName, processedItems); final boolean isEncrypted = item.isEncrypted(); final boolean isDir = item.isFolder(); if (isEncrypted) { logger.log( Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); // NON-NLS hasEncrypted = true; continue; } else { fullEncryption = false; } final Long size = item.getSize(); if (size == null) { // If the size property cannot be determined, out-of-disk-space // situations cannot be ascertained. // Hence skip this file. logger.log( Level.WARNING, "Size cannot be determined. Skipping file in archive: {0}", pathInArchive); // NON-NLS continue; } // check if unpacking this file will result in out of disk space // this is additional to zip bomb prevention mechanism if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && size > 0) { // if known free space and file not empty long newDiskSpace = freeDiskSpace - size; if (newDiskSpace < MIN_FREE_DISK_SPACE) { String msg = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg", archiveFilePath, fileName); String details = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details"); // MessageNotifyUtil.Notify.error(msg, details); services.postMessage( IngestMessage.createErrorMessage( EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); logger.log( Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new Object[] {archiveFilePath, fileName}); // NON-NLS logger.log( Level.INFO, "Available disk space: {0}", new Object[] {freeDiskSpace}); // NON-NLS continue; // skip this file } else { // update est. disk space during this archive, so we don't need to poll for every file // extracted freeDiskSpace = newDiskSpace; } } final String uniqueExtractedName = uniqueArchiveFileName + File.separator + (item.getItemIndex() / 1000) + File.separator + item.getItemIndex() + new File(pathInArchive).getName(); // final String localRelPath = unpackDir + File.separator + localFileRelPath; final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName; final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName; // create local dirs and empty files before extracted File localFile = new java.io.File(localAbsPath); // cannot rely on files in top-bottom order if (!localFile.exists()) { try { if (isDir) { localFile.mkdirs(); } else { localFile.getParentFile().mkdirs(); try { localFile.createNewFile(); } catch (IOException ex) { logger.log( Level.SEVERE, "Error creating extracted file: " + localFile.getAbsolutePath(), ex); // NON-NLS } } } catch (SecurityException e) { logger.log( Level.SEVERE, "Error setting up output path for unpacked file: {0}", pathInArchive); // NON-NLS // TODO consider bail out / msg to the user } } // skip the rest of this loop if we couldn't create the file if (localFile.exists() == false) { continue; } final Date createTime = item.getCreationTime(); final Date accessTime = item.getLastAccessTime(); final Date writeTime = item.getLastWriteTime(); final long createtime = createTime == null ? 0L : createTime.getTime() / 1000; final long modtime = writeTime == null ? 0L : writeTime.getTime() / 1000; final long accesstime = accessTime == null ? 0L : accessTime.getTime() / 1000; // record derived data in unode, to be traversed later after unpacking the archive unpackedNode.addDerivedInfo( size, !isDir, 0L, createtime, accesstime, modtime, localRelPath); // unpack locally if a file if (!isDir) { SevenZipExtractor.UnpackStream unpackStream = null; try { unpackStream = new SevenZipExtractor.UnpackStream(localAbsPath); item.extractSlow(unpackStream); } catch (Exception e) { // could be something unexpected with this file, move on logger.log( Level.WARNING, "Could not extract file from archive: " + localAbsPath, e); // NON-NLS } finally { if (unpackStream != null) { unpackStream.close(); } } } // update units for progress bar ++processedItems; } // add them to the DB. We wait until the end so that we have the metadata on all of the // intermediate nodes since the order is not guaranteed try { unpackedTree.addDerivedFilesToCase(); unpackedFiles = unpackedTree.getAllFileObjects(); // check if children are archives, update archive depth tracking for (AbstractFile unpackedFile : unpackedFiles) { if (isSevenZipExtractionSupported(unpackedFile)) { archiveDepthCountTree.addArchive(parentAr, unpackedFile.getId()); } } } catch (TskCoreException e) { logger.log( Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure"); // NON-NLS // TODO decide if anything to cleanup, for now bailing } } catch (SevenZipException ex) { logger.log(Level.SEVERE, "Error unpacking file: " + archiveFile, ex); // NON-NLS // inbox message // print a message if the file is allocated if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) { String msg = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg", archiveFile.getName()); String details = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details", archiveFilePath, ex.getMessage()); services.postMessage( IngestMessage.createErrorMessage( EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); } } finally { if (inArchive != null) { try { inArchive.close(); } catch (SevenZipException e) { logger.log(Level.SEVERE, "Error closing archive: " + archiveFile, e); // NON-NLS } } if (stream != null) { try { stream.close(); } catch (IOException ex) { logger.log( Level.SEVERE, "Error closing stream after unpacking archive: " + archiveFile, ex); // NON-NLS } } // close progress bar if (progressStarted) { progress.finish(); } } // create artifact and send user message if (hasEncrypted) { String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL; try { BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED); artifact.addAttribute( new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType)); services.fireModuleDataEvent( new ModuleDataEvent( EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED)); } catch (TskCoreException ex) { logger.log( Level.SEVERE, "Error creating blackboard artifact for encryption detected for file: " + archiveFilePath, ex); // NON-NLS } String msg = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg"); String details = NbBundle.getMessage( this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details", archiveFile.getName(), EmbeddedFileExtractorModuleFactory.getModuleName()); services.postMessage( IngestMessage.createWarningMessage( EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); } // adding unpacked extracted derived files to the job after closing relevant resources. if (!unpackedFiles.isEmpty()) { // currently sending a single event for all new files services.fireModuleContentEvent(new ModuleContentEvent(archiveFile)); context.addFilesToJob(unpackedFiles); } }
@Override public void viewArtifact(final BlackboardArtifact art) { BlackboardArtifact.ARTIFACT_TYPE type = BlackboardArtifact.ARTIFACT_TYPE.fromID(art.getArtifactTypeID()); Children rootChilds = em.getRootContext().getChildren(); Node treeNode = null; Node resultsNode = rootChilds.findChild(ResultsNode.NAME); Children resultsChilds = resultsNode.getChildren(); if (type.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT)) { Node hashsetRootNode = resultsChilds.findChild(type.getLabel()); Children hashsetRootChilds = hashsetRootNode.getChildren(); try { String setName = null; List<BlackboardAttribute> attributes = art.getAttributes(); for (BlackboardAttribute att : attributes) { int typeId = att.getAttributeTypeID(); if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { setName = att.getValueString(); } } treeNode = hashsetRootChilds.findChild(setName); } catch (TskException ex) { logger.log(Level.WARNING, "Error retrieving attributes", ex); // NON-NLS } } else if (type.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT)) { Node keywordRootNode = resultsChilds.findChild(type.getLabel()); Children keywordRootChilds = keywordRootNode.getChildren(); try { String listName = null; String keywordName = null; List<BlackboardAttribute> attributes = art.getAttributes(); for (BlackboardAttribute att : attributes) { int typeId = att.getAttributeTypeID(); if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { listName = att.getValueString(); } else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) { keywordName = att.getValueString(); } } Node listNode = keywordRootChilds.findChild(listName); Children listChildren = listNode.getChildren(); treeNode = listChildren.findChild(keywordName); } catch (TskException ex) { logger.log(Level.WARNING, "Error retrieving attributes", ex); // NON-NLS } } else if (type.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT) || type.equals(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT)) { Node interestingItemsRootNode = resultsChilds.findChild(type.getLabel()); Children interestingItemsRootChildren = interestingItemsRootNode.getChildren(); try { String setName = null; List<BlackboardAttribute> attributes = art.getAttributes(); for (BlackboardAttribute att : attributes) { int typeId = att.getAttributeTypeID(); if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { setName = att.getValueString(); } } treeNode = interestingItemsRootChildren.findChild(setName); } catch (TskException ex) { logger.log(Level.WARNING, "Error retrieving attributes", ex); // NON-NLS } } else { Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME); Children extractedChilds = extractedContent.getChildren(); treeNode = extractedChilds.findChild(type.getLabel()); } try { em.setExploredContextAndSelection(treeNode, new Node[] {treeNode}); } catch (PropertyVetoException ex) { logger.log(Level.WARNING, "Property Veto: ", ex); // NON-NLS } // Another thread is needed because we have to wait for dataResult to populate EventQueue.invokeLater( new Runnable() { @Override public void run() { Children resultChilds = dataResult.getRootNode().getChildren(); Node select = resultChilds.findChild(Long.toString(art.getArtifactID())); if (select != null) { dataResult.requestActive(); dataResult.setSelectedNodes(new Node[] {select}); fireViewerComplete(); } } }); }
@Override public synchronized ObservableResult evaluate() { setWarnings(""); if (obj.getAddressValue() == null) { return new ObservableResult( id, "AddressObject: No address value field found", // NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } String origAddressStr = obj.getAddressValue().getValue().toString(); // For now, we don't support "NONE" because it honestly doesn't seem like it // would ever appear in practice. if (((obj.getAddressValue().getApplyCondition() != null) && (obj.getAddressValue().getApplyCondition() == ConditionApplicationEnum.NONE))) { return new ObservableResult( id, "AddressObject: Can not process apply condition " + obj.getAddressValue().getApplyCondition().toString() // NON-NLS + " on Address object", spacing, ObservableResult.ObservableState.INDETERMINATE, null); // NON-NLS } // Set warnings for any unsupported fields setUnsupportedFieldWarnings(); Case case1 = Case.getCurrentCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); try { // Need to check that every part of the string had at least one match // in the AND case boolean everyPartMatched = true; List<BlackboardArtifact> combinedArts = new ArrayList<BlackboardArtifact>(); String searchString = ""; String[] parts = origAddressStr.split("##comma##"); // NON-NLS for (String addressStr : parts) { // Update the string to show in the results if (!searchString.isEmpty()) { if ((obj.getAddressValue().getApplyCondition() != null) && (obj.getAddressValue().getApplyCondition() == ConditionApplicationEnum.ALL)) { searchString += " AND "; // NON-NLS } else { searchString += " OR "; // NON-NLS } } searchString += addressStr; if ((obj.getAddressValue().getCondition() == null) || (obj.getAddressValue().getCondition() == ConditionTypeEnum.EQUALS)) { List<BlackboardArtifact> arts = sleuthkitCase.getBlackboardArtifacts( BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, addressStr); if (arts.isEmpty()) { everyPartMatched = false; } else { combinedArts.addAll(arts); } } else { // This is inefficient, but the easiest way to do it. List<BlackboardArtifact> finalHits = new ArrayList<BlackboardArtifact>(); // Get all the URL artifacts List<BlackboardArtifact> artList = sleuthkitCase.getBlackboardArtifacts( BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT); for (BlackboardArtifact art : artList) { for (BlackboardAttribute attr : art.getAttributes()) { if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) { if (compareStringObject( addressStr, obj.getAddressValue().getCondition(), obj.getAddressValue().getApplyCondition(), attr.getValueString())) { finalHits.add(art); } } } } if (finalHits.isEmpty()) { everyPartMatched = false; } else { combinedArts.addAll(finalHits); } } } // If we're in the ALL case, make sure every piece matched if ((obj.getAddressValue().getApplyCondition() != null) && (obj.getAddressValue().getApplyCondition() == ConditionApplicationEnum.ALL) && (!everyPartMatched)) { return new ObservableResult( id, "AddressObject: No matches for " + searchString, // NON-NLS spacing, ObservableResult.ObservableState.FALSE, null); } if (!combinedArts.isEmpty()) { List<StixArtifactData> artData = new ArrayList<StixArtifactData>(); for (BlackboardArtifact a : combinedArts) { artData.add(new StixArtifactData(a.getObjectID(), id, "AddressObject")); // NON-NLS } return new ObservableResult( id, "AddressObject: Found a match for " + searchString, // NON-NLS spacing, ObservableResult.ObservableState.TRUE, artData); } return new ObservableResult( id, "AddressObject: Found no matches for " + searchString, // NON-NLS spacing, ObservableResult.ObservableState.FALSE, null); } catch (TskCoreException ex) { return new ObservableResult( id, "AddressObject: Exception during evaluation: " + ex.getLocalizedMessage(), // NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } }
@Override public IngestModule.ProcessResult process(AbstractFile file) { if (attrId == -1) { return IngestModule.ProcessResult.ERROR; } // Skip anything other than actual file system files. if ((file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) || (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) || (file.isFile() == false)) { return IngestModule.ProcessResult.OK; } // Skip NSRL / known files. if (skipKnownFiles && file.getKnown() == TskData.FileKnown.KNOWN) { return IngestModule.ProcessResult.OK; } // Do a nonsensical calculation of the number of 0x00 bytes // in the first 1024-bytes of the file. This is for demo // purposes only. try { byte buffer[] = new byte[1024]; int len = file.read(buffer, 0, 1024); int count = 0; for (int i = 0; i < len; i++) { if (buffer[i] == 0x00) { count++; } } // Make an attribute using the ID for the attribute type that // was previously created. BlackboardAttribute attr = new BlackboardAttribute(attrId, SampleIngestModuleFactory.getModuleName(), count); // Add the to the general info artifact for the file. In a // real module, you would likely have more complex data types // and be making more specific artifacts. BlackboardArtifact art = file.getGenInfoArtifact(); art.addAttribute(attr); // This method is thread-safe with per ingest job reference counted // management of shared data. addToBlackboardPostCount(context.getJobId(), 1L); // Fire an event to notify any listeners for blackboard postings. ModuleDataEvent event = new ModuleDataEvent( SampleIngestModuleFactory.getModuleName(), ARTIFACT_TYPE.TSK_GEN_INFO); IngestServices.getInstance().fireModuleDataEvent(event); return IngestModule.ProcessResult.OK; } catch (TskCoreException ex) { IngestServices ingestServices = IngestServices.getInstance(); Logger logger = ingestServices.getLogger(SampleIngestModuleFactory.getModuleName()); logger.log(Level.SEVERE, "Error processing file (id = " + file.getId() + ")", ex); return IngestModule.ProcessResult.ERROR; } }
/** @inheritDoc */ @Override @Messages({ "FilesIdentifierIngestModule.indexError.message=Failed to index interesting file hit artifact for keyword search." }) public ProcessResult process(AbstractFile file) { blackboard = Case.getCurrentCase().getServices().getBlackboard(); // See if the file belongs to any defined interesting files set. List<FilesSet> filesSets = FilesIdentifierIngestModule.interestingFileSetsByJob.get(this.context.getJobId()); for (FilesSet filesSet : filesSets) { String ruleSatisfied = filesSet.fileIsMemberOf(file); if (ruleSatisfied != null) { try { // Post an interesting files set hit artifact to the // blackboard. String moduleName = InterestingItemsIngestModuleFactory.getModuleName(); BlackboardArtifact artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); // Add a set name attribute to the artifact. This adds a // fair amount of redundant data to the attributes table // (i.e., rows that differ only in artifact id), but doing // otherwise would requires reworking the interesting files // set hit artifact. BlackboardAttribute setNameAttribute = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, filesSet.getName()); artifact.addAttribute(setNameAttribute); // Add a category attribute to the artifact to record the // interesting files set membership rule that was satisfied. BlackboardAttribute ruleNameAttribute = new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, moduleName, ruleSatisfied); artifact.addAttribute(ruleNameAttribute); try { // index the artifact for keyword search blackboard.indexArtifact(artifact); } catch (Blackboard.BlackboardException ex) { logger.log( Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); // NON-NLS MessageNotifyUtil.Notify.error( Bundle.FilesIdentifierIngestModule_indexError_message(), artifact.getDisplayName()); } IngestServices.getInstance() .fireModuleDataEvent( new ModuleDataEvent( moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, Collections.singletonList(artifact))); } catch (TskCoreException ex) { FilesIdentifierIngestModule.logger.log( Level.SEVERE, "Error posting to the blackboard", ex); // NOI18N NON-NLS } } } return ProcessResult.OK; }