Skip to content

Instantly share code, notes, and snippets.

@hsheth2
Last active August 24, 2021 03:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hsheth2/3fefdf620da6a2c567208079d305698e to your computer and use it in GitHub Desktop.
Save hsheth2/3fefdf620da6a2c567208079d305698e to your computer and use it in GitHub Desktop.
import pathlib
import time
import re
import dataclasses
from typing import List
from pwnlib.tubes.process import process
from configparser import ConfigParser
# Config defaults.
WINDOWS_SYSTEM_PATH = pathlib.Path("Windows/System32/config")
WINDOWS_KEY_PATHS_TO_TRY = [
"CurrentControlSet\Services\BTHPORT\Parameters\Keys",
"CurrentControlSet\services\BTHPORT\Parameters\Keys",
"ControlSet001\Services\BTHPORT\Parameters\Keys",
]
LINUX_BLUETOOTH_INFO_PATH = "/var/lib/bluetooth/{card}/{device}/info"
DEFAULT_WAIT_TIME = 0.1
ENCODING_TYPE = 'utf-8'
@dataclasses.dataclass
class LinkKey:
bt_card_mac: str
bt_device_mac: str
link_key: str
def clean_mac_address(mac_raw: str) -> str:
# Via https://stackoverflow.com/a/11006780/5004662.
assert len(mac_raw) == 12
mac_with_colons = ':'.join(mac_raw[i:i+2] for i in range(0,12,2))
return mac_with_colons.upper()
def clean_link_key(link_key_raw: str) -> str:
link_key = link_key_raw.replace(' ', '')
assert len(link_key) == 32
return link_key.upper()
def read_keys_from_windows(windows_c_drive: pathlib.Path) -> List[LinkKey]:
# Use chntpw to browse the Windows registry.
print('Reading from Windows SYSTEM registry')
registry = process(
["chntpw", "-e", "SYSTEM"],
cwd=(windows_c_drive / WINDOWS_SYSTEM_PATH),
)
registry.recvuntil(b'? for help')
registry.clean_and_log()
def run_command(command: str) -> str:
registry.sendline(command.encode(ENCODING_TYPE))
time.sleep(DEFAULT_WAIT_TIME)
output = registry.recvS()
return output
# Just try cd into each path, and one of them will probably succeed.
for key_path in WINDOWS_KEY_PATHS_TO_TRY:
run_command(f"cd {key_path}")
# List MAC addresses of the Bluetooth cards.
bluetooth_macs_output = run_command('ls')
bluetooth_card_macs: List[str] = re.findall(r'<([a-z0-9]+)>', bluetooth_macs_output)
print('Found bluetooth cards:', bluetooth_card_macs)
# For each Bluetooth card, get the connected devices and their link keys.
keys: List[LinkKey] = []
for bt_card_mac in bluetooth_card_macs:
print(f'Examining bluetooth card: {bt_card_mac}')
bluetooth_devices_output = run_command(f'ls {bt_card_mac}')
bluetooth_devices_macs: List[str] = re.findall(r'<([a-z0-9]+)>', bluetooth_devices_output)
print(' Found paired bluetooth devices:', bluetooth_card_macs)
for bt_device_mac in bluetooth_devices_macs:
print(' Examining bluetooth device:', bt_device_mac)
link_key_output = run_command(f'hex {bt_card_mac}\{bt_device_mac}')
link_key = re.search(r'^:00000\s+(([A-Z0-9]{2} ){16})', link_key_output, re.MULTILINE).group(1)
print(' Found link key:', link_key)
keys.append(LinkKey(
bt_card_mac=clean_mac_address(bt_card_mac),
bt_device_mac=clean_mac_address(bt_device_mac),
link_key=clean_link_key(link_key),
))
# Cleanup the subprocess.
registry.sendline(b'q')
registry.shutdown('send')
time.sleep(DEFAULT_WAIT_TIME)
registry.clean_and_log()
retcode = registry.poll(block=True)
assert retcode == 0
return keys
def update_bluetooth_config(key: LinkKey) -> None:
config_file = pathlib.Path(LINUX_BLUETOOTH_INFO_PATH.format(card=key.bt_card_mac, device=key.bt_device_mac))
if not config_file.exists():
print(f'Skipping {config_file} - not already paired')
return
with config_file.open() as f:
# Ensure config parser preserves case and comments.
config_info = ConfigParser(allow_no_value=True)
config_info.optionxform = str
config_info.read_file(f)
modified = False
current_key = config_info['LinkKey']['Key']
if current_key != key.link_key:
config_info['LinkKey']['Key'] = key.link_key
config_info['LinkKey']['; (previous) Key'] = current_key
modified = True
if modified:
print(f'Updating {config_file}')
with config_file.open('w') as f:
config_info.write(f, False)
else:
print(f'Already up to date - {config_file}')
if __name__ == '__main__':
# TODO check if chntpw is installed already; sudo apt install chntpw
# TODO add a dry-run option
# TODO use reged instead of chntpw; comes from the same package but won't require the
# pwntools hacks for interactive use. should be something along these lines:
# reged -x SYSTEM '\' 'CurrentControlSet\Services\BTHPORT\Parameters\Keys' /tmp/foo.reg
# TODO this does not work for bluetooth LE devices
# TODO this does not yet reset or restart the bluetooth process; should be something like
# sudo service bluetooth force-reload
# (or on arch) sudo systemctl restart bluetooth.service
WINDOWS_MOUNT_DIRECTORY = pathlib.Path("/media/hsheth/78D6DFB8D6DF74BA")
keys = read_keys_from_windows(WINDOWS_MOUNT_DIRECTORY)
print()
for key in keys:
update_bluetooth_config(key)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment