mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-06 01:25:20 +00:00
Add Shellbags table (#6949)
Hello, this largish PR adds shellbags support to osquery. Shellbags is a complex (imo) windows Registry artifact that primarily keeps track of directories a user has browsed to (specifically directories accessed using Windows Explorer). By parsing shellbags its possible to recreate what directories a user accessed Shellbags are composed of shellitems, this PR also adds support to parsing several shellitems, finally shellbags also contain FAT timestamps that show when a directory was created, modified, accessed, FAT timestamp parsing is also included in this PR Example query of what shellbags looks like ``` osquery> select * from shellbags; +-----------------------------------------------+--------------+-------------------------------------------------------------------------------------+---------------+--------------+---------------+-----------+--------------+ | sid | source | path | modified_time | created_time | accessed_time | mft_entry | mft_sequence | +-----------------------------------------------+--------------+-------------------------------------------------------------------------------------+---------------+--------------+---------------+-----------+--------------+ | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob | 0 | 0 | 0 | 0 | 0 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Downloads | 1571635108 | 1571620406 | 1571635108 | 3074 | 5 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects | 0 | 0 | 0 | 0 | 0 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\osquery | 0 | 0 | 0 | 0 | 0 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\osquery\build | 0 | 0 | 0 | 0 | 0 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\osquery\build\osquery | 0 | 0 | 0 | 0 | 0 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\osquery\build\osquery\RelWithDebInfo | 0 | 0 | 0 | 0 | 0 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\osquery\osquery | 1578192498 | 1571701478 | 1578192498 | 495902 | 4 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\osquery\osquery\killswitch | 1578192406 | 1575859554 | 1578192406 | 707032 | 2 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\clamav-osquery | 1572045050 | 1572045050 | 1572045050 | 221518 | 14 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\clamav-osquery\extension_clamav | 1572045050 | 1572045050 | 1572045050 | 432733 | 8 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\Projects\clamav-osquery\extension_clamav\src | 1572045050 | 1572045050 | 1572045050 | 432736 | 11 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\.osquery | 1571706262 | 1571706212 | 1571706262 | 575462 | 4 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\AppData | 1571623328 | 1571623318 | 1571623328 | 206482 | 7 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\AppData\Local | 1571701908 | 1571623318 | 1571701908 | 206502 | 8 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\AppData\Local\Microsoft | 1593297370 | 1571623318 | 1593297370 | 206504 | 8 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\AppData\Local\Microsoft\Office | 1593297370 | 1593295160 | 1593297370 | 52684 | 60 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\AppData\Local\Microsoft\Office\16.0 | 1593297388 | 1593295160 | 1593297388 | 81742 | 11 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\AppData\Local\autopsy | 1612935328 | 1612935328 | 1612935328 | 37041 | 7 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\AppData\Local\autopsy\Cache | 1612935328 | 1612935328 | 1612935328 | 37104 | 8 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\super secret sensitive stuff | 1613198066 | 1613198066 | 1613198066 | 101729 | 123 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\super secret sensitive stuff\secret data | 1613198092 | 1613198092 | 1613198092 | 383733 | 38 | | S-1-5-21-1079689790-2336414676-942872339-1001 | usrclass.dat | This PC\C:\Users\bob\super secret sensitive stuff\secret data\dont look secret data | 1613198108 | 1613198108 | 1613198108 | 383736 | 15 | +-----------------------------------------------+--------------+-------------------------------------------------------------------------------------+---------------+--------------+---------------+-----------+--------------+ ``` Due to the complexity of shellbags currently this PR does not support (or only has partial support) for the following shellitems: * optical disc * variable (partial support) * mtp (partial support) * user property view data (partial support) I was not able to generate shellbag data (or only some data) for the above shellitems in my windows vms (tested on two different Windows 10 systems, Windows 8.1, and Windows Server 2019), if osquery encounters any unknown shellbag data it will log a warning and mark the shellitem as "[UNKNOWN SHELL FORMAT]" when building directory paths. The main value of shellbags is reconstructing directories accessed as shown above, but this PR does include additional shellbag support such as FTP servers connected to via Windows Explorer, ZIP files opened, MTP devices (partial), and network shares browsed to via Windows Explorer This PR is kind of large, let me know if there are any questions, suggestions for improvements, or issues, thanks! Shellbags references: [Shellitems](https://github.com/libyal/libfwsi/blob/main/documentation/Windows%20Shell%20Item%20format.asciidoc) [Shellbags](https://www.magnetforensics.com/blog/forensic-analysis-of-windows-shellbags/) [Property Stores](https://github.com/libyal/libfwps/blob/main/documentation/Windows%20Property%20Store%20format.asciidoc)
This commit is contained in:
parent
b6b17f7629
commit
121f7e2589
@ -214,6 +214,7 @@ function(generateOsqueryTablesSystemSystemtable)
|
||||
windows/scheduled_tasks.cpp
|
||||
windows/services.cpp
|
||||
windows/shared_resources.cpp
|
||||
windows/shellbags.cpp
|
||||
windows/shimcache.cpp
|
||||
windows/smbios_tables.cpp
|
||||
windows/startup_items.cpp
|
||||
|
411
osquery/tables/system/windows/shellbags.cpp
Normal file
411
osquery/tables/system/windows/shellbags.cpp
Normal file
@ -0,0 +1,411 @@
|
||||
/**
|
||||
* Copyright (c) 2014-present, The osquery authors
|
||||
*
|
||||
* This source code is licensed as defined by the LICENSE file found in the
|
||||
* root directory of this source tree.
|
||||
*
|
||||
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
|
||||
*/
|
||||
|
||||
#include <osquery/core/core.h>
|
||||
#include <osquery/core/tables.h>
|
||||
#include <osquery/logger/logger.h>
|
||||
#include <osquery/tables/system/windows/registry.h>
|
||||
#include <osquery/utils/conversions/join.h>
|
||||
#include <osquery/utils/conversions/windows/windows_time.h>
|
||||
#include <osquery/utils/windows/shellitem.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osquery {
|
||||
namespace tables {
|
||||
constexpr auto kShellBagPath =
|
||||
"\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\BagMRU";
|
||||
constexpr auto kShellBagPathNtuser =
|
||||
"\\Software\\Microsoft\\Windows\\Shell\\BagMRU";
|
||||
|
||||
std::string guidLookup(const std::string& guid) {
|
||||
QueryData guid_data;
|
||||
queryKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID\\{" + guid + "}",
|
||||
guid_data);
|
||||
for (const auto& rKey : guid_data) {
|
||||
auto key_type = rKey.find("type");
|
||||
auto key_path = rKey.find("path");
|
||||
if (key_type == rKey.end() || key_path == rKey.end()) {
|
||||
continue;
|
||||
}
|
||||
auto key_name = rKey.at("name");
|
||||
if (key_name == "(Default)") {
|
||||
if (rKey.at("data") == "") {
|
||||
return "{" + guid + "}";
|
||||
}
|
||||
return rKey.at("data");
|
||||
}
|
||||
}
|
||||
return "{" + guid + "}";
|
||||
}
|
||||
|
||||
void parseShellData(const std::string& shell_data,
|
||||
std::vector<std::string>& build_shellbag,
|
||||
QueryData& results,
|
||||
const std::string& sid,
|
||||
const std::string& source) {
|
||||
Row r;
|
||||
r["sid"] = sid;
|
||||
r["source"] = source;
|
||||
std::string extension_sig = "";
|
||||
// "0400EFBE", "2600EFBE", "2500EFBE" are the primary shell extensions needed
|
||||
// to build directory paths
|
||||
if (shell_data.find("0400EFBE") != std::string::npos) {
|
||||
extension_sig = "0400EFBE";
|
||||
} else if (shell_data.find("2600EFBE") != std::string::npos) {
|
||||
extension_sig = "2600EFBE";
|
||||
} else if (shell_data.find("2500EFBE") != std::string::npos) {
|
||||
extension_sig = "2500EFBE";
|
||||
}
|
||||
|
||||
std::string sig = shell_data.substr(4, 2);
|
||||
ShellFileEntryData file_entry;
|
||||
if (shell_data.length() > 200 && extension_sig == "" &&
|
||||
(shell_data.substr(80, 2) == "2F" ||
|
||||
shell_data.substr(76, 2) == "2F")) { // Zip contents
|
||||
std::string path = zipContentItem(shell_data);
|
||||
build_shellbag.push_back(path);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (sig == "1F" &&
|
||||
shell_data.find("31535053") == std::string::npos) { // Root Folder
|
||||
std::string name;
|
||||
std::string full_path;
|
||||
if (shell_data.substr(8, 2) == "2F") { // User Property View Drive
|
||||
name = propertyViewDrive(shell_data);
|
||||
// osquery::join adds "\" to entries, remove drive "\"
|
||||
name.pop_back();
|
||||
build_shellbag.push_back(name);
|
||||
full_path = osquery::join(build_shellbag, "\\");
|
||||
full_path += "\\";
|
||||
} else {
|
||||
std::string root_name = rootFolderItem(shell_data);
|
||||
name = guidLookup(root_name);
|
||||
build_shellbag.push_back(name);
|
||||
full_path = osquery::join(build_shellbag, "\\");
|
||||
}
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if ((sig == "31" || sig == "30" || sig == "32" || sig == "35" ||
|
||||
sig == "B1") &&
|
||||
extension_sig == "0400EFBE") { // Directory/File Entry
|
||||
file_entry = fileEntry(shell_data);
|
||||
} else if ((sig == "2F" || sig == "23" || sig == "25" || sig == "29" ||
|
||||
sig == "2A" || sig == "2E") &&
|
||||
(extension_sig == "" || extension_sig == "2600EFBE" ||
|
||||
extension_sig == "2500EFBE")) { // Drive Letter
|
||||
if (shell_data.substr(6, 2) == "80" &&
|
||||
(extension_sig == "2600EFBE" || extension_sig == "2500EFBE" ||
|
||||
extension_sig == "")) { // Check if GUID exists
|
||||
std::string guid_little = shell_data.substr(8, 32);
|
||||
std::string guid_string = guidParse(guid_little);
|
||||
std::string guid_name = guidLookup(guid_string);
|
||||
|
||||
build_shellbag.push_back(guid_name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (shell_data.find("5C007500730062002300") != std::string::npos &&
|
||||
extension_sig == "") { // Check for \usb#
|
||||
std::string name = mtpRoot(shell_data);
|
||||
build_shellbag.push_back(name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
}
|
||||
|
||||
// Drive letter should have ":\"
|
||||
if (shell_data.find("3A5C") == std::string::npos) {
|
||||
build_shellbag.push_back("[UNKNOWN DRIVE NAME]");
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
}
|
||||
std::string drive_name = driveLetterItem(shell_data);
|
||||
// osquery::join adds "\" to entries, remove drive "\"
|
||||
drive_name.pop_back();
|
||||
build_shellbag.push_back(drive_name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
|
||||
r["path"] = full_path + "\\";
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (sig == "01") { // Control Panel Category
|
||||
std::string panel = controlPanelCategoryItem(shell_data);
|
||||
build_shellbag.push_back(panel);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (sig == "71") { // Control Panel
|
||||
std::string control_guid = controlPanelItem(shell_data);
|
||||
std::string guid_name = guidLookup(control_guid);
|
||||
|
||||
build_shellbag.push_back(guid_name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (sig == "C3" || sig == "41" || sig == "42" || sig == "46" ||
|
||||
sig == "47" || sig == "4C") { // Network share
|
||||
std::string network_share = networkShareItem(shell_data);
|
||||
build_shellbag.push_back(network_share);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (sig == "61") { // FTP/URI
|
||||
std::vector<std::string> ftp_data = ftpItem(shell_data);
|
||||
long long unix_time = littleEndianToUnixTime(ftp_data[0]);
|
||||
build_shellbag.push_back(ftp_data[1]);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
r["accessed_time"] = INTEGER(unix_time);
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (sig == "74" && shell_data.find("43465346") !=
|
||||
std::string::npos) { // User File View
|
||||
file_entry = fileEntry(shell_data);
|
||||
} else if (sig == "00") { // Variable shell item, can contain a variety of
|
||||
// shell item formats
|
||||
if (shell_data.find("EEBBFE23") != std::string::npos) {
|
||||
std::string guid_string = variableGuid(shell_data);
|
||||
std::string guid_name = guidLookup(guid_string);
|
||||
build_shellbag.push_back(guid_name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (shell_data.substr(12, 8) == "05000000" ||
|
||||
shell_data.substr(12, 8) == "05000300") {
|
||||
std::string ftp_name = variableFtp(shell_data);
|
||||
build_shellbag.push_back(ftp_name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (shell_data.find("31535053") != std::string::npos) {
|
||||
// User Property View contains several signatures, data is likely
|
||||
// associated with Explorer searches?
|
||||
if ((shell_data.find("D5DFA323") != std::string::npos) ||
|
||||
(shell_data.find("81191410") != std::string::npos) ||
|
||||
(shell_data.find("EEBBFE23") != std::string::npos) ||
|
||||
(shell_data.find("00EEBEBE") != std::string::npos)) {
|
||||
build_shellbag.push_back("[VARIABLE USER PROPERTY VIEW]");
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
}
|
||||
std::vector<size_t> wps_list;
|
||||
// check if Windows Property Store version 1SPS is in shellbag data,
|
||||
// there could be multiple
|
||||
size_t wps = shell_data.find("31535053");
|
||||
while (wps != std::string::npos) {
|
||||
wps_list.push_back(wps);
|
||||
wps = shell_data.find("31535053", wps + 1);
|
||||
}
|
||||
std::string property_name = propertyStore(shell_data, wps_list);
|
||||
build_shellbag.push_back(property_name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (shell_data.find("0505203110") !=
|
||||
std::string::npos) { // MTP Device
|
||||
std::string name = mtpDevice(shell_data);
|
||||
build_shellbag.push_back(name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else if (shell_data.find("06201907") != std::string::npos) { // MTP Folder
|
||||
std::string name = mtpFolder(shell_data);
|
||||
build_shellbag.push_back(name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
}
|
||||
LOG(WARNING) << "Unknown variable format: " << shell_data;
|
||||
build_shellbag.push_back("[UNKNOWN VARIABLE FORMAT]");
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else {
|
||||
if (shell_data.find("31535053") != std::string::npos) {
|
||||
if (shell_data.find("D5DFA323") !=
|
||||
std::string::npos) { // User Property View
|
||||
std::string property_guid = shell_data.substr(226, 32);
|
||||
std::string guid_string = guidParse(property_guid);
|
||||
|
||||
std::string guid_name = guidLookup(guid_string);
|
||||
build_shellbag.push_back(guid_name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
}
|
||||
// User Property View may have additional other types of signatures, data
|
||||
// is likely associated with Explorer searches?
|
||||
if ((shell_data.find("81191410") != std::string::npos) ||
|
||||
(shell_data.find("EEBBFE23") != std::string::npos) ||
|
||||
(shell_data.find("BBAF933B") != std::string::npos) ||
|
||||
(shell_data.find("00EEBEBE") != std::string::npos)) {
|
||||
build_shellbag.push_back("[USER PROPERTY VIEW]");
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
}
|
||||
std::vector<size_t> wps_list;
|
||||
// check if Windows Property Store version 1SPS is in shellbag data, there
|
||||
// could be multiple
|
||||
size_t wps = shell_data.find("31535053");
|
||||
while (wps != std::string::npos) {
|
||||
wps_list.push_back(wps);
|
||||
wps = shell_data.find("31535053", wps + 1);
|
||||
}
|
||||
std::string property_name = propertyStore(shell_data, wps_list);
|
||||
build_shellbag.push_back(property_name);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
} else {
|
||||
LOG(WARNING) << "Unsupported Shellbag format: " << shell_data;
|
||||
build_shellbag.push_back("[UNSUPPORTED FORMAT]");
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_entry.path == "[UNSUPPORTED SHELL EXTENSION]") {
|
||||
build_shellbag.push_back(file_entry.path);
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
r["path"] = full_path;
|
||||
results.push_back(r);
|
||||
return;
|
||||
}
|
||||
|
||||
r["modified_time"] = INTEGER(file_entry.dos_modified);
|
||||
r["created_time"] = INTEGER(file_entry.dos_created);
|
||||
r["accessed_time"] = INTEGER(file_entry.dos_accessed);
|
||||
|
||||
build_shellbag.push_back(file_entry.path);
|
||||
long long mft_entry = file_entry.mft_entry;
|
||||
int mft_sequence = file_entry.mft_sequence;
|
||||
std::string full_path = osquery::join(build_shellbag, "\\");
|
||||
|
||||
r["path"] = full_path;
|
||||
r["mft_entry"] = BIGINT(mft_entry);
|
||||
r["mft_sequence"] = INTEGER(mft_sequence);
|
||||
results.push_back(r);
|
||||
}
|
||||
|
||||
// Recursively loop through shellbag entries in the Registry
|
||||
void parseShellbags(const std::string& path,
|
||||
std::vector<std::string>& build_shellbag,
|
||||
QueryData& results,
|
||||
const std::string& sid,
|
||||
const std::string& source) {
|
||||
QueryData shellbag_data;
|
||||
queryKey(path, shellbag_data);
|
||||
for (const auto& rKey : shellbag_data) {
|
||||
auto key_type = rKey.find("type");
|
||||
auto key_path = rKey.find("path");
|
||||
if (key_type == rKey.end() || key_path == rKey.end()) {
|
||||
continue;
|
||||
}
|
||||
// For Shellbags Reg keys the last character is a number
|
||||
if (!isdigit(key_path->second.back())) {
|
||||
continue;
|
||||
}
|
||||
if (rKey.at("data") == "") {
|
||||
continue;
|
||||
}
|
||||
parseShellData(rKey.at("data"), build_shellbag, results, sid, source);
|
||||
parseShellbags(key_path->second, build_shellbag, results, sid, source);
|
||||
if (build_shellbag.size() > 0) {
|
||||
build_shellbag.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseRegistry(const std::string& full_path,
|
||||
const std::string& sid,
|
||||
QueryData& results,
|
||||
const std::string& source) {
|
||||
QueryData shellbag_results;
|
||||
queryKey(full_path, shellbag_results);
|
||||
for (const auto& uKey : shellbag_results) {
|
||||
auto key_type = uKey.find("type");
|
||||
auto key_path = uKey.find("path");
|
||||
if (key_type == uKey.end() || key_path == uKey.end()) {
|
||||
continue;
|
||||
}
|
||||
if (!isdigit(key_path->second.back())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uKey.at("data") == "") {
|
||||
continue;
|
||||
}
|
||||
std::vector<std::string> build_shellbag;
|
||||
parseShellData(uKey.at("data"), build_shellbag, results, sid, source);
|
||||
parseShellbags(key_path->second, build_shellbag, results, sid, source);
|
||||
}
|
||||
}
|
||||
|
||||
QueryData genShellbags(QueryContext& context) {
|
||||
QueryData users;
|
||||
QueryData results;
|
||||
|
||||
queryKey("HKEY_USERS", users);
|
||||
for (const auto& rKey : users) {
|
||||
auto key_type = rKey.find("type");
|
||||
auto key_path = rKey.find("path");
|
||||
if (key_type == rKey.end() || key_path == rKey.end()) {
|
||||
continue;
|
||||
}
|
||||
std::string full_path = key_path->second + kShellBagPath;
|
||||
size_t sid_start = full_path.find("S-");
|
||||
if (sid_start == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
size_t sid_end = full_path.find("_", sid_start);
|
||||
if (sid_end == std::string::npos) {
|
||||
sid_end = full_path.find("\\", sid_start);
|
||||
}
|
||||
std::string sid = full_path.substr(sid_start, sid_end - sid_start);
|
||||
// Shellbags may exist in both SID_Classes (UsrClass.dat) and SID
|
||||
// (NTUSER.dat) Keys but the paths are different
|
||||
if (full_path.find("_Classes") == std::string::npos) {
|
||||
full_path = key_path->second + kShellBagPathNtuser;
|
||||
std::string source = "ntuser.dat";
|
||||
parseRegistry(full_path, sid, results, source);
|
||||
continue;
|
||||
}
|
||||
std::string source = "usrclass.dat";
|
||||
parseRegistry(full_path, sid, results, source);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
} // namespace tables
|
||||
} // namespace osquery
|
@ -45,6 +45,11 @@ function(generateOsqueryUtils)
|
||||
darwin/plist.mm
|
||||
)
|
||||
endif()
|
||||
if(DEFINED PLATFORM_WINDOWS)
|
||||
list(APPEND source_files
|
||||
windows/shellitem.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
add_osquery_library(osquery_utils EXCLUDE_FROM_ALL
|
||||
${source_files}
|
||||
@ -86,6 +91,14 @@ function(generateOsqueryUtils)
|
||||
generateIncludeNamespace(osquery_utils "osquery/utils" "FULL_PATH" ${platform_public_header_files})
|
||||
endif()
|
||||
|
||||
if(DEFINED PLATFORM_WINDOWS)
|
||||
set(platform_public_header_files
|
||||
windows/shellitem.h
|
||||
)
|
||||
|
||||
generateIncludeNamespace(osquery_utils "osquery/utils" "FULL_PATH" ${platform_public_header_files})
|
||||
endif()
|
||||
|
||||
set(attribute_public_header_files
|
||||
attribute.h
|
||||
)
|
||||
@ -110,6 +123,7 @@ function(generateOsqueryUtilsUtilstestsTest)
|
||||
list(APPEND source_files
|
||||
tests/windows/env.cpp
|
||||
tests/windows/filetime.cpp
|
||||
tests/windows/shellitems.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -129,4 +129,13 @@ LONGLONG cimDatetimeToUnixtime(const std::string& src) {
|
||||
return filetimeToUnixtime(timeStore);
|
||||
}
|
||||
|
||||
std::string swapEndianess(const std::string& endian_string) {
|
||||
std::string swap_string = endian_string;
|
||||
std::reverse(swap_string.begin(), swap_string.end());
|
||||
for (std::size_t i = 0; i < swap_string.length(); i += 2) {
|
||||
std::swap(swap_string[i], swap_string[i + 1]);
|
||||
}
|
||||
return swap_string;
|
||||
}
|
||||
|
||||
} // namespace osquery
|
||||
|
@ -52,4 +52,10 @@ LONGLONG cimDatetimeToUnixtime(const std::string& src);
|
||||
*/
|
||||
std::string bstrToString(const BSTR src);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function to swap endianess of a string
|
||||
*
|
||||
* @returns The swap endianess (little endian returns big endian, vice-versa)
|
||||
*/
|
||||
std::string swapEndianess(const std::string& endian_string);
|
||||
} // namespace osquery
|
||||
|
@ -65,4 +65,11 @@ TEST_F(ConversionsTests, test_wstring_to_string_extended) {
|
||||
EXPECT_EQ(narrowString, expected);
|
||||
}
|
||||
|
||||
TEST_F(ConversionsTests, test_swapendianiess) {
|
||||
std::string little_endian{"IJGHEFCDAB"};
|
||||
auto swapendian = swapEndianess(little_endian);
|
||||
std::string expected{"ABCDEFGHIJ"};
|
||||
EXPECT_EQ(swapendian, expected);
|
||||
}
|
||||
|
||||
} // namespace osquery
|
||||
|
@ -41,4 +41,11 @@ TEST_F(ConversionsTests, test_long_int_to_unixtime) {
|
||||
EXPECT_EQ(converted, 1593277666);
|
||||
}
|
||||
|
||||
TEST_F(ConversionsTests, test_fattime_to_unixtime) {
|
||||
std::string fattime = "24450000";
|
||||
|
||||
auto converted = parseFatTime(fattime);
|
||||
EXPECT_EQ(converted, 1409788800);
|
||||
}
|
||||
|
||||
} // namespace osquery
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <osquery/utils/conversions/tryto.h>
|
||||
#include <osquery/utils/conversions/windows/windows_time.h>
|
||||
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
namespace osquery {
|
||||
|
||||
LONGLONG filetimeToUnixtime(const FILETIME& ft) {
|
||||
@ -56,4 +58,40 @@ LONGLONG littleEndianToUnixTime(const std::string& time_data) {
|
||||
return last_time;
|
||||
}
|
||||
|
||||
LONGLONG parseFatTime(const std::string& fat_data) {
|
||||
if (fat_data.length() != 8) {
|
||||
LOG(WARNING)
|
||||
<< "Incorrect FAT timestamp format, expecting string length 8, got: "
|
||||
<< fat_data;
|
||||
return 0ll;
|
||||
}
|
||||
std::string fat_date_data = fat_data.substr(0, 4);
|
||||
std::string fat_time_data = fat_data.substr(4, 4);
|
||||
|
||||
auto fat_date = std::stoi(fat_date_data.substr(2, 2), nullptr, 16) << 8;
|
||||
fat_date |= std::stoi(fat_date_data.substr(0, 2), nullptr, 16);
|
||||
|
||||
// Year is stored as number of years after 1980. Ex: 2020 is stored as 40
|
||||
int fat_year = ((fat_date & 0xfe00) >> 9) + 1980;
|
||||
int fat_month = (fat_date & 0x1e0) >> 5;
|
||||
int fat_day = fat_date & 0x1f;
|
||||
|
||||
auto fat_time = std::stoi(fat_time_data.substr(2, 2), nullptr, 16) << 8;
|
||||
fat_time |= std::stoi(fat_time_data.substr(0, 2), nullptr, 16);
|
||||
int fat_sec = (fat_time & 0x1f) * 2;
|
||||
int fat_min = (fat_time & 0x7e0) >> 5;
|
||||
int fat_hour = (fat_time & 0xf800) >> 11;
|
||||
|
||||
struct tm fat_timestamp = {0};
|
||||
fat_timestamp.tm_year = fat_year - 1900;
|
||||
fat_timestamp.tm_mon = fat_month - 1;
|
||||
fat_timestamp.tm_mday = fat_day;
|
||||
fat_timestamp.tm_hour = fat_hour;
|
||||
fat_timestamp.tm_min = fat_min;
|
||||
fat_timestamp.tm_sec = fat_sec;
|
||||
|
||||
time_t epoch = _mkgmtime(&fat_timestamp);
|
||||
return epoch;
|
||||
}
|
||||
|
||||
} // namespace osquery
|
||||
|
@ -37,4 +37,12 @@ LONGLONG longIntToUnixtime(LARGE_INTEGER& ft);
|
||||
*/
|
||||
LONGLONG littleEndianToUnixTime(const std::string& time_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing and converting FAT time to Unix
|
||||
* epoch.
|
||||
*
|
||||
* @returns The unix epoch timestamp representation of FAT time
|
||||
*/
|
||||
LONGLONG parseFatTime(const std::string& dos_data);
|
||||
|
||||
} // namespace osquery
|
223
osquery/utils/tests/windows/shellitems.cpp
Normal file
223
osquery/utils/tests/windows/shellitems.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Copyright (c) 2014-present, The osquery authors
|
||||
*
|
||||
* This source code is licensed as defined by the LICENSE file found in the
|
||||
* root directory of this source tree.
|
||||
*
|
||||
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <osquery/utils/windows/shellitem.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace osquery {
|
||||
|
||||
class ShellitemTests : public testing::Test {};
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_fileentry) {
|
||||
std::string data =
|
||||
"56003100000000000000000010006F73717565727900400009000400EFBE000000000000"
|
||||
"00002E00000000000000000000000000000000000000000000000000000000006F007300"
|
||||
"71007500650072007900000016000000";
|
||||
auto file_entry = fileEntry(data);
|
||||
ASSERT_TRUE(file_entry.path == "osquery");
|
||||
ASSERT_TRUE(file_entry.mft_entry == 0LL);
|
||||
ASSERT_TRUE(file_entry.dos_created == 0LL);
|
||||
ASSERT_TRUE(file_entry.dos_modified == 0LL);
|
||||
ASSERT_TRUE(file_entry.dos_accessed == 0LL);
|
||||
ASSERT_TRUE(file_entry.mft_sequence == 0LL);
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_ftpserver) {
|
||||
std::string data =
|
||||
"560061034C00030100000400000012122B8B7BFBD601FFFFFFFF00000000000000000000"
|
||||
"000015000000140000007370656564746573742E74656C65322E6E657400040000000000"
|
||||
"00000400000000000000667470000000";
|
||||
std::vector<std::string> ftp_data = ftpItem(data);
|
||||
ASSERT_TRUE(ftp_data[1] == "speedtest.tele2.net");
|
||||
ASSERT_TRUE(ftp_data[0] == "12122B8B7BFBD601");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_zipcontent) {
|
||||
std::string data =
|
||||
"86007E0043003A0000000000000000000000000000000000000000000000000010000000"
|
||||
"4E002F0041000000000000000000000000000000010000000000000004E3CB1700000000"
|
||||
"0100000000000000FFFF000011000000000000004C004900450046002D0030002E003100"
|
||||
"30002E0031002D00770069006E0036003400000000006D6265720000";
|
||||
auto name = zipContentItem(data);
|
||||
ASSERT_TRUE(name == "LIEF-0.10.1-win64");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_mtpdevice) {
|
||||
std::string data =
|
||||
"9C050000960505203110030000001A0020000080AF38060000000000000000000000B602"
|
||||
"00001800000019000000150000000700000049006E007400650072006E0061006C002000"
|
||||
"7300680061007200650064002000730074006F0072006100670065000000530049004400"
|
||||
"2D007B00310030003000300031002C002C00320036003700320030003800320039003400"
|
||||
"340030007D000000470065006E0065007200690063002000680069006500720061007200"
|
||||
"630068006900630061006C0000007B00450046003200310030003700440035002D004100"
|
||||
"3500320041002D0034003200340033002D0041003200360042002D003600320044003400"
|
||||
"310037003600440037003600300033007D0000007B003400410044003200430038003500"
|
||||
"45002D0035004500320044002D0034003500450035002D0038003800360034002D003400"
|
||||
"460032003200390045003300430036004300460030007D0000007B003100410033003300"
|
||||
"46003700450034002D0041004600310033002D0034003800460035002D00390039003400"
|
||||
"45002D003700370033003600390044004600450030003400410033007D0000007B003900"
|
||||
"32003600310042003000330043002D0033004400370038002D0034003500310039002D00"
|
||||
"38003500450033002D003000320043003500450031004600350030004200420039007D00"
|
||||
"00007B00360038003000410044004600350032002D0039003500300041002D0034003000"
|
||||
"340031002D0039004200340031002D003600350045003300390033003600340038003100"
|
||||
"350035007D0000007B00320038004400380044003300310045002D003200340039004300"
|
||||
"2D0034003500340045002D0041004100420043002D003300340038003800330031003600"
|
||||
"380045003600330034007D0000007B00320037004500320045003300390032002D004100"
|
||||
"3100310031002D0034003800450030002D0041004200300043002D004500310037003700"
|
||||
"300035004100300035004600380035007D0000000D00000003D5150C17D0CE4790167B3F"
|
||||
"978721CC0F0000007A05A301D674804EBEA7DC4C212CE50A020000001300000003000000"
|
||||
"7A05A301D674804EBEA7DC4C212CE50A030000001F0000002A000000470065006E006500"
|
||||
"7200690063002000680069006500720061007200630068006900630061006C0000007A05"
|
||||
"A301D674804EBEA7DC4C212CE50A0B00000013000000000000007A05A301D674804EBEA7"
|
||||
"DC4C212CE50A04000000150000000080AF38060000007A05A301D674804EBEA7DC4C212C"
|
||||
"E50A05000000150000000070F58B050000007A05A301D674804EBEA7DC4C212CE50A0600"
|
||||
"00001500000000000040000000007A05A301D674804EBEA7DC4C212CE50A070000001F00"
|
||||
"00003000000049006E007400650072006E0061006C002000730068006100720065006400"
|
||||
"2000730074006F00720061006700650000000D496BEFD85C7A43AFFCDA8B60EE4A3C0500"
|
||||
"00001F000000320000005300490044002D007B00310030003000300031002C002C003200"
|
||||
"36003700320030003800320039003400340030007D0000000D496BEFD85C7A43AFFCDA8B"
|
||||
"60EE4A3C040000001F0000003000000049006E007400650072006E0061006C0020007300"
|
||||
"680061007200650064002000730074006F00720061006700650000007A05A301D674804E"
|
||||
"BEA7DC4C212CE50A080000001F0000000200000000000D496BEFD85C7A43AFFCDA8B60EE"
|
||||
"4A3C0600000048000000000001306CAE044898BAC57B46965FE70D496BEFD85C7A43AFFC"
|
||||
"DA8B60EE4A3C1A0000000B00000000000D496BEFD85C7A43AFFCDA8B60EE4A3C07000000"
|
||||
"480000006001ED99FF17444C9D981D7A6F941921932D058FCAABC54FA5ACB01DF4DBE598"
|
||||
"0200000048000000BC5BF023DE152A4CA55BA9AF5CE412EF0D496BEFD85C7A43AFFCDA8B"
|
||||
"60EE4A3C170000001F0000000E000000730031003000300030003100000000000000";
|
||||
auto name = mtpDevice(data);
|
||||
ASSERT_TRUE(name == "Internal shared storage");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_rootentry) {
|
||||
std::string data =
|
||||
"3A001F44471A0359723FA74489C55595FE6B30EE260001002600EFBE100000002A4B9884"
|
||||
"B387D50168891281D387D501BF5E6881D387D50114000000";
|
||||
auto name = rootFolderItem(data);
|
||||
ASSERT_TRUE(name == "59031A47-3F72-44A7-89C5-5595FE6B30EE");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_driveletterentry) {
|
||||
std::string data = "19002F433A5C000000000000000000000000000000000000000000";
|
||||
auto name = driveLetterItem(data);
|
||||
ASSERT_TRUE(name == "C:\\");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_mtpfolder) {
|
||||
std::string data =
|
||||
"0E030000080306201907FB000000020020000000000000000000000000000000000080FD"
|
||||
"D223D4F9D40192E3E22711A1E048AB0CE17705A05F85400200000D0000000D0000002700"
|
||||
"0000730075007000650072007400750078006B0061007200740000007300750070006500"
|
||||
"72007400750078006B0061007200740000007B0030003000300032004600410042003200"
|
||||
"2D0030003000300031002D0030003000300031002D0030003000300030002D0030003000"
|
||||
"30003000300030003000300030003000300030007D0000000D00000003D5150C17D0CE47"
|
||||
"90167B3F978721CC0C0000000D496BEFD85C7A43AFFCDA8B60EE4A3C020000001F000000"
|
||||
"0E0000006F00320046004100420032000000ABFDD4FB7D987747B3F9726185A9312B0200"
|
||||
"00001F0000000200000000000D496BEFD85C7A43AFFCDA8B60EE4A3C1300000007000000"
|
||||
"A2A702F34B47E5400D496BEFD85C7A43AFFCDA8B60EE4A3C060000004800000000000130"
|
||||
"6CAE044898BAC57B46965FE70D496BEFD85C7A43AFFCDA8B60EE4A3C0700000048000000"
|
||||
"92E3E22711A1E048AB0CE17705A05F850D496BEFD85C7A43AFFCDA8B60EE4A3C04000000"
|
||||
"1F0000001A000000730075007000650072007400750078006B0061007200740000000D49"
|
||||
"6BEFD85C7A43AFFCDA8B60EE4A3C170000001F0000000E00000073003100300030003000"
|
||||
"310000000D496BEFD85C7A43AFFCDA8B60EE4A3C050000001F0000004E0000007B003000"
|
||||
"30003000320046004100420032002D0030003000300031002D0030003000300031002D00"
|
||||
"30003000300030002D003000300030003000300030003000300030003000300030007D00"
|
||||
"00000D496BEFD85C7A43AFFCDA8B60EE4A3C1A0000000B000000FFFF5850544DCE4F7845"
|
||||
"95C88698A9BC0F4903DC00001200000000005850544DCE4F784595C88698A9BC0F494EDC"
|
||||
"00001F000000200000003200300031003700310031003200370054003000360035003300"
|
||||
"3300330000000D496BEFD85C7A43AFFCDA8B60EE4A3C0C0000001F0000001A0000007300"
|
||||
"75007000650072007400750078006B00610072007400000000000000";
|
||||
auto name = mtpFolder(data);
|
||||
ASSERT_TRUE(name == "supertuxkart");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_mtproot) {
|
||||
std::string data =
|
||||
"6E012E004801062031080300000000000000030000006E00000001000000090000005200"
|
||||
"000000004E00650078007500730020003500580000005C005C003F005C00750073006200"
|
||||
"23007600690064005F00310038006400310026007000690064005F003400650065003100"
|
||||
"230030003200350061006300650063003600320064003400380063003200340038002300"
|
||||
"7B00360061006300320037003800370038002D0061003600660061002D00340031003500"
|
||||
"35002D0062006100380035002D0066003900380066003400390031006400340066003300"
|
||||
"33007D0000000D00000003D5150C17D0CE4790167B3F978721CC020000009A97D42643E6"
|
||||
"26469E2B736DC0C92FDC0C0000001F000000120000004E00650078007500730020003500"
|
||||
"58000000932D058FCAABC54FA5ACB01DF4DBE59802000000480000006B46EA08A4E33643"
|
||||
"A1F3A44D2B5C438C0000741A595E96DFD3488D671733BCEE28BA3C6D783575B0B94988DD"
|
||||
"029876E11C010000";
|
||||
auto name = mtpRoot(data);
|
||||
ASSERT_TRUE(name == "Nexus 5X");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_controlpanelcategoryitem) {
|
||||
std::string data = "0C0001008421DE39050000000000";
|
||||
auto name = controlPanelCategoryItem(data);
|
||||
ASSERT_TRUE(name == "System and Security");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_controlpanelitem) {
|
||||
std::string data =
|
||||
"1E007180000000000000000000006ABE817B2BCE7646A29EEB907A5126C50000";
|
||||
auto name = controlPanelItem(data);
|
||||
ASSERT_TRUE(name == "7B81BE6A-CE2B-4676-A29E-EB907A5126C5");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_variableftp) {
|
||||
std::string data =
|
||||
"3E0000000000050003001000000000700A00000000000018389483FBD601550700000000"
|
||||
"000075706C6F61640000750070006C006F0061006400000000000000";
|
||||
auto name = variableFtp(data);
|
||||
ASSERT_TRUE(name == "upload");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_variableguid) {
|
||||
std::string data =
|
||||
"200000001A00EEBBFE23000010003ACCBFB42CDB4C42B0297FE99A87C64100000000";
|
||||
auto name = variableGuid(data);
|
||||
ASSERT_TRUE(name == "B4BFCC3A-DB2C-424C-B029-7FE99A87C641");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_propertyviewdrive) {
|
||||
std::string data =
|
||||
"55001F002F0010B7A6F519002F443A5C0000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000741A595E96DFD3488D671733BCEE28BA772CFB"
|
||||
"F52F0E164AA3813E560C68BC830000";
|
||||
auto name = propertyViewDrive(data);
|
||||
ASSERT_TRUE(name == "D:\\");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_propertystore) {
|
||||
std::string data =
|
||||
"100100000A01BBAF933BFC000400000000002D000000315350537343E50ABE43AD4F85E4"
|
||||
"69DC8633986E110000000B000000000B000000FFFF000000000000450000003153505330"
|
||||
"F125B7EF471A10A5F102608C9EEBAC290000000A000000001F0000000C00000076006D00"
|
||||
"77006100720065002D0068006F00730074000000000000005900000031535053A66A6328"
|
||||
"3D95D211B5D600C04FD918D03D0000001F000000001F0000001600000056004D00770061"
|
||||
"00720065002000530068006100720065006400200046006F006C00640065007200730000"
|
||||
"00000000002D000000315350533AA4BDDEB337834391E74498DA2995AB11000000030000"
|
||||
"00001300000000000000000000000000000000000000";
|
||||
std::vector<size_t> wps_list;
|
||||
size_t wps = data.find("31535053");
|
||||
while (wps != std::string::npos) {
|
||||
wps_list.push_back(wps);
|
||||
wps = data.find("31535053", wps + 1);
|
||||
}
|
||||
auto name = propertyStore(data, wps_list);
|
||||
ASSERT_TRUE(name == "vmware-host");
|
||||
}
|
||||
|
||||
TEST_F(ShellitemTests, test_shellitem_networkshare) {
|
||||
std::string data =
|
||||
"3A00C301815C5C766D776172652D686F73745C53686172656420466F6C6465727300564D"
|
||||
"776172652053686172656420466F6C64657273003F000000";
|
||||
auto name = networkShareItem(data);
|
||||
ASSERT_TRUE(name == "\\\\vmware-host\\Shared Folders");
|
||||
}
|
||||
|
||||
} // namespace osquery
|
442
osquery/utils/windows/shellitem.cpp
Normal file
442
osquery/utils/windows/shellitem.cpp
Normal file
@ -0,0 +1,442 @@
|
||||
/**
|
||||
* Copyright (c) 2014-present, The osquery authors
|
||||
*
|
||||
* This source code is licensed as defined by the LICENSE file found in the
|
||||
* root directory of this source tree.
|
||||
*
|
||||
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
|
||||
*/
|
||||
|
||||
#include <osquery/logger/logger.h>
|
||||
#include <osquery/utils/conversions/windows/strings.h>
|
||||
#include <osquery/utils/conversions/windows/windows_time.h>
|
||||
|
||||
#include <boost/algorithm/hex.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct ShellFileEntryData {
|
||||
std::string path;
|
||||
long long dos_created;
|
||||
long long dos_accessed;
|
||||
long long dos_modified;
|
||||
int version;
|
||||
std::string extension_sig;
|
||||
std::string identifier;
|
||||
long long mft_entry;
|
||||
int mft_sequence;
|
||||
int string_size;
|
||||
};
|
||||
|
||||
const std::string kNetworkShareIds[6] = {"41", "42", "46", "47", "4C", "C3"};
|
||||
|
||||
// Property set GUIDs associated with name entries
|
||||
const std::string kPropertySets[15] = {"000214A1-0000-0000-C000-000000000046",
|
||||
"01A3057A-74D6-4E80-BEA7-DC4C212CE50A",
|
||||
"46588AE2-4CBC-4338-BBFC-139326986DCE",
|
||||
"4D545058-4FCE-4578-95C8-8698A9BC0F49",
|
||||
"56A3372E-CE9C-11D2-9F0E-006097C686F6",
|
||||
"6444048F-4C8B-11D1-8B70-080036B11A03",
|
||||
"64440490-4C8B-11D1-8B70-080036B11A03",
|
||||
"64440491-4C8B-11D1-8B70-080036B11A03",
|
||||
"64440492-4C8B-11D1-8B70-080036B11A03",
|
||||
"8F052D93-ABCA-4FC5-A5AC-B01DF4DBE598",
|
||||
"B725F130-47EF-101A-A5F1-02608C9EEBAC",
|
||||
"D5CDD502-2E9C-101B-9397-08002B2CF9AE",
|
||||
"D5CDD505-2E9C-101B-9397-08002B2CF9AE",
|
||||
"EF6B490D-5CD8-437A-AFFC-DA8B60EE4A3C",
|
||||
"F29F85E0-4FF9-1068-AB91-08002B27B3D9"};
|
||||
namespace osquery {
|
||||
std::string guidParse(const std::string& guid_little) {
|
||||
std::vector<std::string> guids;
|
||||
guids.push_back(guid_little.substr(0, 8));
|
||||
guids.push_back(guid_little.substr(8, 4));
|
||||
guids.push_back(guid_little.substr(12, 4));
|
||||
|
||||
std::string guid_4 = guid_little.substr(16, 4);
|
||||
std::string guid_5 = guid_little.substr(20, 12);
|
||||
|
||||
// The first 16 GUID characters are in litte endian format
|
||||
for (auto& guid : guids) {
|
||||
std::reverse(guid.begin(), guid.end());
|
||||
for (std::size_t i = 0; i < guid.length(); i += 2) {
|
||||
std::swap(guid[i], guid[i + 1]);
|
||||
}
|
||||
}
|
||||
std::string guid_string =
|
||||
guids[0] + "-" + guids[1] + "-" + guids[2] + "-" + guid_4 + "-" + guid_5;
|
||||
return guid_string;
|
||||
}
|
||||
|
||||
ShellFileEntryData fileEntry(const std::string& shell_data) {
|
||||
size_t offset;
|
||||
std::string extension_sig;
|
||||
size_t entry_offset = 0;
|
||||
// Find "0400EFBE" offset
|
||||
if (shell_data.find("0400EFBE") != std::string::npos) {
|
||||
offset = shell_data.find("0400EFBE");
|
||||
extension_sig = shell_data.substr(offset, 8);
|
||||
entry_offset = offset - 8;
|
||||
}
|
||||
ShellFileEntryData file_entry;
|
||||
|
||||
if (entry_offset <= 0) {
|
||||
LOG(WARNING)
|
||||
<< "Could not find supported file entry extension in shell data: "
|
||||
<< shell_data;
|
||||
file_entry.path = "[UNSUPPORTED SHELL EXTENSION]";
|
||||
return file_entry;
|
||||
}
|
||||
|
||||
std::string version = shell_data.substr(entry_offset + 4, 4);
|
||||
version = swapEndianess(version);
|
||||
file_entry.version = std::stoi(version, nullptr, 16);
|
||||
if (file_entry.version < 7) {
|
||||
LOG(WARNING) << "Shellitem format unsupported. Expecting version 7 or "
|
||||
"higher: "
|
||||
<< shell_data;
|
||||
file_entry.path = "[UNSUPPORTED SHELL EXTENSION]";
|
||||
return file_entry;
|
||||
}
|
||||
file_entry.extension_sig = shell_data.substr(entry_offset + 8, 8);
|
||||
|
||||
// Shell data may contain Users Files folder signature, modified time is at
|
||||
// offset 0x18
|
||||
std::string timestamp = "";
|
||||
if (shell_data.find("43465346") != std::string::npos) {
|
||||
timestamp = shell_data.substr(36, 8);
|
||||
file_entry.dos_modified =
|
||||
(timestamp == "00000000") ? 0LL : parseFatTime(timestamp);
|
||||
} else {
|
||||
timestamp = shell_data.substr(16, 8);
|
||||
file_entry.dos_modified =
|
||||
(timestamp == "00000000") ? 0LL : parseFatTime(timestamp);
|
||||
}
|
||||
timestamp = shell_data.substr(entry_offset + 16, 8);
|
||||
file_entry.dos_created =
|
||||
(timestamp == "00000000") ? 0LL : parseFatTime(timestamp);
|
||||
timestamp = shell_data.substr(entry_offset + 24, 8);
|
||||
file_entry.dos_accessed =
|
||||
(timestamp == "00000000") ? 0LL : parseFatTime(timestamp);
|
||||
file_entry.identifier = shell_data.substr(entry_offset + 32, 4);
|
||||
std::string ntfs_data = shell_data.substr(entry_offset + 40, 16);
|
||||
std::string mft_entry = ntfs_data.substr(0, 12);
|
||||
mft_entry = swapEndianess(mft_entry);
|
||||
|
||||
if (mft_entry == "000000000000") {
|
||||
file_entry.mft_entry = 0LL;
|
||||
} else {
|
||||
file_entry.mft_entry = std::stoll(mft_entry, nullptr, 16);
|
||||
}
|
||||
|
||||
std::string mft_sequence = ntfs_data.substr(12, 4);
|
||||
mft_sequence = swapEndianess(mft_sequence);
|
||||
if (mft_sequence == "0000") {
|
||||
file_entry.mft_sequence = 0;
|
||||
} else {
|
||||
file_entry.mft_sequence = std::stoi(mft_sequence, nullptr, 16);
|
||||
}
|
||||
|
||||
std::string string_size = shell_data.substr(entry_offset + 72, 4);
|
||||
string_size = swapEndianess(string_size);
|
||||
file_entry.string_size = std::stoi(string_size, nullptr, 16);
|
||||
std::string entry_name = shell_data.substr(entry_offset + 92);
|
||||
|
||||
// path name ends with 0000 (end of string)
|
||||
size_t name_end = entry_name.find("0000");
|
||||
std::string shell_name = entry_name.substr(0, name_end);
|
||||
// Path is in unicode, extra 00
|
||||
boost::erase_all(shell_name, "00");
|
||||
|
||||
// verify the the hex string length is even. This fixes issues with 10 base
|
||||
// hex values Example 70006900700000... (pip)
|
||||
if (shell_name.length() % 2 != 0) {
|
||||
shell_name += "0";
|
||||
}
|
||||
std::string name;
|
||||
// Convert hex path to readable string
|
||||
try {
|
||||
name = boost::algorithm::unhex(shell_name);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< shell_name;
|
||||
file_entry.path = "[UNSUPPORTED SHELL EXTENSION]";
|
||||
return file_entry;
|
||||
}
|
||||
file_entry.path = name;
|
||||
return file_entry;
|
||||
}
|
||||
|
||||
// returns property store name or GUID/id if name not found
|
||||
std::string propertyStore(const std::string& shell_data,
|
||||
const std::vector<size_t>& wps_list) {
|
||||
std::string guid_string;
|
||||
for (const auto& offsets : wps_list) {
|
||||
std::string guid_little = shell_data.substr(offsets + 8, 32);
|
||||
guid_string = guidParse(guid_little);
|
||||
// If GUID property set is found get the property set name
|
||||
for (const auto& property_list : kPropertySets) {
|
||||
if (guid_string != property_list) {
|
||||
continue;
|
||||
}
|
||||
std::string name_size = shell_data.substr(offsets + 48, 8);
|
||||
name_size = swapEndianess(name_size);
|
||||
int size = std::stoi(name_size, nullptr, 16);
|
||||
std::string string_hex = shell_data.substr(offsets + 74, (size + 1) * 4);
|
||||
boost::erase_all(string_hex, "00");
|
||||
std::string name;
|
||||
// Convert hex path to readable string
|
||||
try {
|
||||
name = boost::algorithm::unhex(string_hex);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING)
|
||||
<< "Failed to decode Windows Property List hex values to string: "
|
||||
<< shell_data;
|
||||
return guid_string;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return guid_string;
|
||||
}
|
||||
|
||||
std::string networkShareItem(const std::string& shell_data) {
|
||||
for (const auto& net_id : kNetworkShareIds) {
|
||||
if (net_id == shell_data.substr(4, 2)) {
|
||||
// Network path ends with "00"
|
||||
std::string network_path =
|
||||
shell_data.substr(10, shell_data.find("00", 10) - 10);
|
||||
std::string name;
|
||||
try {
|
||||
name = boost::algorithm::unhex(network_path);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< shell_data;
|
||||
return "[UNKNOWN NETWORK SHELL ITEM]";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return "[UNKNOWN NETWORK SHELL ITEM]";
|
||||
}
|
||||
|
||||
std::string zipContentItem(const std::string& shell_data) {
|
||||
std::string path_size_string = shell_data.substr(168, 4);
|
||||
path_size_string = swapEndianess(path_size_string);
|
||||
int path_size = std::stoi(path_size_string, nullptr, 16);
|
||||
|
||||
std::string path = shell_data.substr(184, path_size * 4);
|
||||
// Path is in unicode, extra 00
|
||||
boost::erase_all(path, "00");
|
||||
|
||||
try {
|
||||
path = boost::algorithm::unhex(path);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< path;
|
||||
return "[ZIP PATH DECODE ERROR]";
|
||||
}
|
||||
// Zip folders can go down a max of two directories
|
||||
std::string second_path_size_string = shell_data.substr(176, 4);
|
||||
second_path_size_string = swapEndianess(second_path_size_string);
|
||||
int second_path_size = std::stoi(second_path_size_string, nullptr, 16);
|
||||
|
||||
if (second_path_size != 0) {
|
||||
path += "/";
|
||||
std::string second_path =
|
||||
shell_data.substr((184 + (path_size * 4) + 4), second_path_size * 4);
|
||||
boost::erase_all(second_path, "00");
|
||||
|
||||
try {
|
||||
second_path = boost::algorithm::unhex(second_path);
|
||||
path += second_path;
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< second_path;
|
||||
path += "[ZIP PATH DECODE ERROR]";
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string rootFolderItem(const std::string& shell_data) {
|
||||
std::string guid_little = shell_data.substr(8, 32);
|
||||
std::string guid_string = guidParse(guid_little);
|
||||
return guid_string;
|
||||
}
|
||||
|
||||
std::string driveLetterItem(const std::string& shell_data) {
|
||||
std::string volume;
|
||||
try {
|
||||
volume = boost::algorithm::unhex(shell_data.substr(6, 6));
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode Shellbag hex values to string: "
|
||||
<< shell_data;
|
||||
return "[UNKNOWN DRIVE VOLUME]";
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
std::string controlPanelCategoryItem(const std::string& shell_data) {
|
||||
std::string panel_id = shell_data.substr(16, 2);
|
||||
if (panel_id == "00") {
|
||||
return "All Control Panel Items";
|
||||
} else if (panel_id == "01") {
|
||||
return "Appearence and Personalization";
|
||||
} else if (panel_id == "02") {
|
||||
return "Hardware and Sound";
|
||||
} else if (panel_id == "03") {
|
||||
return "Network and Internet";
|
||||
} else if (panel_id == "04") {
|
||||
return "Sound, Speech, and Audio Devices";
|
||||
} else if (panel_id == "05") {
|
||||
return "System and Security";
|
||||
} else if (panel_id == "06") {
|
||||
return "Clock, Language, and Region";
|
||||
} else if (panel_id == "07") {
|
||||
return "Ease of Access";
|
||||
} else if (panel_id == "08") {
|
||||
return "Programs";
|
||||
} else if (panel_id == "09") {
|
||||
return "User Accounts";
|
||||
} else if (panel_id == "10") {
|
||||
return "Security Center";
|
||||
} else if (panel_id == "11") {
|
||||
return "Mobile PC";
|
||||
} else {
|
||||
LOG(WARNING) << "Unknown panel category: " << shell_data;
|
||||
return "[UNKNOWN PANEL CATEGORY]";
|
||||
}
|
||||
}
|
||||
|
||||
std::string controlPanelItem(const std::string& shell_data) {
|
||||
std::string guid_little = shell_data.substr(28, 32);
|
||||
std::string guid_string = guidParse(guid_little);
|
||||
return guid_string;
|
||||
}
|
||||
|
||||
std::vector<std::string> ftpItem(const std::string& shell_data) {
|
||||
std::vector<std::string> ftp_data;
|
||||
std::string access_time =
|
||||
shell_data.substr(28, 16); // shell data contains connection time
|
||||
ftp_data.push_back(access_time);
|
||||
|
||||
// find end of string
|
||||
size_t offset = shell_data.find("00", 92);
|
||||
size_t hostname_size = offset - 92;
|
||||
std::string ftp_hostname = shell_data.substr(92, hostname_size);
|
||||
std::string name;
|
||||
try {
|
||||
name = boost::algorithm::unhex(ftp_hostname);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< shell_data;
|
||||
ftp_data.push_back("[UNKNOWN NAME]");
|
||||
}
|
||||
ftp_data.push_back(name);
|
||||
return ftp_data;
|
||||
}
|
||||
|
||||
std::string propertyViewDrive(const std::string& shell_data) {
|
||||
std::string drive_hex = shell_data.substr(26, 6);
|
||||
std::string name;
|
||||
try {
|
||||
name = boost::algorithm::unhex(drive_hex);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< shell_data;
|
||||
return "[UNKNOWN USER PROPERTY DRIVE NAME]";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string variableFtp(const std::string& shell_data) {
|
||||
// Short FTP name starts at string offset 76
|
||||
if (shell_data.length() < 76) {
|
||||
LOG(WARNING) << "FTP Variable name smaller than 76 chars: " << shell_data;
|
||||
return "[UNKNOWN VARIABLE FTP NAME]";
|
||||
}
|
||||
std::string name_start = shell_data.substr(76);
|
||||
// Short name should end with 0000
|
||||
size_t offset = name_start.find("0000");
|
||||
|
||||
if (offset == std::string::npos) {
|
||||
LOG(WARNING) << "Could not identify Variable FTP name: " << shell_data;
|
||||
return "[UNKNOWN VARIABLE FTP NAME]";
|
||||
}
|
||||
std::string long_name = name_start.substr(offset);
|
||||
boost::erase_all(long_name, "00");
|
||||
// Check to make sure name is even, fixes issues with 10 base characters
|
||||
// Ex: p is 70
|
||||
if (long_name.length() % 2 != 0) {
|
||||
long_name += "0";
|
||||
}
|
||||
std::string name;
|
||||
try {
|
||||
name = boost::algorithm::unhex(long_name);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< shell_data;
|
||||
return "[UNKNOWN VARIABLE FTP NAME]";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string variableGuid(const std::string& shell_data) {
|
||||
std::string guid_little = shell_data.substr(28, 32);
|
||||
std::string guid_string = guidParse(guid_little);
|
||||
return guid_string;
|
||||
}
|
||||
|
||||
std::string mtpFolder(const std::string& shell_data) {
|
||||
std::string name_size = shell_data.substr(124, 8);
|
||||
name_size = swapEndianess(name_size);
|
||||
int size = std::stoi(name_size, nullptr, 16);
|
||||
std::string path_name = shell_data.substr(148, size * 4);
|
||||
boost::erase_all(path_name, "00");
|
||||
std::string name;
|
||||
try {
|
||||
name = boost::algorithm::unhex(path_name);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< shell_data;
|
||||
return "[UNKNOWN MTP FOLDER NAME]";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string mtpDevice(const std::string& shell_data) {
|
||||
std::string name_size = shell_data.substr(76, 8);
|
||||
name_size = swapEndianess(name_size);
|
||||
int size = std::stoi(name_size, nullptr, 16);
|
||||
std::string path_name = shell_data.substr(108, size * 4);
|
||||
boost::erase_all(path_name, "00");
|
||||
std::string name;
|
||||
try {
|
||||
name = boost::algorithm::unhex(path_name);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< shell_data;
|
||||
return "[UNKNOWN MTP DEVICE NAME]";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string mtpRoot(const std::string& shell_data) {
|
||||
size_t name_end = shell_data.find("000000", 80);
|
||||
std::string path_name = shell_data.substr(80, name_end - 80);
|
||||
boost::erase_all(path_name, "00");
|
||||
std::string name;
|
||||
try {
|
||||
name = boost::algorithm::unhex(path_name);
|
||||
} catch (const boost::algorithm::hex_decode_error& /* e */) {
|
||||
LOG(WARNING) << "Failed to decode ShellItem path hex values to string: "
|
||||
<< shell_data;
|
||||
return "[UNKNOWN MTP ROOT NAME]";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
} // namespace osquery
|
157
osquery/utils/windows/shellitem.h
Normal file
157
osquery/utils/windows/shellitem.h
Normal file
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (c) 2014-present, The osquery authors
|
||||
*
|
||||
* This source code is licensed as defined by the LICENSE file found in the
|
||||
* root directory of this source tree.
|
||||
*
|
||||
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osquery/utils/system/system.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
struct ShellFileEntryData {
|
||||
std::string path;
|
||||
long long dos_created;
|
||||
long long dos_accessed;
|
||||
long long dos_modified;
|
||||
int version;
|
||||
std::string extension_sig;
|
||||
std::string identifier;
|
||||
long long mft_entry;
|
||||
int mft_sequence;
|
||||
int string_size;
|
||||
};
|
||||
namespace osquery {
|
||||
/**
|
||||
* @brief Windows helper function for parsing file entry shell items
|
||||
* epoch.
|
||||
*
|
||||
* @returns The file entry data structure
|
||||
*/
|
||||
ShellFileEntryData fileEntry(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing Windows Property lists
|
||||
* epoch.
|
||||
*
|
||||
* @returns The Windows Property List GUID name or GUID value
|
||||
*/
|
||||
std::string propertyStore(const std::string& shell_data,
|
||||
const std::vector<size_t>& wps_list);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing netshare shell items
|
||||
* epoch.
|
||||
*
|
||||
* @returns The network share name
|
||||
*/
|
||||
std::string networkShareItem(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing zip content shell items
|
||||
* epoch.
|
||||
*
|
||||
* @returns The zip content name
|
||||
*/
|
||||
std::string zipContentItem(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing root folder shell items
|
||||
* epoch.
|
||||
*
|
||||
* @returns The root folder name
|
||||
*/
|
||||
std::string rootFolderItem(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing drive letter shell items
|
||||
* epoch.
|
||||
*
|
||||
* @returns The drive name
|
||||
*/
|
||||
std::string driveLetterItem(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing conrol panel category shell items
|
||||
* epoch.
|
||||
*
|
||||
* @returns The control panel category name
|
||||
*/
|
||||
std::string controlPanelCategoryItem(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing conrol panel shell items
|
||||
* epoch.
|
||||
*
|
||||
* @returns The control panel name
|
||||
*/
|
||||
std::string controlPanelItem(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing ftp shell items
|
||||
* epoch.
|
||||
*
|
||||
* @returns The ftp hostname
|
||||
*/
|
||||
std::vector<std::string> ftpItem(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing little endian guid data
|
||||
* epoch.
|
||||
*
|
||||
* @returns GUID string in the proper order
|
||||
*/
|
||||
std::string guidParse(const std::string& guid_little);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing user property drive data
|
||||
* epoch.
|
||||
*
|
||||
* @returns The drive name
|
||||
*/
|
||||
std::string propertyViewDrive(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing user variable GUID data
|
||||
* epoch.
|
||||
*
|
||||
* @returns The GUID name or GUID
|
||||
*/
|
||||
std::string variableGuid(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing variable FTP data
|
||||
* epoch.
|
||||
*
|
||||
* @returns The ftp string
|
||||
*/
|
||||
std::string variableFtp(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing MTP device data
|
||||
* epoch.
|
||||
*
|
||||
* @returns The MTP device name
|
||||
*/
|
||||
std::string mtpDevice(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing MTP folder name data
|
||||
* epoch.
|
||||
*
|
||||
* @returns The MTP folder name
|
||||
*/
|
||||
std::string mtpFolder(const std::string& shell_data);
|
||||
|
||||
/**
|
||||
* @brief Windows helper function for parsing MTP root name data
|
||||
* epoch.
|
||||
*
|
||||
* @returns The MTP root name
|
||||
*/
|
||||
std::string mtpRoot(const std::string& shell_data);
|
||||
} // namespace osquery
|
@ -300,6 +300,7 @@ function(generateNativeTables)
|
||||
"windows/dns_cache.table:windows"
|
||||
"windows/hvci_status.table:windows"
|
||||
"windows/shimcache.table:windows"
|
||||
"windows/shellbags.table:windows"
|
||||
"yara/yara_events.table:linux,macos"
|
||||
"yara/yara.table:linux,macos,freebsd,windows"
|
||||
)
|
||||
|
16
specs/windows/shellbags.table
Normal file
16
specs/windows/shellbags.table
Normal file
@ -0,0 +1,16 @@
|
||||
table_name("shellbags")
|
||||
description("Shows directories accessed via Windows Explorer.")
|
||||
schema([
|
||||
Column("sid", TEXT, "User SID"),
|
||||
Column("source", TEXT, "Shellbags source Registry file"),
|
||||
Column("path", TEXT, "Directory name."),
|
||||
Column("modified_time", INTEGER, "Directory Modified time."),
|
||||
Column("created_time", INTEGER, "Directory Created time."),
|
||||
Column("accessed_time", INTEGER, "Directory Accessed time."),
|
||||
Column("mft_entry", BIGINT, "Directory master file table entry."),
|
||||
Column("mft_sequence", INTEGER, "Directory master file table sequence."),
|
||||
])
|
||||
implementation("shellbags@genShellbags")
|
||||
examples([
|
||||
"select * from shellbags;",
|
||||
])
|
@ -308,6 +308,7 @@ function(generateTestsIntegrationTablesTestsTest)
|
||||
scheduled_tasks.cpp
|
||||
services.cpp
|
||||
shared_resources.cpp
|
||||
shellbags.cpp
|
||||
shimcache.cpp
|
||||
startup_items.cpp
|
||||
userassist.cpp
|
||||
|
44
tests/integration/tables/shellbags.cpp
Normal file
44
tests/integration/tables/shellbags.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2014-present, The osquery authors
|
||||
*
|
||||
* This source code is licensed as defined by the LICENSE file found in the
|
||||
* root directory of this source tree.
|
||||
*
|
||||
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
|
||||
*/
|
||||
|
||||
#include <osquery/tests/integration/tables/helper.h>
|
||||
|
||||
namespace osquery {
|
||||
namespace table_tests {
|
||||
class ShellbagsTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
setUpEnvironment();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ShellbagsTest, test_sanity) {
|
||||
QueryData const rows = execute_query("select * from shellbags");
|
||||
if (!rows.empty()) {
|
||||
QueryData const specific_query_rows =
|
||||
execute_query("select * from shellbags where path like '%This PC%'");
|
||||
|
||||
ASSERT_GT(rows.size(), 0ul);
|
||||
ASSERT_GT(specific_query_rows.size(), 0ul);
|
||||
ValidationMap row_map = {
|
||||
{"sid", NonEmptyString},
|
||||
{"source", NonEmptyString},
|
||||
{"path", NonEmptyString},
|
||||
{"modified_time", NormalType},
|
||||
{"created_time", NormalType},
|
||||
{"accessed_time", NormalType},
|
||||
{"mft_entry", NormalType},
|
||||
{"mft_sequence", NormalType},
|
||||
};
|
||||
validate_rows(rows, row_map);
|
||||
validate_rows(specific_query_rows, row_map);
|
||||
}
|
||||
}
|
||||
} // namespace table_tests
|
||||
} // namespace osquery
|
Loading…
Reference in New Issue
Block a user