aboutsummaryrefslogtreecommitdiff
path: root/tests/exploit.c
blob: 814337c7c338854e93380ee20a5f5884dd63eba4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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);
}