├── .gitignore ├── ESP32_blt_scanner.py ├── ESP32_deauther.py ├── ESP32_ota.py ├── ESP32_sniffer.py ├── ESP32_wifi_connect.py ├── LICENSE ├── README.md ├── requirements.txt └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | capture.pcap 2 | ota.logs 3 | 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | pip-wheel-metadata/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | -------------------------------------------------------------------------------- /ESP32_blt_scanner.py: -------------------------------------------------------------------------------- 1 | import json 2 | from utils import connect_serial 3 | import time 4 | 5 | SCAN_DURATION = 10 6 | 7 | if __name__ == "__main__": 8 | 9 | ser, filename = connect_serial(timeout=1, default_filename="blt_scan.json") 10 | 11 | f = open(filename, 'wb') 12 | 13 | ser.write("blt_scan\r\n".encode()) 14 | print("[+] Scanner started...") 15 | 16 | start_time = time.perf_counter() 17 | 18 | try: 19 | while True: 20 | ln = ser.read_until(b"") 21 | f.write(ln.replace(b"", b"")) 22 | f.flush() 23 | if time.perf_counter() - start_time >= SCAN_DURATION + 2: 24 | break 25 | except KeyboardInterrupt: 26 | ser.write("stop\r\n".encode()) 27 | print("[+] Stopping...") 28 | 29 | f.close() 30 | ser.close() 31 | print("[+] Done.") 32 | -------------------------------------------------------------------------------- /ESP32_deauther.py: -------------------------------------------------------------------------------- 1 | import click 2 | import json 3 | from utils import connect_serial 4 | 5 | 6 | @click.command() 7 | @click.argument('target', default="FF-FF-FF-FF-FF-FF") # Setup your default Macs here 8 | @click.argument('ap', default="") # Setup your default Macs here 9 | @click.argument("ssid", default="None") # Setup your default Evil SSID here 10 | @click.argument('chan', default=11) # Setup your default Chan here 11 | @click.argument('delay', default=1000) # Setup your default delay here 12 | def main(target, ap, chan, ssid, delay): 13 | try: 14 | targetInput = input( 15 | "[?] Select a target (default '%s'): " % target) 16 | if targetInput != "": 17 | target = targetInput 18 | except KeyboardInterrupt: 19 | print("\n[+] Exiting...") 20 | exit() 21 | 22 | try: 23 | apInput = input("[?] Select an AP (default '%s'): " % ap) 24 | if apInput != "": 25 | ap = apInput 26 | except KeyboardInterrupt: 27 | print("\n[+] Exiting...") 28 | exit() 29 | 30 | try: 31 | delayInput = input("[?] Select delay (default '%s'): " % delay) 32 | if delayInput != "": 33 | delay = int(delayInput) 34 | except KeyboardInterrupt: 35 | print("\n[+] Exiting...") 36 | exit() 37 | 38 | apInput = apInput.upper().replace(":", "-").replace(" ", "-") 39 | targetInput = targetInput.upper().replace(":", "-").replace(" ", "-") 40 | 41 | country = "FR" 42 | try: 43 | countryInput = input("[?] Select a country (default '%s'): " % country) 44 | if countryInput != "": 45 | country = countryInput 46 | except KeyboardInterrupt: 47 | print("\n[+] Exiting...") 48 | exit() 49 | 50 | try: 51 | chanInput = input("[?] Select a channel (default '%d'): " % chan) 52 | if chanInput != "": 53 | chan = int(chanInput) 54 | except KeyboardInterrupt: 55 | print("\n[+] Exiting...") 56 | exit() 57 | 58 | try: 59 | ssidInput = input("[?] Select a twin ssid (default '%s'): " % ssid) 60 | ssid = ssidInput 61 | except KeyboardInterrupt: 62 | print("\n[+] Exiting...") 63 | exit() 64 | 65 | ser, filename = connect_serial(timeout=1) 66 | 67 | f = open(filename, 'wb') 68 | 69 | # set country 70 | ser.write(("set country %s\r\n" % country).encode()) 71 | ln = ser.read_until(b"\r\n").decode("utf-8") 72 | try: 73 | if (ln == None or len(ln) == 0): 74 | print("Empty country setting, exiting...") 75 | exit() 76 | try: 77 | j = json.loads(ln) 78 | recv_country = j.get("WIFI_COUNTRY") 79 | if recv_country != country: 80 | print("Error 1 setting country, exiting...") 81 | exit() 82 | except: 83 | print("Error 2 setting country, exiting...") 84 | exit() 85 | except: 86 | print("Error 3 setting country, exiting...") 87 | exit() 88 | 89 | # set channel 90 | ser.write(("set chan %d\r\n" % (chan)).encode()) 91 | ln = ser.read_until(b"\r\n").decode("utf-8") 92 | try: 93 | if (ln == None or len(ln) == 0): 94 | print("Empty channel setting, exiting...") 95 | exit() 96 | try: 97 | j = json.loads(ln) 98 | recv_chan = j.get("WIFI_CHANNEL") 99 | if recv_chan != chan: 100 | print("Error 1 setting channel, exiting...") 101 | exit() 102 | except: 103 | print("Error 2 setting channel, exiting...") 104 | exit() 105 | except: 106 | print("Error 3 setting channel, exiting...") 107 | exit() 108 | 109 | ser.write(("wifi_deauth %s %s %s %d 1\r\n" % (target, ap, ssid, delay)).encode()) 110 | print("[+] Deauther started...") 111 | 112 | try: 113 | while True: 114 | ln = ser.read_until(b"") 115 | f.write(ln.replace(b"", b"")) 116 | f.flush() 117 | except KeyboardInterrupt: 118 | ser.write("stop\r\n".encode()) 119 | print("[+] Stopping...") 120 | 121 | f.close() 122 | ser.close() 123 | print("[+] Done.") 124 | 125 | 126 | if __name__ == "__main__": 127 | main() 128 | -------------------------------------------------------------------------------- /ESP32_ota.py: -------------------------------------------------------------------------------- 1 | from utils import connect_serial 2 | 3 | if __name__ == "__main__": 4 | 5 | ser, filename = connect_serial(timeout=1, default_filename="ota.log") 6 | 7 | f = open(filename, 'wb') 8 | 9 | ota_firmware = "C:/Users/Rock_/Desktop/Projects/ESP32_Network_Toolbox/ESP32_code/build/esp32_network_toolbox.bin" 10 | 11 | with open(ota_firmware, "rb") as firmwarefile: 12 | data = firmwarefile.read() 13 | 14 | size = len(data) 15 | 16 | print(f"[+] OTA file loaded with size: {size}") 17 | 18 | ser.write(f"version\r\n".encode()) 19 | try: 20 | while True: 21 | ln = ser.read_until(b"\r\n") 22 | print(ln) 23 | f.write(ln) 24 | f.flush() 25 | if ln.startswith(b'{"VERSION":'): 26 | break 27 | except KeyboardInterrupt: 28 | print("[+] Stopping...") 29 | 30 | ser.write(f"ota_flash {size}\r\n".encode()) 31 | print("[+] OTA started...") 32 | 33 | try: 34 | while True: 35 | ln = ser.read_until(b"\r\n") 36 | print(ln) 37 | f.write(ln) 38 | f.flush() 39 | if ln == b'esp_ota_begin succeeded \r\n': 40 | break 41 | except KeyboardInterrupt: 42 | print("[+] Stopping...") 43 | 44 | print("[+] Start sending.") 45 | 46 | try: 47 | for i in range(0, size, 4096): 48 | written = ser.write(data[i:i+4096]) 49 | while True: 50 | ln = ser.read_until(b"\r\n") 51 | if ln == b"OK\r\n": 52 | break 53 | else: 54 | print(ln) 55 | f.write(ln) 56 | f.flush() 57 | if ln == b"OTA Update has Ended \r\n": 58 | break 59 | except KeyboardInterrupt: 60 | print("[+] Stopping...") 61 | 62 | print("[+] Sending done.") 63 | 64 | try: 65 | while True: 66 | ln = ser.read_until(b"\r\n") 67 | print(ln) 68 | f.write(ln) 69 | f.flush() 70 | except KeyboardInterrupt: 71 | print("[+] Stopping...") 72 | 73 | f.close() 74 | ser.close() 75 | print("[+] Done.") 76 | -------------------------------------------------------------------------------- /ESP32_sniffer.py: -------------------------------------------------------------------------------- 1 | import json 2 | from utils import connect_serial 3 | 4 | if __name__ == "__main__": 5 | 6 | ser, filename = connect_serial(timeout=1) 7 | 8 | f = open(filename, 'wb') 9 | 10 | # set country 11 | country = "FR" 12 | 13 | try: 14 | countryInput = input("[?] Select a country (default '%s'): " % country) 15 | if countryInput != "": 16 | country = countryInput 17 | except KeyboardInterrupt: 18 | print("\n[+] Exiting...") 19 | exit() 20 | 21 | ser.write(("set country %s\r\n" % country).encode()) 22 | ln = ser.read_until(b"\r\n").decode("utf-8") 23 | try: 24 | if (ln == None or len(ln) == 0): 25 | print("Empty country setting, exiting...") 26 | exit() 27 | try: 28 | j = json.loads(ln) 29 | recv_country = j.get("WIFI_COUNTRY") 30 | if recv_country != country: 31 | print("Error 1 setting country, exiting...") 32 | exit() 33 | except: 34 | print("Error 2 setting country, exiting...") 35 | exit() 36 | except: 37 | print("Error 3 setting country, exiting...") 38 | exit() 39 | 40 | # set channel 41 | chan = 11 42 | try: 43 | chanInput = input("[?] Select a channel (default '%d'): " % chan) 44 | if chanInput != "": 45 | chan = int(chanInput) 46 | except KeyboardInterrupt: 47 | print("\n[+] Exiting...") 48 | exit() 49 | 50 | ser.write(("set chan %d\r\n" % (chan)).encode()) 51 | ln = ser.read_until(b"\r\n").decode("utf-8") 52 | try: 53 | if (ln == None or len(ln) == 0): 54 | print("Empty channel setting, exiting...") 55 | exit() 56 | try: 57 | j = json.loads(ln) 58 | recv_chan = j.get("WIFI_CHANNEL") 59 | if recv_chan != chan: 60 | print("Error 1 setting channel, exiting...") 61 | exit() 62 | except: 63 | print("Error 2 setting channel, exiting...") 64 | exit() 65 | except: 66 | print("Error 3 setting channel, exiting...") 67 | exit() 68 | 69 | ser.write("wifi_sniff\r\n".encode()) 70 | print("[+] Sniffer started...") 71 | 72 | try: 73 | while True: 74 | ln = ser.read_until(b"") 75 | f.write(ln.replace(b"", b"")) 76 | f.flush() 77 | except KeyboardInterrupt: 78 | ser.write("stop\r\n".encode()) 79 | print("[+] Stopping...") 80 | 81 | f.close() 82 | ser.close() 83 | print("[+] Done.") 84 | -------------------------------------------------------------------------------- /ESP32_wifi_connect.py: -------------------------------------------------------------------------------- 1 | import json 2 | from utils import connect_serial 3 | import time 4 | 5 | if __name__ == "__main__": 6 | 7 | ser, filename = connect_serial(timeout=1) 8 | 9 | f = open(filename, 'wb') 10 | 11 | ser.write("wifi_connect colloc\ 50 bienvenuecheznous\r\n".encode()) 12 | print("[+] Connecting wifi...") 13 | 14 | try: 15 | tmp = b"" 16 | while True: 17 | ln = ser.read_until(b"") 18 | f.write(ln.replace(b"", b"")) 19 | f.flush() 20 | if len(tmp): 21 | ln = tmp + ln 22 | try: 23 | print(ln) 24 | json.loads(ln) 25 | tmp = b"" 26 | except: 27 | tmp = ln 28 | continue 29 | try: 30 | ip = json.loads(ln)["IP"] 31 | if ip != "None": 32 | print("Connected with ip %s" % ip) 33 | break 34 | except: 35 | pass 36 | except KeyboardInterrupt: 37 | ser.write("stop\r\n".encode()) 38 | print("[+] Stopping...") 39 | 40 | 41 | f.close() 42 | ser.close() 43 | print("[+] Done.") 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Eric Parisot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32_Network_Toolbox_Scripts 2 | ESP32 Network Toolbox Scripts 3 | 4 | This tool needs an ESP32 controller specially crafted from here: 5 | https://www.tindie.com/products/klhnikov/esp32-network-toolbox/ 6 | 7 | ## Install 8 | ``` 9 | pip install -r requirements.txt 10 | ``` 11 | You can open scripts and edit default values. 12 | 13 | ## Usage: 14 | ``` 15 | python3 ESP32_sniffer.py 16 | OR 17 | python3 ESP32_deauther.py 18 | ``` 19 | I sell on Tindie 20 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | click 2 | pyserial -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | import serial 3 | import serial.tools.list_ports 4 | import json 5 | 6 | BAUD_RATE = 115200 7 | 8 | def connect_serial(timeout=None, default_filename="capture.pcap"): 9 | try: 10 | ports_list = list(serial.tools.list_ports.comports()) 11 | if len(ports_list) == 0: 12 | print("No COM port detected, exiting...") 13 | exit() 14 | first_port = None 15 | for l in ports_list: 16 | print(l) 17 | if "USB" in str(l) or "usb" in str(l) or "Serial" in str(l) or "serial" in str(l): 18 | first_port = str(l).split(" - ")[0] 19 | if first_port == None: 20 | first_port = str(ports_list[0]).split(" - ")[0] 21 | serialportInput = input("[?] Select a serial port (default '%s'): " % first_port) 22 | if serialportInput == "": 23 | serialport = first_port 24 | else: 25 | serialport = serialportInput 26 | canBreak = False 27 | while not canBreak: 28 | boardRateInput = input( 29 | "[?] Select a baudrate (default %d): " % BAUD_RATE) 30 | if boardRateInput == "": 31 | boardRate = BAUD_RATE 32 | canBreak = True 33 | else: 34 | try: 35 | boardRate = int(boardRateInput) 36 | except KeyboardInterrupt: 37 | print("\n[+] Exiting...") 38 | exit() 39 | except Exception: 40 | print("[!] Please enter a number!") 41 | continue 42 | canBreak = True 43 | filenameInput = input( 44 | "[?] Select a filename (default '%s'): " % default_filename) 45 | if filenameInput == "": 46 | filename = default_filename 47 | else: 48 | filename = filenameInput 49 | except KeyboardInterrupt: 50 | print("\n[+] Exiting...") 51 | exit() 52 | 53 | canBreak = False 54 | while not canBreak: 55 | try: 56 | ser = serial.Serial(serialport, boardRate, timeout=timeout) 57 | if (not ser.isOpen()): 58 | print("[!] Serial connection failed... Retrying...") 59 | time.sleep(2) 60 | continue 61 | ser.write("test\r\n".encode()) 62 | res = ser.read_until(b"\r\n").decode("utf-8") 63 | if res != None and len(res): 64 | json_res = json.loads(res) 65 | if json_res != None and json_res.get("TEST") == "OK": 66 | canBreak = True 67 | else: 68 | print("[!] Serial connection failed... Retrying...") 69 | ser.close() 70 | time.sleep(2) 71 | continue 72 | except KeyboardInterrupt: 73 | print("\n[+] Exiting...") 74 | exit() 75 | except Exception as e: 76 | print("[!] Serial connection failed with error: %s" % str(e)) 77 | time.sleep(2) 78 | continue 79 | 80 | print("[+] Serial connected. Name: %s, rate: %s" % (ser.name, str(boardRate))) 81 | return ser, filename 82 | --------------------------------------------------------------------------------