|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +# Imports |
| 4 | +import sys |
| 5 | +import argparse |
| 6 | +from nmap import PortScanner |
| 7 | +from whois import whois |
| 8 | +import colorama |
| 9 | +from colorama import Fore, Style |
| 10 | +import itertools |
| 11 | + |
| 12 | +# Initialize colorama for colored output on Windows |
| 13 | +colorama.init(autoreset=True) |
| 14 | + |
| 15 | +# Constants |
| 16 | +EVIL_CHARS = { |
| 17 | + 'a': '\u0430', 'c': '\u03F2', 'e': '\u0435', |
| 18 | + 'o': '\u043E', 'p': '\u0440', 's': '\u0455', |
| 19 | + 'd': '\u0501', 'q': '\u051B', 'w': '\u051D' |
| 20 | +} |
| 21 | + |
| 22 | +UNICODES = { |
| 23 | + '\u0430': 'Cyrillic Small Letter A', |
| 24 | + '\u03F2': 'Greek Lunate Sigma Symbol', |
| 25 | + '\u0435': 'Cyrillic Small Letter Ie', |
| 26 | + '\u043E': 'Cyrillic Small Letter O', |
| 27 | + '\u0440': 'Cyrillic Small Letter Er', |
| 28 | + '\u0455': 'Cyrillic Small Letter Dze', |
| 29 | + '\u0501': 'Cyrillic Small Letter Komi De', |
| 30 | + '\u051B': 'Cyrillic Small Letter Qa', |
| 31 | + '\u051D': 'Cyrillic Small Letter We' |
| 32 | +} |
| 33 | + |
| 34 | +def generate_evil(domain, check_connection=False, output_file=None, check_availability=False): |
| 35 | + # Generate evil domains based on the given domain name |
| 36 | + domain = domain.lower() |
| 37 | + tld = '.' + domain.split('.')[-1] |
| 38 | + |
| 39 | + evils = list(EVIL_CHARS.keys()) |
| 40 | + e_matchs = [ch for ch in evils if ch.upper() in domain.upper()] |
| 41 | + |
| 42 | + words = [] |
| 43 | + for i in range(1, 9): |
| 44 | + for combination in itertools.combinations(e_matchs, i): |
| 45 | + words.append(''.join(combination)) |
| 46 | + |
| 47 | + with open(output_file, 'w') as output: |
| 48 | + for word in words: |
| 49 | + new_url = domain |
| 50 | + unicodes_used = [] |
| 51 | + chars_replaced = [] |
| 52 | + for w in word: |
| 53 | + if w in EVIL_CHARS: |
| 54 | + char = EVIL_CHARS[w] |
| 55 | + unicodes_used.append(char) |
| 56 | + chars_replaced.append(w) |
| 57 | + name = UNICODES[char] |
| 58 | + new_url = new_url.replace(w, char) |
| 59 | + |
| 60 | + evil_url = new_url + tld |
| 61 | + output.write(f"{domain} -> {''.join(chars_replaced)} -> {''.join(unicodes_used)} -> {evil_url}\n") |
| 62 | + print_evil(evil_url, chars_replaced, unicodes_used) |
| 63 | + |
| 64 | + if check_connection: |
| 65 | + check_url_connection(evil_url) |
| 66 | + |
| 67 | + if check_availability: |
| 68 | + check_domain_availability(evil_url) |
| 69 | + |
| 70 | +def check_evil(url): |
| 71 | + # Check if the given URL contains evil characters |
| 72 | + evil_chars = [EVIL_CHARS[ch] for ch in EVIL_CHARS if ch in url] |
| 73 | + |
| 74 | + if evil_chars: |
| 75 | + msg = f"{Fore.GREEN}[+] Evil URL detected: {url}\n" |
| 76 | + msg += f"{Fore.GREEN}[+] Unicode characters used: {', '.join(evil_chars)}" |
| 77 | + else: |
| 78 | + msg = f"{Fore.YELLOW}[-] Evil URL NOT detected: {url}" |
| 79 | + |
| 80 | + print(msg) |
| 81 | + |
| 82 | +def check_url_connection(url): |
| 83 | + # Check connection to the given URL |
| 84 | + nm_scan = PortScanner() |
| 85 | + result = nm_scan.scan(url, arguments='-sn') |
| 86 | + |
| 87 | + if int(result['nmap']['scanstats']['uphosts']) > 0: |
| 88 | + msg = f"{Fore.GREEN}[+] Connection test: UP" |
| 89 | + else: |
| 90 | + msg = f"{Fore.YELLOW}[-] Connection test: DOWN" |
| 91 | + |
| 92 | + print(msg) |
| 93 | + |
| 94 | +def check_domain_availability(domain): |
| 95 | + # Check if the domain is available |
| 96 | + try: |
| 97 | + whois_info = whois(domain) |
| 98 | + if whois_info.registrar is None: |
| 99 | + msg = f"{Fore.GREEN}[+] Available domain" |
| 100 | + else: |
| 101 | + msg = f"{Fore.YELLOW}[-] Unavailable domain" |
| 102 | + except: |
| 103 | + msg = f"{Fore.RED}[!] Unable to check domain availability" |
| 104 | + |
| 105 | + print(msg) |
| 106 | + |
| 107 | +def print_evil(url, chars_replaced, unicodes_used): |
| 108 | + # Print the evil URL details |
| 109 | + unicodes_used_str = ', '.join([f"{unicode} ({UNICODES[unicode]})" for unicode in unicodes_used]) |
| 110 | + chars_replaced_str = ', '.join(chars_replaced) |
| 111 | + print(f"{Fore.CYAN}[+] Domain name: {url}") |
| 112 | + print(f"{Fore.CYAN}[+] Char replaced: {chars_replaced_str}") |
| 113 | + print(f"{Fore.CYAN}[+] Using Unicode: {unicodes_used_str}") |
| 114 | + |
| 115 | +def main(): |
| 116 | + parser = argparse.ArgumentParser(description="Generate unicode evil domains for IDN Homograph Attack and detect them.") |
| 117 | + subparsers = parser.add_subparsers(dest='mode', title='Modes', description='Available modes') |
| 118 | + |
| 119 | + generate_parser = subparsers.add_parser('generate', help='Generate unicode evil domains') |
| 120 | + generate_parser.add_argument('domain', type=str, help='Domain name with termination (example.com)') |
| 121 | + generate_parser.add_argument('-c', '--check-connection', action='store_true', help='Check generated domain connections') |
| 122 | + generate_parser.add_argument('-o', '--output-file', type=str, help='Save generated evil domains to a file') |
| 123 | + generate_parser.add_argument('-a', '--check-availability', action='store_true', help='Check if domain is available') |
| 124 | + |
| 125 | + check_parser = subparsers.add_parser('check', help='Check if a URL contains evil characters') |
| 126 | + check_parser.add_argument('url', type=str, help='URL to check for evil characters') |
| 127 | + |
| 128 | + args = parser.parse_args() |
| 129 | + |
| 130 | + if args.mode == 'generate': |
| 131 | + generate_evil(args.domain, args.check_connection, args.output_file, args.check_availability) |
| 132 | + elif args.mode == 'check': |
| 133 | + check_evil(args.url) |
| 134 | + else: |
| 135 | + parser.print_help() |
| 136 | + |
| 137 | +if __name__ == '__main__': |
| 138 | + main() |
0 commit comments