Skip to content

Commit

Permalink
TSK integration and example tables
Browse files Browse the repository at this point in the history
  • Loading branch information
Teddy Reed committed Dec 4, 2015
1 parent 4f0ea13 commit 33e400b
Show file tree
Hide file tree
Showing 14 changed files with 426 additions and 7 deletions.
5 changes: 4 additions & 1 deletion osquery/tables/CMakeLists.txt
Expand Up @@ -19,6 +19,7 @@ if(APPLE)

ADD_OSQUERY_LINK_ADDITIONAL("archive")
ADD_OSQUERY_LINK_ADDITIONAL("magic")
ADD_OSQUERY_LINK_ADDITIONAL("tsk")
elseif(FREEBSD)
file(GLOB OSQUERY_FREEBSD_TABLES "*/freebsd/*.cpp")
ADD_OSQUERY_LIBRARY_ADDITIONAL(osquery_tables_freebsd
Expand Down Expand Up @@ -70,12 +71,13 @@ else()
ADD_OSQUERY_LINK_ADDITIONAL("libuuid.so")
ADD_OSQUERY_LINK_ADDITIONAL("ip4tc")
ADD_OSQUERY_LINK_ADDITIONAL("libmagic.so")
ADD_OSQUERY_LINK_ADDITIONAL("tsk")
endif()


file(GLOB OSQUERY_CROSS_APPLICATIONS_TABLES "applications/*.cpp")
file(GLOB OSQUERY_CROSS_SYSTEM_TABLES "system/*.cpp")
file(GLOB OSQUERY_CROSS_NETWORKING_TABLES "networking/*.cpp")
file(GLOB OSQUERY_CROSS_FORENSIC_TABLES "forensic/*.cpp")
if(APPLE OR LINUX)
file(GLOB OSQUERY_CROSS_EVENTS_TABLES "events/*.cpp")
else()
Expand All @@ -86,6 +88,7 @@ ADD_OSQUERY_LIBRARY_ADDITIONAL(osquery_tables
${OSQUERY_CROSS_SYSTEM_TABLES}
${OSQUERY_CROSS_NETWORKING_TABLES}
${OSQUERY_CROSS_EVENTS_TABLES}
${OSQUERY_CROSS_FORENSIC_TABLES}
)

file(GLOB OSQUERY_CROSS_TABLES_TESTS "[!uo]*/tests/*/*.cpp")
Expand Down
350 changes: 350 additions & 0 deletions osquery/tables/forensic/sleuthkit.cpp
@@ -0,0 +1,350 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

#include <map>
#include <set>

#include <boost/noncopyable.hpp>
#include <boost/filesystem.hpp>

#include <tsk/libtsk.h>

#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/tables.h>

#include "osquery/core/conversions.h"

namespace fs = boost::filesystem;

namespace osquery {
namespace tables {

const std::map<TSK_FS_META_TYPE_ENUM, std::string> kTSKTypeNames{
{TSK_FS_META_TYPE_REG, "regular"},
{TSK_FS_META_TYPE_DIR, "directory"},
{TSK_FS_META_TYPE_LNK, "symlink"},
{TSK_FS_META_TYPE_BLK, "block"},
{TSK_FS_META_TYPE_CHR, "character"},
{TSK_FS_META_TYPE_FIFO, "fifo"},
{TSK_FS_META_TYPE_SOCK, "socket"},
};

class DeviceHelper : private boost::noncopyable {
public:
DeviceHelper(const std::string& device_path)
: image_(std::make_shared<TskImgInfo>()),
volume_(std::make_shared<TskVsInfo>()),
device_path_(device_path) {}

/// Volume partition iterator.
void forEachPartition(
std::function<void(const TskVsPartInfo* partition)> predicate) {
if (open()) {
for (TSK_PNUM_T i = 0; i < volume_->getPartCount(); ++i) {
auto* part = volume_->getPart(i);
if (part == nullptr) {
continue;
}
predicate(part);
}
}
}

/// Provide a partition description for context and iterate from path.
void generateFiles(const std::string& partition,
TskFsInfo* fs,
const std::string& path,
QueryData& results,
TSK_INUM_T inode = 0);

/// Similar to generateFiles but only yield a row to results.
void generateFile(const std::string& partition,
TskFsFile* file,
const std::string& path,
QueryData& results);

/// Volume accessor, used for computing offsets using block/sector size.
const std::shared_ptr<TskVsInfo>& getVolume() { return volume_; }

/// Reset stack counting for directory iteration.
void resetStack() {
stack_ = 0;
std::set<std::string>().swap(loops_);
}

private:
/// Attempt to open the provided device image and volume.
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:
/// Has the device open been attempted.
bool opened_{false};

/// The result of the opened request.
bool opened_result_{false};

/// Image structure.
std::shared_ptr<TskImgInfo> image_{nullptr};

/// Volume structure.
std::shared_ptr<TskVsInfo> volume_{nullptr};

/// Filesystem path to the device node.
std::string device_path_;

size_t stack_{0};
std::set<std::string> loops_;
};

QueryData genDevicePartitions(QueryContext& context) {
QueryData results;

auto devices = context.constraints["device"].getAll(EQUALS);
for (const auto& dev : devices) {
DeviceHelper dh(dev);
dh.forEachPartition(([&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());
delete fs;
results.push_back(r);
return;
}

// 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;
}

void DeviceHelper::generateFile(const std::string& partition,
TskFsFile* file,
const std::string& path,
QueryData& results) {
Row r;
r["device"] = device_path_;
r["partition"] = partition;
r["path"] = path;
r["filename"] = fs::path(path).leaf().string();

const auto* fs = file->getFsInfo();
if (fs != nullptr) {
r["block_size"] = BIGINT(fs->getBlockSize());
delete fs;
}

const auto* meta = file->getMeta();
if (meta != nullptr) {
r["inode"] = BIGINT(meta->getAddr());
r["uid"] = BIGINT(meta->getUid());
r["gid"] = BIGINT(meta->getGid());
r["mode"] = TEXT(meta->getMode());
r["size"] = BIGINT(meta->getSize());
r["atime"] = BIGINT(meta->getATime());
r["mtime"] = BIGINT(meta->getMTime());
r["ctime"] = BIGINT(meta->getCrTime());
r["hard_links"] = INTEGER(meta->getNLink());
if (kTSKTypeNames.count(meta->getType())) {
r["type"] = kTSKTypeNames.at(meta->getType());
} else {
r["type"] = "unknown";
}
delete meta;
}
results.push_back(r);
}

void DeviceHelper::generateFiles(const std::string& partition,
TskFsInfo* fs,
const std::string& path,
QueryData& results,
TSK_INUM_T inode) {
if (stack_++ > 1024) {
return;
}

auto* dir = new TskFsDir();
if (dir->open(fs, ((inode == 0) ? fs->getRootINum() : inode))) {
delete dir;
return;
}

// Iterate through the directory.
std::map<TSK_INUM_T, std::string> additional;
for (size_t i = 0; i < dir->getSize(); i++) {
auto* file = dir->getFile(i);
if (file == nullptr) {
continue;
}

// Failure to access the file's metadata information.
auto* meta = file->getMeta();
if (meta == nullptr) {
delete file;
continue;
}

std::string leaf;
auto* name = file->getName();
if (name != nullptr) {
leaf = (fs::path(path) / name->getName()).string();
}

if (meta->getType() == TSK_FS_META_TYPE_REG) {
generateFile(partition, file, leaf, results);
} else if (meta->getType() == TSK_FS_META_TYPE_DIR) {
if (name != nullptr && !TSK_FS_ISDOT(name->getName())) {
additional[meta->getAddr()] = leaf;
}
}

if (name != nullptr) {
delete name;
}
delete meta;
delete file;
}
delete dir;

// If we are recursing.
for (const auto& dir : additional) {
if (std::find(loops_.begin(), loops_.end(), dir.second) == loops_.end()) {
generateFiles(partition, fs, dir.second, results, dir.first);
loops_.insert(dir.second);
}
}
}

QueryData genDeviceFile(QueryContext& context) {
QueryData results;

auto devices = context.constraints["device"].getAll(EQUALS);
// This table requires two or more columns to determine an action.
auto parts = context.constraints["partition"].getAll(EQUALS);
// Additionally, paths or inodes can be used to search.
auto paths = context.constraints["path"].getAll(EQUALS);
auto inodes = context.constraints["inode"].getAll(EQUALS);

if (devices.empty() || parts.size() != 1) {
TLOG << "Device files require at least one device and a single partition";
return {};
}

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.forEachPartition(([&results, &dev, &dh, &parts, &inodes, &paths](
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 fail information without accessing the filesystem.
if (status) {
delete fs;
return;
}

// If no inodes or paths were provided as constraints assume a walk of
// the partition was requested.
if (inodes.empty() && paths.empty()) {
dh.generateFiles(address, fs, "/", results);
dh.resetStack();
}

// For each path the canonical name must be mapped to an inode address.
for (const auto& path : paths) {
auto* file = new TskFsFile();
if (file->open(fs, file, path.c_str()) == 0) {
dh.generateFile(address, file, path, results);
}
delete file;
}

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) {
std::string path;
auto* meta = file->getMeta();
if (meta != nullptr) {
path = std::string(meta->getName2(0)->getName());
}
delete meta;
dh.generateFile(address, file, path, results);
}
delete file;
}
delete fs;
}));
}

return results;
}
}
}
2 changes: 2 additions & 0 deletions specs/blacklist
Expand Up @@ -9,6 +9,8 @@ freebsd:chrome_extensions
freebsd:disk_encryption
freebsd:file_events
freebsd:firefox_addons
freebsd:device_file
freebsd:device_partitions
freebsd:user_groups
freebsd:hardware_events
freebsd:kernel_info
Expand Down

0 comments on commit 33e400b

Please sign in to comment.