diff options
author | Dongge Liu <donggeliu@google.com> | 2022-05-26 15:37:04 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-26 15:37:04 +1000 |
commit | e249bcc6697e45ba657ed36a9cf38cd2b944c3cf (patch) | |
tree | dd0bfcefa5e4272dd343b553aadb8ca7d61dff81 /infra | |
parent | a6156786706d83a73c35d2494429153168ba1b42 (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/Makefile | 20 | ||||
-rw-r--r-- | infra/experimental/sanitizers/ExecSan/README.md | 33 | ||||
-rw-r--r-- | infra/experimental/sanitizers/ExecSan/execSan.c | 125 | ||||
-rw-r--r-- | infra/experimental/sanitizers/ExecSan/target.cpp | 28 | ||||
-rw-r--r-- | infra/experimental/sanitizers/ExecSan/tripwire.cpp | 25 | ||||
-rw-r--r-- | infra/experimental/sanitizers/ExecSan/vuln.dict | 1 |
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, ®s) == -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, ®s); + + 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" |