mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-08 02:18:53 +00:00
Merge pull request #1708 from theopolis/tsk_more
Additional TSK table: device_hash
This commit is contained in:
commit
877c050466
@ -86,7 +86,9 @@ std::string Hash::digest() {
|
|||||||
return digest.str();
|
return digest.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string hashFromBuffer(HashType hash_type, const void* buffer, size_t size) {
|
std::string hashFromBuffer(HashType hash_type,
|
||||||
|
const void* buffer,
|
||||||
|
size_t size) {
|
||||||
Hash hash(hash_type);
|
Hash hash(hash_type);
|
||||||
hash.update(buffer, size);
|
hash.update(buffer, size);
|
||||||
return hash.digest();
|
return hash.digest();
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <tsk/libtsk.h>
|
#include <tsk/libtsk.h>
|
||||||
|
|
||||||
#include <osquery/filesystem.h>
|
#include <osquery/filesystem.h>
|
||||||
|
#include <osquery/hash.h>
|
||||||
#include <osquery/logger.h>
|
#include <osquery/logger.h>
|
||||||
#include <osquery/tables.h>
|
#include <osquery/tables.h>
|
||||||
|
|
||||||
@ -58,6 +59,12 @@ class DeviceHelper : private boost::noncopyable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void inodes(
|
||||||
|
const std::set<std::string>& inodes,
|
||||||
|
TskFsInfo* fs,
|
||||||
|
std::function<void(const std::string&, TskFsFile*, const std::string&)>
|
||||||
|
predicate);
|
||||||
|
|
||||||
/// Provide a partition description for context and iterate from path.
|
/// Provide a partition description for context and iterate from path.
|
||||||
void generateFiles(const std::string& partition,
|
void generateFiles(const std::string& partition,
|
||||||
TskFsInfo* fs,
|
TskFsInfo* fs,
|
||||||
@ -68,6 +75,7 @@ class DeviceHelper : private boost::noncopyable {
|
|||||||
/// Similar to generateFiles but only yield a row to results.
|
/// Similar to generateFiles but only yield a row to results.
|
||||||
void generateFile(const std::string& partition,
|
void generateFile(const std::string& partition,
|
||||||
TskFsFile* file,
|
TskFsFile* file,
|
||||||
|
TskFsInfo* fs,
|
||||||
const std::string& path,
|
const std::string& path,
|
||||||
QueryData& results);
|
QueryData& results);
|
||||||
|
|
||||||
@ -82,24 +90,7 @@ class DeviceHelper : private boost::noncopyable {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// Attempt to open the provided device image and volume.
|
/// Attempt to open the provided device image and volume.
|
||||||
bool open() {
|
bool open();
|
||||||
if (opened_) {
|
|
||||||
return opened_result_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to open the device image.
|
|
||||||
opened_ = true;
|
|
||||||
auto status = image_->open(device_path_.c_str(), TSK_IMG_TYPE_DETECT, 0);
|
|
||||||
if (status) {
|
|
||||||
opened_result_ = false;
|
|
||||||
return opened_result_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to open the device image volumn.
|
|
||||||
status = volume_->open(&*image_, 0, TSK_VS_TYPE_DETECT);
|
|
||||||
opened_result_ = (status == 0);
|
|
||||||
return opened_result_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Has the device open been attempted.
|
/// Has the device open been attempted.
|
||||||
@ -121,59 +112,56 @@ class DeviceHelper : private boost::noncopyable {
|
|||||||
std::set<std::string> loops_;
|
std::set<std::string> loops_;
|
||||||
};
|
};
|
||||||
|
|
||||||
QueryData genDevicePartitions(QueryContext& context) {
|
bool DeviceHelper::open() {
|
||||||
QueryData results;
|
if (opened_) {
|
||||||
|
return opened_result_;
|
||||||
auto devices = context.constraints["device"].getAll(EQUALS);
|
|
||||||
for (const auto& dev : devices) {
|
|
||||||
DeviceHelper dh(dev);
|
|
||||||
dh.partitions(([&results, &dev, &dh](const TskVsPartInfo* part) {
|
|
||||||
Row r;
|
|
||||||
r["device"] = dev;
|
|
||||||
r["partition"] = std::to_string(part->getAddr());
|
|
||||||
|
|
||||||
const auto* desc = part->getDesc();
|
|
||||||
if (desc != nullptr) {
|
|
||||||
r["label"] = desc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r["flags"] = "0";
|
// Attempt to open the device image.
|
||||||
if (part->getFlags() & TSK_VS_PART_FLAG_META) {
|
opened_ = true;
|
||||||
r["type"] = "meta";
|
auto status = image_->open(device_path_.c_str(), TSK_IMG_TYPE_DETECT, 0);
|
||||||
} else if (part->getFlags() & TSK_VS_PART_FLAG_UNALLOC) {
|
|
||||||
r["type"] = "unallocated";
|
|
||||||
} else {
|
|
||||||
r["type"] = "normal";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* fs = new TskFsInfo();
|
|
||||||
auto status = fs->open(part, TSK_FS_TYPE_DETECT);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
r["offset"] = BIGINT(part->getStart() * dh.getVolume()->getBlockSize());
|
opened_result_ = false;
|
||||||
r["blocks_size"] = BIGINT(dh.getVolume()->getBlockSize());
|
return opened_result_;
|
||||||
r["blocks"] = BIGINT(part->getLen());
|
|
||||||
r["inodes"] = "-1";
|
|
||||||
r["flags"] = INTEGER(part->getFlags());
|
|
||||||
} else {
|
|
||||||
// If there is a filesystem in this partition we can use the name/type
|
|
||||||
// of the filesystem as the "type".
|
|
||||||
r["type"] = TskFsInfo::typeToName(fs->getFsType());
|
|
||||||
r["flags"] = INTEGER(fs->getFlags());
|
|
||||||
r["offset"] = BIGINT(fs->getOffset());
|
|
||||||
r["blocks_size"] = BIGINT(fs->getBlockSize());
|
|
||||||
r["blocks"] = BIGINT(fs->getBlockCount());
|
|
||||||
r["inodes"] = BIGINT(fs->getINumCount());
|
|
||||||
}
|
|
||||||
delete fs;
|
|
||||||
results.push_back(r);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
// Attempt to open the device image volumn.
|
||||||
|
status = volume_->open(&*image_, 0, TSK_VS_TYPE_DETECT);
|
||||||
|
opened_result_ = (status == 0);
|
||||||
|
return opened_result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceHelper::inodes(
|
||||||
|
const std::set<std::string>& inodes,
|
||||||
|
TskFsInfo* fs,
|
||||||
|
std::function<void(const std::string&, TskFsFile*, const std::string&)>
|
||||||
|
predicate) {
|
||||||
|
// Given a set of constraint inodes, convert each to an INUM.
|
||||||
|
for (const auto& inode : inodes) {
|
||||||
|
long int meta = 0;
|
||||||
|
safeStrtol(inode, 10, meta);
|
||||||
|
auto* file = new TskFsFile();
|
||||||
|
if (file->open(fs, file, static_cast<TSK_INUM_T>(meta)) == 0) {
|
||||||
|
// Attempt to get the meta and filesystem name for the inode.
|
||||||
|
// If this inode is a file a valid meta/name structures are parsed.
|
||||||
|
auto* meta = file->getMeta();
|
||||||
|
if (meta != nullptr) {
|
||||||
|
const auto* name = meta->getName2(0);
|
||||||
|
if (name != nullptr) {
|
||||||
|
auto path = std::string(name->getName());
|
||||||
|
predicate(inode, file, path);
|
||||||
|
delete name;
|
||||||
|
}
|
||||||
|
delete meta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceHelper::generateFile(const std::string& partition,
|
void DeviceHelper::generateFile(const std::string& partition,
|
||||||
TskFsFile* file,
|
TskFsFile* file,
|
||||||
|
TskFsInfo* fs,
|
||||||
const std::string& path,
|
const std::string& path,
|
||||||
QueryData& results) {
|
QueryData& results) {
|
||||||
Row r;
|
Row r;
|
||||||
@ -182,10 +170,8 @@ void DeviceHelper::generateFile(const std::string& partition,
|
|||||||
r["path"] = path;
|
r["path"] = path;
|
||||||
r["filename"] = fs::path(path).leaf().string();
|
r["filename"] = fs::path(path).leaf().string();
|
||||||
|
|
||||||
const auto* fs = file->getFsInfo();
|
|
||||||
if (fs != nullptr) {
|
if (fs != nullptr) {
|
||||||
r["block_size"] = BIGINT(fs->getBlockSize());
|
r["block_size"] = BIGINT(fs->getBlockSize());
|
||||||
delete fs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* meta = file->getMeta();
|
const auto* meta = file->getMeta();
|
||||||
@ -246,7 +232,7 @@ void DeviceHelper::generateFiles(const std::string& partition,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (meta->getType() == TSK_FS_META_TYPE_REG) {
|
if (meta->getType() == TSK_FS_META_TYPE_REG) {
|
||||||
generateFile(partition, file, leaf, results);
|
generateFile(partition, file, fs, leaf, results);
|
||||||
} else if (meta->getType() == TSK_FS_META_TYPE_DIR) {
|
} else if (meta->getType() == TSK_FS_META_TYPE_DIR) {
|
||||||
if (name != nullptr && !TSK_FS_ISDOT(name->getName())) {
|
if (name != nullptr && !TSK_FS_ISDOT(name->getName())) {
|
||||||
additional[meta->getAddr()] = leaf;
|
additional[meta->getAddr()] = leaf;
|
||||||
@ -270,6 +256,111 @@ void DeviceHelper::generateFiles(const std::string& partition,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DeviceHashes {
|
||||||
|
std::string md5;
|
||||||
|
std::string sha1;
|
||||||
|
std::string sha256;
|
||||||
|
};
|
||||||
|
|
||||||
|
DeviceHashes hashInode(TskFsFile* file) {
|
||||||
|
Hash md5(HASH_TYPE_MD5);
|
||||||
|
Hash sha1(HASH_TYPE_SHA1);
|
||||||
|
Hash sha256(HASH_TYPE_SHA256);
|
||||||
|
|
||||||
|
// We are guaranteed by the expected callsite to have a valid meta.
|
||||||
|
auto* meta = file->getMeta();
|
||||||
|
if (meta == nullptr) {
|
||||||
|
return DeviceHashes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a maximum 'chunk' or block size to 1 page or the file size.
|
||||||
|
TSK_OFF_T size = meta->getSize();
|
||||||
|
auto buffer_size = (size < 4096) ? size : 4096;
|
||||||
|
|
||||||
|
// Allocate some heap memory and iterate over reading a chunk and updating.
|
||||||
|
auto* buffer = (char*)malloc(buffer_size * sizeof(char*));
|
||||||
|
if (buffer != nullptr) {
|
||||||
|
ssize_t chunk_size = 0;
|
||||||
|
for (ssize_t offset = 0; offset < size; offset += chunk_size) {
|
||||||
|
// Here max represents the local max requested bytes.
|
||||||
|
auto max = (size - offset < buffer_size) ? (size - offset) : buffer_size;
|
||||||
|
chunk_size =
|
||||||
|
file->read(offset, buffer, max, (TSK_FS_FILE_READ_FLAG_ENUM)0U);
|
||||||
|
if (chunk_size == -1 || chunk_size != max) {
|
||||||
|
// Huge problem, either a read failed or didn't read the max size.
|
||||||
|
free(buffer);
|
||||||
|
delete meta;
|
||||||
|
return DeviceHashes();
|
||||||
|
}
|
||||||
|
|
||||||
|
md5.update(buffer, chunk_size);
|
||||||
|
sha1.update(buffer, chunk_size);
|
||||||
|
sha256.update(buffer, chunk_size);
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
delete meta;
|
||||||
|
|
||||||
|
// Convert the set of hashes into a device hashes transport.
|
||||||
|
DeviceHashes dhs;
|
||||||
|
dhs.md5 = md5.digest();
|
||||||
|
dhs.sha1 = sha1.digest();
|
||||||
|
dhs.sha256 = sha256.digest();
|
||||||
|
return dhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryData genDeviceHash(QueryContext& context) {
|
||||||
|
QueryData results;
|
||||||
|
|
||||||
|
auto devices = context.constraints["device"].getAll(EQUALS);
|
||||||
|
// This table requires three columns to determine an action.
|
||||||
|
auto parts = context.constraints["partition"].getAll(EQUALS);
|
||||||
|
auto inodes = context.constraints["inode"].getAll(EQUALS);
|
||||||
|
|
||||||
|
for (const auto& dev : devices) {
|
||||||
|
// For each require device path, open a device helper that checks the
|
||||||
|
// image, checks the volume, and allows partition iteration.
|
||||||
|
DeviceHelper dh(dev);
|
||||||
|
dh.partitions(([&results, &dev, &dh, &parts, &inodes](
|
||||||
|
const TskVsPartInfo* part) {
|
||||||
|
// The table also requires a partition for searching.
|
||||||
|
auto address = std::to_string(part->getAddr());
|
||||||
|
if (address != *parts.begin()) {
|
||||||
|
// If this partition does not match the requested, continue.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* fs = new TskFsInfo();
|
||||||
|
auto status = fs->open(part, TSK_FS_TYPE_DETECT);
|
||||||
|
// Cannot retrieve file information without accessing the filesystem.
|
||||||
|
if (status) {
|
||||||
|
delete fs;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dh.inodes(inodes,
|
||||||
|
fs,
|
||||||
|
([&results, &address, &dev, &dh, &fs](const std::string& inode,
|
||||||
|
TskFsFile* file,
|
||||||
|
const std::string& path) {
|
||||||
|
Row r;
|
||||||
|
r["device"] = dev;
|
||||||
|
r["partition"] = address;
|
||||||
|
r["inode"] = inode;
|
||||||
|
|
||||||
|
auto hashes = hashInode(file);
|
||||||
|
r["md5"] = std::move(hashes.md5);
|
||||||
|
r["sha1"] = std::move(hashes.sha1);
|
||||||
|
r["sha256"] = std::move(hashes.sha256);
|
||||||
|
results.push_back(r);
|
||||||
|
}));
|
||||||
|
delete fs;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
QueryData genDeviceFile(QueryContext& context) {
|
QueryData genDeviceFile(QueryContext& context) {
|
||||||
QueryData results;
|
QueryData results;
|
||||||
|
|
||||||
@ -300,7 +391,7 @@ QueryData genDeviceFile(QueryContext& context) {
|
|||||||
|
|
||||||
auto* fs = new TskFsInfo();
|
auto* fs = new TskFsInfo();
|
||||||
auto status = fs->open(part, TSK_FS_TYPE_DETECT);
|
auto status = fs->open(part, TSK_FS_TYPE_DETECT);
|
||||||
// Cannot retrieve fail information without accessing the filesystem.
|
// Cannot retrieve file information without accessing the filesystem.
|
||||||
if (status) {
|
if (status) {
|
||||||
delete fs;
|
delete fs;
|
||||||
return;
|
return;
|
||||||
@ -317,27 +408,70 @@ QueryData genDeviceFile(QueryContext& context) {
|
|||||||
for (const auto& path : paths) {
|
for (const auto& path : paths) {
|
||||||
auto* file = new TskFsFile();
|
auto* file = new TskFsFile();
|
||||||
if (file->open(fs, file, path.c_str()) == 0) {
|
if (file->open(fs, file, path.c_str()) == 0) {
|
||||||
dh.generateFile(address, file, path, results);
|
dh.generateFile(address, file, fs, path, results);
|
||||||
}
|
}
|
||||||
delete file;
|
delete file;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& inode : inodes) {
|
dh.inodes(inodes,
|
||||||
long int meta = 0;
|
fs,
|
||||||
safeStrtol(inode, 10, meta);
|
([&results, &address, &dh, &fs](const std::string& inode,
|
||||||
auto* file = new TskFsFile();
|
TskFsFile* file,
|
||||||
if (file->open(fs, file, static_cast<TSK_INUM_T>(meta)) == 0) {
|
const std::string& path) {
|
||||||
std::string path;
|
dh.generateFile(address, file, fs, path, results);
|
||||||
auto* meta = file->getMeta();
|
}));
|
||||||
if (meta != nullptr) {
|
delete fs;
|
||||||
path = std::string(meta->getName2(0)->getName());
|
}));
|
||||||
}
|
}
|
||||||
delete meta;
|
|
||||||
dh.generateFile(address, file, path, results);
|
return results;
|
||||||
}
|
}
|
||||||
delete file;
|
|
||||||
|
QueryData genDevicePartitions(QueryContext& context) {
|
||||||
|
QueryData results;
|
||||||
|
|
||||||
|
auto devices = context.constraints["device"].getAll(EQUALS);
|
||||||
|
for (const auto& dev : devices) {
|
||||||
|
DeviceHelper dh(dev);
|
||||||
|
dh.partitions(([&results, &dev, &dh](const TskVsPartInfo* part) {
|
||||||
|
Row r;
|
||||||
|
r["device"] = dev;
|
||||||
|
r["partition"] = std::to_string(part->getAddr());
|
||||||
|
|
||||||
|
const auto* desc = part->getDesc();
|
||||||
|
if (desc != nullptr) {
|
||||||
|
r["label"] = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
r["flags"] = "0";
|
||||||
|
if (part->getFlags() & TSK_VS_PART_FLAG_META) {
|
||||||
|
r["type"] = "meta";
|
||||||
|
} else if (part->getFlags() & TSK_VS_PART_FLAG_UNALLOC) {
|
||||||
|
r["type"] = "unallocated";
|
||||||
|
} else {
|
||||||
|
r["type"] = "normal";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* fs = new TskFsInfo();
|
||||||
|
auto status = fs->open(part, TSK_FS_TYPE_DETECT);
|
||||||
|
if (status) {
|
||||||
|
r["offset"] = BIGINT(part->getStart() * dh.getVolume()->getBlockSize());
|
||||||
|
r["blocks_size"] = BIGINT(dh.getVolume()->getBlockSize());
|
||||||
|
r["blocks"] = BIGINT(part->getLen());
|
||||||
|
r["inodes"] = "-1";
|
||||||
|
r["flags"] = INTEGER(part->getFlags());
|
||||||
|
} else {
|
||||||
|
// If there is a filesystem in this partition we can use the name/type
|
||||||
|
// of the filesystem as the "type".
|
||||||
|
r["type"] = TskFsInfo::typeToName(fs->getFsType());
|
||||||
|
r["flags"] = INTEGER(fs->getFlags());
|
||||||
|
r["offset"] = BIGINT(fs->getOffset());
|
||||||
|
r["blocks_size"] = BIGINT(fs->getBlockSize());
|
||||||
|
r["blocks"] = BIGINT(fs->getBlockCount());
|
||||||
|
r["inodes"] = BIGINT(fs->getINumCount());
|
||||||
}
|
}
|
||||||
delete fs;
|
delete fs;
|
||||||
|
results.push_back(r);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
specs/device_hash.table
Normal file
11
specs/device_hash.table
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
table_name("device_hash")
|
||||||
|
description("Similar to the hash table, but use TSK and allow block address access.")
|
||||||
|
schema([
|
||||||
|
Column("device", TEXT, "Absolute file path to device node", required=True),
|
||||||
|
Column("partition", TEXT, "A partition number", required=True),
|
||||||
|
Column("inode", BIGINT, "Filesystem inode number", required=True),
|
||||||
|
Column("md5", TEXT, "MD5 hash of provided inode data"),
|
||||||
|
Column("sha1", TEXT, "SHA1 hash of provided inode data"),
|
||||||
|
Column("sha256", TEXT, "SHA256 hash of provided inode data"),
|
||||||
|
])
|
||||||
|
implementation("forensic/sleuthkit@genDeviceHash")
|
@ -17,7 +17,6 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import utils
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import argparse
|
import argparse
|
||||||
@ -28,6 +27,8 @@ except ImportError:
|
|||||||
# Import the testing utils
|
# Import the testing utils
|
||||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/tests/")
|
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/tests/")
|
||||||
|
|
||||||
|
import utils
|
||||||
|
|
||||||
KB = 1024 * 1024
|
KB = 1024 * 1024
|
||||||
RANGES = {
|
RANGES = {
|
||||||
"colors": (utils.blue, utils.green, utils.yellow, utils.red),
|
"colors": (utils.blue, utils.green, utils.yellow, utils.red),
|
||||||
@ -306,7 +307,7 @@ if __name__ == "__main__":
|
|||||||
profile1 = json.loads(fh.read())
|
profile1 = json.loads(fh.read())
|
||||||
|
|
||||||
if not os.path.exists(args.shell):
|
if not os.path.exists(args.shell):
|
||||||
print("Cannot find --daemon: %s" % (args.shell))
|
print("Cannot find --shell: %s" % (args.shell))
|
||||||
exit(1)
|
exit(1)
|
||||||
if args.config is None and not os.path.exists(args.tables):
|
if args.config is None and not os.path.exists(args.tables):
|
||||||
print("Cannot find --tables: %s" % (args.tables))
|
print("Cannot find --tables: %s" % (args.tables))
|
||||||
|
Loading…
Reference in New Issue
Block a user