summaryrefslogtreecommitdiff
path: root/tests/exploit.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/exploit.c')
-rw-r--r--tests/exploit.c159
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);
+}