2015-04-17 20:03:43 +00:00
|
|
|
/*
|
2016-02-11 19:48:58 +00:00
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
2015-04-17 20:03:43 +00:00
|
|
|
* 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 <boost/filesystem.hpp>
|
|
|
|
|
2015-04-22 23:41:51 +00:00
|
|
|
#include <osquery/filesystem.h>
|
2015-04-17 20:03:43 +00:00
|
|
|
#include <osquery/logger.h>
|
|
|
|
#include <osquery/tables.h>
|
|
|
|
#include <osquery/status.h>
|
|
|
|
|
2015-06-01 22:53:52 +00:00
|
|
|
#include "osquery/tables/other/yara_utils.h"
|
2015-04-17 20:03:43 +00:00
|
|
|
|
|
|
|
#ifdef CONCAT
|
|
|
|
#undef CONCAT
|
|
|
|
#endif
|
|
|
|
#include <yara.h>
|
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
namespace tables {
|
|
|
|
|
2015-09-20 07:06:57 +00:00
|
|
|
void doYARAScan(YR_RULES* rules,
|
|
|
|
const std::string& path,
|
|
|
|
QueryData& results,
|
|
|
|
const std::string& group,
|
|
|
|
const std::string& sigfile) {
|
2015-04-22 23:41:51 +00:00
|
|
|
Row r;
|
|
|
|
|
|
|
|
// These are default values, to be updated in YARACallback.
|
|
|
|
r["count"] = INTEGER(0);
|
|
|
|
r["matches"] = std::string("");
|
2015-07-26 19:38:44 +00:00
|
|
|
r["strings"] = std::string("");
|
|
|
|
r["tags"] = std::string("");
|
2015-04-22 23:41:51 +00:00
|
|
|
|
2015-09-20 07:06:57 +00:00
|
|
|
// This could use target_path instead to be consistent with yara_events.
|
2015-04-22 23:41:51 +00:00
|
|
|
r["path"] = path;
|
|
|
|
r["sig_group"] = std::string(group);
|
|
|
|
r["sigfile"] = std::string(sigfile);
|
|
|
|
|
2015-09-20 07:06:57 +00:00
|
|
|
// Perform the scan, using the static YARA subscriber callback.
|
2016-02-17 07:10:08 +00:00
|
|
|
int result = yr_rules_scan_file(
|
|
|
|
rules, path.c_str(), SCAN_FLAGS_FAST_MODE, YARACallback, (void*)&r, 0);
|
2015-09-20 07:06:57 +00:00
|
|
|
if (result == ERROR_SUCCESS) {
|
|
|
|
results.push_back(std::move(r));
|
2015-04-22 23:41:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-17 20:03:43 +00:00
|
|
|
QueryData genYara(QueryContext& context) {
|
|
|
|
QueryData results;
|
|
|
|
|
2015-09-20 07:06:57 +00:00
|
|
|
// Must specify a path constraint and at least one of sig_group or sigfile.
|
2015-04-17 20:03:43 +00:00
|
|
|
auto groups = context.constraints["sig_group"].getAll(EQUALS);
|
|
|
|
auto sigfiles = context.constraints["sigfile"].getAll(EQUALS);
|
2015-04-22 23:41:51 +00:00
|
|
|
if (groups.size() == 0 && sigfiles.size() == 0) {
|
2015-04-17 20:03:43 +00:00
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2015-09-20 07:06:57 +00:00
|
|
|
// This could be abstracted into a common "get rules for group" function.
|
[fix #1390] query pack re-org
This commit contains the features specified in #1390 as well as a
refactoring of the general osquery configuration code.
The API for the config plugins hasn't changed, although now there's a
`genPack` method that config plugins can implement. If a plugin doesn't
implement `genPack`, then the map<string, string> format cannot be used.
The default config plugin, the filesystem plugin, now implements
`genPack`, so existing query packs code will continue to work as it
always has.
Now many other config plugins can implement custom pack handling for
what makes sense in their context. `genPacks` is not a pure virtual, so
it doesn't have to be implemented in your plugin if you don't want to
use it. Also, more importantly, all config plugins can use the standard
inline pack format if they want to use query packs. Which is awesome.
For more information, refer to #1390, the documentation and the doxygen
comments included with this pull requests, as well as the following
example config which is now supported, regardless of what config plugin
you're using:
```json
{
"options": {
"enable_monitor": "true"
},
"packs": {
"core_os_monitoring": {
"version": "1.4.5",
"discovery": [
"select pid from processes where name like '%osqueryd%';"
],
"queries": {
"kernel_modules": {
"query": "SELECT name, size FROM kernel_modules;",
"interval": 600
},
"system_controls": {
"query": "SELECT * FROM system_controls;",
"interval": 600,
"snapshot": true,
},
"usb_devices": {
"query": "SELECT * FROM usb_devices;",
"interval": 600
}
}
},
"osquery_internal_info": {
"version": "1.4.5",
"discovery": [
"select pid from processes where name like '%osqueryd%';"
],
"queries": {
"info": {
"query": "select i.*, p.resident_size, p.user_time, p.system_time, time.minutes as counter from osquery_info i, processes p, time where p.pid = i.pid;",
"interval": 60,
"snapshot": true
},
"registry": {
"query": "SELECT * FROM osquery_registry;",
"interval": 600,
"snapshot": true
},
"schedule": {
"query": "select name, interval, executions, output_size, wall_time, (user_time/executions) as avg_user_time, (system_time/executions) as avg_system_time, average_memory from osquery_schedule;",
"interval": 60,
"snapshot": true
}
}
}
}
}
```
The `osquery_packs` table was modified to remove the superfluous
columns which could already have been found in `osquery_schedule`. Two
more columns were added in their place, representing stats about pack's
discovery query execution history.
Notably, the internal API for the `osquery::Config` class has changed
rather dramatically as apart of the refactoring. We think this is an
improvement. While strictly adhering to the osquery config plugin
interface will have avoided any compatibility errors, advanced users may
notice compilation errors if they access config data directly. All
internal users of the config have obviously been updated. Yet another
reason to merge your code into mainline; we update it for you when we
refactor!
2015-08-19 20:27:49 +00:00
|
|
|
auto parser = Config::getParser("yara");
|
|
|
|
if (parser == nullptr || parser.get() == nullptr) {
|
|
|
|
LOG(ERROR) << "YARA config parser plugin has no pointer";
|
2015-04-17 20:03:43 +00:00
|
|
|
return results;
|
|
|
|
}
|
2015-08-14 01:04:03 +00:00
|
|
|
|
2015-09-20 07:06:57 +00:00
|
|
|
std::shared_ptr<YARAConfigParserPlugin> yaraParser = nullptr;
|
[fix #1390] query pack re-org
This commit contains the features specified in #1390 as well as a
refactoring of the general osquery configuration code.
The API for the config plugins hasn't changed, although now there's a
`genPack` method that config plugins can implement. If a plugin doesn't
implement `genPack`, then the map<string, string> format cannot be used.
The default config plugin, the filesystem plugin, now implements
`genPack`, so existing query packs code will continue to work as it
always has.
Now many other config plugins can implement custom pack handling for
what makes sense in their context. `genPacks` is not a pure virtual, so
it doesn't have to be implemented in your plugin if you don't want to
use it. Also, more importantly, all config plugins can use the standard
inline pack format if they want to use query packs. Which is awesome.
For more information, refer to #1390, the documentation and the doxygen
comments included with this pull requests, as well as the following
example config which is now supported, regardless of what config plugin
you're using:
```json
{
"options": {
"enable_monitor": "true"
},
"packs": {
"core_os_monitoring": {
"version": "1.4.5",
"discovery": [
"select pid from processes where name like '%osqueryd%';"
],
"queries": {
"kernel_modules": {
"query": "SELECT name, size FROM kernel_modules;",
"interval": 600
},
"system_controls": {
"query": "SELECT * FROM system_controls;",
"interval": 600,
"snapshot": true,
},
"usb_devices": {
"query": "SELECT * FROM usb_devices;",
"interval": 600
}
}
},
"osquery_internal_info": {
"version": "1.4.5",
"discovery": [
"select pid from processes where name like '%osqueryd%';"
],
"queries": {
"info": {
"query": "select i.*, p.resident_size, p.user_time, p.system_time, time.minutes as counter from osquery_info i, processes p, time where p.pid = i.pid;",
"interval": 60,
"snapshot": true
},
"registry": {
"query": "SELECT * FROM osquery_registry;",
"interval": 600,
"snapshot": true
},
"schedule": {
"query": "select name, interval, executions, output_size, wall_time, (user_time/executions) as avg_user_time, (system_time/executions) as avg_system_time, average_memory from osquery_schedule;",
"interval": 60,
"snapshot": true
}
}
}
}
}
```
The `osquery_packs` table was modified to remove the superfluous
columns which could already have been found in `osquery_schedule`. Two
more columns were added in their place, representing stats about pack's
discovery query execution history.
Notably, the internal API for the `osquery::Config` class has changed
rather dramatically as apart of the refactoring. We think this is an
improvement. While strictly adhering to the osquery config plugin
interface will have avoided any compatibility errors, advanced users may
notice compilation errors if they access config data directly. All
internal users of the config have obviously been updated. Yet another
reason to merge your code into mainline; we update it for you when we
refactor!
2015-08-19 20:27:49 +00:00
|
|
|
try {
|
|
|
|
yaraParser = std::dynamic_pointer_cast<YARAConfigParserPlugin>(parser);
|
|
|
|
} catch (const std::bad_cast& e) {
|
|
|
|
LOG(ERROR) << "Error casting yara config parser plugin";
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
if (yaraParser == nullptr || yaraParser.get() == nullptr) {
|
|
|
|
LOG(ERROR) << "YARA config parser plugin has no pointer";
|
2015-05-21 08:22:00 +00:00
|
|
|
return results;
|
|
|
|
}
|
2015-08-14 01:04:03 +00:00
|
|
|
auto& rules = yaraParser->rules();
|
2015-04-17 20:03:43 +00:00
|
|
|
|
2015-04-22 23:41:51 +00:00
|
|
|
// Collect all paths specified too.
|
2015-09-20 07:06:57 +00:00
|
|
|
auto paths = context.constraints["path"].getAll(EQUALS);
|
2016-02-17 07:10:08 +00:00
|
|
|
context.expandConstraints(
|
|
|
|
"path",
|
|
|
|
LIKE,
|
|
|
|
paths,
|
|
|
|
([&](const std::string& pattern, std::set<std::string>& out) {
|
|
|
|
std::vector<std::string> patterns;
|
|
|
|
auto status =
|
|
|
|
resolveFilePattern(pattern, patterns, GLOB_FILES | GLOB_NO_CANON);
|
|
|
|
if (status.ok()) {
|
|
|
|
for (const auto& resolved : patterns) {
|
|
|
|
// Check that each resolved path is readable.
|
|
|
|
if (isReadable(resolved)) {
|
|
|
|
paths.insert(resolved);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}));
|
2015-04-17 20:03:43 +00:00
|
|
|
|
2015-04-22 23:41:51 +00:00
|
|
|
// Compile all sigfiles into a map.
|
|
|
|
for (const auto& file : sigfiles) {
|
2015-09-20 07:06:57 +00:00
|
|
|
// Check if this "ad-hoc" signature file has not been used/compiled.
|
2015-08-14 01:04:03 +00:00
|
|
|
if (rules.count(file) == 0) {
|
2015-09-20 07:06:57 +00:00
|
|
|
// If this is a relative path append the default yara search path.
|
2015-08-14 01:04:03 +00:00
|
|
|
auto path = (file[0] != '/') ? std::string("/etc/osquery/yara/") : "";
|
|
|
|
path += file;
|
2015-04-17 20:03:43 +00:00
|
|
|
|
2015-08-14 01:04:03 +00:00
|
|
|
YR_RULES* tmp_rules = nullptr;
|
|
|
|
auto status = compileSingleFile(path, &tmp_rules);
|
|
|
|
if (!status.ok()) {
|
2015-09-20 07:06:57 +00:00
|
|
|
VLOG(1) << "YARA compile error: " << status.toString();
|
|
|
|
continue;
|
2015-08-14 01:04:03 +00:00
|
|
|
}
|
2015-09-20 07:06:57 +00:00
|
|
|
// Cache the compiled rules by setting the unique signature file path
|
|
|
|
// as the lookup name. Additional signature file uses will skip the
|
|
|
|
// compile step and be added as rule groups.
|
|
|
|
rules[file] = tmp_rules;
|
2015-04-22 23:41:51 +00:00
|
|
|
}
|
2015-09-20 07:06:57 +00:00
|
|
|
// Assemble an "ad-hoc" group using the signature file path as the name.
|
|
|
|
groups.insert(file);
|
2015-04-22 23:41:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Scan every path pair.
|
2016-02-17 07:10:08 +00:00
|
|
|
for (const auto& path : paths) {
|
2015-09-20 07:06:57 +00:00
|
|
|
// Scan using the signature groups.
|
2015-04-17 20:03:43 +00:00
|
|
|
for (const auto& group : groups) {
|
2015-09-20 07:06:57 +00:00
|
|
|
if (rules.count(group) > 0) {
|
2016-02-17 07:10:08 +00:00
|
|
|
doYARAScan(rules[group], path.c_str(), results, group, group);
|
2015-04-17 20:03:43 +00:00
|
|
|
}
|
|
|
|
}
|
2015-04-22 23:41:51 +00:00
|
|
|
}
|
|
|
|
|
2015-10-21 06:02:58 +00:00
|
|
|
return results;
|
2015-04-17 20:03:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|