Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions infra/experimental/SystemSan/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
CXX = clang++
CFLAGS = -std=c++17 -Wall -Wextra -O3 -g3 -Werror

all: SystemSan target target_file target_dns
all: SystemSan target target_file target_dns target_argument

SystemSan: SystemSan.cpp inspect_dns.cpp inspect_utils.cpp
$(CXX) $(CFLAGS) -lpthread -o $@ $^
Expand All @@ -17,13 +17,17 @@ target: target.cpp
target_file: target_file.cpp
$(CXX) $(CFLAGS) -fsanitize=address,fuzzer -o $@ $^

target_argument: target_argument.cpp
$(CXX) $(CFLAGS) -fsanitize=address,fuzzer -o $@ $^

target_dns: target_dns.cpp
$(CXX) $(CFLAGS) -fsanitize=address,fuzzer -o $@ $^

test: all vuln.dict
./SystemSan ./target -dict=vuln.dict
./SystemSan ./target_file -dict=vuln.dict
./SystemSan ./target_dns -dict=vuln.dict
./SystemSan ./target_argument -dict=vuln.dict

pytorch-lightning-1.5.10:
cp SystemSan.cpp PoEs/pytorch-lightning-1.5.10/; \
Expand All @@ -38,4 +42,4 @@ node-shell-quote-v1.7.3:
docker run -t systemsan_node-shell-quote:latest;

clean:
rm -f SystemSan /tmp/tripwire target target_file target_dns
rm -f SystemSan /tmp/tripwire target target_file target_dns target_argument
36 changes: 15 additions & 21 deletions infra/experimental/SystemSan/SystemSan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,31 +43,15 @@
#include "inspect_utils.h"
#include "inspect_dns.h"

#define DEBUG_LOGS 0

#if DEBUG_LOGS
#define debug_log(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
fflush(stdout); \
fputc('\n', stderr); \
} while (0)
#else
#define debug_log(...)
#endif

#define fatal_log(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
fputc('\n', stderr); \
exit(EXIT_FAILURE); \
} while (0)

// The magic string that we'll use to detect full control over the command
// executed.
const std::string kTripWire = "/tmp/tripwire";
// Shell injection bug confirmed with /tmp/tripwire.
const std::string kInjectionError = "Shell injection";
// Argument injection bug confirmed with --tripwire.
const std::string kArgumentInjectionError = "Argument injection";
// The magic string we'll use to detect argument injection
const std::string kArgumentTripWire = "--tripwire";
// Shell corruption bug detected based on syntax error.
const std::string kCorruptionError = "Shell corruption";
// The magic string that we'll use to detect arbitrary file open
Expand Down Expand Up @@ -169,14 +153,24 @@ std::string read_string(pid_t pid, unsigned long reg, unsigned long length) {

void inspect_for_injection(pid_t pid, const user_regs_struct &regs) {
// Inspect a PID's registers for the sign of shell injection.
std::string path = read_string(pid, regs.rdi, kTripWire.length());
std::string path = read_null_terminated(pid, regs.rdi);
if (!path.length()) {
return;
}
debug_log("inspecting");
if (path == kTripWire) {
report_bug(kInjectionError, pid);
}

// Inspect a PID's argv for signs of argument injection
for (auto i: read_argv(pid, regs.rsi)) {
if (i == "--") {
break;
}
else if (i.find(kArgumentTripWire) == 0) {
report_bug(kArgumentInjectionError, pid);
}
}
}

std::string get_pathname(pid_t pid, const user_regs_struct &regs) {
Expand Down
38 changes: 38 additions & 0 deletions infra/experimental/SystemSan/inspect_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

/* POSIX */
#include <unistd.h>
#include <limits.h>

/* Linux */
#include <sys/ptrace.h>
Expand Down Expand Up @@ -51,6 +52,43 @@ std::vector<std::byte> read_memory(pid_t pid, unsigned long long address,
return memory;
}

std::string read_null_terminated(pid_t pid, unsigned long long address) {
std::string str;
while (1) {
long word = ptrace(PTRACE_PEEKDATA, pid, address, 0);
if (word == -1) {
return str;
}
address += sizeof(long);
const char *word_bytes = reinterpret_cast<const char*>(&word);
for (size_t i = 0; i < sizeof(long); i++) {
if (word_bytes[i] == 0) {
debug_log("read_null_terminated() read %s (%lu bytes)", str.c_str(), str.length());
return str;
}
str.push_back(word_bytes[i]);
}
}
}

std::vector<std::string> read_argv(pid_t pid, unsigned long long address) {
std::vector<std::string> argv;
for (size_t i = 0; _POSIX_ARG_MAX; i++) {
long p = ptrace(PTRACE_PEEKDATA, pid, address, 0);
debug_log("argv[%lu] @ 0x%llx = 0x%lx", i, address, p);
if (p == -1) {
break;
}
address += sizeof(long);
std::string arg = read_null_terminated(pid, p);
argv.push_back(arg);
if (p == 0) {
break;
}
}
return argv;
}

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";
Expand Down
24 changes: 24 additions & 0 deletions infra/experimental/SystemSan/inspect_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@
#include <string>
#include <vector>

#define DEBUG_LOGS 0

#if DEBUG_LOGS
#define debug_log(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
fflush(stdout); \
fputc('\n', stderr); \
} while (0)
#else
#define debug_log(...)
#endif

#define fatal_log(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
fputc('\n', stderr); \
exit(EXIT_FAILURE); \
} while (0)

// Structure to know which thread id triggered the bug.
struct ThreadParent {
// Parent thread ID, ie creator.
Expand All @@ -36,4 +56,8 @@ struct ThreadParent {
std::vector<std::byte> read_memory(pid_t pid, unsigned long long address,
size_t size);

std::vector<std::string> read_argv(pid_t pid, unsigned long long address);

std::string read_null_terminated(pid_t pid, unsigned long long address);

void report_bug(std::string bug_type, pid_t tid);
29 changes: 29 additions & 0 deletions infra/experimental/SystemSan/target_argument.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2023 Google LLC

* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at

* http://www.apache.org/licenses/LICENSE-2.0

* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <unistd.h>
#include <iostream>

void vulnerable(const char *str) {
char *const argv[] = { (char *const) "id", (char *const) str, NULL };
execve("id", argv, NULL);
}

extern "C" int LLVMFuzzerTestOneInput(char* data, size_t size) {
std::string str(data, size);
std::cout << "INPUT" << str << std::endl;
vulnerable(str.c_str());
return 0;
}
3 changes: 2 additions & 1 deletion infra/experimental/SystemSan/vuln.dict
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"/tmp/tripwire"
"/fz/"
"f.z"
"f.z"
"--tripwire"