aboutsummaryrefslogtreecommitdiffhomepage
path: root/infra
diff options
context:
space:
mode:
authorGravatar Dongge Liu <donggeliu@google.com>2022-05-26 15:37:04 +1000
committerGravatar GitHub <noreply@github.com>2022-05-26 15:37:04 +1000
commite249bcc6697e45ba657ed36a9cf38cd2b944c3cf (patch)
treedd0bfcefa5e4272dd343b553aadb8ca7d61dff81 /infra
parenta6156786706d83a73c35d2494429153168ba1b42 (diff)
An attempt to detect shell injection with `ptrace` (#7757)
* An attempt to detect shell injection with ptrace * Relocate sanitizer files * Add headers and file descriptions * Better cleanup * Name and analogy * TODOs * safer cleanup * More descriptive name * More descriptive README.md * More descriptive file names * One more TODOs
Diffstat (limited to 'infra')
-rw-r--r--infra/experimental/sanitizers/ExecSan/Makefile20
-rw-r--r--infra/experimental/sanitizers/ExecSan/README.md33
-rw-r--r--infra/experimental/sanitizers/ExecSan/execSan.c125
-rw-r--r--infra/experimental/sanitizers/ExecSan/target.cpp28
-rw-r--r--infra/experimental/sanitizers/ExecSan/tripwire.cpp25
-rw-r--r--infra/experimental/sanitizers/ExecSan/vuln.dict1
6 files changed, 232 insertions, 0 deletions
diff --git a/infra/experimental/sanitizers/ExecSan/Makefile b/infra/experimental/sanitizers/ExecSan/Makefile
new file mode 100644
index 00000000..09a6bbba
--- /dev/null
+++ b/infra/experimental/sanitizers/ExecSan/Makefile
@@ -0,0 +1,20 @@
+.POSIX:
+CC = cc
+CFLAGS = -std=c99 -Wall -Wextra -O3 -g3
+
+all: clean execSan tripwire target
+
+execSan: execSan.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+tripwire: tripwire.cpp
+ clang++ -o /tmp/$@ $^
+
+target: target.cpp
+ clang -fsanitize=fuzzer -o $@ $^
+
+test: all vuln.dict
+ ./execSan ./target -dict=vuln.dict
+
+clean:
+ rm -f execSan /tmp/tripwire target /tmp/injected
diff --git a/infra/experimental/sanitizers/ExecSan/README.md b/infra/experimental/sanitizers/ExecSan/README.md
new file mode 100644
index 00000000..960bf861
--- /dev/null
+++ b/infra/experimental/sanitizers/ExecSan/README.md
@@ -0,0 +1,33 @@
+# Shell Injection Detection with `ptrace`
+
+We use `ptrace` to instrument system calls made by the target program to detect if our `/tmp/tripwire` command in `vuln.dict` was injected into the shell of the testing target program and executed by the program to produce of a `/tmp/injected` file.
+Our instrumentation verifies the existence of `/tmp/injected` after every `execve` or each process spawned via `clone`, which proves the existence of shell injection vulnerabilities.
+
+## Quick test
+
+### Cleanup
+Note this will delete /tmp/tripwire and /tmp/injected if they exist
+```shell
+make clean
+```
+
+### Run test
+Note this will overwrite /tmp/tripwire and /tmp/injected if they exist
+```shell
+make test
+```
+
+Look for the following line:
+
+> ===BUG DETECTED: Shell injection===
+
+which indicates the detection of shell injections
+
+
+## TODOs
+1. Trace the `execve` syscalls in child processes of the target, not the `clone`
+ and `wait4` in the target;
+2. Flag syntax errors of shell commands, as they are suspicious enough even without
+ seeing the proof of error (i.e. `/tmp/injected`);
+3. Suffix the injected file with the corresponding PID (e.g. `/tmp/injected_{PID}`).
+
diff --git a/infra/experimental/sanitizers/ExecSan/execSan.c b/infra/experimental/sanitizers/ExecSan/execSan.c
new file mode 100644
index 00000000..9658f1fd
--- /dev/null
+++ b/infra/experimental/sanitizers/ExecSan/execSan.c
@@ -0,0 +1,125 @@
+/*
+ * 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. */
+
+#define _POSIX_C_SOURCE 200112L
+
+/* C standard library */
+#include <errno.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+/* POSIX */
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+/* Linux */
+#include <syscall.h>
+#include <sys/reg.h>
+#include <sys/ptrace.h>
+
+#define FATAL(...) \
+ do { \
+ fprintf(stderr, "strace: " __VA_ARGS__); \
+ fputc('\n', stderr); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+pid_t run_child(char **argv) {
+ // Run the program under test with its args as a child process
+ pid_t pid = fork();
+ switch (pid) {
+ case -1: /* error */
+ FATAL("Fork failed: %s", strerror(errno));
+ case 0: /* child */
+ ptrace(PTRACE_TRACEME, 0, 0, 0);
+ /* Because we're now a tracee, execvp will block until the parent
+ * attaches and allows us to continue. */
+ execv(argv[1], argv + 1);
+ FATAL("%s", strerror(errno));
+ }
+ return pid;
+}
+
+void sync_syscall(pid_t pid) {
+ // Run and pause the child process before/after of the next system call
+ if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL) == -1)
+ FATAL("%s", strerror(errno));
+ if (waitpid(pid, NULL, WUNTRACED) == -1)
+ FATAL("%s", strerror(errno));
+}
+
+void inspect(pid_t pid) {
+ // Check for the file injected upon each execve and every wait4 after clone
+ struct stat stat_buf;
+ if (!stat("/tmp/injected", &stat_buf)) {
+ ptrace(PTRACE_KILL, pid, NULL, NULL);
+ printf("===BUG DETECTED: Shell injection===\n");
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv) {
+ if (argc <= 1)
+ FATAL("Expecting at least one arguments, received %d", argc - 1);
+
+ pid_t pid = run_child(argv);
+ /* Gather system call arguments */
+ struct user_regs_struct regs;
+ int cloned = 0;
+
+ /* parent */
+ // sync with child process
+ if (waitpid(pid, NULL, WUNTRACED) == -1)
+ FATAL("%s", strerror(errno));
+ // Ensures that the tracee will never escape
+ long data = PTRACE_O_EXITKILL
+// | PTRACE_O_TRACEFORK
+// | PTRACE_O_TRACEVFORK
+// | PTRACE_O_TRACECLONE
+// | PTRACE_O_TRACEEXEC
+ ;
+ ptrace(PTRACE_SETOPTIONS, pid, NULL, data);
+
+ while (kill(pid, 0) != -1) {
+ /* Enter next system call */
+ sync_syscall(pid);
+
+ /* Gather system call arguments */
+ if (ptrace(PTRACE_GETREGS, pid, 0, &regs) == -1)
+ FATAL("%s", strerror(errno));
+
+ cloned = cloned || regs.orig_rax == __NR_clone;
+
+ /* Run system call and stop on exit */
+ sync_syscall(pid);
+
+ ptrace(PTRACE_GETREGS, pid, 0, &regs);
+
+ if (regs.orig_rax == __NR_execve) {
+ inspect(pid);
+ }
+ if (regs.orig_rax == __NR_wait4 && cloned) {
+ cloned = 0;
+ inspect(pid);
+ }
+ }
+}
diff --git a/infra/experimental/sanitizers/ExecSan/target.cpp b/infra/experimental/sanitizers/ExecSan/target.cpp
new file mode 100644
index 00000000..7e1dad3e
--- /dev/null
+++ b/infra/experimental/sanitizers/ExecSan/target.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * the sand program will be injected into its shell command */
+
+#include <stdlib.h>
+#include <string>
+#include <iostream>
+
+extern "C" int LLVMFuzzerTestOneInput(char* data, size_t size) {
+ std::string str(data, size);
+ std::cout << "INPUT" << str << std::endl;
+ system(str.c_str());
+ return 0;
+}
diff --git a/infra/experimental/sanitizers/ExecSan/tripwire.cpp b/infra/experimental/sanitizers/ExecSan/tripwire.cpp
new file mode 100644
index 00000000..a49f4261
--- /dev/null
+++ b/infra/experimental/sanitizers/ExecSan/tripwire.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+*/
+
+/* Generates the proof of error (i.e. pearl) when executed */
+
+#include <fstream>
+int main() {
+ std::ofstream pearlfile("/tmp/injected");
+ pearlfile << "Injected!" << std::endl;
+ pearlfile.close();
+ return 0;
+}
diff --git a/infra/experimental/sanitizers/ExecSan/vuln.dict b/infra/experimental/sanitizers/ExecSan/vuln.dict
new file mode 100644
index 00000000..ae43a495
--- /dev/null
+++ b/infra/experimental/sanitizers/ExecSan/vuln.dict
@@ -0,0 +1 @@
+"/tmp/tripwire"