Created
September 19, 2023 10:41
-
-
Save sitano/01f0ddf9b1f3504829d5ac7d5154f22b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2020 Andrew G Morgan <[email protected]> | |
* | |
* 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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://sites.google.com/site/fullycapable/who-ordered-libpsx