From 0303aab0b754e746f4a5deb6008db0c5bd2ac977 Mon Sep 17 00:00:00 2001 From: jonathanmetzman <31354670+jonathanmetzman@users.noreply.github.com> Date: Thu, 15 Dec 2022 08:38:18 -0500 Subject: [PATCH] Fix symlink bug detection (#9215) --- infra/experimental/SystemSan/SystemSan.cpp | 45 +++++++++++++------ .../experimental/SystemSan/inspect_utils.cpp | 2 +- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/infra/experimental/SystemSan/SystemSan.cpp b/infra/experimental/SystemSan/SystemSan.cpp index 711312a3c..17a84b82b 100644 --- a/infra/experimental/SystemSan/SystemSan.cpp +++ b/infra/experimental/SystemSan/SystemSan.cpp @@ -32,6 +32,8 @@ #include #include +#include +#include #include #include #include @@ -87,7 +89,9 @@ std::map root_pids; constexpr int kShellPathnameLength = 20; std::string kEvilLinkBombfile = "/tmp/evil-link-bombfile"; +std::string kEvilLinkBombfileContents = "initial"; const std:: string kEvilLinkError = "Symbolic link followed"; +const size_t kPathMax = 4096; // Syntax error messages of each shell. const std::map> kShellSyntaxErrors = { @@ -165,8 +169,10 @@ std::string read_string(pid_t pid, unsigned long reg, unsigned long length) { return ""; } + auto location = std::find(memory.begin(), memory.end(), static_cast(NULL)); + size_t str_length = location - memory.begin(); std::string content(reinterpret_cast(memory.data()), - std::min(memory.size(), length)); + std::min(str_length, length)); return content; } @@ -324,7 +330,8 @@ void inspect_for_arbitrary_file_open(pid_t pid, const user_regs_struct ®s) { } std::string read_evil_link_bombfile() { - const std::ifstream bombfile(kEvilLinkBombfile, std::ios_base::binary); + const std::ifstream bombfile(kEvilLinkBombfile, + std::ios_base::binary); if (bombfile.fail()) return ""; std::stringstream stream; @@ -332,36 +339,47 @@ std::string read_evil_link_bombfile() { return stream.str(); } +// https://oss-fuzz.com/testcase-detail/4882113260552192 +void report_bug_in_process(std::string bug_type, pid_t pid) { + std::cerr << "===BUG DETECTED: " << bug_type << "===" << std::endl; + tgkill(root_pids[pid].parent_tid, pid, SIGABRT); +} + void inspect_for_evil_link(pid_t pid, const user_regs_struct ®s) { (void) regs; std::string contents = read_evil_link_bombfile(); - if ((contents.compare("original")) != 0) { - report_bug(kEvilLinkError, pid); - exit(1); + if ((contents.compare(kEvilLinkBombfileContents)) != 0) { + + report_bug_in_process(kEvilLinkError, pid); } } -void evil_open_hook(pid_t pid, const user_regs_struct ®s) { - // Inspect a PID's register for the sign of arbitrary file open. - std::string path = read_string(pid, regs.rsi, kRootDirMaxLength); +void evil_openat_hook(pid_t pid, const user_regs_struct ®s) { + std::string path = read_string(pid, regs.rsi, kPathMax); if (!path.length()) { return; } + if (std::filesystem::exists(path)) + return; size_t slash_idx = path.rfind('/'); if (slash_idx == std::string::npos) return; std::string dir = path.substr(0, slash_idx); - std::cout << "DIR" << slash_idx << " " << dir << std::endl; if ((dir.compare("/tmp")) != 0) return; - std::string command = "ln -s " + kEvilLinkBombfile + " " + path; - std::cout << "COMMAND" << command << std::endl; + + std::string command = "rm -f " + path + " && ln -s " + kEvilLinkBombfile + " " + path; + std::cout << "COMMAND " << command << std::endl; system(command.c_str()); } void initialize_evil_link_bombfile() { - system(("printf 'original' > " + kEvilLinkBombfile).c_str()); + std::string command = ("printf " + kEvilLinkBombfileContents + " > " + + kEvilLinkBombfile); + std::cout << "COMMAND " << command << std::endl; + system(command.c_str()); + system(("cat " + kEvilLinkBombfile).c_str()); } int trace(std::map pids) { @@ -467,7 +485,7 @@ int trace(std::map pids) { if (regs.orig_rax == __NR_openat) { // TODO(metzman): Re-enable this once we have config/flag support. // inspect_for_arbitrary_file_open(pid, regs); - evil_open_hook(pid, regs); + evil_openat_hook(pid, regs); } if (regs.orig_rax == __NR_close) { @@ -512,6 +530,7 @@ int main(int argc, char **argv) { fatal_log("Expecting at least one arguments, received %d", argc - 1); } + initialize_evil_link_bombfile(); // Create an executable tripwire file, as programs may check for existence diff --git a/infra/experimental/SystemSan/inspect_utils.cpp b/infra/experimental/SystemSan/inspect_utils.cpp index 713d61d75..47f4b43ad 100644 --- a/infra/experimental/SystemSan/inspect_utils.cpp +++ b/infra/experimental/SystemSan/inspect_utils.cpp @@ -53,7 +53,7 @@ std::vector read_memory(pid_t pid, unsigned long long address, void report_bug(std::string bug_type, pid_t tid) { // Report the bug found based on the bug code. - std::cerr << "===BUG DETECTED: " << bug_type.c_str() << "===\n"; + std::cerr << "===BUG DETECTED: " << bug_type << "===" << std::endl; // Rely on sanitizers/libFuzzer to produce a stacktrace by sending SIGABRT // to the root process. // Note: this may not be reliable or consistent if shell injection happens