1 /*
2  * This file is part of John the Ripper password cracker,
3  * Copyright (c) 2015 by Kai Zhao
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted.
7  *
8  * There's ABSOLUTELY NO WARRANTY, express or implied.
9  */
10 
11 #include "os.h"
12 
13 #include <sys/stat.h>
14 
15 #if _MSC_VER || __MINGW32__ || __MINGW64__ || __CYGWIN__ || HAVE_WINDOWS_H
16 #include "win32_memmap.h"
17 #undef MEM_FREE
18 #if !defined(__CYGWIN__) && !defined(__MINGW64__)
19 #include "mmap-windows.c"
20 #endif /* __CYGWIN */
21 #endif /* _MSC_VER ... */
22 
23 #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
24 #include <io.h> /* mingW _mkdir */
25 #endif
26 
27 #if defined(HAVE_MMAP)
28 #include <sys/mman.h>
29 #endif
30 
31 #include "jumbo.h"
32 #include "misc.h"	// error()
33 #include "config.h"
34 #include "john.h"
35 #include "params.h"
36 #include "signals.h"
37 #include "unicode.h"
38 
39 #define _STR_VALUE(arg)         #arg
40 #define STR_MACRO(n)            _STR_VALUE(n)
41 
42 #define CHAR_FROM -128
43 #define CHAR_TO 127
44 
45 // old value from params.h
46 #define FUZZ_LINE_BUFFER_SIZE 0x30000
47 static char fuzz_hash[FUZZ_LINE_BUFFER_SIZE];
48 static char status_file_path[PATH_BUFFER_SIZE + 1];
49 
50 struct FuzzDic {
51 	struct FuzzDic *next;
52 	char *value;
53 };
54 
55 static struct FuzzDic *rfd;
56 
57 static FILE *s_file; // Status file which is ./fuzz_status/'format->params.label'
58 
59 extern int pristine_gecos;
60 extern int single_skip_login;
61 
62 static char *file_pos, *file_end;
63 
get_line()64 static char *get_line()
65 {
66 	char *new_line, *line_start;
67 
68 	line_start = file_pos;
69 	while (file_pos < file_end && *file_pos != '\n')
70 		file_pos++;
71 
72 	if (file_pos == file_end)
73 		return NULL;
74 	file_pos++;
75 
76 	new_line = mem_alloc(file_pos - line_start);
77 	strncpy(new_line, line_start, file_pos - line_start);
78 	new_line[file_pos - line_start - 1] = 0;
79 
80 	return new_line;
81 }
82 
fuzz_init_dictionary()83 static void fuzz_init_dictionary()
84 {
85 	FILE *file;
86 	char *line;
87 	struct FuzzDic *last_fd, *pfd;
88 	int64_t file_len = 0;
89 #ifdef HAVE_MMAP
90 	char *mem_map;
91 #else
92 	char *file_start;
93 #endif
94 
95 	if (!options.fuzz_dic)
96 		return;
97 
98 	if (!(file = jtr_fopen(options.fuzz_dic, "r")))
99 		pexit("fopen: %s", options.fuzz_dic);
100 
101 	jtr_fseek64(file, 0, SEEK_END);
102 	if ((file_len = jtr_ftell64(file)) == -1)
103 		pexit(STR_MACRO(jtr_ftell64));
104 	jtr_fseek64(file, 0, SEEK_SET);
105 	if (file_len == 0) {
106 		if (john_main_process)
107 			fprintf(stderr, "Error, dictionary file is empty\n");
108 		error();
109 	}
110 
111 #ifdef HAVE_MMAP
112 	mem_map = MAP_FAILED;
113 	if (file_len < ((1LL)<<32))
114 		mem_map = mmap(NULL, file_len, PROT_READ, MAP_SHARED,
115 			fileno(file), 0);
116 	if (mem_map == MAP_FAILED) {
117 		mem_map = NULL;
118 		fprintf(stderr, "fuzz: memory mapping failed (%s)\n",
119 		        strerror(errno));
120 		error();
121 	} else {
122 		file_pos = mem_map;
123 		file_end = mem_map + file_len;
124 	}
125 #else
126 	file_pos = file_start = mem_alloc(file_len);
127 	file_end = file_start + file_len;
128 	if (fread(file_pos, 1, (size_t)file_len, file) != file_len) {
129 		if (ferror(file))
130 			pexit("fread");
131 		fprintf(stderr, "fread: Unexpected EOF\n");
132 		error();
133 	}
134 #endif
135 
136 	rfd = mem_alloc(sizeof(struct FuzzDic));
137 	rfd->next = NULL;
138 	last_fd = rfd;
139 
140 	while ((line = get_line()) != NULL) {
141 		pfd = mem_alloc(sizeof(struct FuzzDic));
142 		pfd->next = NULL;
143 		pfd->value = line;
144 		last_fd->next = pfd;
145 		last_fd = pfd;
146 	}
147 
148 #ifdef HAVE_MMAP
149 	if (mem_map)
150 		munmap(mem_map, file_len);
151 #else
152 	MEM_FREE(file_start);
153 #endif
154 	file_pos = file_end = NULL;
155 
156 	if (ferror(file)) pexit("fgets");
157 
158 	if (fclose(file)) pexit("fclose");
159 }
160 
161 // Replace chars with '9$*#'
replace_each_chars(char * ciphertext,int * is_replace_finish)162 static char * replace_each_chars(char *ciphertext, int *is_replace_finish)
163 {
164 	static int replaced_chars_index = 0;
165 	static int cipher_index = 0;
166 	static char replaced_chars[5] = "\xFF" "9$*#";
167 
168 	while (replaced_chars_index < sizeof(replaced_chars)) {
169 		if (ciphertext[cipher_index] != replaced_chars[replaced_chars_index]) {
170 			fuzz_hash[cipher_index] = replaced_chars[replaced_chars_index];
171 			replaced_chars_index++;
172 			return fuzz_hash;
173 		}
174 		replaced_chars_index++;
175 	}
176 	if (replaced_chars_index == sizeof(replaced_chars)) {
177 		replaced_chars_index = 0;
178 		cipher_index++;
179 	}
180 	if (cipher_index >= strlen(ciphertext)) {
181 		*is_replace_finish = 1;
182 		cipher_index = 0;
183 		replaced_chars_index = 0;
184 		return NULL;
185 	} else {
186 		while (replaced_chars_index < sizeof(replaced_chars)) {
187 			if (ciphertext[cipher_index] != replaced_chars[replaced_chars_index]) {
188 				fuzz_hash[cipher_index] = replaced_chars[replaced_chars_index];
189 				replaced_chars_index++;
190 				return fuzz_hash;
191 			}
192 			replaced_chars_index++;
193 		}
194 	}
195 	// It will never reach here
196 	return NULL;
197 }
198 
199 // Swap two adjacent chars
200 // e.g
201 // ABCDE -> BACDE, ACBDE, ABDCE, ABCED
swap_chars(char * origin_ctext,int * is_swap_finish)202 static char * swap_chars(char *origin_ctext, int *is_swap_finish)
203 {
204 	static int cipher_index = 1;
205 
206 	while (cipher_index < strlen(fuzz_hash)) {
207 		if (origin_ctext[cipher_index - 1] != origin_ctext[cipher_index]) {
208 			fuzz_hash[cipher_index - 1] = origin_ctext[cipher_index];
209 			fuzz_hash[cipher_index] = origin_ctext[cipher_index - 1];
210 			cipher_index++;
211 			return fuzz_hash;
212 		}
213 		cipher_index++;
214 	}
215 
216 	cipher_index = 1;
217 	*is_swap_finish = 1;
218 	return NULL;
219 }
220 
221 // Append times of the last char
222 // times: 1, 2, 6, 42, 1806
append_last_char(char * origin_ctext,int * is_append_finish)223 static char * append_last_char(char *origin_ctext, int *is_append_finish)
224 {
225 	static int times = 1;
226 	static int i = 0;
227 	int origin_ctext_len = 0;
228 	int append_len = 0;
229 
230 	origin_ctext_len = strlen(origin_ctext);
231 
232 	if (origin_ctext_len == 0 || i == 5) {
233 		times = 1;
234 		i = 0;
235 		*is_append_finish = 1;
236 		return NULL;
237 	}
238 
239 	if (origin_ctext_len + times < FUZZ_LINE_BUFFER_SIZE)
240 		append_len = times;
241 	else
242 		append_len = FUZZ_LINE_BUFFER_SIZE - origin_ctext_len - 1;
243 
244 	memset(fuzz_hash + origin_ctext_len, origin_ctext[origin_ctext_len - 1], append_len);
245 	fuzz_hash[origin_ctext_len + append_len] = 0;
246 
247 	i++;
248 	times *= times + 1;
249 
250 	return fuzz_hash;
251 }
252 
253 // Change hash cases, such as "abcdef" -> "Abcdef"
change_case(char * origin_ctext,int * is_chgcase_finish)254 static char * change_case(char *origin_ctext, int *is_chgcase_finish)
255 {
256 	char c;
257 	char *pc;
258 	static int flag = 2;
259 	static int cipher_index = 0;
260 
261 	while (origin_ctext[cipher_index]) {
262 		c = origin_ctext[cipher_index];
263 		if ('a' <= c && 'z' >= c) {
264 			fuzz_hash[cipher_index] = c - 'a' + 'A';
265 			cipher_index++;
266 			return fuzz_hash;
267 		} else if ('A' <= c && 'Z' >= c) {
268 			fuzz_hash[cipher_index] = c - 'A' + 'a';
269 			cipher_index++;
270 			return fuzz_hash;
271 		}
272 		cipher_index++;
273 	}
274 
275 	if (flag == 2) {
276 		// Change all to upper cases
277 		pc = fuzz_hash;
278 		while (*pc) {
279 			if ('a' <= *pc && 'z' >= *pc)
280 				*pc = *pc - 'a' + 'A';
281 			pc++;
282 		}
283 
284 		flag--;
285 		return fuzz_hash;
286 	} else if (flag == 1) {
287 		// Change all to lower cases
288 		pc = fuzz_hash;
289 		while (*pc) {
290 			if ('A' <= *pc && 'Z' >= *pc)
291 				*pc = *pc - 'A' + 'a';
292 			pc++;
293 		}
294 
295 		flag--;
296 		return fuzz_hash;
297 	}
298 
299 	flag = 2;
300 	cipher_index = 0;
301 	*is_chgcase_finish = 1;
302 	return NULL;
303 }
304 
305 // Insert str before pos in origin_ctext, and copy the result
306 // to out
insert_str(char * origin_ctext,int pos,char * str,char * out)307 static void insert_str(char *origin_ctext, int pos, char *str, char *out)
308 {
309 	const int origin_ctext_len = strlen(origin_ctext);
310 	int str_len = strlen(str);
311 
312 	if (str_len + origin_ctext_len >= FUZZ_LINE_BUFFER_SIZE)
313 		str_len = FUZZ_LINE_BUFFER_SIZE - origin_ctext_len - 1;
314 
315 	strncpy(out, origin_ctext, pos);
316 	strncpy(out + pos, str, str_len);
317 	strcpy(out + pos + str_len, origin_ctext + pos);
318 }
319 
320 // Insert strings from dictionary before each char
insert_dic(char * origin_ctext,int * is_insertdic_finish)321 static char * insert_dic(char *origin_ctext, int *is_insertdic_finish)
322 {
323 	static int flag = 0;
324 	static struct FuzzDic *pfd = NULL;
325 	static int index = 0;
326 	static int flag_long = 0;
327 
328 	if (!options.fuzz_dic)
329 		return NULL;
330 
331 	if (!flag) {
332 		pfd = rfd->next;
333 		flag = 1;
334 	}
335 
336 	if (!pfd) {
337 		flag = 0;
338 		*is_insertdic_finish = 1;
339 		return NULL;
340 	}
341 
342 	if (100000 > strlen(origin_ctext)) {
343 		// Insert strings before each char
344 		insert_str(origin_ctext, index++, pfd->value, fuzz_hash);
345 		if (index >= strlen(origin_ctext) + 1) {
346 			index = 0;
347 			pfd = pfd->next;
348 		}
349 	} else {
350 		// Insert strings before and after these chars: ",.:#$*@"
351 		while (index < strlen(origin_ctext)) {
352 			switch (origin_ctext[index]) {
353 			case ',':
354 			case '.':
355 			case ':':
356 			case '#':
357 			case '$':
358 			case '*':
359 			case '@':
360 				if (!flag_long) {
361 					insert_str(origin_ctext, index, pfd->value, fuzz_hash);
362 					flag_long = 1;
363 				} else {
364 					insert_str(origin_ctext, index + 1, pfd->value, fuzz_hash);
365 					flag_long = 0;
366 					index++;
367 				}
368 			default:
369 				index++;
370 				break;
371 			}
372 		}
373 		if (index >= strlen(origin_ctext)) {
374 			index = 0;
375 			pfd = pfd->next;
376 		}
377 	}
378 
379 	return fuzz_hash;
380 }
381 
382 // Insert str before pos in origin_ctext, and copy the result
383 // to out
insert_char(char * origin_ctext,int pos,char c,int size,char * out)384 static void insert_char(char *origin_ctext, int pos, char c, int size, char *out)
385 {
386 	const int origin_ctext_len = strlen(origin_ctext);
387 
388 	if (size + origin_ctext_len >= FUZZ_LINE_BUFFER_SIZE)
389 		size = FUZZ_LINE_BUFFER_SIZE- origin_ctext_len - 1;
390 
391 	strncpy(out, origin_ctext, pos);
392 	memset(out + pos, c, size);
393 	strcpy(out + pos + size, origin_ctext + pos);
394 }
395 
396 // Insert chars from -128 to 127
insert_chars(char * origin_ctext,int * is_insertchars_finish)397 static char * insert_chars(char *origin_ctext, int *is_insertchars_finish)
398 {
399 	static int oc_index = 0;
400 	static int c_index = CHAR_FROM;
401 	static int flag_long = 0;
402 	static int times[5] = { 1, 10, 100, 1000, 10000 };
403 	static int times_index = 0;
404 
405 //printf("%s:%d %s(oc='%s', times_index=%d, c_index=%d, oc_index=%d)\n",
406 //	__FILE__, __LINE__, __FUNCTION__, origin_ctext,
407 //	times_index, c_index, oc_index);
408 
409 	if (times_index > 4) {
410 		times_index = 0;
411 		c_index++;
412 		if (c_index > CHAR_TO) {
413 			c_index = CHAR_FROM;
414 			oc_index++;
415 			flag_long = 0;
416 			if (oc_index > strlen(origin_ctext)) {
417 				oc_index = 0;
418 				*is_insertchars_finish = 1;
419 				return NULL;
420 			}
421 		}
422 	}
423 
424 	if (100000 > strlen(origin_ctext)) {
425 		// Insert chars before each char
426 		insert_char(origin_ctext, oc_index, (char)c_index, times[times_index++], fuzz_hash);
427 	} else {
428 		// Insert chars before and after these chars: ",.:#$*"
429 		while (oc_index < strlen(origin_ctext)) {
430 			switch (origin_ctext[oc_index]) {
431 			case ',':
432 			case '.':
433 			case ':':
434 			case '#':
435 			case '$':
436 			case '*':
437 				if (!flag_long) {
438 					insert_char(origin_ctext, oc_index, (char)c_index, times[times_index], fuzz_hash);
439 					flag_long = 1;
440 				} else {
441 					insert_char(origin_ctext, oc_index + 1, (char)c_index, times[times_index], fuzz_hash);
442 					times_index++;
443 					flag_long = 0;
444 				}
445 				return fuzz_hash;
446 			default:
447 				oc_index++;
448 				break;
449 			}
450 		}
451 		oc_index = 0;
452 		c_index = CHAR_FROM;
453 		flag_long = 0;
454 		times_index = 0;
455 		return NULL;
456 	}
457 
458 	return fuzz_hash;
459 }
460 
get_next_fuzz_case(const char * label,char * ciphertext)461 static char * get_next_fuzz_case(const char *label, char *ciphertext)
462 {
463 	static int is_replace_finish = 0; // is_replace_finish = 1 if all the replaced cases have been generated
464 	static int is_swap_finish = 0; // is_swap_finish = 1 if all the swaped cases have been generated
465 	static int is_append_finish = 0; // is_append_finish = 1 if all the appended cases have been generated
466 	static int is_chgcase_finish = 0; // is_chgcase_finish = 1 if all the change cases have been generated
467 	static int is_insertdic_finish = 0; // is_insertdic_finish = 1 if all the insert dictionary cases have been generated
468 	static int is_insertchars_finish = 0; // is_insertchars_finish = 1 if all the chars from -128 to 127 cases have been generated
469 	static const char *last_label = NULL;
470 	static char *last_ciphertext = NULL;
471 
472 	if (strlen(ciphertext) > FUZZ_LINE_BUFFER_SIZE) {
473 		fprintf(stderr, "ciphertext='%s' is bigger than the FUZZ_LINE_BUFFER_SIZE=%d\n",
474 			ciphertext, FUZZ_LINE_BUFFER_SIZE);
475 		error();
476 	}
477 	strcpy(fuzz_hash, ciphertext);
478 
479 	if (!last_label)
480 		last_label = label;
481 
482 	if (!last_ciphertext)
483 		last_ciphertext = ciphertext;
484 
485 	if (strcmp(label, last_label) != 0 || strcmp(ciphertext, last_ciphertext) != 0) {
486 		is_replace_finish = 0;
487 		is_swap_finish = 0;
488 		is_append_finish = 0;
489 		is_chgcase_finish = 0;
490 		is_insertdic_finish = 0;
491 		is_insertchars_finish = 0;
492 		last_label = label;
493 		last_ciphertext = ciphertext;
494 	}
495 
496 	if (!is_replace_finish)
497 		if (replace_each_chars(ciphertext, &is_replace_finish))
498 			return fuzz_hash;
499 
500 	if (!is_swap_finish)
501 		if (swap_chars(ciphertext, &is_swap_finish))
502 			return fuzz_hash;
503 
504 	if (!is_append_finish)
505 		if (append_last_char(ciphertext, &is_append_finish))
506 			return fuzz_hash;
507 
508 	if (!is_chgcase_finish)
509 		if (change_case(ciphertext, &is_chgcase_finish))
510 			return fuzz_hash;
511 
512 	if (!is_insertdic_finish)
513 		if (insert_dic(ciphertext, &is_insertdic_finish))
514 			return fuzz_hash;
515 
516 	if (!is_insertchars_finish)
517 		if (insert_chars(ciphertext, &is_insertchars_finish))
518 			return fuzz_hash;
519 
520 	return NULL;
521 }
522 
init_status(const char * format_label)523 static void init_status(const char *format_label)
524 {
525 	sprintf(status_file_path, "%s", "fuzz_status");
526 #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
527 	if (_mkdir(status_file_path)) { // MingW
528 #else
529 	if (mkdir(status_file_path, S_IRUSR | S_IWUSR | S_IXUSR)) {
530 #endif
531 		if (errno != EEXIST) pexit("mkdir: %s", status_file_path);
532 	} else
533 		fprintf(stderr, "Created directory: %s\n", status_file_path);
534 
535 	sprintf(status_file_path, "%s/%s", "fuzz_status", format_label);
536 	if (!(s_file = fopen(status_file_path, "w")))
537 		pexit("fopen: %s", status_file_path);
538 }
539 
540 static void save_index(const int index)
541 {
542 	fprintf(s_file, "%d\n", index);
543 	fflush(s_file);
544 }
545 
546 static void fuzz_test(struct db_main *db, struct fmt_main *format)
547 {
548 	int index;
549 	char *ret, *line;
550 	struct fmt_tests *current;
551 
552 	printf("Fuzzing: %s%s%s%s [%s]%s... ",
553 	       format->params.label,
554 	       format->params.format_name[0] ? ", " : "",
555 	       format->params.format_name,
556 	       format->params.benchmark_comment,
557 	       format->params.algorithm_name,
558 #ifndef BENCH_BUILD
559 	       (options.target_enc == UTF_8 &&
560 	       format->params.flags & FMT_UNICODE) ?
561 	       " in UTF-8 mode" : "");
562 #else
563 	       "");
564 #endif
565 	fflush(stdout);
566 
567 
568 	// validate that there are no NULL function pointers
569 	if (format->methods.prepare == NULL)    return;
570 	if (format->methods.valid == NULL)      return;
571 	if (format->methods.split == NULL)      return;
572 	if (format->methods.init == NULL)       return;
573 
574 	index = 0;
575 	current = format->params.tests;
576 
577 	init_status(format->params.label);
578 	db->format = format;
579 
580 	while (1) {
581 		ret = get_next_fuzz_case(format->params.label, current->ciphertext);
582 		save_index(index++);
583 		line = fuzz_hash;
584 		db->format = format;
585 		ldr_load_pw_line(db, line);
586 
587 		if (!ret) {
588 			if (!(++current)->ciphertext)
589 				break;
590 		}
591 	}
592 	if (fclose(s_file)) pexit("fclose");
593 	remove(status_file_path);
594 	printf("   Completed\n");
595 }
596 
597 // Dump fuzzed hashes which index is between from and to, including from and excluding to
598 static void fuzz_dump(struct fmt_main *format, const int from, const int to)
599 {
600 	int index;
601 	char *ret;
602 	struct fmt_tests *current;
603 	char file_name[PATH_BUFFER_SIZE];
604 	FILE *file;
605 	size_t len = 0;
606 
607 	sprintf(file_name, "pwfile.%s", format->params.label);
608 
609 	printf("Generating %s for %s%s%s%s ... ",
610 	       file_name,
611 	       format->params.label,
612 	       format->params.format_name[0] ? ", " : "",
613 	       format->params.format_name,
614 	       format->params.benchmark_comment);
615 	fflush(stdout);
616 
617 	if (!(file = fopen(file_name, "w")))
618 		pexit("fopen: %s", file_name);
619 
620 	index = 0;
621 	current = format->params.tests;
622 
623 	while (1) {
624 		ret = get_next_fuzz_case(format->params.label, current->ciphertext);
625 		if (index >= from) {
626 			if (index == to)
627 				break;
628 			fprintf(file, "%s\n", fuzz_hash);
629 			len += 1 + strlen(fuzz_hash);
630 		}
631 		index++;
632 		if (!ret) {
633 			if (!(++current)->ciphertext)
634 				break;
635 		}
636 	}
637 	printf(LLu" bytes\n", (unsigned long long) len);
638 	if (fclose(file)) pexit("fclose");
639 }
640 
641 
642 int fuzz(struct db_main *db)
643 {
644 	char *p;
645 	int from, to;
646 	unsigned int total;
647 	struct fmt_main *format;
648 
649 	pristine_gecos = cfg_get_bool(SECTION_OPTIONS, NULL,
650 	        "PristineGecos", 0);
651 	single_skip_login = cfg_get_bool(SECTION_OPTIONS, NULL,
652 		"SingleSkipLogin", 0);
653 
654 	if (options.flags & FLG_FUZZ_DUMP_CHK) {
655 		from = -1;
656 		to = -1;
657 
658 		if (options.fuzz_dump) {
659 			p = strtok(options.fuzz_dump, ",");
660 			if (p) {
661 				sscanf(p, "%d", &from);
662 				p = strtok(NULL, ",");
663 
664 				if (p)
665 					sscanf(p, "%d", &to);
666 			}
667 		}
668 		if (from > to) {
669 			fprintf(stderr, "--fuzz-dump from=%d is bigger than to=%d\n",
670 				from, to);
671 			error();
672 		}
673 	}
674 
675 	fuzz_init_dictionary();
676 
677 	total = 0;
678 	if ((format = fmt_list))
679 	do {
680 /* Silently skip formats for which we have no tests, unless forced */
681 		if (!format->params.tests && format != fmt_list)
682 			continue;
683 
684 		if (options.flags & FLG_FUZZ_DUMP_CHK)
685 			fuzz_dump(format, from, to);
686 		else
687 			fuzz_test(db, format);
688 
689 		total++;
690 	} while ((format = format->next) && !event_abort);
691 
692 	if (options.flags & FLG_FUZZ_DUMP_CHK)
693 		printf("Generated pwfile.<format> for %u formats\n", total);
694 	else
695 		printf("All %u formats passed fuzzing test!\n", total);
696 
697 	return 0;
698 }
699