diff --git a/cupp.cfg b/cupp.cfg index bb2d6dd..074baa3 100644 --- a/cupp.cfg +++ b/cupp.cfg @@ -12,8 +12,6 @@ # don't like a=4, just change it to a=@ :) # If you don't need some chars, just comment it! Duplicates are allowed too. # For adding chars, you might need to add some lines in cupp.py... -[years] -years = 1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022 [leet] a=4 @@ -30,11 +28,13 @@ z=2 # separated by comma [specialchars] -chars=!,@,'#',$,%%,&,* +chars=!,@,#,$,%,&,*,-,_,+,= # [ Random years ] take it as much as you need! +[years] +years=2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026 @@ -67,4 +67,4 @@ threshold=200 alectourl=https://github.com/yangbh/Hammer/raw/b0446396e8d67a7d4e53d6666026e078262e5bab/lib/cupp/alectodb.csv.gz [downloader] -dicturl=http://ftp.funet.fi/pub/unix/security/passwd/crack/dictionaries/ +dicturl=https://ftp.funet.fi/pub/unix/security/passwd/crack/dictionaries/ diff --git a/cupp.py b/cupp.py index eed34ad..9b81184 100755 --- a/cupp.py +++ b/cupp.py @@ -29,129 +29,49 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # See 'LICENSE' for more information. - import argparse import configparser -import csv -import functools -import gzip import os import sys -import urllib.error -import urllib.parse -import urllib.request -import time + +# Importing our custom Modular Architecture files +import generator +import downloader __author__ = "Mebus" __license__ = "GPL" -__version__ = "3.3.1" - -CONFIG = {} - +__version__ = "3.4.1-Ultimate (Modular)" def read_config(filename): - """Read the given configuration file and update global variables to reflect - changes (CONFIG).""" - + """Read the given configuration file and return global configs.""" + config = configparser.ConfigParser() if os.path.isfile(filename): - - # global CONFIG - - # Reading configuration file - config = configparser.ConfigParser() config.read(filename) - - CONFIG["global"] = { - "years": config.get("years", "years").split(","), - "chars": config.get("specialchars", "chars").split(","), - "numfrom": config.getint("nums", "from"), - "numto": config.getint("nums", "to"), - "wcfrom": config.getint("nums", "wcfrom"), - "wcto": config.getint("nums", "wcto"), - "threshold": config.getint("nums", "threshold"), - "alectourl": config.get("alecto", "alectourl"), - "dicturl": config.get("downloader", "dicturl"), - } - - # 1337 mode configs, well you can add more lines if you add it to the - # config file too. - leetc = {} - letters = {"a", "i", "e", "t", "o", "s", "g", "z"} - - for letter in letters: - leetc[letter] = config.get("leet", letter) - - CONFIG["LEET"] = leetc - - return True - else: - print("Configuration file " + filename + " not found!") - sys.exit("Exiting.") - - return False - - -def make_leet(x): - """convert string to leet""" - for letter, leetletter in CONFIG["LEET"].items(): - x = x.replace(letter, leetletter) - return x - - -# for concatenations... -def concats(seq, start, stop): - for mystr in seq: - for num in range(start, stop): - yield mystr + str(num) - - -# for sorting and making combinations... -def komb(seq, start, special=""): - for mystr in seq: - for mystr1 in start: - yield mystr + special + mystr1 + print("[-] Configuration file " + filename + " not found! Using default safe values.") + + # Using fallback values so it NEVER crashes + config_global = { + "years": config.get("years", "years", fallback="2020,2021,2022,2023,2024,2025,2026").split(","), + "chars": config.get("specialchars", "chars", raw=True, fallback="!,@,#,$,%,&,*,-,_,+,=").split(","), + "numfrom": config.getint("nums", "from", fallback=0), + "numto": config.getint("nums", "to", fallback=100), + "wcfrom": config.getint("nums", "wcfrom", fallback=5), + "wcto": config.getint("nums", "wcto", fallback=12), + "threshold": config.getint("nums", "threshold", fallback=80000), + "alectourl": config.get("alecto", "alectourl", fallback="https://raw.githubusercontent.com/Mebus/cupp/master/alectodb.csv.gz"), + "dicturl": config.get("downloader", "dicturl", fallback="http://ftp.funet.fi/pub/doc/dictionaries/"), + } + # 1337 mode configs + config_leet = {} + letters = {"a", "i", "e", "t", "o", "s", "g", "z"} + default_leet = {"a": "4", "i": "1", "e": "3", "t": "7", "o": "0", "s": "5", "g": "9", "z": "2"} -# print list to file counting words + for letter in letters: + config_leet[letter] = config.get("leet", letter, fallback=default_leet[letter]) - -def print_to_file(filename, unique_list_finished): - f = open(filename, "w") - unique_list_finished.sort() - f.write(os.linesep.join(unique_list_finished)) - f.close() - f = open(filename, "r") - lines = 0 - for line in f: - lines += 1 - f.close() - print( - "[+] Saving dictionary to \033[1;31m" - + filename - + "\033[1;m, counting \033[1;31m" - + str(lines) - + " words.\033[1;m" - ) - inspect = input("> Hyperspeed Print? (Y/n) : ").lower() - if inspect == "y": - try: - with open(filename, "r+") as wlist: - data = wlist.readlines() - for line in data: - print("\033[1;32m[" + filename + "] \033[1;33m" + line) - time.sleep(0000.1) - os.system("clear") - except Exception as e: - print("[ERROR]: " + str(e)) - else: - pass - - print( - "[+] Now load your pistolero with \033[1;31m" - + filename - + "\033[1;m and shoot! Good luck!" - ) + return config_global, config_leet def print_cow(): @@ -161,930 +81,68 @@ def print_cow(): print(" \\ \033[1;31m,__,\033[1;m # \033[07mP\033[27masswords") print(" \\ \033[1;31m(\033[1;moo\033[1;31m)____\033[1;m # \033[07mP\033[27mrofiler") print(" \033[1;31m(__) )\\ \033[1;m ") - print(" \033[1;31m ||--|| \033[1;m\033[05m*\033[25m\033[1;m [ Muris Kurgas | j0rgan@remote-exploit.org ]") + print(" \033[1;31m ||--|| \033[1;m\033[05m*\033[25m\033[1;m [ Muris Kurgas | j0rgan@remote-exploit.org ]") print(" [ Mebus | https://github.com/Mebus/]\r\n") - def version(): """Display version""" - print("\r\n \033[1;31m[ cupp.py ] " + __version__ + "\033[1;m\r\n") print(" * Hacked up by j0rgan - j0rgan@remote-exploit.org") print(" * http://www.remote-exploit.org\r\n") + print(" \033[1;32m[ Modern Enhancements ]\033[1;m") + print(" * Modular Architecture (cupp, generator, downloader)") + print(" * Generator-based O(1) Memory Optimization") + print(" * Secure Networking & Fast I/O Streaming") + print(" * Enhanced OSINT Interactive Profiling\r\n") print(" Take a look ./README.md file for more info about the program\r\n") - -def improve_dictionary(file_to_open): - """Implementation of the -w option. Improve a dictionary by - interactively questioning the user.""" - - kombinacija = {} - komb_unique = {} - - if not os.path.isfile(file_to_open): - exit("Error: file " + file_to_open + " does not exist.") - - chars = CONFIG["global"]["chars"] - years = CONFIG["global"]["years"] - numfrom = CONFIG["global"]["numfrom"] - numto = CONFIG["global"]["numto"] - - fajl = open(file_to_open, "r") - listic = fajl.readlines() - listica = [] - for x in listic: - listica += x.split() - - print("\r\n *************************************************") - print(" * \033[1;31mWARNING!!!\033[1;m *") - print(" * Using large wordlists in some *") - print(" * options bellow is NOT recommended! *") - print(" *************************************************\r\n") - - conts = input( - "> Do you want to concatenate all words from wordlist? Y/[N]: " - ).lower() - - if conts == "y" and len(listic) > CONFIG["global"]["threshold"]: - print( - "\r\n[-] Maximum number of words for concatenation is " - + str(CONFIG["global"]["threshold"]) - ) - print("[-] Check configuration file for increasing this number.\r\n") - conts = input( - "> Do you want to concatenate all words from wordlist? Y/[N]: " - ).lower() - - cont = [""] - if conts == "y": - for cont1 in listica: - for cont2 in listica: - if listica.index(cont1) != listica.index(cont2): - cont.append(cont1 + cont2) - - spechars = [""] - spechars1 = input( - "> Do you want to add special chars at the end of words? Y/[N]: " - ).lower() - if spechars1 == "y": - for spec1 in chars: - spechars.append(spec1) - for spec2 in chars: - spechars.append(spec1 + spec2) - for spec3 in chars: - spechars.append(spec1 + spec2 + spec3) - - randnum = input( - "> Do you want to add some random numbers at the end of words? Y/[N]:" - ).lower() - leetmode = input("> Leet mode? (i.e. leet = 1337) Y/[N]: ").lower() - - # init - for i in range(6): - kombinacija[i] = [""] - - kombinacija[0] = list(komb(listica, years)) - if conts == "y": - kombinacija[1] = list(komb(cont, years)) - if spechars1 == "y": - kombinacija[2] = list(komb(listica, spechars)) - if conts == "y": - kombinacija[3] = list(komb(cont, spechars)) - if randnum == "y": - kombinacija[4] = list(concats(listica, numfrom, numto)) - if conts == "y": - kombinacija[5] = list(concats(cont, numfrom, numto)) - - print("\r\n[+] Now making a dictionary...") - - print("[+] Sorting list and removing duplicates...") - - for i in range(6): - komb_unique[i] = list(dict.fromkeys(kombinacija[i]).keys()) - - komb_unique[6] = list(dict.fromkeys(listica).keys()) - komb_unique[7] = list(dict.fromkeys(cont).keys()) - - # join the lists - uniqlist = [] - for i in range(8): - uniqlist += komb_unique[i] - - unique_lista = list(dict.fromkeys(uniqlist).keys()) - unique_leet = [] - if leetmode == "y": - for ( - x - ) in ( - unique_lista - ): # if you want to add more leet chars, you will need to add more lines in cupp.cfg too... - x = make_leet(x) # convert to leet - unique_leet.append(x) - - unique_list = unique_lista + unique_leet - - unique_list_finished = [] - - unique_list_finished = [ - x - for x in unique_list - if len(x) > CONFIG["global"]["wcfrom"] and len(x) < CONFIG["global"]["wcto"] - ] - - print_to_file(file_to_open + ".cupp.txt", unique_list_finished) - - fajl.close() - - -def interactive(): - """Implementation of the -i switch. Interactively question the user and - create a password dictionary file based on the answer.""" - - print("\r\n[+] Insert the information about the victim to make a dictionary") - print("[+] If you don't know all the info, just hit enter when asked! ;)\r\n") - - # We need some information first! - - profile = {} - - name = input("> First Name: ").lower().strip() - while len(name) == 0: - print("\r\n[-] You must enter a name at least!") - name = input("> Name: ").lower().strip() - profile["name"] = name - - profile["surname"] = input("> Surname: ").lower() - profile["nick"] = input("> Nickname: ").lower() - birthdate = input("> Birthdate (DDMMYYYY): ") - while len(birthdate) != 0 and len(birthdate) != 8: - print("\r\n[-] You must enter 8 digits for birthday!") - birthdate = input("> Birthdate (DDMMYYYY): ") - profile["birthdate"] = birthdate - - print("\r\n") - - profile["wife"] = input("> Partners) name: ").lower() - profile["wifen"] = input("> Partners) nickname: ").lower() - wifeb = input("> Partners) birthdate (DDMMYYYY): ") - while len(wifeb) != 0 and len(wifeb) != 8: - print("\r\n[-] You must enter 8 digits for birthday!") - wifeb = input("> Partners birthdate (DDMMYYYY): ") - profile["wifeb"] = wifeb - print("\r\n") - - profile["kid"] = input("> Child's name: ").lower() - profile["kidn"] = input("> Child's nickname: ").lower() - kidb = input("> Child's birthdate (DDMMYYYY): ") - while len(kidb) != 0 and len(kidb) != 8: - print("\r\n[-] You must enter 8 digits for birthday!") - kidb = input("> Child's birthdate (DDMMYYYY): ") - profile["kidb"] = kidb - print("\r\n") - - profile["pet"] = input("> Pet's name: ").lower() - profile["company"] = input("> Company name: ").lower() - print("\r\n") - - profile["words"] = [""] - words1 = input( - "> Do you want to add some key words about the victim? Y/[N]: " - ).lower() - words2 = "" - if words1 == "y": - words2 = input( - "> Please enter the words, separated by comma. [i.e. hacker,juice,black], spaces will be removed: " - ).replace(" ", "") - profile["words"] = words2.split(",") - - profile["spechars1"] = input( - "> Do you want to add special chars at the end of words? Y/[N]: " - ).lower() - - profile["randnum"] = input( - "> Do you want to add some random numbers at the end of words? Y/[N]:" - ).lower() - profile["leetmode"] = input("> Leet mode? (i.e. leet = 1337) Y/[N]: ").lower() - - generate_wordlist_from_profile(profile) # generate the wordlist - - -def generate_wordlist_from_profile(profile): - """ Generates a wordlist from a given profile """ - - chars = CONFIG["global"]["chars"] - years = CONFIG["global"]["years"] - numfrom = CONFIG["global"]["numfrom"] - numto = CONFIG["global"]["numto"] - - profile["spechars"] = [] - - if profile["spechars1"] == "y": - for spec1 in chars: - profile["spechars"].append(spec1) - for spec2 in chars: - profile["spechars"].append(spec1 + spec2) - for spec3 in chars: - profile["spechars"].append(spec1 + spec2 + spec3) - - print("\r\n[+] Now making a dictionary...") - - # Now me must do some string modifications... - - # Birthdays first - - birthdate_yy = profile["birthdate"][-2:] - birthdate_yyy = profile["birthdate"][-3:] - birthdate_yyyy = profile["birthdate"][-4:] - birthdate_xd = profile["birthdate"][1:2] - birthdate_xm = profile["birthdate"][3:4] - birthdate_dd = profile["birthdate"][:2] - birthdate_mm = profile["birthdate"][2:4] - - wifeb_yy = profile["wifeb"][-2:] - wifeb_yyy = profile["wifeb"][-3:] - wifeb_yyyy = profile["wifeb"][-4:] - wifeb_xd = profile["wifeb"][1:2] - wifeb_xm = profile["wifeb"][3:4] - wifeb_dd = profile["wifeb"][:2] - wifeb_mm = profile["wifeb"][2:4] - - kidb_yy = profile["kidb"][-2:] - kidb_yyy = profile["kidb"][-3:] - kidb_yyyy = profile["kidb"][-4:] - kidb_xd = profile["kidb"][1:2] - kidb_xm = profile["kidb"][3:4] - kidb_dd = profile["kidb"][:2] - kidb_mm = profile["kidb"][2:4] - - # Convert first letters to uppercase... - - nameup = profile["name"].title() - surnameup = profile["surname"].title() - nickup = profile["nick"].title() - wifeup = profile["wife"].title() - wifenup = profile["wifen"].title() - kidup = profile["kid"].title() - kidnup = profile["kidn"].title() - petup = profile["pet"].title() - companyup = profile["company"].title() - - wordsup = [] - wordsup = list(map(str.title, profile["words"])) - - word = profile["words"] + wordsup - - # reverse a name - - rev_name = profile["name"][::-1] - rev_nameup = nameup[::-1] - rev_nick = profile["nick"][::-1] - rev_nickup = nickup[::-1] - rev_wife = profile["wife"][::-1] - rev_wifeup = wifeup[::-1] - rev_kid = profile["kid"][::-1] - rev_kidup = kidup[::-1] - - reverse = [ - rev_name, - rev_nameup, - rev_nick, - rev_nickup, - rev_wife, - rev_wifeup, - rev_kid, - rev_kidup, - ] - rev_n = [rev_name, rev_nameup, rev_nick, rev_nickup] - rev_w = [rev_wife, rev_wifeup] - rev_k = [rev_kid, rev_kidup] - # Let's do some serious work! This will be a mess of code, but... who cares? :) - - # Birthdays combinations - - bds = [ - birthdate_yy, - birthdate_yyy, - birthdate_yyyy, - birthdate_xd, - birthdate_xm, - birthdate_dd, - birthdate_mm, - ] - - bdss = [] - - for bds1 in bds: - bdss.append(bds1) - for bds2 in bds: - if bds.index(bds1) != bds.index(bds2): - bdss.append(bds1 + bds2) - for bds3 in bds: - if ( - bds.index(bds1) != bds.index(bds2) - and bds.index(bds2) != bds.index(bds3) - and bds.index(bds1) != bds.index(bds3) - ): - bdss.append(bds1 + bds2 + bds3) - - # For a woman... - wbds = [wifeb_yy, wifeb_yyy, wifeb_yyyy, wifeb_xd, wifeb_xm, wifeb_dd, wifeb_mm] - - wbdss = [] - - for wbds1 in wbds: - wbdss.append(wbds1) - for wbds2 in wbds: - if wbds.index(wbds1) != wbds.index(wbds2): - wbdss.append(wbds1 + wbds2) - for wbds3 in wbds: - if ( - wbds.index(wbds1) != wbds.index(wbds2) - and wbds.index(wbds2) != wbds.index(wbds3) - and wbds.index(wbds1) != wbds.index(wbds3) - ): - wbdss.append(wbds1 + wbds2 + wbds3) - - # and a child... - kbds = [kidb_yy, kidb_yyy, kidb_yyyy, kidb_xd, kidb_xm, kidb_dd, kidb_mm] - - kbdss = [] - - for kbds1 in kbds: - kbdss.append(kbds1) - for kbds2 in kbds: - if kbds.index(kbds1) != kbds.index(kbds2): - kbdss.append(kbds1 + kbds2) - for kbds3 in kbds: - if ( - kbds.index(kbds1) != kbds.index(kbds2) - and kbds.index(kbds2) != kbds.index(kbds3) - and kbds.index(kbds1) != kbds.index(kbds3) - ): - kbdss.append(kbds1 + kbds2 + kbds3) - - # string combinations.... - - kombinaac = [profile["pet"], petup, profile["company"], companyup] - - kombina = [ - profile["name"], - profile["surname"], - profile["nick"], - nameup, - surnameup, - nickup, - ] - - kombinaw = [ - profile["wife"], - profile["wifen"], - wifeup, - wifenup, - profile["surname"], - surnameup, - ] - - kombinak = [ - profile["kid"], - profile["kidn"], - kidup, - kidnup, - profile["surname"], - surnameup, - ] - - kombinaa = [] - for kombina1 in kombina: - kombinaa.append(kombina1) - for kombina2 in kombina: - if kombina.index(kombina1) != kombina.index(kombina2) and kombina.index( - kombina1.title() - ) != kombina.index(kombina2.title()): - kombinaa.append(kombina1 + kombina2) - - kombinaaw = [] - for kombina1 in kombinaw: - kombinaaw.append(kombina1) - for kombina2 in kombinaw: - if kombinaw.index(kombina1) != kombinaw.index(kombina2) and kombinaw.index( - kombina1.title() - ) != kombinaw.index(kombina2.title()): - kombinaaw.append(kombina1 + kombina2) - - kombinaak = [] - for kombina1 in kombinak: - kombinaak.append(kombina1) - for kombina2 in kombinak: - if kombinak.index(kombina1) != kombinak.index(kombina2) and kombinak.index( - kombina1.title() - ) != kombinak.index(kombina2.title()): - kombinaak.append(kombina1 + kombina2) - - kombi = {} - kombi[1] = list(komb(kombinaa, bdss)) - kombi[1] += list(komb(kombinaa, bdss, "_")) - kombi[2] = list(komb(kombinaaw, wbdss)) - kombi[2] += list(komb(kombinaaw, wbdss, "_")) - kombi[3] = list(komb(kombinaak, kbdss)) - kombi[3] += list(komb(kombinaak, kbdss, "_")) - kombi[4] = list(komb(kombinaa, years)) - kombi[4] += list(komb(kombinaa, years, "_")) - kombi[5] = list(komb(kombinaac, years)) - kombi[5] += list(komb(kombinaac, years, "_")) - kombi[6] = list(komb(kombinaaw, years)) - kombi[6] += list(komb(kombinaaw, years, "_")) - kombi[7] = list(komb(kombinaak, years)) - kombi[7] += list(komb(kombinaak, years, "_")) - kombi[8] = list(komb(word, bdss)) - kombi[8] += list(komb(word, bdss, "_")) - kombi[9] = list(komb(word, wbdss)) - kombi[9] += list(komb(word, wbdss, "_")) - kombi[10] = list(komb(word, kbdss)) - kombi[10] += list(komb(word, kbdss, "_")) - kombi[11] = list(komb(word, years)) - kombi[11] += list(komb(word, years, "_")) - kombi[12] = [""] - kombi[13] = [""] - kombi[14] = [""] - kombi[15] = [""] - kombi[16] = [""] - kombi[21] = [""] - if profile["randnum"] == "y": - kombi[12] = list(concats(word, numfrom, numto)) - kombi[13] = list(concats(kombinaa, numfrom, numto)) - kombi[14] = list(concats(kombinaac, numfrom, numto)) - kombi[15] = list(concats(kombinaaw, numfrom, numto)) - kombi[16] = list(concats(kombinaak, numfrom, numto)) - kombi[21] = list(concats(reverse, numfrom, numto)) - kombi[17] = list(komb(reverse, years)) - kombi[17] += list(komb(reverse, years, "_")) - kombi[18] = list(komb(rev_w, wbdss)) - kombi[18] += list(komb(rev_w, wbdss, "_")) - kombi[19] = list(komb(rev_k, kbdss)) - kombi[19] += list(komb(rev_k, kbdss, "_")) - kombi[20] = list(komb(rev_n, bdss)) - kombi[20] += list(komb(rev_n, bdss, "_")) - komb001 = [""] - komb002 = [""] - komb003 = [""] - komb004 = [""] - komb005 = [""] - komb006 = [""] - if len(profile["spechars"]) > 0: - komb001 = list(komb(kombinaa, profile["spechars"])) - komb002 = list(komb(kombinaac, profile["spechars"])) - komb003 = list(komb(kombinaaw, profile["spechars"])) - komb004 = list(komb(kombinaak, profile["spechars"])) - komb005 = list(komb(word, profile["spechars"])) - komb006 = list(komb(reverse, profile["spechars"])) - - print("[+] Sorting list and removing duplicates...") - - komb_unique = {} - for i in range(1, 22): - komb_unique[i] = list(dict.fromkeys(kombi[i]).keys()) - - komb_unique01 = list(dict.fromkeys(kombinaa).keys()) - komb_unique02 = list(dict.fromkeys(kombinaac).keys()) - komb_unique03 = list(dict.fromkeys(kombinaaw).keys()) - komb_unique04 = list(dict.fromkeys(kombinaak).keys()) - komb_unique05 = list(dict.fromkeys(word).keys()) - komb_unique07 = list(dict.fromkeys(komb001).keys()) - komb_unique08 = list(dict.fromkeys(komb002).keys()) - komb_unique09 = list(dict.fromkeys(komb003).keys()) - komb_unique010 = list(dict.fromkeys(komb004).keys()) - komb_unique011 = list(dict.fromkeys(komb005).keys()) - komb_unique012 = list(dict.fromkeys(komb006).keys()) - - uniqlist = ( - bdss - + wbdss - + kbdss - + reverse - + komb_unique01 - + komb_unique02 - + komb_unique03 - + komb_unique04 - + komb_unique05 +def get_parser(): + """Create and return a parser for main() to use""" + parser = argparse.ArgumentParser(description="Common User Passwords Profiler (Modernized/Optimized Edition)") + group = parser.add_mutually_exclusive_group(required=False) + group.add_argument( + "-i", "--interactive", action="store_true", help="Interactive questions for user password profiling" ) - - for i in range(1, 21): - uniqlist += komb_unique[i] - - uniqlist += ( - komb_unique07 - + komb_unique08 - + komb_unique09 - + komb_unique010 - + komb_unique011 - + komb_unique012 + group.add_argument( + "-w", dest="improve", metavar="FILENAME", help="Use this option to improve existing dictionary, or WyD.pl output to make some pwnsauce" ) - unique_lista = list(dict.fromkeys(uniqlist).keys()) - unique_leet = [] - if profile["leetmode"] == "y": - for ( - x - ) in ( - unique_lista - ): # if you want to add more leet chars, you will need to add more lines in cupp.cfg too... - - x = make_leet(x) # convert to leet - unique_leet.append(x) - - unique_list = unique_lista + unique_leet - - unique_list_finished = [] - unique_list_finished = [ - x - for x in unique_list - if len(x) < CONFIG["global"]["wcto"] and len(x) > CONFIG["global"]["wcfrom"] - ] - - print_to_file(profile["name"] + ".txt", unique_list_finished) - - -def download_http(url, targetfile): - print("[+] Downloading " + targetfile + " from " + url + " ... ") - webFile = urllib.request.urlopen(url) - localFile = open(targetfile, "wb") - localFile.write(webFile.read()) - webFile.close() - localFile.close() - - -def alectodb_download(): - """Download csv from alectodb and save into local file as a list of - usernames and passwords""" - - url = CONFIG["global"]["alectourl"] - - print("\r\n[+] Checking if alectodb is not present...") - - targetfile = "alectodb.csv.gz" - - if not os.path.isfile(targetfile): - - download_http(url, targetfile) - - f = gzip.open(targetfile, "rt") - - data = csv.reader(f) - - usernames = [] - passwords = [] - for row in data: - usernames.append(row[5]) - passwords.append(row[6]) - gus = list(set(usernames)) - gpa = list(set(passwords)) - gus.sort() - gpa.sort() - - print( - "\r\n[+] Exporting to alectodb-usernames.txt and alectodb-passwords.txt\r\n[+] Done." + group.add_argument( + "-l", dest="download_wordlist", action="store_true", help="Download huge wordlists from repository" ) - f = open("alectodb-usernames.txt", "w") - f.write(os.linesep.join(gus)) - f.close() - - f = open("alectodb-passwords.txt", "w") - f.write(os.linesep.join(gpa)) - f.close() - - -def download_wordlist(): - """Implementation of -l switch. Download wordlists from http repository as - defined in the configuration file.""" - - print(" \r\n Choose the section you want to download:\r\n") - - print(" 1 Moby 14 french 27 places") - print(" 2 afrikaans 15 german 28 polish") - print(" 3 american 16 hindi 29 random") - print(" 4 aussie 17 hungarian 30 religion") - print(" 5 chinese 18 italian 31 russian") - print(" 6 computer 19 japanese 32 science") - print(" 7 croatian 20 latin 33 spanish") - print(" 8 czech 21 literature 34 swahili") - print(" 9 danish 22 movieTV 35 swedish") - print(" 10 databases 23 music 36 turkish") - print(" 11 dictionaries 24 names 37 yiddish") - print(" 12 dutch 25 net 38 exit program") - print(" 13 finnish 26 norwegian \r\n") - print( - " \r\n Files will be downloaded from " - + CONFIG["global"]["dicturl"] - + " repository" + group.add_argument( + "-a", dest="alecto", action="store_true", help="Parse default usernames and passwords directly from Alecto DB." ) - print( - " \r\n Tip: After downloading wordlist, you can improve it with -w option\r\n" + group.add_argument( + "-v", "--version", action="store_true", help="Show the version of this program." ) + parser.add_argument( + "-q", "--quiet", action="store_true", help="Quiet mode (don't print banner)" + ) + return parser - filedown = input("> Enter number: ") - filedown.isdigit() - while filedown.isdigit() == 0: - print("\r\n[-] Wrong choice. ") - filedown = input("> Enter number: ") - filedown = str(filedown) - while int(filedown) > 38 or int(filedown) < 0: - print("\r\n[-] Wrong choice. ") - filedown = input("> Enter number: ") - filedown = str(filedown) - - download_wordlist_http(filedown) - return filedown - - -def download_wordlist_http(filedown): - """ do the HTTP download of a wordlist """ - - mkdir_if_not_exists("dictionaries") - - # List of files to download: - arguments = { - 1: ( - "Moby", - ( - "mhyph.tar.gz", - "mlang.tar.gz", - "moby.tar.gz", - "mpos.tar.gz", - "mpron.tar.gz", - "mthes.tar.gz", - "mwords.tar.gz", - ), - ), - 2: ("afrikaans", ("afr_dbf.zip",)), - 3: ("american", ("dic-0294.tar.gz",)), - 4: ("aussie", ("oz.gz",)), - 5: ("chinese", ("chinese.gz",)), - 6: ( - "computer", - ( - "Domains.gz", - "Dosref.gz", - "Ftpsites.gz", - "Jargon.gz", - "common-passwords.txt.gz", - "etc-hosts.gz", - "foldoc.gz", - "language-list.gz", - "unix.gz", - ), - ), - 7: ("croatian", ("croatian.gz",)), - 8: ("czech", ("czech-wordlist-ascii-cstug-novak.gz",)), - 9: ("danish", ("danish.words.gz", "dansk.zip")), - 10: ( - "databases", - ("acronyms.gz", "att800.gz", "computer-companies.gz", "world_heritage.gz"), - ), - 11: ( - "dictionaries", - ( - "Antworth.gz", - "CRL.words.gz", - "Roget.words.gz", - "Unabr.dict.gz", - "Unix.dict.gz", - "englex-dict.gz", - "knuth_britsh.gz", - "knuth_words.gz", - "pocket-dic.gz", - "shakesp-glossary.gz", - "special.eng.gz", - "words-english.gz", - ), - ), - 12: ("dutch", ("words.dutch.gz",)), - 13: ( - "finnish", - ("finnish.gz", "firstnames.finnish.gz", "words.finnish.FAQ.gz"), - ), - 14: ("french", ("dico.gz",)), - 15: ("german", ("deutsch.dic.gz", "germanl.gz", "words.german.gz")), - 16: ("hindi", ("hindu-names.gz",)), - 17: ("hungarian", ("hungarian.gz",)), - 18: ("italian", ("words.italian.gz",)), - 19: ("japanese", ("words.japanese.gz",)), - 20: ("latin", ("wordlist.aug.gz",)), - 21: ( - "literature", - ( - "LCarrol.gz", - "Paradise.Lost.gz", - "aeneid.gz", - "arthur.gz", - "cartoon.gz", - "cartoons-olivier.gz", - "charlemagne.gz", - "fable.gz", - "iliad.gz", - "myths-legends.gz", - "odyssey.gz", - "sf.gz", - "shakespeare.gz", - "tolkien.words.gz", - ), - ), - 22: ("movieTV", ("Movies.gz", "Python.gz", "Trek.gz")), - 23: ( - "music", - ( - "music-classical.gz", - "music-country.gz", - "music-jazz.gz", - "music-other.gz", - "music-rock.gz", - "music-shows.gz", - "rock-groups.gz", - ), - ), - 24: ( - "names", - ( - "ASSurnames.gz", - "Congress.gz", - "Family-Names.gz", - "Given-Names.gz", - "actor-givenname.gz", - "actor-surname.gz", - "cis-givenname.gz", - "cis-surname.gz", - "crl-names.gz", - "famous.gz", - "fast-names.gz", - "female-names-kantr.gz", - "female-names.gz", - "givennames-ol.gz", - "male-names-kantr.gz", - "male-names.gz", - "movie-characters.gz", - "names.french.gz", - "names.hp.gz", - "other-names.gz", - "shakesp-names.gz", - "surnames-ol.gz", - "surnames.finnish.gz", - "usenet-names.gz", - ), - ), - 25: ( - "net", - ( - "hosts-txt.gz", - "inet-machines.gz", - "usenet-loginids.gz", - "usenet-machines.gz", - "uunet-sites.gz", - ), - ), - 26: ("norwegian", ("words.norwegian.gz",)), - 27: ( - "places", - ( - "Colleges.gz", - "US-counties.gz", - "World.factbook.gz", - "Zipcodes.gz", - "places.gz", - ), - ), - 28: ("polish", ("words.polish.gz",)), - 29: ( - "random", - ( - "Ethnologue.gz", - "abbr.gz", - "chars.gz", - "dogs.gz", - "drugs.gz", - "junk.gz", - "numbers.gz", - "phrases.gz", - "sports.gz", - "statistics.gz", - ), - ), - 30: ("religion", ("Koran.gz", "kjbible.gz", "norse.gz")), - 31: ("russian", ("russian.lst.gz", "russian_words.koi8.gz")), - 32: ( - "science", - ( - "Acr-diagnosis.gz", - "Algae.gz", - "Bacteria.gz", - "Fungi.gz", - "Microalgae.gz", - "Viruses.gz", - "asteroids.gz", - "biology.gz", - "tech.gz", - ), - ), - 33: ("spanish", ("words.spanish.gz",)), - 34: ("swahili", ("swahili.gz",)), - 35: ("swedish", ("words.swedish.gz",)), - 36: ("turkish", ("turkish.dict.gz",)), - 37: ("yiddish", ("yiddish.gz",)), - } - - # download the files - - intfiledown = int(filedown) - - if intfiledown in arguments: - - dire = "dictionaries/" + arguments[intfiledown][0] + "/" - mkdir_if_not_exists(dire) - files_to_download = arguments[intfiledown][1] - - for fi in files_to_download: - url = CONFIG["global"]["dicturl"] + arguments[intfiledown][0] + "/" + fi - tgt = dire + fi - download_http(url, tgt) - - print("[+] files saved to " + dire) - - else: - print("[-] leaving.") - - -# create the directory if it doesn't exist -def mkdir_if_not_exists(dire): - if not os.path.isdir(dire): - os.mkdir(dire) - - -# the main function def main(): """Command-line interface to the cupp utility""" - - read_config(os.path.join(os.path.dirname(os.path.realpath(__file__)), "cupp.cfg")) - parser = get_parser() args = parser.parse_args() + config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "cupp.cfg") + config_global, config_leet = read_config(config_path) + if not args.quiet: print_cow() if args.version: version() elif args.interactive: - interactive() + generator.interactive(config_global, config_leet) elif args.download_wordlist: - download_wordlist() + downloader.download_wordlist(config_global) elif args.alecto: - alectodb_download() + downloader.alectodb_download(config_global) elif args.improve: - improve_dictionary(args.improve) + generator.improve_dictionary(args.improve, config_global, config_leet) else: parser.print_help() - -# Separate into a function for testing purposes -def get_parser(): - """Create and return a parser (argparse.ArgumentParser instance) for main() - to use""" - parser = argparse.ArgumentParser(description="Common User Passwords Profiler") - group = parser.add_mutually_exclusive_group(required=False) - group.add_argument( - "-i", - "--interactive", - action="store_true", - help="Interactive questions for user password profiling", - ) - group.add_argument( - "-w", - dest="improve", - metavar="FILENAME", - help="Use this option to improve existing dictionary," - " or WyD.pl output to make some pwnsauce", - ) - group.add_argument( - "-l", - dest="download_wordlist", - action="store_true", - help="Download huge wordlists from repository", - ) - group.add_argument( - "-a", - dest="alecto", - action="store_true", - help="Parse default usernames and passwords directly" - " from Alecto DB. Project Alecto uses purified" - " databases of Phenoelit and CIRT which were merged" - " and enhanced", - ) - group.add_argument( - "-v", "--version", action="store_true", help="Show the version of this program." - ) - parser.add_argument( - "-q", "--quiet", action="store_true", help="Quiet mode (don't print banner)" - ) - - return parser - - if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/generator.py b/generator.py new file mode 100644 index 0000000..ed816aa --- /dev/null +++ b/generator.py @@ -0,0 +1,280 @@ +# generator.py +import os +import time +import itertools + +def make_leet(x, config_leet): + """convert string to leet""" + for letter, leetletter in config_leet.items(): + x = x.replace(letter, leetletter) + return x + +def stream_processor(stream, min_len, max_len, leetmode, config_leet): + """ Process items on the fly: deduplicate, length check, and leetmode convert """ + seen = set() + for item in stream: + # Check explicit Minimum and Maximum length limits strictly + if min_len <= len(item) <= max_len: + if item not in seen: + seen.add(item) + yield item + + if leetmode == "y": + leeted = make_leet(item, config_leet) + if leeted not in seen and min_len <= len(leeted) <= max_len: + seen.add(leeted) + yield leeted + +def print_to_file(filename, iterable_stream, target_volume=0): + print(f"\r\n[+] Saving dictionary to \033[1;31m{filename}\033[1;m. Processing stream...") + lines = 0 + start_time = time.time() + + with open(filename, "w", encoding="utf-8") as f: + for word in iterable_stream: + f.write(word + "\n") + lines += 1 + # Strictly break when EXACT target volume is reached + if target_volume > 0 and lines >= target_volume: + break + + end_time = time.time() + print(f"[+] Finished! Generated EXACTLY \033[1;32m{lines}\033[1;m pro-level passwords in {round(end_time - start_time, 2)} seconds.") + + inspect = input("> Hyperspeed Print? (Y/n) : ").lower() + if inspect == "y": + try: + with open(filename, "r", encoding="utf-8") as wlist: + for line in wlist: + print("\033[1;32m[" + filename + "] \033[1;33m" + line.strip()) + time.sleep(0.001) + except Exception as e: + print("[ERROR]: " + str(e)) + + print("[+] Now load your pistolero with \033[1;31m" + filename + "\033[1;m and shoot! Good luck!") + +def security_check(filepath): + print("\n" + "="*55) + print(" 🛡️ PUBLIC SECURITY CHECK (AWARENESS TOOL) 🛡️ ") + print("="*55) + print("Do you want to test if your actual password could be guessed based on the data you provided?") + choice = input("> Run Security Check? Y/[N]: ").lower().strip() + + if choice == 'y': + test_pw = input("> Enter your actual password to test: ").strip() + if not test_pw: + return + + print(f"\n[*] Scanning {os.path.basename(filepath)} for your password...") + time.sleep(1) + + found = False + with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: + for line in f: + if line.strip() == test_pw: + found = True + break + + if found: + print("\n\033[1;31m[!] 🚨 CRITICAL WARNING: YOUR PASSWORD WAS SUCCESSFULLY GUESSED! 🚨\033[1;m") + print("Your password is too predictable and is based entirely on your public OSINT data.") + print("Action Required: Please update your password immediately using random phrases.") + else: + print("\n\033[1;32m[+] ✅ SAFE: Your password was NOT found in the generated dictionary.\033[1;m") + print("Good job! Your password does not rely purely on obvious personal information.") + print("="*55 + "\n") + +def interactive(config_global, config_leet): + print("\r\n[+] Insert the information about the victim to make a dictionary") + print("[+] If you don't know all the info, just hit enter when asked! ;)") + + profile = {} + + print("\n\033[1;36m--- Core Info ---\033[1;m") + name = input("> First Name: ").lower().strip() + while len(name) == 0: + print("\r\n[-] You must enter a name at least!") + name = input("> First Name: ").lower().strip() + profile["name"] = name + + profile["surname"] = input("> Surname: ").lower().strip() + profile["nick"] = input("> Nickname: ").lower().strip() + birthdate = input("> Birthdate (DDMMYYYY): ").strip() + while len(birthdate) != 0 and len(birthdate) != 8: + print("\r\n[-] You must enter 8 digits for birthday!") + birthdate = input("> Birthdate (DDMMYYYY): ").strip() + profile["birthdate"] = birthdate + + print("\n\033[1;36m--- Digital Footprint [Optional] ---\033[1;m") + profile["phone_no"] = input("> Victim's Phone Number (e.g., 9876543210): ").strip() + profile["email"] = input("> Victim's Email ID (e.g., target@gmail.com): ").lower().strip() + + has_family = input("\n> Do you have Family information? Y/[N]: ").lower().strip() + if has_family == 'y': + print("\n\033[1;36m--- Family Info [Optional: Press Enter to skip] ---\033[1;m") + profile["wife"] = input("> Partner's name: ").lower().strip() + profile["wifen"] = input("> Partner's nickname: ").lower().strip() + wifeb = input("> Partner's birthdate (DDMMYYYY): ").strip() + while len(wifeb) != 0 and len(wifeb) != 8: + print("\r\n[-] You must enter 8 digits for birthday!") + wifeb = input("> Partner's birthdate (DDMMYYYY): ").strip() + profile["wifeb"] = wifeb + print("") + profile["kid"] = input("> Child's name: ").lower().strip() + profile["kidn"] = input("> Child's nickname: ").lower().strip() + kidb = input("> Child's birthdate (DDMMYYYY): ").strip() + while len(kidb) != 0 and len(kidb) != 8: + print("\r\n[-] You must enter 8 digits for birthday!") + kidb = input("> Child's birthdate (DDMMYYYY): ").strip() + profile["kidb"] = kidb + else: + profile["wife"] = profile["wifen"] = profile["wifeb"] = "" + profile["kid"] = profile["kidn"] = profile["kidb"] = "" + + print("\n\033[1;36m--- Pet/Work Info [Optional: Press Enter to skip] ---\033[1;m") + profile["pet"] = input("> Pet's name: ").lower().strip() + profile["company"] = input("> Company name: ").lower().strip() + + print("\n\033[1;36m--- 2026 Modern Lifestyle & Tech Info [Optional: Press Enter to skip] ---\033[1;m") + profile["phone"] = input("> Smartphone/Gadget Model (e.g., iphone15, esp32): ").lower().strip() + profile["vehicle"] = input("> Dream Bike/Car (e.g., ktm, tesla, thar): ").lower().strip() + profile["game"] = input("> Favorite Game/Anime (e.g., valorant, naruto, gta6): ").lower().strip() + profile["crypto"] = input("> Favorite Crypto/Tech (e.g., bitcoin, ai): ").lower().strip() + profile["sports"] = input("> Favorite Sports Team/Player (e.g., rcb, virat, messi): ").lower().strip() + profile["bestfriend"] = input("> Best Friend's Name: ").lower().strip() + + print("\n\033[1;36m--- Password Length & Advanced Modifiers ---\033[1;m") + + min_len = input("> Minimum password length [Default 8]: ").strip() + profile["min_len"] = int(min_len) if min_len.isdigit() else 8 + + max_len = input("> Maximum password length [Default 30]: ").strip() + profile["max_len"] = int(max_len) if max_len.isdigit() else 30 + + profile["words"] = [] + words1 = input("\n> Do you want to add custom keywords/numbers (Out of Syllabus)? Y/[N]: ").lower().strip() + if words1 == "y": + words2 = input("> Please enter words, separated by comma. [i.e. hacker,1433,KTM]: ").replace(" ", "") + profile["words"] = [w for w in words2.split(",") if w] + + profile["spechars1"] = input("> Do you want to enable Pro-Level Special Chars combination? [Y]/n: ").lower().strip() + + target = input("> Target password volume (e.g., 1000000 for 1 Million) [Press Enter for Auto]: ").strip() + profile["target_volume"] = int(target) if target.isdigit() else 0 + + profile["leetmode"] = input("> Leet mode? (i.e. leet = 1337) Y/[N]: ").lower().strip() + + generate_wordlist_from_profile(profile, config_global, config_leet) + + +# PRO-LEVEL GENERATOR PIPELINES +def generate_wordlist_from_profile(profile, config_global, config_leet): + print("\r\n[+] Compiling Pro-Level 3D/4D Generator Pipelines...") + + email_user = profile.get("email", "").split('@')[0] if "@" in profile.get("email", "") else profile.get("email", "") + + raw_words = [ + profile["name"], profile["surname"], profile["nick"], email_user, + profile["wife"], profile["wifen"], profile["kid"], profile["kidn"], + profile["pet"], profile["company"], + profile.get("phone", ""), profile.get("vehicle", ""), + profile.get("game", ""), profile.get("crypto", ""), + profile.get("sports", ""), profile.get("bestfriend", "") + ] + profile["words"] + + base_words = list(set([w for w in raw_words if w])) + cap_words = [w.title() for w in base_words] + all_words = list(set(base_words + cap_words)) + + double_words = [f"{w1}{w2}" for w1 in all_words for w2 in all_words if w1 != w2] + all_words.extend(double_words) + + numbers = set(config_global["years"]) + + phone_no = profile.get("phone_no", "") + if phone_no: + numbers.add(phone_no) + if len(phone_no) >= 10: + numbers.update([phone_no[-4:], phone_no[-6:]]) + + dates = [profile["birthdate"], profile["wifeb"], profile["kidb"]] + for d in dates: + if len(d) == 8: + numbers.update([d[-2:], d[-4:], d[:2], d[2:4], d[:4], d[4:]]) + + # --- DEEP SCALING MATRIX: Guaranteed Volume Hit --- + target_vol = profile.get("target_volume", 0) + if target_vol > 0: + word_count = max(1, len(all_words)) + # Mathematically calculate how many numbers we need to generate to satisfy the target volume + estimated_numbers_needed = int(target_vol / word_count) + 1000 + # Protect RAM by limiting the number array to max 500,000 (Provides up to ~10-20 Million combos) + max_num = min(estimated_numbers_needed, 500000) + + print(f"[*] Deep Scaling Matrix Active: Ramping up number sequences to guarantee exactly {target_vol} passwords...") + numbers.update([str(i) for i in range(max_num)]) + numbers.update(["123", "1234", "12345", "123456", "1433", "007", "111", "999", "6969", "8055"]) + else: + numfrom = config_global["numfrom"] + numto = config_global["numto"] + if numto > 0: + numbers.update([str(i) for i in range(numfrom, numto + 1)]) + + numbers = list(numbers) + specials = config_global["chars"] if profile.get("spechars1", "y") != "n" else [""] + + def pro_generator(): + for w, n in itertools.product(all_words, numbers): + yield f"{w}{n}" + yield f"{n}{w}" + + if specials != [""]: + for w, s, n in itertools.product(all_words, specials, numbers): + yield f"{w}{s}{n}" + yield f"{n}{s}{w}" + yield f"{s}{w}{s}{n}" + yield f"{w}{n}{s}" + + for w, s in itertools.product(all_words, specials): + yield f"{w}{s}" + yield f"{s}{w}" + yield f"{w}{s}{s}" + + raw_stream = pro_generator() + final_stream = stream_processor( + raw_stream, + profile["min_len"], + profile["max_len"], + profile["leetmode"], + config_leet + ) + + filename = profile["name"] + ".txt" + print_to_file(filename, final_stream, target_vol) + security_check(filename) + +def improve_dictionary(file_to_open, config_global, config_leet): + if not os.path.isfile(file_to_open): + print("Error: file " + file_to_open + " does not exist.") + return + + with open(file_to_open, "r", encoding="utf-8", errors="ignore") as fajl: + base_words = [line.strip() for line in fajl if line.strip()] + + print("\r\n[+] Pro-Level Dictionary Improvement Active...") + specials = config_global["chars"] + numbers = config_global["years"] + [str(i) for i in range(config_global["numfrom"], config_global["numto"] + 1)] + leetmode = input("> Leet mode? (i.e. leet = 1337) Y/[N]: ").lower().strip() + + def improve_generator(): + for w in base_words: + yield w + for n in numbers: + yield f"{w}{n}" + for s in specials: + yield f"{w}{s}" + for n in numbers: + yield f"{w}{s}{n}" + + final_stream = stream_processor(improve_generator(), config_global["wcfrom"], config_global["wcto"], leetmode, config_leet) + print_to_file(file_to_open + ".cupp.txt", final_stream, 0) \ No newline at end of file