19 package com.basistech.df.cybertriage.autopsy.malwarescan;
27 import java.text.MessageFormat;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.List;
33 import java.util.Optional;
35 import java.util.logging.Level;
36 import java.util.stream.Collectors;
37 import java.util.stream.Stream;
38 import org.apache.commons.collections4.CollectionUtils;
39 import org.apache.commons.lang3.StringUtils;
40 import org.openide.util.NbBundle.Messages;
69 sharedProcessing.startUp(context);
74 return sharedProcessing.process(af);
79 sharedProcessing.shutDown();
98 "application/x-dosexec",
99 "application/vnd.microsoft.portable-executable",
100 "application/x-msdownload",
103 "application/dos-exe",
105 "application/x-winexe",
106 "application/msdos-windows",
107 "application/x-msdos-program"
108 ).collect(Collectors.toSet());
129 "MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title=Hash Lookups Low",
130 "# {0} - remainingLookups",
131 "MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc=This license only has {0} lookups remaining",
132 "MalwareScanIngestModule_malwareTypeDisplayName=Malware",
133 "MalwareScanIngestModule_ShareProcessing_noLicense_title=No Cyber Triage License",
134 "MalwareScanIngestModule_ShareProcessing_noLicense_desc=No Cyber Triage license could be loaded. Cyber Triage processing will be disabled.",
135 "MalwareScanIngestModule_ShareProcessing_noRemaining_title=No remaining lookups",
136 "MalwareScanIngestModule_ShareProcessing_noRemaining_desc=There are no more remaining hash lookups for this license at this time. Cyber Triage processing will be disabled."
146 Optional<LicenseInfo> licenseInfoOpt = ctSettingsPersistence.
loadLicenseInfo();
147 if (licenseInfoOpt.isEmpty() || licenseInfoOpt.get().getDecryptedLicense() == null) {
149 Bundle.MalwareScanIngestModule_ShareProcessing_noLicense_title(),
150 Bundle.MalwareScanIngestModule_ShareProcessing_noLicense_desc(),
156 AuthTokenResponse authTokenResponse = ctApiDAO.
getAuthToken(licenseInfoOpt.get().getDecryptedLicense());
160 long lookupsRemaining =
remaining(authTokenResponse.getHashLookupLimit(), authTokenResponse.getHashLookupCount());
161 if (lookupsRemaining <= 0) {
163 Bundle.MalwareScanIngestModule_ShareProcessing_noRemaining_title(),
164 Bundle.MalwareScanIngestModule_ShareProcessing_noRemaining_desc(),
168 }
else if (lookupsRemaining < LOW_LOOKUPS_REMAINING) {
170 Bundle.MalwareScanIngestModule_ShareProcessing_lowLimitWarning_title(),
171 Bundle.MalwareScanIngestModule_ShareProcessing_lowLimitWarning_desc(lookupsRemaining),
176 tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
177 malwareType = tskCase.getBlackboard().getOrAddArtifactType(
179 Bundle.MalwareScanIngestModule_malwareTypeDisplayName(),
180 BlackboardArtifact.Category.ANALYSIS_RESULT);
181 fileTypeDetector =
new FileTypeDetector();
182 dsId = context.getDataSource().getId();
183 ingestJobId = context.getJobId();
184 licenseInfo = licenseInfoOpt.get();
188 }
catch (Exception ex) {
190 throw new IngestModuleException(
"An exception occurred on MalwareScanIngestModule startup", ex);
195 limit = limit == null ? 0 : limit;
196 used = used == null ? 0 : used;
201 if (StringUtils.isNotBlank(af.getMd5Hash())) {
202 return af.getMd5Hash();
206 List<HashResult> hashResults = HashUtility.calculateHashes(af, Collections.singletonList(HashType.MD5));
207 if (CollectionUtils.isNotEmpty(hashResults)) {
208 for (HashResult hashResult : hashResults) {
209 if (hashResult.getType() == HashType.MD5) {
210 return hashResult.getValue();
214 }
catch (TskCoreException ex) {
215 logger.log(Level.WARNING,
216 MessageFormat.format(
"An error occurred while processing file name: {0} and obj id: {1}.",
226 "MalwareScanIngestModule_ShareProcessing_batchTimeout_title=Batch Processing Timeout",
227 "MalwareScanIngestModule_ShareProcessing_batchTimeout_desc=Batch processing timed out"
232 && af.getKnown() != TskData.FileKnown.KNOWN
233 && EXECUTABLE_MIME_TYPES.contains(StringUtils.defaultString(fileTypeDetector.
getMIMEType(af)).trim().toLowerCase())
234 && CollectionUtils.isEmpty(af.getAnalysisResults(
malwareType))) {
237 if (StringUtils.isNotBlank(md5)) {
238 batchProcessor.
add(
new FileRecord(af.getId(), md5));
241 return ProcessResult.OK;
242 }
catch (TskCoreException ex) {
244 Bundle.MalwareScanIngestModule_SharedProcessing_generalProcessingError_title(),
245 Bundle.MalwareScanIngestModule_SharedProcessing_generalProcessingError_desc(),
247 return IngestModule.ProcessResult.ERROR;
248 }
catch (InterruptedException ex) {
250 Bundle.MalwareScanIngestModule_ShareProcessing_batchTimeout_title(),
251 Bundle.MalwareScanIngestModule_ShareProcessing_batchTimeout_desc(),
253 return IngestModule.ProcessResult.ERROR;
258 "MalwareScanIngestModule_SharedProcessing_authTokenResponseError_title=Authentication API error",
259 "# {0} - errorResponse",
260 "MalwareScanIngestModule_SharedProcessing_authTokenResponseError_desc=Received error: ''{0}'' when fetching the API authentication token for the license",
261 "MalwareScanIngestModule_SharedProcessing_repServicenResponseError_title=Lookup API error",
262 "# {0} - errorResponse",
263 "MalwareScanIngestModule_SharedProcessing_repServicenResponseError_desc=Received error: ''{0}'' when fetching hash lookup results",
264 "MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title=Hash Lookups Exhausted",
265 "MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc=The remaining hash lookups for this license have been exhausted",
266 "MalwareScanIngestModule_SharedProcessing_generalProcessingError_title=Hash Lookup Error",
267 "MalwareScanIngestModule_SharedProcessing_generalProcessingError_desc=An error occurred while processing hash lookup results",})
269 if (runState !=
RunState.
STARTED_UP || fileRecords == null || fileRecords.isEmpty()) {
274 Map<String, List<Long>> md5ToObjId =
new HashMap<>();
276 for (FileRecord fr : fileRecords) {
277 if (fr == null || StringUtils.isBlank(fr.getMd5hash()) || fr.getObjId() <= 0) {
283 .computeIfAbsent(sanitizedMd5, (k) ->
new ArrayList<>())
288 List<String> md5Hashes =
new ArrayList<>(md5ToObjId.keySet());
290 if (md5Hashes.isEmpty()) {
300 if (remainingScans <= 0) {
303 Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_title(),
304 Bundle.MalwareScanIngestModule_SharedProcessing_exhaustedHashLookups_desc(),
315 List<BlackboardArtifact> createdArtifacts =
new ArrayList<>();
316 if (!CollectionUtils.isEmpty(repResult)) {
317 SleuthkitCase.CaseDbTransaction trans = null;
319 trans = tskCase.beginTransaction();
322 List<Long> objIds = md5ToObjId.remove(sanitizedMd5);
323 if (objIds == null || objIds.isEmpty()) {
327 for (Long objId : objIds) {
330 createdArtifacts.add(res);
340 createdArtifacts.clear();
345 if (!CollectionUtils.isEmpty(createdArtifacts)) {
346 tskCase.getBlackboard().postArtifacts(createdArtifacts, Bundle.MalwareScanIngestModuleFactory_displayName(),
ingestJobId);
349 }
catch (Exception ex) {
351 Bundle.MalwareScanIngestModule_SharedProcessing_generalProcessingError_title(),
352 Bundle.MalwareScanIngestModule_SharedProcessing_generalProcessingError_desc(),
358 return StringUtils.defaultString(orig).trim().toLowerCase();
362 "MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes=YES",
363 "MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No=NO"
366 if (objId == null || cloudBean == null || cloudBean.
getMalwareResult() == null) {
371 ? Score.SCORE_UNKNOWN
374 String conclusion = score.getSignificance() == Score.Significance.NOTABLE || score.getSignificance() == Score.Significance.LIKELY_NOTABLE
375 ? Bundle.MalwareScanIngestModule_SharedProcessing_createAnalysisResult_Yes()
376 : Bundle.MalwareScanIngestModule_SharedProcessing_createAnalysisResult_No();
380 return tskCase.getBlackboard().newAnalysisResult(
388 Collections.emptyList(),
389 trans).getAnalysisResult();
393 "MalwareScanIngestModule_SharedProcessing_flushTimeout_title=Processing Timeout",
394 "MalwareScanIngestModule_SharedProcessing_flushTimeout_desc=A timeout occurred while finishing processing"
396 synchronized void shutDown() {
405 }
catch (InterruptedException ex) {
407 Bundle.MalwareScanIngestModule_SharedProcessing_flushTimeout_title(),
408 Bundle.MalwareScanIngestModule_SharedProcessing_flushTimeout_desc(),
418 logger.log(Level.WARNING, message, ex);
427 private final long objId;
428 private final String md5hash;
430 FileRecord(
long objId, String md5hash) {
432 this.md5hash = md5hash;
439 String getMd5hash() {
static final Set< String > EXECUTABLE_MIME_TYPES
DecryptedLicenseResponse getDecryptedLicense()
BlackboardArtifact.Type malwareType
static long remaining(Long limit, Long used)
synchronized void flushAndReset()
String getStatusDescription()
static CTApiDAO getInstance()
synchronized void add(T item)
final BatchProcessor< FileRecord > batchProcessor
MalwareResultBean getMalwareResult()
ProcessResult process(AbstractFile af)
static final SharedProcessing sharedProcessing
String getMIMEType(AbstractFile file)
synchronized Optional< LicenseInfo > loadLicenseInfo()
List< CTCloudBean > getReputationResults(AuthenticatedRequestData authenticatedRequestData, List< String > md5Hashes)
String sanitizedMd5(String orig)
static CTLicensePersistence getInstance()
static final Logger logger
void startUp(IngestJobContext context)
static final long FLUSH_SECS_TIMEOUT
static final String MALWARE_TYPE_NAME
Long getHashLookupLimit()
FileTypeDetector fileTypeDetector
String getOrCalcHash(AbstractFile af)
static final long LOW_LOOKUPS_REMAINING
static final int BATCH_SIZE
Long getHashLookupCount()
AuthTokenResponse getAuthToken(DecryptedLicenseResponse decrypted)
AnalysisResult createAnalysisResult(Long objId, CTCloudBean cloudBean, SleuthkitCase.CaseDbTransaction trans)
final CTLicensePersistence ctSettingsPersistence
synchronized static Logger getLogger(String name)
static final String MALWARE_CONFIG
void notifyWarning(String title, String message, Exception ex)
static void warn(String title, String message)
void handleBatch(List< FileRecord > fileRecords)