mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-07 18:08:53 +00:00
[Fix #758] Parse startup_items Alias data
This commit is contained in:
parent
deae24b662
commit
1ea06a9d15
@ -25,6 +25,8 @@ namespace osquery {
|
||||
|
||||
typedef bai::binary_from_base64<const char*> base64_str;
|
||||
typedef bai::transform_width<base64_str, 8, 6> base64_dec;
|
||||
typedef bai::transform_width<std::string::const_iterator, 6, 8> base64_enc;
|
||||
typedef bai::base64_from_binary<base64_enc> it_base64;
|
||||
|
||||
std::string base64Decode(const std::string& encoded) {
|
||||
std::string is;
|
||||
@ -44,7 +46,7 @@ std::string base64Decode(const std::string& encoded) {
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return std::string();
|
||||
return "";
|
||||
}
|
||||
|
||||
std::copy(base64_dec(is.data()),
|
||||
@ -57,14 +59,12 @@ std::string base64Decode(const std::string& encoded) {
|
||||
std::string base64Encode(const std::string& unencoded) {
|
||||
std::stringstream os;
|
||||
|
||||
typedef boost::archive::iterators::base64_from_binary<boost::archive::iterators::transform_width<std::string::const_iterator,6,8> > it_base64_t;
|
||||
uint32_t size = unencoded.size();
|
||||
|
||||
if (size == 0) {
|
||||
if (unencoded.size() == 0) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
unsigned int writePaddChars = (3-unencoded.length()%3)%3;
|
||||
std::string base64(it_base64_t(unencoded.begin()),it_base64_t(unencoded.end()));
|
||||
std::string base64(it_base64(unencoded.begin()), it_base64(unencoded.end()));
|
||||
base64.append(writePaddChars,'=');
|
||||
os << base64;
|
||||
return os.str();
|
||||
|
@ -30,6 +30,15 @@ TEST_F(ConversionsTests, test_conversion) {
|
||||
boost::shared_ptr<Foobar> b2 = std_to_boost_shared_ptr(s2);
|
||||
EXPECT_EQ(s2.get(), b2.get());
|
||||
}
|
||||
|
||||
TEST_F(ConversionsTests, test_base64) {
|
||||
std::string unencoded = "HELLO";
|
||||
auto encoded = base64Encode(unencoded);
|
||||
EXPECT_NE(encoded.size(), 0);
|
||||
|
||||
auto unencoded2 = base64Decode(encoded);
|
||||
EXPECT_EQ(unencoded, unencoded2);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
@ -8,8 +8,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <osquery/core.h>
|
||||
@ -17,6 +15,8 @@
|
||||
#include <osquery/filesystem.h>
|
||||
#include <osquery/logger.h>
|
||||
|
||||
#include "osquery/core/conversions.h"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
@ -30,115 +30,115 @@ const std::vector<std::string> kLibraryStartupItemPaths = {
|
||||
// Path (after /Users/foo) where the login items plist will be found
|
||||
const std::string kLoginItemsPlistPath =
|
||||
"Library/Preferences/com.apple.loginitems.plist";
|
||||
|
||||
// Key to the array within the Login Items plist containing the items
|
||||
const std::string kLoginItemsKeyPath = "SessionItems.CustomListItems";
|
||||
|
||||
/**
|
||||
* Find startup items in Library directories
|
||||
*
|
||||
* Based on
|
||||
* https://github.com/synack/knockknock/blob/master/plugins/startupItem.py
|
||||
*/
|
||||
void getLibraryStartupItems(QueryData& results) {
|
||||
for (const auto& dir : kLibraryStartupItemPaths) {
|
||||
fs::directory_iterator it((fs::path(dir))), end;
|
||||
try {
|
||||
for (; it != end; ++it) {
|
||||
if (!fs::exists(it->status()) || !fs::is_directory(it->status())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Row r;
|
||||
r["name"] = it->path().string();
|
||||
r["path"] = it->path().string();
|
||||
r["type"] = "Startup Item";
|
||||
r["source"] = dir;
|
||||
results.push_back(r);
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
VLOG(1) << "Error traversing " << dir << ": " << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a Login Items Plist Alias data for bin path
|
||||
*/
|
||||
Status parseAliasData(const std::string& data, std::string& filepath) {
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
int size = (int)data[i];
|
||||
if (size < 2 || size > data.length() - i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string possible_file = "/" + data.substr(i + 1, size);
|
||||
// This data sometimes contains null bytes. We don't want to consider a
|
||||
// path that wasn't the expected length due to null bytes.
|
||||
if (strlen(possible_file.c_str()) != size + 1) {
|
||||
continue;
|
||||
}
|
||||
if (fs::exists(possible_file)) {
|
||||
filepath = possible_file;
|
||||
return Status(0, "OK");
|
||||
}
|
||||
}
|
||||
return Status(1, "No file paths found");
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the login items available in System Preferences
|
||||
*
|
||||
* Based on
|
||||
* https://github.com/synack/knockknock/blob/master/plugins/loginItem.py
|
||||
*/
|
||||
void getLoginItems(QueryData& results) {
|
||||
for (const auto& dir : getHomeDirectories()) {
|
||||
pt::ptree tree;
|
||||
fs::path plist_path = dir / kLoginItemsPlistPath;
|
||||
try {
|
||||
if (!fs::exists(plist_path) || !fs::is_regular_file(plist_path)) {
|
||||
void genLibraryStartupItems(const std::string& sysdir, QueryData& results) {
|
||||
try {
|
||||
fs::directory_iterator it((fs::path(sysdir))), end;
|
||||
for (; it != end; ++it) {
|
||||
if (!fs::exists(it->status()) || !fs::is_directory(it->status())) {
|
||||
continue;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
// Likely permission denied
|
||||
VLOG(1) << "Error checking path " << plist_path << ": " << e.what();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto status = osquery::parsePlist(plist_path.string(), tree);
|
||||
if (!status.ok()) {
|
||||
VLOG(1) << "Error parsing " << plist_path << ": " << status.toString();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Enumerate Login Items if we successfully opened the plist
|
||||
for (const auto& entry : tree.get_child(kLoginItemsKeyPath)) {
|
||||
Row r;
|
||||
|
||||
auto name = entry.second.get<std::string>("Name");
|
||||
r["name"] = name;
|
||||
r["type"] = "Login Item";
|
||||
r["source"] = plist_path.string();
|
||||
auto alias_data = entry.second.get<std::string>("Alias");
|
||||
try {
|
||||
std::string bin_path;
|
||||
if (!parseAliasData(alias_data, bin_path).ok()) {
|
||||
VLOG(1) << "No valid path found for " << name << " in " << plist_path;
|
||||
}
|
||||
r["path"] = bin_path;
|
||||
} catch (const std::exception& e) {
|
||||
VLOG(1) << "Error parsing alias data for " << name << " in "
|
||||
<< plist_path;
|
||||
}
|
||||
r["name"] = it->path().string();
|
||||
r["path"] = it->path().string();
|
||||
r["type"] = "Startup Item";
|
||||
r["source"] = sysdir;
|
||||
results.push_back(r);
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
VLOG(1) << "Error traversing " << sysdir << ": " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a Login Items Plist Alias data for bin path
|
||||
Status parseAliasData(const std::string& data, std::string& result) {
|
||||
auto decoded = base64Decode(data);
|
||||
if (decoded.size() == 0) {
|
||||
// Base64 encoded data (from plist parsing) failed to decode.
|
||||
return Status(1, "Failed base64 decode");
|
||||
}
|
||||
|
||||
auto alias = CFDataCreate(
|
||||
kCFAllocatorDefault, (const UInt8*)decoded.c_str(), decoded.size());
|
||||
if (alias == nullptr) {
|
||||
// Failed to create CFData object.
|
||||
return Status(2, "CFData allocation failed");
|
||||
}
|
||||
|
||||
auto bookmark =
|
||||
CFURLCreateBookmarkDataFromAliasRecord(kCFAllocatorDefault, alias);
|
||||
if (bookmark == nullptr) {
|
||||
CFRelease(alias);
|
||||
return Status(1, "Alias data is not a bookmark");
|
||||
}
|
||||
|
||||
auto url = CFURLCreateByResolvingBookmarkData(
|
||||
kCFAllocatorDefault, bookmark, 0, NULL, NULL, NULL, NULL);
|
||||
if (url == nullptr) {
|
||||
CFRelease(alias);
|
||||
CFRelease(bookmark);
|
||||
return Status(1, "Alias data is not a URL bookmark");
|
||||
}
|
||||
|
||||
// Get the URL-formatted path.
|
||||
result = stringFromCFString(CFURLGetString(url));
|
||||
if (result.substr(0, 7) == "file://") {
|
||||
result = result.substr(7);
|
||||
}
|
||||
|
||||
CFRelease(alias);
|
||||
CFRelease(bookmark);
|
||||
CFRelease(url);
|
||||
return Status(0, "OK");
|
||||
}
|
||||
|
||||
void genLoginItems(const fs::path& homedir, QueryData& results) {
|
||||
pt::ptree tree;
|
||||
fs::path sipath = homedir / kLoginItemsPlistPath;
|
||||
if (!pathExists(sipath.string()).ok() || !isReadable(sipath.string()).ok()) {
|
||||
// User does not have a startup items list, or bad permissions.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!osquery::parsePlist(sipath.string(), tree).ok()) {
|
||||
// Could not parse the user's startup items plist.
|
||||
return;
|
||||
}
|
||||
|
||||
// Enumerate Login Items if we successfully opened the plist.
|
||||
for (const auto& entry : tree.get_child(kLoginItemsKeyPath)) {
|
||||
Row r;
|
||||
r["name"] = entry.second.get<std::string>("Name");
|
||||
r["type"] = "Login Item";
|
||||
r["source"] = sipath.string();
|
||||
|
||||
auto alias_data = entry.second.get<std::string>("Alias");
|
||||
std::string bin_path;
|
||||
if (!parseAliasData(alias_data, bin_path).ok()) {
|
||||
VLOG(1) << "No valid path found for " << r["name"] << " in " << sipath;
|
||||
}
|
||||
r["path"] = bin_path;
|
||||
results.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
QueryData genStartupItems(QueryContext& context) {
|
||||
QueryData results;
|
||||
getLoginItems(results);
|
||||
getLibraryStartupItems(results);
|
||||
|
||||
// Get the login items available in System Preferences for each user.
|
||||
for (const auto& dir : getHomeDirectories()) {
|
||||
genLoginItems(dir, results);
|
||||
}
|
||||
|
||||
// Find system wide startup items in Library directories.
|
||||
for (const auto& dir : kLibraryStartupItemPaths) {
|
||||
genLibraryStartupItems(dir, results);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user