xref: /aosp_15_r20/external/libcap/tests/exploit.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1 /*
2  * Copyright (c) 2020 Andrew G Morgan <[email protected]>
3  *
4  * This program exploit demonstrates why libcap alone in a
5  * multithreaded C/C++ program is inherently vulnerable to privilege
6  * escalation.
7  *
8  * The code also serves as a demonstration of how linking with libpsx
9  * can eliminate this vulnerability by maintaining a process wide
10  * common security state.
11  *
12  * The basic idea (which is well known and why POSIX stipulates "posix
13  * semantics" for security relevant state at the abstraction of a
14  * process) is that, because of shared memory, if a single thread alone
15  * is vulnerable to code injection, then it can cause any other thread
16  * to execute arbitrary code. As such, if all but one thread drops
17  * privilege, privilege escalation is somewhat trivial.
18  */
19 
20 /* as per "man sigaction" */
21 #define _POSIX_C_SOURCE 200809L
22 
23 #include <pthread.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/capability.h>
28 #include <sys/types.h>
29 
30 /* thread coordination */
31 pthread_mutex_t mu;
32 pthread_cond_t cond;
33 int hits;
34 
35 /* evidence of highest privilege attained */
36 ssize_t greatest_len;
37 char *text;
38 
39 /*
40  * interrupt handler - potentially watching for an opportunity to
41  * perform an exploit when invoked as a privileged thread.
42  */
handler(int signum,siginfo_t * info,void * ignore)43 static void handler(int signum, siginfo_t *info, void *ignore) {
44     ssize_t length;
45     char *working;
46     pthread_mutex_lock(&mu);
47 
48     cap_t caps = cap_get_proc();
49     working = cap_to_text(caps, &length);
50     if (length > greatest_len) {
51 	/*
52 	 * This is where the exploit code might go.
53 	 */
54 	cap_free(text);
55 	text = working;
56 	greatest_len = length;
57     }
58     cap_free(caps);
59     hits++;
60 
61     pthread_cond_signal(&cond);
62     pthread_mutex_unlock(&mu);
63 
64 }
65 
66 /*
67  * privileged thread code (imagine it doing whatever needs privilege).
68  */
victim(void * args)69 static void *victim(void *args) {
70     pthread_mutex_lock(&mu);
71     hits = 1;
72     printf("started privileged thread\n");
73     pthread_cond_signal(&cond);
74     pthread_mutex_unlock(&mu);
75 
76     pthread_mutex_lock(&mu);
77     while (hits < 2) {
78 	pthread_cond_wait(&cond, &mu);
79     }
80     pthread_mutex_unlock(&mu);
81 
82     return NULL;
83 }
84 
main(int argc,char ** argv)85 int main(int argc, char **argv) {
86     pthread_t peer;
87     cap_t caps = cap_init();
88     struct sigaction sig_action;
89 
90     printf("program starting\n");
91     if (pthread_create(&peer, NULL, victim, NULL)) {
92 	perror("unable to start the victim thread");
93 	exit(1);
94     }
95 
96     /*
97      * Wait until the peer thread is fully up.
98      */
99     pthread_mutex_lock(&mu);
100     while (hits < 1) {
101 	pthread_cond_wait(&cond, &mu);
102     }
103     pthread_mutex_unlock(&mu);
104 
105     printf("dropping privilege from main process thread\n");
106 
107     if (cap_set_proc(caps)) {
108 	perror("unable to drop capabilities from main process thread");
109 	exit(1);
110     }
111     cap_free(caps);
112 
113     /* confirm the low privilege of the process' main thread */
114 
115     caps = cap_get_proc();
116     text = cap_to_text(caps, &greatest_len);
117     cap_free(caps);
118 
119     printf("no privilege in main process thread: len:%ld, caps:\"%s\"\n",
120 	   greatest_len, text);
121     if (greatest_len != 1) {
122 	printf("failed to lower privilege as expected\n");
123 	exit(1);
124     }
125 
126     /*
127      * So, we have confirmed that this running thread has no
128      * privilege. From this thread we setup an interrupt handler and
129      * then trigger it on the privileged peer thread.
130      */
131 
132     sig_action.sa_sigaction = &handler;
133     sigemptyset(&sig_action.sa_mask);
134     sig_action.sa_flags = SA_SIGINFO | SA_RESTART;;
135     sigaction(SIGRTMIN, &sig_action, NULL);
136 
137     pthread_kill(peer, SIGRTMIN);
138 
139     /*
140      * Wait for the thread to exit.
141      */
142     pthread_join(peer, NULL);
143 
144     /*
145      * Let's see how we did with the exploit.
146      */
147 
148     printf("greatest privilege in main process thread: len:%ld, caps:\"%s\"\n",
149 	   greatest_len, text);
150 
151     cap_free(text);
152     if (greatest_len != 1) {
153 	printf("exploit succeeded\n");
154 	exit(1);
155     }
156 
157     printf("exploit failed\n");
158     exit(0);
159 }
160