diff --git a/infra/experimental/SystemSan/Makefile b/infra/experimental/SystemSan/Makefile index c7f83df000b1..d38729351d6d 100644 --- a/infra/experimental/SystemSan/Makefile +++ b/infra/experimental/SystemSan/Makefile @@ -2,9 +2,9 @@ CXX = clang++ CFLAGS = -std=c++17 -Wall -Wextra -O3 -g3 -all: clean SystemSan target target_file target_dns +all: clean SystemSan target target_file -SystemSan: SystemSan.cpp inspect_dns.cpp inspect_utils.cpp +SystemSan: SystemSan.cpp $(CXX) $(CFLAGS) -lpthread -o $@ $^ target: target.cpp @@ -13,13 +13,9 @@ target: target.cpp target_file: target_file.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 pytorch-lightning-1.5.10: cp SystemSan.cpp PoEs/pytorch-lightning-1.5.10/; \ @@ -34,4 +30,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 diff --git a/infra/experimental/SystemSan/SystemSan.cpp b/infra/experimental/SystemSan/SystemSan.cpp index 393675bbd16d..3689fd88d2d5 100644 --- a/infra/experimental/SystemSan/SystemSan.cpp +++ b/infra/experimental/SystemSan/SystemSan.cpp @@ -40,9 +40,6 @@ #include #include -#include "inspect_utils.h" -#include "inspect_dns.h" - #define DEBUG_LOGS 0 #if DEBUG_LOGS @@ -165,6 +162,23 @@ pid_t run_child(char **argv) { return pid; } +std::vector read_memory(pid_t pid, unsigned long long address, + size_t size) { + std::vector memory; + + for (size_t i = 0; i < size; i += sizeof(long)) { + long word = ptrace(PTRACE_PEEKTEXT, pid, address + i, 0); + if (word == -1) { + return memory; + } + + std::byte *word_bytes = reinterpret_cast(&word); + memory.insert(memory.end(), word_bytes, word_bytes + sizeof(long)); + } + + return memory; +} + // Construct a string with the memory specified in a register. std::string read_string(pid_t pid, unsigned long reg, unsigned long length) { auto memory = read_memory(pid, reg, length); @@ -177,6 +191,27 @@ std::string read_string(pid_t pid, unsigned long reg, unsigned long length) { return content; } +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"; + // 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 + // in an async way. + // Find the thread group id, that is the pid. + pid_t pid = tid; + auto parent = root_pids[tid]; + while (!parent.ran_exec) { + // Find the first parent which ran exec syscall. + if (parent.parent_tid == g_root_pid) { + break; + } + pid = parent.parent_tid; + parent = root_pids[parent.parent_tid]; + } + tgkill(pid, tid, SIGABRT); +} + void inspect_for_injection(pid_t pid, const user_regs_struct ®s) { // Inspect a PID's registers for the sign of shell injection. std::string path = read_string(pid, regs.rdi, kTripWire.length()); @@ -424,8 +459,6 @@ int trace(std::map pids) { } } - inspect_dns_syscalls(pid, regs); - if (regs.orig_rax == __NR_openat) { inspect_for_arbitrary_file_open(pid, regs); } diff --git a/infra/experimental/SystemSan/inspect_dns.cpp b/infra/experimental/SystemSan/inspect_dns.cpp deleted file mode 100644 index e1d07a3f7b43..000000000000 --- a/infra/experimental/SystemSan/inspect_dns.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2022 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. - */ -/* A detector that uses ptrace to identify shell injection vulnerabilities. */ - -/* POSIX */ -#include -#include - -/* Linux */ -#include -#include -#include - -#include - -#include "inspect_utils.h" - -// Arbitrary domain name resolution -const std::string kArbitraryDomainNameResolution = "Arbitrary domain name resolution"; - -// Global constant for one file descriptor about of a DNS socket -int kFdDns = 0; - -#define DNS_HEADER_LEN 12 - - -void inspect_for_arbitrary_dns_connect(pid_t pid, const user_regs_struct ®s) { - auto memory = read_memory(pid, regs.rsi, sizeof(struct sockaddr_in)); - if (memory.size()) { - struct sockaddr_in * sa = reinterpret_cast(memory.data()); - if (sa->sin_family == AF_INET && htons(sa->sin_port) == 53) { - // save file descriptor for later sendmmsg - kFdDns = regs.rdi; - } - } -} - -struct DnsHeader { - uint16_t tx_id; - uint16_t flags; - uint16_t questions; - uint16_t answers; - uint16_t nameservers; - uint16_t additional; -}; - -struct DnsHeader parse_dns_header(std::vector data) { - struct DnsHeader h; - h.tx_id = (((uint16_t) data[0]) << 8) | ((uint16_t) data[1]); - h.flags = (((uint16_t) data[2]) << 8) | ((uint16_t) data[3]); - h.questions = (((uint16_t) data[4]) << 8) | ((uint16_t) data[5]); - h.answers = (((uint16_t) data[6]) << 8) | ((uint16_t) data[7]); - h.nameservers = (((uint16_t) data[8]) << 8) | ((uint16_t) data[9]); - h.additional = (((uint16_t) data[10]) << 8) | ((uint16_t) data[11]); - return h; -} - -bool dns_flags_standard_query(uint16_t flags) { - if ((flags & 0x8000) == 0) { - // Query, not response. - if (((flags & 0x7800) >> 11) == 0) { - // Opcode 0 is standard query. - if ((flags & 0x0200) == 0) { - // Message is not truncated. - if ((flags & 0x0040) == 0) { - // Z-bit reserved flag is unset. - return true; - } - } - } - } - return false; -} - -struct DnsRequest { - // Start of name in the byte vector. - size_t offset; - // End of name in the byte vector. - size_t end; - // Length of top level domain. - uint8_t tld_size; - // Number of levels/dots in domain name. - size_t nb_levels; - // DNS type like A is 1. - uint16_t dns_type; - // DNS class like IN is 1. - uint16_t dns_class; -}; - -struct DnsRequest parse_dns_request(std::vector data, size_t offset) { - struct DnsRequest r; - r.offset = offset; - r.tld_size = 0; - r.nb_levels = 0; - while(offset < data.size()) { - uint8_t rlen = uint8_t(data[offset]); - if (rlen == 0) { - break; - } - r.nb_levels++; - offset += rlen+1; - r.tld_size = rlen; - } - if (offset <= 4 + data.size()) { - r.end = offset; - r.dns_type = (((uint16_t) data[offset]) << 8) | ((uint16_t) data[offset+1]); - r.dns_class = (((uint16_t) data[offset+2]) << 8) | ((uint16_t) data[offset+3]); - } else { - r.end = data.size(); - } - return r; -} - -void log_dns_request(struct DnsRequest r, std::vector data) { - size_t offset = r.offset; - std::cerr << "===Domain resolved: "; - while(offset < r.end) { - uint8_t rlen = uint8_t(data[offset]); - if (rlen == 0) { - break; - } - std::cerr << '.'; - for (uint8_t i = 1; i < rlen+1; i++) { - std::cerr << (char) data[offset + i]; - } - offset += rlen+1; - } - std::cerr << "===\n"; - std::cerr << "===DNS request type: " << r.dns_type << ", class: " << r.dns_class << "===\n"; -} - -void inspect_for_arbitrary_dns_pkt(std::vector data) { - if (data.size() < DNS_HEADER_LEN + 1) { - return; - } - struct DnsHeader h = parse_dns_header(data); - if (h.questions != 1) { - return; - } - if (h.answers != 0 || h.nameservers != 0 || h.additional != 0) { - return; - } - if (!dns_flags_standard_query(h.flags)) { - return; - } - - struct DnsRequest req = parse_dns_request(data, DNS_HEADER_LEN); - // Alert if the top level domain is only one character and - // if there is more than just the TLD. - if (req.tld_size == 1 && req.nb_levels > 1 && req.end < data.size()) { - report_bug(kArbitraryDomainNameResolution); - log_dns_request(req, data); - } -} - -void inspect_for_arbitrary_dns_fdbuffer(pid_t pid, const user_regs_struct ®s) { - if (kFdDns > 0 && kFdDns == (int) regs.rdi) { - auto memory = read_memory(pid, regs.rsi, regs.rdx); - if (memory.size()) { - inspect_for_arbitrary_dns_pkt(memory); - } - } -} - -void inspect_for_arbitrary_dns_iov(pid_t pid, unsigned long iov) { - auto memory = read_memory(pid, iov, sizeof(struct iovec)); - if (memory.size()) { - struct iovec * iovec = reinterpret_cast(memory.data()); - memory = read_memory(pid, (unsigned long) iovec->iov_base, iovec->iov_len); - if (memory.size()) { - inspect_for_arbitrary_dns_pkt(memory); - } - } -} - -void inspect_for_arbitrary_dns_sendmsg(pid_t pid, const user_regs_struct ®s) { - if (kFdDns > 0 && kFdDns == (int) regs.rdi) { - auto memory = read_memory(pid, regs.rsi, sizeof(struct msghdr)); - if (memory.size()) { - struct msghdr * msg = reinterpret_cast(memory.data()); - if (msg->msg_iovlen == 1) { - inspect_for_arbitrary_dns_iov(pid, (unsigned long) msg->msg_iov); - } - } - } -} - -void inspect_for_arbitrary_dns_sendmmsg(pid_t pid, const user_regs_struct ®s) { - if (kFdDns > 0 && kFdDns == (int) regs.rdi) { - auto memory = read_memory(pid, regs.rsi, sizeof(struct mmsghdr)); - if (memory.size()) { - struct mmsghdr * msg = reinterpret_cast(memory.data()); - if (msg->msg_hdr.msg_iovlen == 1) { - inspect_for_arbitrary_dns_iov(pid, (unsigned long) msg->msg_hdr.msg_iov); - } - } - } -} - -void inspect_dns_syscalls(pid_t pid, const user_regs_struct ®s) { - switch (regs.orig_rax) { - case __NR_connect: - inspect_for_arbitrary_dns_connect(pid, regs); - break; - case __NR_close: - if (kFdDns > 0 && kFdDns == (int) regs.rdi) { - // reset DNS file descriptor on close - kFdDns = 0; - } - break; - case __NR_sendmmsg: - inspect_for_arbitrary_dns_sendmmsg(pid, regs); - break; - case __NR_sendmsg: - inspect_for_arbitrary_dns_sendmsg(pid, regs); - break; - case __NR_sendto: - // fallthrough - case __NR_write: - inspect_for_arbitrary_dns_fdbuffer(pid, regs); - } -} diff --git a/infra/experimental/SystemSan/inspect_dns.h b/infra/experimental/SystemSan/inspect_dns.h deleted file mode 100644 index 849af4e98067..000000000000 --- a/infra/experimental/SystemSan/inspect_dns.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2022 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. - */ -/* A detector that uses ptrace to identify DNS arbitrary resolutions. */ - - -/* POSIX */ -#include - -/* Linux */ -#include - - -void inspect_dns_syscalls(pid_t pid, const user_regs_struct ®s); diff --git a/infra/experimental/SystemSan/inspect_utils.cpp b/infra/experimental/SystemSan/inspect_utils.cpp deleted file mode 100644 index e7dd294884e2..000000000000 --- a/infra/experimental/SystemSan/inspect_utils.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2022 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. - */ -/* A detector that uses ptrace to identify DNS arbitrary resolutions. */ - -/* C standard library */ -#include - -/* POSIX */ -#include - -/* Linux */ -#include - -#include -#include -#include - -extern pid_t g_root_pid; -extern std::map root_pids; - -std::vector read_memory(pid_t pid, unsigned long long address, - size_t size) { - std::vector memory; - - for (size_t i = 0; i < size; i += sizeof(long)) { - long word = ptrace(PTRACE_PEEKTEXT, pid, address + i, 0); - if (word == -1) { - return memory; - } - - std::byte *word_bytes = reinterpret_cast(&word); - memory.insert(memory.end(), word_bytes, word_bytes + sizeof(long)); - } - - return memory; -} - -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"; - // 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 - // in an async way. - // Find the thread group id, that is the pid. - pid_t pid = tid; - auto parent = root_pids[tid]; - while (!parent.ran_exec) { - // Find the first parent which ran exec syscall. - if (parent.parent_tid == g_root_pid) { - break; - } - pid = parent.parent_tid; - parent = root_pids[parent.parent_tid]; - } - tgkill(pid, tid, SIGABRT); -} diff --git a/infra/experimental/SystemSan/inspect_utils.h b/infra/experimental/SystemSan/inspect_utils.h deleted file mode 100644 index ccd8e61d8a29..000000000000 --- a/infra/experimental/SystemSan/inspect_utils.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2022 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. - */ -/* A detector that uses ptrace to identify DNS arbitrary resolutions. */ - - -/* POSIX */ -#include - -#include -#include - -std::vector read_memory(pid_t pid, unsigned long long address, - size_t size); - -void report_bug(std::string bug_type, pid_t tid); diff --git a/infra/experimental/SystemSan/target_dns.cpp b/infra/experimental/SystemSan/target_dns.cpp deleted file mode 100644 index 6452ce60f1d3..000000000000 --- a/infra/experimental/SystemSan/target_dns.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2022 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. -*/ -/* A sample target program under test, - * /tmp/tripwire or other commands will be injected into its shell command */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(char* data, size_t size) { - std::string str(data, size); - std::cout << "INPUT" << str << std::endl; - - struct addrinfo *result; - - int s = getaddrinfo(str.c_str(), NULL, NULL, &result); - if (s == 0) { - freeaddrinfo(result); - } - - return 0; -} diff --git a/infra/experimental/SystemSan/vuln.dict b/infra/experimental/SystemSan/vuln.dict index bf066ea4829f..6414a13cc8fa 100644 --- a/infra/experimental/SystemSan/vuln.dict +++ b/infra/experimental/SystemSan/vuln.dict @@ -1,3 +1,2 @@ "/tmp/tripwire" "/fz/" -"f.z" \ No newline at end of file