diff options
Diffstat (limited to 'tests/exploit.c')
-rw-r--r-- | tests/exploit.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/tests/exploit.c b/tests/exploit.c new file mode 100644 index 0000000..814337c --- /dev/null +++ b/tests/exploit.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020 Andrew G Morgan <morgan@kernel.org> + * + * This program exploit demonstrates why libcap alone in a + * multithreaded C/C++ program is inherently vulnerable to privilege + * escalation. + * + * The code also serves as a demonstration of how linking with libpsx + * can eliminate this vulnerability by maintaining a process wide + * common security state. + * + * The basic idea (which is well known and why POSIX stipulates "posix + * semantics" for security relevant state at the abstraction of a + * process) is that, because of shared memory, if a single thread alone + * is vulnerable to code injection, then it can cause any other thread + * to execute arbitrary code. As such, if all but one thread drops + * privilege, privilege escalation is somewhat trivial. + */ + +/* as per "man sigaction" */ +#define _POSIX_C_SOURCE 200809L + +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/capability.h> +#include <sys/types.h> + +/* thread coordination */ +pthread_mutex_t mu; +pthread_cond_t cond; +int hits; + +/* evidence of highest privilege attained */ +ssize_t greatest_len; +char *text; + +/* + * interrupt handler - potentially watching for an opportunity to + * perform an exploit when invoked as a privileged thread. + */ +static void handler(int signum, siginfo_t *info, void *ignore) { + ssize_t length; + char *working; + pthread_mutex_lock(&mu); + + cap_t caps = cap_get_proc(); + working = cap_to_text(caps, &length); + if (length > greatest_len) { + /* + * This is where the exploit code might go. + */ + cap_free(text); + text = working; + greatest_len = length; + } + cap_free(caps); + hits++; + + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mu); + +} + +/* + * privileged thread code (imagine it doing whatever needs privilege). + */ +static void *victim(void *args) { + pthread_mutex_lock(&mu); + hits = 1; + printf("started privileged thread\n"); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mu); + + pthread_mutex_lock(&mu); + while (hits < 2) { + pthread_cond_wait(&cond, &mu); + } + pthread_mutex_unlock(&mu); + + return NULL; +} + +int main(int argc, char **argv) { + pthread_t peer; + cap_t caps = cap_init(); + struct sigaction sig_action; + + printf("program starting\n"); + if (pthread_create(&peer, NULL, victim, NULL)) { + perror("unable to start the victim thread"); + exit(1); + } + + /* + * Wait until the peer thread is fully up. + */ + pthread_mutex_lock(&mu); + while (hits < 1) { + pthread_cond_wait(&cond, &mu); + } + pthread_mutex_unlock(&mu); + + printf("dropping privilege from main process thread\n"); + + if (cap_set_proc(caps)) { + perror("unable to drop capabilities from main process thread"); + exit(1); + } + cap_free(caps); + + /* confirm the low privilege of the process' main thread */ + + caps = cap_get_proc(); + text = cap_to_text(caps, &greatest_len); + cap_free(caps); + + printf("no privilege in main process thread: len:%ld, caps:\"%s\"\n", + greatest_len, text); + if (greatest_len != 1) { + printf("failed to lower privilege as expected\n"); + exit(1); + } + + /* + * So, we have confirmed that this running thread has no + * privilege. From this thread we setup an interrupt handler and + * then trigger it on the privileged peer thread. + */ + + sig_action.sa_sigaction = &handler; + sigemptyset(&sig_action.sa_mask); + sig_action.sa_flags = SA_SIGINFO | SA_RESTART;; + sigaction(SIGRTMIN, &sig_action, NULL); + + pthread_kill(peer, SIGRTMIN); + + /* + * Wait for the thread to exit. + */ + pthread_join(peer, NULL); + + /* + * Let's see how we did with the exploit. + */ + + printf("greatest privilege in main process thread: len:%ld, caps:\"%s\"\n", + greatest_len, text); + + cap_free(text); + if (greatest_len != 1) { + printf("exploit succeeded\n"); + exit(1); + } + + printf("exploit failed\n"); + exit(0); +} |