extract content from archive having unknown size - sevenzipextractor

This commit is contained in:
sidheshenator 2015-08-07 14:27:29 -04:00
parent d5bf4c5e0e
commit d7d640e352
3 changed files with 69 additions and 37 deletions

View File

@ -37,7 +37,7 @@ public class FileTypeExtensions {
private final static List<String> TEXT_EXTENSIONS = Arrays.asList(".txt", ".rtf", ".log", ".text", ".xml"); //NON-NLS private final static List<String> TEXT_EXTENSIONS = Arrays.asList(".txt", ".rtf", ".log", ".text", ".xml"); //NON-NLS
private final static List<String> WEB_EXTENSIONS = Arrays.asList(".html", ".htm", ".css", ".js", ".php", ".aspx"); //NON-NLS private final static List<String> WEB_EXTENSIONS = Arrays.asList(".html", ".htm", ".css", ".js", ".php", ".aspx"); //NON-NLS
private final static List<String> PDF_EXTENSIONS = Arrays.asList(".pdf"); //NON-NLS private final static List<String> PDF_EXTENSIONS = Arrays.asList(".pdf"); //NON-NLS
private final static List<String> ARCHIVE_EXTENSIONS = Arrays.asList(".zip", ".rar", ".7zip", ".7z", ".arj", ".tar", ".gzip", ".bzip", ".bzip2", ".cab", ".jar", ".cpio", ".ar", ".gz", ".tgz"); //NON-NLS private final static List<String> ARCHIVE_EXTENSIONS = Arrays.asList(".zip", ".rar", ".7zip", ".7z", ".arj", ".tar", ".gzip", ".bzip", ".bzip2", ".cab", ".jar", ".cpio", ".ar", ".gz", ".tgz", ".bz2"); //NON-NLS
public static List<String> getImageExtensions() { public static List<String> getImageExtensions() {
return IMAGE_EXTENSIONS; return IMAGE_EXTENSIONS;

View File

@ -37,3 +37,4 @@ EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err=Xls conta
EmbeddedFileExtractorIngestModule.ImageExtractor.xlsxContainer.init.err=Xlsx container could not be initialized while reading: {0} EmbeddedFileExtractorIngestModule.ImageExtractor.xlsxContainer.init.err=Xlsx container could not be initialized while reading: {0}
EmbeddedFileExtractorIngestModule.ImageExtractor.extractImage.addToDB.exception.msg=Unable to add the derived files to the database. EmbeddedFileExtractorIngestModule.ImageExtractor.extractImage.addToDB.exception.msg=Unable to add the derived files to the database.
EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg=Could not get path for image extraction from Abstract File: {0} EmbeddedFileExtractorIngestModule.ImageExtractor.getOutputFolderPath.exception.msg=Could not get path for image extraction from Abstract File: {0}
EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg=Unable to write content to disk. Not enough space.

View File

@ -24,6 +24,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -40,7 +42,6 @@ import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.FileManager;
@ -98,7 +99,8 @@ class SevenZipExtractor {
GZIP("application/gzip"), GZIP("application/gzip"),
XGZIP("application/x-gzip"), XGZIP("application/x-gzip"),
XBZIP2("application/x-bzip2"), XBZIP2("application/x-bzip2"),
XTAR("application/x-tar"); XTAR("application/x-tar"),
XGTAR("application/x-gtar");
private final String mimeType; private final String mimeType;
@ -121,9 +123,9 @@ class SevenZipExtractor {
logger.log(Level.INFO, "7-Zip-JBinding library was initialized on supported platform: {0}", platform); //NON-NLS logger.log(Level.INFO, "7-Zip-JBinding library was initialized on supported platform: {0}", platform); //NON-NLS
} catch (SevenZipNativeInitializationException e) { } catch (SevenZipNativeInitializationException e) {
logger.log(Level.SEVERE, "Error initializing 7-Zip-JBinding library", e); //NON-NLS logger.log(Level.SEVERE, "Error initializing 7-Zip-JBinding library", e); //NON-NLS
String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.msg", String msg = NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.msg",
EmbeddedFileExtractorModuleFactory.getModuleName()); EmbeddedFileExtractorModuleFactory.getModuleName());
String details = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errCantInitLib", String details = NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errCantInitLib",
e.getMessage()); e.getMessage());
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
throw new IngestModuleException(e.getMessage()); throw new IngestModuleException(e.getMessage());
@ -204,7 +206,7 @@ class SevenZipExtractor {
if (cRatio >= MAX_COMPRESSION_RATIO) { if (cRatio >= MAX_COMPRESSION_RATIO) {
String itemName = archiveFileItem.getPath(); String itemName = archiveFileItem.getPath();
logger.log(Level.INFO, "Possible zip bomb detected, compression ration: {0} for in archive item: {1}", new Object[]{cRatio, itemName}); //NON-NLS logger.log(Level.INFO, "Possible zip bomb detected, compression ration: {0} for in archive item: {1}", new Object[]{cRatio, itemName}); //NON-NLS
String msg = NbBundle.getMessage(this.getClass(), String msg = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), itemName); "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), itemName);
String path; String path;
try { try {
@ -212,7 +214,7 @@ class SevenZipExtractor {
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
path = archiveFile.getParentPath() + archiveFile.getName(); path = archiveFile.getParentPath() + archiveFile.getName();
} }
String details = NbBundle.getMessage(this.getClass(), String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails", cRatio, path); "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails", cRatio, path);
//MessageNotifyUtil.Notify.error(msg, details); //MessageNotifyUtil.Notify.error(msg, details);
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
@ -311,9 +313,9 @@ class SevenZipExtractor {
if (parentAr == null) { if (parentAr == null) {
parentAr = archiveDepthCountTree.addArchive(null, archiveId); parentAr = archiveDepthCountTree.addArchive(null, archiveId);
} else if (parentAr.getDepth() == MAX_DEPTH) { } else if (parentAr.getDepth() == MAX_DEPTH) {
String msg = NbBundle.getMessage(this.getClass(), String msg = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb", archiveFile.getName()); "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb", archiveFile.getName());
String details = NbBundle.getMessage(this.getClass(), String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb", "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
parentAr.getDepth(), archiveFilePath); parentAr.getDepth(), archiveFilePath);
//MessageNotifyUtil.Notify.error(msg, details); //MessageNotifyUtil.Notify.error(msg, details);
@ -328,7 +330,7 @@ class SevenZipExtractor {
SevenZipContentReadStream stream = null; SevenZipContentReadStream stream = null;
final ProgressHandle progress = ProgressHandleFactory.createHandle( final ProgressHandle progress = ProgressHandleFactory.createHandle(
NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.moduleName")); NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.moduleName"));
int processedItems = 0; int processedItems = 0;
boolean progressStarted = false; boolean progressStarted = false;
@ -400,7 +402,7 @@ class SevenZipExtractor {
pathInArchive = "/" + useName; pathInArchive = "/" + useName;
} }
String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg", String msg = NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
archiveFilePath, pathInArchive); archiveFilePath, pathInArchive);
logger.log(Level.WARNING, msg); logger.log(Level.WARNING, msg);
@ -432,24 +434,19 @@ class SevenZipExtractor {
fullEncryption = false; fullEncryption = false;
} }
final Long size = item.getSize(); // NOTE: item.getSize() may return null in case of certain
if (size == null) { // archiving formats. Eg: BZ2
// If the size property cannot be determined, out-of-disk-space Long size = item.getSize();
// 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 //check if unpacking this file will result in out of disk space
//this is additional to zip bomb prevention mechanism //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 if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && size != null && size > 0) { //if free space is known and file is not empty.
long newDiskSpace = freeDiskSpace - size; long newDiskSpace = freeDiskSpace - size;
if (newDiskSpace < MIN_FREE_DISK_SPACE) { if (newDiskSpace < MIN_FREE_DISK_SPACE) {
String msg = NbBundle.getMessage(this.getClass(), String msg = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg", "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
archiveFilePath, fileName); archiveFilePath, fileName);
String details = NbBundle.getMessage(this.getClass(), String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details"); "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
//MessageNotifyUtil.Notify.error(msg, details); //MessageNotifyUtil.Notify.error(msg, details);
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
@ -501,21 +498,20 @@ class SevenZipExtractor {
final long modtime = writeTime == null ? 0L : writeTime.getTime() / 1000; final long modtime = writeTime == null ? 0L : writeTime.getTime() / 1000;
final long accesstime = accessTime == null ? 0L : accessTime.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 //unpack locally if a file
SevenZipExtractor.UnpackStream unpackStream = null;
if (!isDir) { if (!isDir) {
SevenZipExtractor.UnpackStream unpackStream = null;
try { try {
unpackStream = new SevenZipExtractor.UnpackStream(localAbsPath); unpackStream = new SevenZipExtractor.UnpackStream(localAbsPath, freeDiskSpace, size == null);
item.extractSlow(unpackStream); item.extractSlow(unpackStream);
} catch (Exception e) { } catch (Exception e) {
//could be something unexpected with this file, move on //could be something unexpected with this file, move on
logger.log(Level.WARNING, "Could not extract file from archive: " + localAbsPath, e); //NON-NLS logger.log(Level.WARNING, "Could not extract file from archive: " + localAbsPath, e); //NON-NLS
} finally { } finally {
if (unpackStream != null) { if (unpackStream != null) {
//record derived data in unode, to be traversed later after unpacking the archive
unpackedNode.addDerivedInfo(unpackStream.getNumberOfBytesWritten(), !isDir,
0L, createtime, accesstime, modtime, localRelPath);
unpackStream.close(); unpackStream.close();
} }
} }
@ -549,9 +545,9 @@ class SevenZipExtractor {
// print a message if the file is allocated // print a message if the file is allocated
if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) { if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg", String msg = NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
archiveFile.getName()); archiveFile.getName());
String details = NbBundle.getMessage(this.getClass(), String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details", "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
archiveFilePath, ex.getMessage()); archiveFilePath, ex.getMessage());
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
@ -590,8 +586,8 @@ class SevenZipExtractor {
logger.log(Level.SEVERE, "Error creating blackboard artifact for encryption detected for file: " + archiveFilePath, ex); //NON-NLS 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 msg = NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
String details = NbBundle.getMessage(this.getClass(), String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details", "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
archiveFile.getName(), EmbeddedFileExtractorModuleFactory.getModuleName()); archiveFile.getName(), EmbeddedFileExtractorModuleFactory.getModuleName());
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
@ -612,8 +608,15 @@ class SevenZipExtractor {
private OutputStream output; private OutputStream output;
private String localAbsPath; private String localAbsPath;
private long freeDiskSpace;
private boolean sizeUnknown = false;
private boolean outOfSpace = false;
private long bytesWritten = 0;
UnpackStream(String localAbsPath) { UnpackStream(String localAbsPath, long freeDiskSpace, boolean sizeUnknown) {
this.sizeUnknown = sizeUnknown;
this.freeDiskSpace = freeDiskSpace;
this.localAbsPath = localAbsPath;
try { try {
output = new BufferedOutputStream(new FileOutputStream(localAbsPath)); output = new BufferedOutputStream(new FileOutputStream(localAbsPath));
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
@ -622,13 +625,38 @@ class SevenZipExtractor {
} }
public long getNumberOfBytesWritten() {
return this.bytesWritten;
}
@Override @Override
public int write(byte[] bytes) throws SevenZipException { public int write(byte[] bytes) throws SevenZipException {
try { try {
output.write(bytes); if (!sizeUnknown) {
output.write(bytes);
} else {
// If the content size is unknown, cautiously write to disk.
// Write only if byte array is less than 80% of the current
// free disk space.
if (freeDiskSpace == IngestMonitor.DISK_FREE_SPACE_UNKNOWN || bytes.length < 0.8 * freeDiskSpace) {
output.write(bytes);
// NOTE: this method is called multiple times for a
// single extractSlow() call. Update bytesWritten and
// freeDiskSpace after every write operation.
this.bytesWritten += bytes.length;
this.freeDiskSpace -= bytes.length;
} else {
this.outOfSpace = true;
logger.log(Level.INFO, NbBundle.getMessage(
SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg"));
throw new SevenZipException(
NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg"));
}
}
} catch (IOException ex) { } catch (IOException ex) {
throw new SevenZipException( throw new SevenZipException(
NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg", NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
localAbsPath), ex); localAbsPath), ex);
} }
return bytes.length; return bytes.length;
@ -639,6 +667,9 @@ class SevenZipExtractor {
try { try {
output.flush(); output.flush();
output.close(); output.close();
if (this.outOfSpace) {
Files.delete(Paths.get(this.localAbsPath));
}
} catch (IOException e) { } catch (IOException e) {
logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS
} }
@ -774,7 +805,7 @@ class SevenZipExtractor {
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding a derived file to db:" + fileName, ex); //NON-NLS logger.log(Level.SEVERE, "Error adding a derived file to db:" + fileName, ex); //NON-NLS
throw new TskCoreException( throw new TskCoreException(
NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg", NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
fileName), ex); fileName), ex);
} }