Skip to content

Ble ctf

Bluetooth CTF

Scripts

detection_callback.py:

import argparse
import asyncio
import logging

from bleak import BleakScanner
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData

logger = logging.getLogger(__name__)

printed_devices = {}

def simple_callback(device: BLEDevice, advertisement_data: AdvertisementData):
    if device.address not in printed_devices:
        printed_devices[device.address] = advertisement_data
        logger.info("{}: {}".format(device.address, advertisement_data))


async def main(args: argparse.Namespace):
    scanner = BleakScanner(
        simple_callback, args.services, cb=dict(use_bdaddr=args.macos_use_bdaddr)
    )

    while True:
        #logger.info("(re)starting scanner")
        await scanner.start()
        await asyncio.sleep(5.0)
        await scanner.stop()
        #Print New BLE Devices


if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument(
        "--macos-use-bdaddr",
        action="store_true",
        help="when true use Bluetooth address instead of UUID on macOS",
    )

    parser.add_argument(
        "--services",
        metavar="<uuid>",
        nargs="*",
        help="UUIDs of one or more services to filter for",
    )

    parser.add_argument(
        "-d",
        "--debug",
        action="store_true",
        help="sets the logging level to debug",
    )

    args = parser.parse_args()

    log_level = logging.DEBUG if args.debug else logging.INFO
    logging.basicConfig(
        level=log_level,
        format="%(asctime)-15s %(name)-8s %(levelname)s: %(message)s",
    )

    asyncio.run(main(args))

service_explorer.py

import argparse
import asyncio
import logging
import collections

from bleak import BleakClient, BleakScanner

logger = logging.getLogger(__name__)

async def main(args: argparse.Namespace):
	output = {}
	logger.info("starting scan...")

	if args.address:
		device = await BleakScanner.find_device_by_address(
			args.address, cb=dict(use_bdaddr=args.macos_use_bdaddr)
		)
		if device is None:
			logger.error("could not find device with address '%s'", args.address)
			return
	else:
		device = await BleakScanner.find_device_by_name(
			args.name, cb=dict(use_bdaddr=args.macos_use_bdaddr)
		)
		if device is None:
			logger.error("could not find device with name '%s'", args.name)
			return

	logger.info("connecting to device...")

	async with BleakClient(device, services=args.services) as client:
		logger.info("connected")

		for service in client.services:
			#logger.info("[Service] %s", service)

			output[service.handle] = {"info": "[Service] {}".format(service)}
			output[service.handle]["characteristics"] = {}

			for char in service.characteristics:
				if "read" in char.properties:
					try:
						value = await client.read_gatt_char(char.uuid)
						output[service.handle]["characteristics"][char.handle] = {"info": "  [Characteristic] {} ({}), Value: {}".format(char, ",".join(char.properties), value)}

					except Exception as e:
						output[service.handle]["characteristics"][char.handle] = {"info": "  [Characteristic] {} ({}), Error: {}".format(char, ",".join(char.properties), e)}

				else:
					output[service.handle]["characteristics"][char.handle] = {"info": "  [Characteristic] {} ({})".format(char, ",".join(char.properties))}

				#init descriptors
				output[service.handle]["characteristics"][char.handle]["descriptors"] = {}
				for descriptor in char.descriptors:

					try:
						value = await client.read_gatt_descriptor(descriptor.handle)
						output[service.handle]["characteristics"][char.handle]["descriptors"][descriptor.handle] = {"info": "    [Descriptor] {}, Value: {}".format(descriptor, value)}
					except Exception as e:
						output[service.handle]["characteristics"][char.handle]["descriptors"][descriptor.handle] = {"info": "    [Descriptor] {}, Error: {}".format(descriptor, e)}

				#Sort Descriptors
				output[service.handle]["characteristics"][char.handle]["descriptors"] = dict(sorted(output[service.handle]["characteristics"][char.handle]["descriptors"].items()))

			#Sort Characteristics
			output[service.handle]["characteristics"] = dict(sorted(output[service.handle]["characteristics"].items()))

		#Sort Services
		output = dict(sorted(output.items()))

		#Print Services
		for service_handle in output:
			logger.info(output[service_handle]["info"])

			#Print Characteristics
			for characteristic in output[service_handle]["characteristics"]:
				logger.info(output[service_handle]["characteristics"][characteristic]["info"])

				#Print Descriptors
				for descriptor in output[service_handle]["characteristics"][characteristic]["descriptors"]:
					logger.info(output[service_handle]["characteristics"][characteristic]["descriptors"][descriptor]["info"])

		logger.info("disconnecting...")

	logger.info("disconnected")


if __name__ == "__main__":
	parser = argparse.ArgumentParser()

	device_group = parser.add_mutually_exclusive_group(required=True)

	device_group.add_argument(
		"--name",
		metavar="<name>",
		help="the name of the bluetooth device to connect to",
	)
	device_group.add_argument(
		"--address",
		metavar="<address>",
		help="the address of the bluetooth device to connect to",
	)

	parser.add_argument(
		"--macos-use-bdaddr",
		action="store_true",
		help="when true use Bluetooth address instead of UUID on macOS",
	)

	parser.add_argument(
		"--services",
		nargs="+",
		metavar="<uuid>",
		help="if provided, only enumerate matching service(s)",
	)

	parser.add_argument(
		"-d",
		"--debug",
		action="store_true",
		help="sets the log level to debug",
	)

	args = parser.parse_args()

	log_level = logging.DEBUG if args.debug else logging.INFO
	logging.basicConfig(
		level=log_level,
		format="%(asctime)-15s %(name)-8s %(levelname)s: %(message)s",
	)

	asyncio.run(main(args))

service_writer.py:

import asyncio, string, platform, sys, argparse

from bleak import BleakClient
from bleak.uuids import normalize_uuid_16, uuid16_dict

async def main(args: argparse.Namespace):
	async with BleakClient(args.device, winrt=dict(use_cached_services=True)) as client:
		print(f"Connected: {client.is_connected}")

		#Read UUID or handle
		if args.debug:
			read_data = await client.read_gatt_char(args.service)
			print(f"Read[{args.service}]: {read_data}")

		#Write data to UUID or handle
		print(f"Write[{args.service}]: {args.data}")
		value = await client.read_gatt_char(args.service)
		print(f"Returned Data: {value}")


if __name__ == "__main__":
	parser = argparse.ArgumentParser()

	parser.add_argument(
		"--macos-use-bdaddr",
		action="store_true",
		help="when true use Bluetooth address instead of UUID on macOS",
	)

	parser.add_argument(
		"--device",
		required=True,
		help="Which Device MAC to connect to",
	)

	parser.add_argument(
		"--service",
		required=True,
		help="UUID or Handle to write to",
	)

	parser.add_argument(
		"--data",
		required=True,
		help="the Data to write. Can be 0x02, or \"test\"",
	)

	parser.add_argument(
		"-v",
		dest="debug",
		action="store_true",
		help="sets the logging level to debug",
	)

	args = parser.parse_args()
	print(args)

	#Convert to bytes
	if args.data.starts_with("0x") and all(c in string.hexdigits for c in args.data):
		args.data = int(args.data, 16)
	else:
		args.data = args.data.encode()

	log_level = logging.DEBUG if args.debug else logging.INFO
	logging.basicConfig(
		level=log_level,
		format="%(asctime)-15s %(name)-8s %(levelname)s: %(message)s",
	)

	asyncio.run(main(args))

Setup

Setup Bluetooth:

yay -S bleah bleak
sudo systemctl start bluetooth.service
# You might need to edit the service file and add a -E to the end of the ExecStart
#ExecStart=/usr/lib/bluetooth/bluetoothd -E

Scanning for Devices:

>>> python detection_callback.py
2023-11-13 22:21:01,667 __main__ INFO: E1:A1:E5:38:52:DD: AdvertisementData(local_name='F48F92878BF433A8EA', manufacturer_data={1447: b'\x05\x10\x01\x00\x00\x00\x00\x00\x00\x04#\x00\xca'}, service_uuids=['0000fe07-0000-1000-8000-00805f9b34fb'], tx_power=0, rssi=-77)
2023-11-13 22:21:01,671 __main__ INFO: 64:E8:33:82:AF:6A: AdvertisementData(local_name='BLECTF', service_uuids=['000000ff-0000-1000-8000-00805f9b34fb'], tx_power=-21, rssi=-33)
2023-11-13 22:21:01,731 __main__ INFO: 5B:40:40:4E:17:66: AdvertisementData(manufacturer_data={76: b'\x07\x19\x01\x14 !2\x8f\x11\x00\x043C\xe1\xee84$#\xa1(\xcb\x94\x81\x8a\x83\x85'}, rssi=-63)
2023-11-13 22:21:01,731 __main__ INFO: D8:EB:80:A5:37:B0: AdvertisementData(manufacturer_data={76: b'\x12\x02\x00\x00'}, rssi=-83)
2023-11-13 22:21:01,742 __main__ INFO: 08:2A:49:00:56:1E: AdvertisementData(manufacturer_data={6: b'\x01\t "\xf5\xb4\x10\xec\xe8\xbc\xeaS7\xe6%\x00,\xe1\x9a\x11E\xa0\xf4\x90#\x07\x1e'}, rssi=-72)
2023-11-13 22:21:01,745 __main__ INFO: 49:03:08:8E:A1:91: AdvertisementData(manufacturer_data={76: b'\x10\x06s\x1de\xccHx'}, tx_power=7, rssi=-78)
2023-11-13 22:21:01,772 __main__ INFO: 64:13:D4:80:A3:4C: AdvertisementData(manufacturer_data={76: b'\x10\x07x\x1fe>G\x16\x80'}, tx_power=7, rssi=-84)
2023-11-13 22:21:01,773 __main__ INFO: 00:4D:32:11:6F:C1: AdvertisementData(local_name='HS2S 11070', manufacturer_data={71: b'\x00\x00\x00\x00\x00M2\x11o\xc1'}, service_uuids=['636f6d2e-6a69-7561-6e2e-424653563232'], tx_power=0, rssi=-80)
2023-11-13 22:21:01,783 __main__ INFO: 71:A5:1D:B5:32:01: AdvertisementData(manufacturer_data={76: b'\x10\x07\x1d\x1b\xb6\xf3\xb0\xc8('}, tx_power=11, rssi=-81)
2023-11-13 22:21:01,815 __main__ INFO: 0A:4A:29:47:E9:FF: AdvertisementData(manufacturer_data={76: b'\t\x06\x03\xb8\xc0\xa8\x01F'}, rssi=-78)
2023-11-13 22:21:01,863 __main__ INFO: 2C:4C:C6:0E:C4:81: AdvertisementData(local_name='Venus_2C4CC60EC481', manufacturer_data={9474: b'\x00\x8c\x00\x00\xc16'}, service_uuids=['06aa1910-f22a-11e3-9daa-0002a5d5c51b'], rssi=-83)
2023-11-13 22:21:01,864 __main__ INFO: C9:01:59:A3:44:2B: AdvertisementData(manufacturer_data={76: b'\x12\x02d\x02\x07\x11\x06\x91\xdb\xb3h\x8eS\xd0.\xbdSU\xba\xa69\x8aG'}, rssi=-75)
2023-11-13 22:21:01,895 __main__ INFO: 57:97:97:9E:7F:9C: AdvertisementData(manufacturer_data={224: b'X\x07\xcas`\x10'}, service_data={'0000fe9f-0000-1000-8000-00805f9b34fb': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'}, service_uuids=['0000fe9f-0000-1000-8000-00805f9b34fb'], rssi=-52)
2023-11-13 22:21:01,936 __main__ INFO: 7E:48:71:E2:7D:73: AdvertisementData(manufacturer_data={76: b'\x10\x06O\x1dFbo\x18'}, tx_power=5, rssi=-82)
2023-11-13 22:21:01,936 __main__ INFO: 5C:C1:D7:72:87:97: AdvertisementData(local_name='[TV] Samsung 7 Series (50)', manufacturer_data={117: b'B\x04\x01\x80f\\\xc1\xd7r\x87\x97^\xc1\xd7r\x87\x96\x01\x00\x00\x00\x00\x00\x00'}, rssi=-78)
2023-11-13 22:21:01,966 __main__ INFO: 7D:09:8F:04:AB:40: AdvertisementData(manufacturer_data={76: b'\x0c\x0e\x00!IU\xfb\x17\xa0\xe4\xd0\x0e\xc7@\t\x19\x10\x06s\x1d\xd0\x9bI('}, rssi=-86)
2023-11-13 22:21:01,966 __main__ INFO: 1C:B3:C9:31:23:8F: AdvertisementData(manufacturer_data={76: b'\x10\x05\x01\x10+\xf0<'}, tx_power=4, rssi=-71)
2023-11-13 22:21:02,271 __main__ INFO: E4:FC:95:73:04:94: AdvertisementData(local_name='Hatch Rest', service_data={'0000180f-0000-1000-8000-00805f9b34fb': b'd'}, service_uuids=['0000180a-0000-1000-8000-00805f9b34fb', '0000180f-0000-1000-8000-00805f9b34fb'], rssi=-83)
2023-11-13 22:21:02,340 __main__ INFO: 0E:8F:3C:F4:EE:31: AdvertisementData(manufacturer_data={76: b'\t\x08\x13\xa0\xc0\xa827\x1bX'}, rssi=-85)
2023-11-13 22:21:02,527 __main__ INFO: 7E:39:F0:B7:2B:4D: AdvertisementData(manufacturer_data={76: b'\x10\x05<\x1c\x80\x15\x0c'}, tx_power=12, rssi=-85)
2023-11-13 22:21:02,946 __main__ INFO: 75:4E:57:6B:5A:DA: AdvertisementData(manufacturer_data={76: b'\x10\x05&\x18\x91\t\x8c'}, tx_power=8, rssi=-80)
2023-11-13 22:21:03,337 __main__ INFO: CB:0B:A7:63:74:BF: AdvertisementData(manufacturer_data={76: b'\x12\x02\x00\x01'}, rssi=-83)
2023-11-13 22:21:03,410 __main__ INFO: 7B:75:51:E5:65:CE: AdvertisementData(manufacturer_data={76: b'\t\x08\x13\x84\xc0\xa8\x00y\x1bX\x13\x07\x02\xf1:\xe6h\xd5\xa7'}, rssi=-88)
2023-11-13 22:21:03,425 __main__ INFO: FA:BA:C9:96:86:5F: AdvertisementData(local_name='98E78202A2C83397E9', manufacturer_data={1447: b'\x05\x10\x01\x00\x00\x00\x00\x00\x00\x02#\x00\xca'}, service_uuids=['0000fe07-0000-1000-8000-00805f9b34fb'], rssi=-84)
2023-11-13 22:21:03,778 __main__ INFO: 72:0D:34:BE:A0:C4: AdvertisementData(manufacturer_data={76: b'\x10\x07z\x1f\xb8\xccG\xfa\x80'}, tx_power=7, rssi=-80)
2023-11-13 22:21:03,778 __main__ INFO: C5:06:F6:45:C4:8B: AdvertisementData(manufacturer_data={76: b'\x12\x02\x00\x00'}, rssi=-82)
2023-11-13 22:21:04,399 __main__ INFO: 7D:38:31:ED:39:43: AdvertisementData(manufacturer_data={76: b'\x10\x06O\x1dFbo\x18'}, tx_power=5, rssi=-82)
2023-11-13 22:21:04,885 __main__ INFO: DE:07:E3:73:56:E2: AdvertisementData(manufacturer_data={76: b'\x12\x02n\x03\x07\x11\x06I\x15\xb67\xd3J^\x99t\xa4\tVyu\xdeV'}, rssi=-78)
2023-11-13 22:21:05,028 __main__ INFO: 57:3F:49:79:A1:CF: AdvertisementData(manufacturer_data={76: b'\t\x08\x13\xa7\xc0\xa8\x01\x0c\x1bX'}, rssi=-83)
2023-11-13 22:21:05,043 __main__ INFO: E7:38:83:A1:46:9D: AdvertisementData(manufacturer_data={76: b'\x12\x02\x00\x03'}, rssi=-79)
2023-11-13 22:21:05,319 __main__ INFO: E3:36:5D:AE:20:D4: AdvertisementData(manufacturer_data={76: b'\x12\x19\x102\x8dM\xdb#d\x0f\xccN\x1d\xb2\xce\x17!l\xdb\xef\xacn\xf6F\xcc\x03\xd4'}, rssi=-76)
2023-11-13 22:21:05,415 __main__ INFO: C7:2D:EA:36:45:B2: AdvertisementData(manufacturer_data={76: b'\x12\x02\xac\x02'}, rssi=-71)
2023-11-13 22:21:05,601 __main__ INFO: C4:57:DC:51:D9:E4: AdvertisementData(manufacturer_data={76: b'\x12\x02\xa5\x03'}, rssi=-63)
2023-11-13 22:21:05,870 __main__ INFO: 52:03:53:87:0B:A2: AdvertisementData(manufacturer_data={76: b'\x10\x05{\x1c\x88\xb8\r'}, tx_power=8, rssi=-85)
2023-11-13 22:21:06,404 __main__ INFO: 44:F2:D3:91:C7:1A: AdvertisementData(service_uuids=['0000180f-0000-1000-8000-00805f9b34fb'], rssi=-83)
2023-11-13 22:21:06,479 __main__ INFO: 4B:BA:AF:E7:1E:21: AdvertisementData(manufacturer_data={76: b'\x10\x06\r\x1d\x8a\xa6\x80\x08'}, tx_power=4, rssi=-88)

List Capabilities (bleah):

>>> sudo bleah -b "64:E8:33:82:AF:6A" -e

@ Scanning for 5s [-128 dBm of sensitivity] ...

┌ 64:e8:33:82:af:6a (-34 dBm) ──────────────────────────────────────────┐
│ Vendor                 ?                                             │
│ Allows Connections                                                  │
│ Address Type           public                                        │
│ Tx Power               u'eb'                                         │
│ Complete 16b Services  '000000ff-0000-1000-8000-00805f9b34fb'        │
│ Complete Local Name    BLECTF                                        │
│ Flags                  LE General Discoverable, BR/EDR Not Supported │
└───────────────────────┴───────────────────────────────────────────────┘

@ Connecting to 64:e8:33:82:af:6a ... connected.
@ Enumerating all the things ...

┌──────────────┬────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────┐
│ Handles       Service > Characteristics                                               Properties                                        Data                                                         │
├──────────────┼────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0001 -> 000b  Generic Attribute ( 00001801-0000-1000-8000-00805f9b34fb )                                                                                                                             │
│ 0003            Service Changed ( 00002a05-0000-1000-8000-00805f9b34fb )              INDICATE                                                                                                       │
│ 0006            2b29 ( 00002b29-0000-1000-8000-00805f9b34fb )                         READ WRITE                                        '\x04'                                                       │
│ 0008            2b2a ( 00002b2a-0000-1000-8000-00805f9b34fb )                         READ                                              '\xe5\xc2\xaa\x8b\xb1!\xce\xaf\xef\xe3\xb9\xf3\xd5\xa3\xc21' │
│ 000a            2b3a ( 00002b3a-0000-1000-8000-00805f9b34fb )                         READ                                              '\x00'                                                       │
│                                                                                                                                                                                                      │
│ 0014 -> 001c  Generic Access ( 00001800-0000-1000-8000-00805f9b34fb )                                                                                                                                │
│ 0016            Device Name ( 00002a00-0000-1000-8000-00805f9b34fb )                  READ                                              u'2b00042f7481c7b056c4b410d28f33cf'                          │
│ 0018            Appearance ( 00002a01-0000-1000-8000-00805f9b34fb )                   READ                                              Unknown                                                      │
│ 001a            Central Address Resolution ( 00002aa6-0000-1000-8000-00805f9b34fb )   READ                                              '\x00'                                                       │
│                                                                                                                                                                                                      │
│ 0028 -> ffff  00ff ( 000000ff-0000-1000-8000-00805f9b34fb )                                                                                                                                          │
│ 002a            ff01 ( 0000ff01-0000-1000-8000-00805f9b34fb )                         READ                                              u'Score: 0/20'                                               │
│ 002c            ff02 ( 0000ff02-0000-1000-8000-00805f9b34fb )                         READ WRITE                                        u'Write Flags Here'                                          │
│ 002e            ff03 ( 0000ff03-0000-1000-8000-00805f9b34fb )                         READ                                              u'd205303e099ceff44835'                                      │
│ 0030            ff04 ( 0000ff04-0000-1000-8000-00805f9b34fb )                         READ                                              u'MD5 of Device Name'                                        │
│ 0032            ff05 ( 0000ff05-0000-1000-8000-00805f9b34fb )                         READ WRITE                                        u'Write anything here'                                       │
│ 0034            ff06 ( 0000ff06-0000-1000-8000-00805f9b34fb )                         READ WRITE                                        u'Write the ascii value "yo" here'                           │
│ 0036            ff07 ( 0000ff07-0000-1000-8000-00805f9b34fb )                         READ WRITE                                        u'Write the hex value 0x07 here'                             │
│ 0038            ff08 ( 0000ff08-0000-1000-8000-00805f9b34fb )                         READ                                              u'Write 0xC9 to handle 58'                                   │
│ 003a            ff09 ( 0000ff09-0000-1000-8000-00805f9b34fb )                         WRITE                                                                                                          │
│ 003c            ff0a ( 0000ff0a-0000-1000-8000-00805f9b34fb )                         READ WRITE                                        u'Brute force my value 00 to ff'                             │
│ 003e            ff0b ( 0000ff0b-0000-1000-8000-00805f9b34fb )                         READ                                              u'Read me 1000 times'                                        │
│ 0040            ff0c ( 0000ff0c-0000-1000-8000-00805f9b34fb )                         NOTIFY READ WRITE                                 u'Listen to me for a single notification'                    │
│ 0042            ff0d ( 0000ff0d-0000-1000-8000-00805f9b34fb )                         READ                                              u'Listen to handle 0x0044 for a single indication'           │
│ 0044            ff0e ( 0000ff0e-0000-1000-8000-00805f9b34fb )                         READ INDICATE WRITE                                                                                            │
│ 0046            ff0f ( 0000ff0f-0000-1000-8000-00805f9b34fb )                         NOTIFY READ WRITE                                 u'Listen to me for multi notifications'                      │
│ 0048            ff10 ( 0000ff10-0000-1000-8000-00805f9b34fb )                         READ                                              u'Listen to handle 0x004a for multi indications'             │
│ 004a            ff11 ( 0000ff11-0000-1000-8000-00805f9b34fb )                         READ INDICATE WRITE                                                                                            │
│ 004c            ff12 ( 0000ff12-0000-1000-8000-00805f9b34fb )                         READ                                              u'Connect with BT MAC address 11:22:33:44:55:66'             │
│ 004e            ff13 ( 0000ff13-0000-1000-8000-00805f9b34fb )                         READ                                              u'Set your connection MTU to 444'                            │
│ 0050            ff14 ( 0000ff14-0000-1000-8000-00805f9b34fb )                         READ WRITE                                        u"Write+resp 'hello'  "                                      │
│ 0052            ff15 ( 0000ff15-0000-1000-8000-00805f9b34fb )                         READ WRITE                                        u'No notifications here! really?'                            │
│ 0054            ff16 ( 0000ff16-0000-1000-8000-00805f9b34fb )                         NOTIFY BROADCAST READ WRITE EXTENDED PROPERTIES   u'So many properties!'                                       │
│ 0056            ff17 ( 0000ff17-0000-1000-8000-00805f9b34fb )                         READ                                              u"md5 of author's twitter handle"                            │
│                                                                                                                                                                                                      │
└──────────────┴────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────┘
()

List Capabilities Python:

>>> python service_explorer.py --name BLECTF
2023-11-13 22:55:50,730 __main__ INFO: starting scan...
2023-11-13 22:55:50,861 __main__ INFO: connecting to device...
2023-11-13 22:55:52,262 __main__ INFO: connected
2023-11-13 22:55:54,629 __main__ INFO: [Service] 00001801-0000-1000-8000-00805f9b34fb (Handle: 1): Generic Attribute Profile
2023-11-13 22:55:54,629 __main__ INFO:   [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb (Handle: 2): Service Changed (indicate)
2023-11-13 22:55:54,629 __main__ INFO:     [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 4): Client Characteristic Configuration, Value: bytearray(b'\x02\x00')
2023-11-13 22:55:54,629 __main__ INFO:   [Characteristic] 00002b29-0000-1000-8000-00805f9b34fb (Handle: 5): Client Supported Features (read,write), Value: bytearray(b'\x05')
2023-11-13 22:55:54,629 __main__ INFO:   [Characteristic] 00002b2a-0000-1000-8000-00805f9b34fb (Handle: 7): Database Hash (read), Value: bytearray(b'\xe5\xc2\xaa\x8b\xb1!\xce\xaf\xef\xe3\xb9\xf3\xd5\xa3\xc21')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 00002b3a-0000-1000-8000-00805f9b34fb (Handle: 9): Server Supported Features (read), Value: bytearray(b'\x00')
2023-11-13 22:55:54,630 __main__ INFO: [Service] 000000ff-0000-1000-8000-00805f9b34fb (Handle: 40): Vendor specific
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:8 /20')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff02-0000-1000-8000-00805f9b34fb (Handle: 43): Vendor specific (read,write), Value: bytearray(b'Write Flags Here')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff03-0000-1000-8000-00805f9b34fb (Handle: 45): Vendor specific (read), Value: bytearray(b'd205303e099ceff44835')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff04-0000-1000-8000-00805f9b34fb (Handle: 47): Vendor specific (read), Value: bytearray(b'MD5 of Device Name')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff05-0000-1000-8000-00805f9b34fb (Handle: 49): Vendor specific (read,write), Value: bytearray(b'3873c0270763568cf7aa')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff06-0000-1000-8000-00805f9b34fb (Handle: 51): Vendor specific (read,write), Value: bytearray(b'c55c6314b3db0a6128af')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff07-0000-1000-8000-00805f9b34fb (Handle: 53): Vendor specific (read,write), Value: bytearray(b'1179080b29f8da16ad66')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff08-0000-1000-8000-00805f9b34fb (Handle: 55): Vendor specific (read), Value: bytearray(b'f8b136d937fad6a2be9f')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff09-0000-1000-8000-00805f9b34fb (Handle: 57): Vendor specific (write)
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff0a-0000-1000-8000-00805f9b34fb (Handle: 59): Vendor specific (read,write), Value: bytearray(b'Brute force my value 00 to ff')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff0b-0000-1000-8000-00805f9b34fb (Handle: 61): Vendor specific (read), Value: bytearray(b'Read me 1000 times')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff0c-0000-1000-8000-00805f9b34fb (Handle: 63): Vendor specific (read,write,notify), Value: bytearray(b'Listen to me for a single notification')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff0d-0000-1000-8000-00805f9b34fb (Handle: 65): Vendor specific (read), Value: bytearray(b'Listen to handle 0x0044 for a single indication')
2023-11-13 22:55:54,630 __main__ INFO:   [Characteristic] 0000ff0e-0000-1000-8000-00805f9b34fb (Handle: 67): Vendor specific (read,write,indicate), Value: bytearray(b'Listen to handle 0x0044 for a single indication\x00')
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff0f-0000-1000-8000-00805f9b34fb (Handle: 69): Vendor specific (read,write,notify), Value: bytearray(b'Listen to me for multi notifications')
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff10-0000-1000-8000-00805f9b34fb (Handle: 71): Vendor specific (read), Value: bytearray(b'Listen to handle 0x004a for multi indications')
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff11-0000-1000-8000-00805f9b34fb (Handle: 73): Vendor specific (read,write,indicate), Value: bytearray(b'Listen to handle 0x004a for multi indications\x00')
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff12-0000-1000-8000-00805f9b34fb (Handle: 75): Vendor specific (read), Value: bytearray(b'Connect with BT MAC address 11:22:33:44:55:66')
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff13-0000-1000-8000-00805f9b34fb (Handle: 77): Vendor specific (read), Value: bytearray(b'Set your connection MTU to 444')
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff14-0000-1000-8000-00805f9b34fb (Handle: 79): Vendor specific (read,write), Value: bytearray(b"Write+resp \'hello\'  ")
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff15-0000-1000-8000-00805f9b34fb (Handle: 81): Vendor specific (read,write), Value: bytearray(b'No notifications here! really?')
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff16-0000-1000-8000-00805f9b34fb (Handle: 83): Vendor specific (broadcast,read,write,notify,extended-properties), Value: bytearray(b'So many properties!')
2023-11-13 22:55:54,631 __main__ INFO:   [Characteristic] 0000ff17-0000-1000-8000-00805f9b34fb (Handle: 85): Vendor specific (read), Value: bytearray(b"md5 of author\'s twitter handle")
2023-11-13 22:55:54,631 __main__ INFO: disconnecting...
2023-11-13 22:55:56,665 __main__ INFO: disconnected

Common Functions

Read Values (bluetoothctl):

>>> bluetoothctl
hci0 new_settings: powered bondable ssp br/edr le secure-conn
Agent registered
[CHG] Controller A8:7E:EA:C2:EB:46 Pairable: yes
[2b00042f7481c7b056c4b410d28f33cf]# menu gatt
[2b00042f7481c7b056c4b410d28f33cf]# select-attribute 0000ff01-0000-1000-8000-00805f9b34fb
[2b00042f7481c7b056c4b410d28f33cf:/service0028/char0029]# read
Attempting to read /org/bluez/hci0/dev_64_E8_33_82_AF_6A/service0028/char0029
[CHG] Attribute /org/bluez/hci0/dev_64_E8_33_82_AF_6A/service0028/char0029 Value:
  53 63 6f 72 65 3a 20 30 2f 32 30                 Score: 0/20
  53 63 6f 72 65 3a 20 30 2f 32 30                 Score: 0/20

Read Values (python):

>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep 0000ff01-0000-1000-8000-00805f9b34fb
2023-11-13 23:00:37,073 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:8 /20')

Write Values (python):


Write Values (bleah):

sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "5cd56d74049ae40f442ece036c6f4f06"
sudo bleah -b "64:E8:33:82:AF:6A" -n 0x002c -d "5cd56d74049ae40f442ece036c6f4f06"
sudo bleah -b "64:E8:33:82:AF:6A" -n 43 -d "5cd56d74049ae40f442ece036c6f4f06"

Write Values (bluetoothctl):

#Dosent work

Flag 1

Write a Flag:

>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "12345678901234567890"
@ Scanning for 5s [-128 dBm of sensitivity] ...

┌ 64:e8:33:82:af:6a (-37 dBm) ──────────────────────────────────────────┐
│ Vendor                 ?                                             │
│ Allows Connections                                                  │
│ Address Type           public                                        │
│ Tx Power               u'eb'                                         │
│ Complete 16b Services  '000000ff-0000-1000-8000-00805f9b34fb'        │
│ Complete Local Name    BLECTF                                        │
│ Flags                  LE General Discoverable, BR/EDR Not Supported │
└───────────────────────┴───────────────────────────────────────────────┘

@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff02-0000-1000-8000-00805f9b34fb) ... found
@ Sending 20 bytes ... done
()
()

Flag 2

Read the Flag with bluetoothctl:

>>> bluetoothctl
scan on 
[2b00042f7481c7b056c4b410d28f33cf]# menu gatt
[2b00042f7481c7b056c4b410d28f33cf]# select-attribute 0000ff03-0000-1000-8000-00805f9b34fb
[2b00042f7481c7b056c4b410d28f33cf:/service0028/char0029]# read
Attempting to read /org/bluez/hci0/dev_64_E8_33_82_AF_6A/service0028/char0029
[CHG] Attribute /org/bluez/hci0/dev_64_E8_33_82_AF_6A/service0028/char0029 Value:
  53 63 6f 72 65 3a 20 30 2f 32 30                 Score: 0/20
  53 63 6f 72 65 3a 20 30 2f 32 30                 Score: 0/20

Note

Cant write with bluetoothctl

Write a Flag with bluetoothctl:

sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "d205303e099ceff44835"

Flag 3

#md5("BLECTF") = 5cd56d74049ae40f442ece036c6f4f06
>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "5cd56d74049ae40f442ece036c6f4f06"
[...]
@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff02-0000-1000-8000-00805f9b34fb) ... found
@ Sending 32 bytes ... done
()
()
#Actually wants the first 20 characters
>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "5cd56d74049ae40f442e"

Flag 4

Get device name and sent it to 0000ff02-0000-1000-8000-00805f9b34fb.

[BLECTF]# info
Device 64:E8:33:82:AF:6A (public)
        Name: 2b00042f7481c7b056c4b410d28f33cf
        Alias: 2b00042f7481c7b056c4b410d28f33cf
        Paired: no
        Bonded: no
        Trusted: no
        Blocked: no
        Connected: yes
        LegacyPairing: no
        UUID: Unknown                   (000000ff-0000-1000-8000-00805f9b34fb)
        UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)
        UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
        AdvertisingFlags:
  06
>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "2b00042f7481c7b056c4"

@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff02-0000-1000-8000-00805f9b34fb) ... found
@ Sending 20 bytes ... done
()
()

Flag 5

Write anything to 0000ff05-0000-1000-8000-00805f9b34fb

>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff05-0000-1000-8000-00805f9b34fb -d "anything"

@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff02-0000-1000-8000-00805f9b34fb) ... found
@ Sending 20 bytes ... done
()
()

After anything is written the data is changed

[BLECTF]# select-attribute 0000ff05-0000-1000-8000-00805f9b34fb
[2b00042f7481c7b056c4b410d28f33cf:/service0028/char0031]# read
Attempting to read /org/bluez/hci0/dev_64_E8_33_82_AF_6A/service0028/char0031
[CHG] Attribute /org/bluez/hci0/dev_64_E8_33_82_AF_6A/service0028/char0031 Value:
  33 38 37 33 63 30 32 37 30 37 36 33 35 36 38 63  3873c0270763568c
  66 37 61 61                                      f7aa
  33 38 37 33 63 30 32 37 30 37 36 33 35 36 38 63  3873c0270763568c
  66 37 61 61                                      f7aa
[2b00042f7481c7b056c4b410d28f33cf:/service0028/char0031]#

Write the flag

>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "3873c0270763568cf7aa"
[...]
@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff02-0000-1000-8000-00805f9b34fb) ... found
@ Sending 20 bytes ... done
()
()

Flag 6

Write ascii "yo"

>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff06-0000-1000-8000-00805f9b34fb -d "yo"
[...]
@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff06-0000-1000-8000-00805f9b34fb) ... found
@ Sending 2 bytes ... done
()
()

Read the flag from the 0000ff06-0000-1000-8000-00805f9b34fb handle

Write the flag

>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "c55c6314b3db0a6128af"
[...]
@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff02-0000-1000-8000-00805f9b34fb) ... found
@ Sending 20 bytes ... done
()
()

Flag 7

Write binary 0x07

>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff07-0000-1000-8000-00805f9b34fb -d 0x07
[...]
@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff07-0000-1000-8000-00805f9b34fb) ... found
@ Sending 1 bytes ... done
()
()

Read the flag from the 0000ff07-0000-1000-8000-00805f9b34fb handle

Write the flag

>>> sudo bleah -b "64:E8:33:82:AF:6A" -u 0000ff02-0000-1000-8000-00805f9b34fb -d "1179080b29f8da16ad66"
[...]
@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff02-0000-1000-8000-00805f9b34fb) ... found
@ Sending 20 bytes ... done
()
()

Flag 8

Write 0xC9 to handle 58. 58 == 0x003a

sudo bleah -b "64:E8:33:82:AF:6A" -n 0x003a -d 0xC9
[...]
@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic handle(58) ... found
@ Sending 1 bytes ... done
()
()

Read the flag from the 0000ff08-0000-1000-8000-00805f9b34fb handle

Write the flag

>>> sudo bleah -b "64:E8:33:82:AF:6A" -n 0x002a -d "f8b136d937fad6a2be9f"
[...]
@ Connecting to 64:e8:33:82:af:6a ... connected.
()
@ Searching for characteristic uuid(0000ff02-0000-1000-8000-00805f9b34fb) ... found
@ Sending 20 bytes ... done
()
()

Flag 9

Modified write Script

import asyncio, string, platform, sys, argparse, logging

from bleak import BleakClient
from bleak.uuids import normalize_uuid_16, uuid16_dict

logger = logging.getLogger(__name__)

async def flag9(args: argparse.Namespace):
	async with BleakClient(args.device, winrt=dict(use_cached_services=True)) as client:
		logger.info(f"Connected: {client.is_connected}")

		for i in range(255):
			write_data = bytearray([i])
			#Read UUID or handle
			read_data = await client.read_gatt_char(args.service)
			logger.info(f"Read[{args.service}]: {read_data}")

			#Write data to UUID or handle
			logger.info(f"Write[{args.service}]: {write_data}")
			value = await client.write_gatt_char(args.service, write_data, response=True)
			logger.info(f"Returned Data: {value}")


async def main(args: argparse.Namespace):
	async with BleakClient(args.device, winrt=dict(use_cached_services=True)) as client:
		logger.info(f"Connected: {client.is_connected}")

		#Read UUID or handle
		read_data = await client.read_gatt_char(args.service)
		logger.info(f"Read[{args.service}]: {read_data}")

		#Write data to UUID or handle
		logger.info(f"Write[{args.service}]: {args.data}")
		value = await client.write_gatt_char(args.service, args.data, response=True)
		logger.info(f"Returned Data: {value}")


if __name__ == "__main__":
	parser = argparse.ArgumentParser()

	parser.add_argument(
		"--macos-use-bdaddr",
		action="store_true",
		help="when true use Bluetooth address instead of UUID on macOS",
	)

	parser.add_argument(
		"--device",
		required=True,
		help="Which Device MAC to connect to",
	)

	parser.add_argument(
		"--service",
		required=True,
		help="UUID or Handle to write to",
	)

	parser.add_argument(
		"--data",
		required=True,
		help="the Data to write. Can be 0x02, or \"test\"",
	)

	parser.add_argument(
		"-v",
		dest="debug",
		#default=True,
		action="store_true",
		help="sets the logging level to debug",
	)

	args = parser.parse_args()

	#Convert to bytes
	if args.data.startswith("0x") and all(c in string.hexdigits for c in args.data):
		args.data = int(args.data, 16)
	else:
		args.data = args.data.encode()

	log_level = logging.DEBUG if args.debug else logging.INFO
	logging.basicConfig(
		level=log_level,
		format="%(asctime)-15s %(name)-8s %(levelname)s: %(message)s",
	)

	#asyncio.run(main(args))
	asyncio.run(flag9(args))

Run the script:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff0a-0000-1000-8000-00805f9b34fb --data 0x00
2023-11-13 23:14:19,135 __main__ INFO: Connected: True
2023-11-13 23:14:19,529 __main__ INFO: Read[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'933c1fcfa8ed52d2ec05')
2023-11-13 23:14:19,529 __main__ INFO: Write[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'\x00')
2023-11-13 23:14:19,608 __main__ INFO: Returned Data: None
2023-11-13 23:14:19,689 __main__ INFO: Read[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'933c1fcfa8ed52d2ec05')
2023-11-13 23:14:19,689 __main__ INFO: Write[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'\x01')
2023-11-13 23:14:19,768 __main__ INFO: Returned Data: None
2023-11-13 23:14:19,849 __main__ INFO: Read[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'933c1fcfa8ed52d2ec05')
2023-11-13 23:14:19,849 __main__ INFO: Write[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'\x02')
2023-11-13 23:14:19,929 __main__ INFO: Returned Data: None
2023-11-13 23:14:20,009 __main__ INFO: Read[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'933c1fcfa8ed52d2ec05')
2023-11-13 23:14:20,009 __main__ INFO: Write[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'\x03')
2023-11-13 23:14:20,089 __main__ INFO: Returned Data: None
2023-11-13 23:14:20,168 __main__ INFO: Read[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'933c1fcfa8ed52d2ec05')
2023-11-13 23:14:20,168 __main__ INFO: Write[0000ff0a-0000-1000-8000-00805f9b34fb]: bytearray(b'\x04')
2023-11-13 23:14:20,249 __main__ INFO: Returned Data: None

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "933c1fcfa8ed52d2ec05"
2023-11-13 23:19:52,034 __main__ INFO: Connected: True
2023-11-13 23:19:52,412 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-13 23:19:52,412 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'933c1fcfa8ed52d2ec05'
2023-11-13 23:19:52,492 __main__ INFO: Returned Data: None

Flag 10

Modified read Script

import asyncio, string, platform, sys, argparse, logging

from bleak import BleakClient
from bleak.uuids import normalize_uuid_16, uuid16_dict

logger = logging.getLogger(__name__)

async def flag(args: argparse.Namespace):
        async with BleakClient(args.device, winrt=dict(use_cached_services=True)) as client:
                logger.info(f"Connected: {client.is_connected}")

                for i in range(1000):
                        #Read UUID or handle
                        read_data = await client.read_gatt_char(args.service)
                        logger.info(f"Read[{args.service}]: {read_data}")


if __name__ == "__main__":
        parser = argparse.ArgumentParser()

        parser.add_argument(
                "--macos-use-bdaddr",
                action="store_true",
                help="when true use Bluetooth address instead of UUID on macOS",
        )

        parser.add_argument(
                "--device",
                required=True,
                help="Which Device MAC to connect to",
        )

        parser.add_argument(
                "--service",
                required=True,
                help="UUID or Handle to write to",
        )

        parser.add_argument(
                "--data",
                required=True,
                help="the Data to write. Can be 0x02, or \"test\"",
        )

        parser.add_argument(
                "-v",
                dest="debug",
                #default=True,
                action="store_true",
                help="sets the logging level to debug",
        )

        args = parser.parse_args()

        #Convert to bytes
        if args.data.startswith("0x") and all(c in string.hexdigits for c in args.data):
                args.data = int(args.data, 16)
        else:
                args.data = args.data.encode()

        log_level = logging.DEBUG if args.debug else logging.INFO
        logging.basicConfig(
                level=log_level,
                format="%(asctime)-15s %(name)-8s %(levelname)s: %(message)s",
        )

        asyncio.run(flag(args))

Run the script:

>>> python solve10.py --device "64:E8:33:82:AF:6A" --service 0000ff0b-0000-1000-8000-00805f9b34fb --data a
[...]
2023-11-13 23:35:08,025 __main__ INFO: Read[0000ff0b-0000-1000-8000-00805f9b34fb]: bytearray(b'6ffcd214ffebdc0d069e')
2023-11-13 23:35:08,105 __main__ INFO: Read[0000ff0b-0000-1000-8000-00805f9b34fb]: bytearray(b'6ffcd214ffebdc0d069e')

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "933c1fcfa8ed52d2ec05"
2023-11-13 23:19:52,034 __main__ INFO: Connected: True
2023-11-13 23:19:52,412 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-13 23:19:52,412 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'933c1fcfa8ed52d2ec05'
2023-11-13 23:19:52,492 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-13 23:37:37,829 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:10/20')

Flag 11

Modified notification Script

import argparse
import asyncio
import logging

from bleak import BleakClient, BleakScanner
from bleak.backends.characteristic import BleakGATTCharacteristic

logger = logging.getLogger(__name__)


def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray):
	"""Simple notification handler which prints the data received."""
	logger.info(f"{characteristic.description}: {data}")


async def main(args: argparse.Namespace):
	logger.info("starting scan...")

	if args.address:
		device = await BleakScanner.find_device_by_address(
			args.address, cb=dict(use_bdaddr=args.macos_use_bdaddr)
		)
		if device is None:
			logger.error("could not find device with address '%s'", args.address)
			return
	else:
		device = await BleakScanner.find_device_by_name(
			args.name, cb=dict(use_bdaddr=args.macos_use_bdaddr)
		)
		if device is None:
			logger.error("could not find device with name '%s'", args.name)
			return

	logger.info("connecting to device...")

	async with BleakClient(device) as client:
		logger.info("Connected")

		await client.start_notify(args.characteristic, notification_handler)
		read_data = await client.read_gatt_char(args.characteristic)
		logger.info(f"Read[{args.characteristic}]: {read_data}")
		read_data = await client.write_gatt_char(args.characteristic, bytearray([0x01]), response=True)
		logger.info(f"Write[{args.characteristic}]: 0x01 {read_data}")
		await asyncio.sleep(20.0)
		await client.stop_notify(args.characteristic)


if __name__ == "__main__":
	parser = argparse.ArgumentParser()

	device_group = parser.add_mutually_exclusive_group(required=True)

	device_group.add_argument(
		"--name",
		metavar="<name>",
		help="the name of the bluetooth device to connect to",
	)
	device_group.add_argument(
		"--address",
		metavar="<address>",
		help="the address of the bluetooth device to connect to",
	)

	parser.add_argument(
		"--macos-use-bdaddr",
		action="store_true",
		help="when true use Bluetooth address instead of UUID on macOS",
	)

	parser.add_argument(
		"characteristic",
		metavar="<notify uuid>",
		help="UUID of a characteristic that supports notifications",
	)

	parser.add_argument(
		"-d",
		"--debug",
		action="store_true",
		help="sets the log level to debug",
	)

	args = parser.parse_args()

	log_level = logging.DEBUG if args.debug else logging.INFO
	logging.basicConfig(
		level=log_level,
		format="%(asctime)-15s %(name)-8s %(levelname)s: %(message)s",
	)

	asyncio.run(main(args))

Run the script:

>>> python enable_notifications.py --address "64:E8:33:82:AF:6A" 0000ff0c-0000-1000-8000-00805f9b34fb
2023-11-14 00:01:32,679 __main__ INFO: starting scan...
2023-11-14 00:01:32,824 __main__ INFO: connecting to device...
2023-11-14 00:01:34,198 __main__ INFO: Connected
2023-11-14 00:01:34,765 __main__ INFO: Vendor specific: bytearray(b'Listen to me for a single notification')
2023-11-14 00:01:34,765 __main__ INFO: Read[0000ff0c-0000-1000-8000-00805f9b34fb]: bytearray(b'Listen to me for a single notification')
2023-11-14 00:01:34,848 __main__ INFO: Write[0000ff0c-0000-1000-8000-00805f9b34fb]: 0x01 None
2023-11-14 00:01:34,849 __main__ INFO: Vendor specific: bytearray(b'5ec3772bcd00cf06d8eb')

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "5ec3772bcd00cf06d8eb"
2023-11-14 00:04:41,191 __main__ INFO: Connected: True
2023-11-14 00:04:41,608 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-14 00:04:41,609 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'5ec3772bcd00cf06d8eb'
2023-11-14 00:04:41,688 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-14 00:04:54,249 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:11/20')

Flag 12

Listen to handle 0x0044 for a single indication

import argparse
import asyncio
import logging

from bleak import BleakClient, BleakScanner
from bleak.backends.characteristic import BleakGATTCharacteristic

logger = logging.getLogger(__name__)


def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray):
	"""Simple notification handler which prints the data received."""
	logger.info(f"{characteristic.description}: {data}")


async def main(args: argparse.Namespace):
	logger.info("starting scan...")

	if args.address:
		device = await BleakScanner.find_device_by_address(
			args.address, cb=dict(use_bdaddr=args.macos_use_bdaddr)
		)
		if device is None:
			logger.error("could not find device with address '%s'", args.address)
			return
	else:
		device = await BleakScanner.find_device_by_name(
			args.name, cb=dict(use_bdaddr=args.macos_use_bdaddr)
		)
		if device is None:
			logger.error("could not find device with name '%s'", args.name)
			return

	logger.info("connecting to device...")

	async with BleakClient(device) as client:
		logger.info("Connected")

		await client.start_notify(args.characteristic, notification_handler)
		read_data = await client.read_gatt_char(args.characteristic)
		logger.info(f"Read[{args.characteristic}]: {read_data}")
		read_data = await client.write_gatt_char(args.characteristic, bytearray([0x01]), response=True)
		logger.info(f"Write[{args.characteristic}]: 0x01 {read_data}")
		await asyncio.sleep(20.0)
		await client.stop_notify(args.characteristic)


if __name__ == "__main__":
	parser = argparse.ArgumentParser()

	device_group = parser.add_mutually_exclusive_group(required=True)

	device_group.add_argument(
		"--name",
		metavar="<name>",
		help="the name of the bluetooth device to connect to",
	)
	device_group.add_argument(
		"--address",
		metavar="<address>",
		help="the address of the bluetooth device to connect to",
	)

	parser.add_argument(
		"--macos-use-bdaddr",
		action="store_true",
		help="when true use Bluetooth address instead of UUID on macOS",
	)

	parser.add_argument(
		"characteristic",
		metavar="<notify uuid>",
		help="UUID of a characteristic that supports notifications",
	)

	parser.add_argument(
		"-d",
		"--debug",
		action="store_true",
		help="sets the log level to debug",
	)

	args = parser.parse_args()

	log_level = logging.DEBUG if args.debug else logging.INFO
	logging.basicConfig(
		level=log_level,
		format="%(asctime)-15s %(name)-8s %(levelname)s: %(message)s",
	)

	asyncio.run(main(args))

Run the script:

>>> python enable_notifications.py --address "64:E8:33:82:AF:6A" 0000ff0e-0000-1000-8000-00805f9b34fb
2023-11-14 00:10:18,764 __main__ INFO: starting scan...
2023-11-14 00:10:18,923 __main__ INFO: connecting to device...
2023-11-14 00:10:20,253 __main__ INFO: Connected
2023-11-14 00:10:20,612 __main__ INFO: Vendor specific: bytearray(b'Listen to handle 0x0044 for a single indication\x00')
2023-11-14 00:10:20,612 __main__ INFO: Read[0000ff0e-0000-1000-8000-00805f9b34fb]: bytearray(b'Listen to handle 0x0044 for a single indication\x00')
2023-11-14 00:10:20,695 __main__ INFO: Write[0000ff0e-0000-1000-8000-00805f9b34fb]: 0x01 None
2023-11-14 00:10:20,695 __main__ INFO: Vendor specific: bytearray(b'c7b86dd121848c77c113')

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "c7b86dd121848c77c113"
2023-11-14 00:11:07,529 __main__ INFO: Connected: True
2023-11-14 00:11:07,775 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-14 00:11:07,775 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'c7b86dd121848c77c113'
2023-11-14 00:11:07,855 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-14 00:11:20,576 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:12/20')

Flag 13

Listen for a single indication

Run the script:

>>> python enable_notifications.py --address "64:E8:33:82:AF:6A" 0000ff0f-0000-1000-8000-00805f9b34fb
2023-11-14 00:18:31,246 __main__ INFO: starting scan...
2023-11-14 00:18:31,408 __main__ INFO: connecting to device...
2023-11-14 00:18:32,436 __main__ INFO: Connected
2023-11-14 00:18:32,658 __main__ INFO: Vendor specific: bytearray(b'Listen to me for multi notifications')
2023-11-14 00:18:32,659 __main__ INFO: Read[0000ff0f-0000-1000-8000-00805f9b34fb]: bytearray(b'Listen to me for multi notifications')
2023-11-14 00:18:32,738 __main__ INFO: Write[0000ff0f-0000-1000-8000-00805f9b34fb]: 0x01 None
2023-11-14 00:18:32,738 __main__ INFO: Vendor specific: bytearray(b'U no want this msg\x00\x00')
2023-11-14 00:18:33,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:34,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:35,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:36,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:37,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:38,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:39,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:40,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:41,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:42,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:43,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:44,739 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:45,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:46,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:47,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:48,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:49,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:50,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:51,741 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')
2023-11-14 00:18:52,738 __main__ INFO: Vendor specific: bytearray(b'c9457de5fd8cafe349fd')

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "c9457de5fd8cafe349fd"
2023-11-14 00:19:15,739 __main__ INFO: Connected: True
2023-11-14 00:19:15,978 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-14 00:19:15,979 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'c9457de5fd8cafe349fd'
2023-11-14 00:19:16,058 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-14 00:19:31,739 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:13/20')

Flag 14

Listen to handle 0x004a for multi indications

Run the script:

>>> python enable_notifications.py --address "64:E8:33:82:AF:6A" 0000ff11-0000-1000-8000-00805f9b34fb
2023-11-14 00:20:20,240 __main__ INFO: starting scan...
2023-11-14 00:20:20,390 __main__ INFO: connecting to device...
2023-11-14 00:20:21,743 __main__ INFO: Connected
2023-11-14 00:20:22,141 __main__ INFO: Vendor specific: bytearray(b'Listen to handle 0x004a for multi indications\x00')
2023-11-14 00:20:22,142 __main__ INFO: Read[0000ff11-0000-1000-8000-00805f9b34fb]: bytearray(b'Listen to handle 0x004a for multi indications\x00')
2023-11-14 00:20:22,218 __main__ INFO: Write[0000ff11-0000-1000-8000-00805f9b34fb]: 0x01 None
2023-11-14 00:20:22,219 __main__ INFO: Vendor specific: bytearray(b'U no want this msg\x00\x00')
2023-11-14 00:20:22,302 __main__ INFO: Vendor specific: bytearray(b'b6f3a47f207d38e16ffa')
2023-11-14 00:20:22,378 __main__ INFO: Vendor specific: bytearray(b'b6f3a47f207d38e16ffa')
2023-11-14 00:20:22,458 __main__ INFO: Vendor specific: bytearray(b'b6f3a47f207d38e16ffa')

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "b6f3a47f207d38e16ffa"
2023-11-14 00:21:05,699 __main__ INFO: Connected: True
2023-11-14 00:21:05,938 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-14 00:21:05,939 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'b6f3a47f207d38e16ffa'
2023-11-14 00:21:06,021 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-14 00:21:15,783 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:14/20')

Flag 15

Connect with BT MAC address 11:22:33:44:55:66'

TODO

Flag 16

Set your connection MTU to 444

Needed a separate library for MTU Support bluepy

Script:

#!/usr/bin/env python3

import bluepy.btle as btle

dev = btle.Peripheral('64:E8:33:82:AF:6A')
dev.setMTU(444)

print(dev.readCharacteristic(0x4e).decode())

Run the script:

>>> python  mtu.py
b1e409e5a4eaf9fe5158

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "b1e409e5a4eaf9fe5158"
2023-11-22 15:05:33,163 __main__ INFO: Connected: True
2023-11-22 15:05:33,537 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-22 15:05:33,537 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'b1e409e5a4eaf9fe5158'
2023-11-22 15:05:33,617 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-22 15:08:29,981 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:15/20')

Flag 17

Write+resp 'hello'

Write the String:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff14-0000-1000-8000-00805f9b34fb --data "hello"
2023-11-22 15:11:26,263 __main__ INFO: Connected: True
2023-11-22 15:11:26,621 __main__ INFO: Read[0000ff14-0000-1000-8000-00805f9b34fb]: bytearray(b"Write+resp \'hello\'  ")
2023-11-22 15:11:26,621 __main__ INFO: Write[0000ff14-0000-1000-8000-00805f9b34fb]: b'hello'
2023-11-22 15:11:26,700 __main__ INFO: Returned Data: None

Read the flag:

>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep 0000ff14-0000-1000-8000-00805f9b34fb
2023-11-22 15:12:19,182 __main__ INFO:   [Characteristic] 0000ff14-0000-1000-8000-00805f9b34fb (Handle: 79): Vendor specific (read,write), Value: bytearray(b'd41d8cd98f00b204e980\x00')

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "d41d8cd98f00b204e980"
2023-11-22 15:14:09,430 __main__ INFO: Connected: True
2023-11-22 15:14:09,824 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-22 15:14:09,824 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'd41d8cd98f00b204e980'
2023-11-22 15:14:09,904 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-22 15:14:33,105 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:16/20')

Flag 18

'No notifications here! really?'
Read Notification:


Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "b1e409e5a4eaf9fe5158"
2023-11-22 15:05:33,163 __main__ INFO: Connected: True
2023-11-22 15:05:33,537 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-22 15:05:33,537 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'b1e409e5a4eaf9fe5158'
2023-11-22 15:05:33,617 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-22 15:08:29,981 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:15/20')

Flag 19

'So many properties!'

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "b1e409e5a4eaf9fe5158"
2023-11-22 15:05:33,163 __main__ INFO: Connected: True
2023-11-22 15:05:33,537 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-22 15:05:33,537 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'b1e409e5a4eaf9fe5158'
2023-11-22 15:05:33,617 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-22 15:08:29,981 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:15/20')

Flag 20

"md5 of author's twitter handle"

Write the flag:

>>> python write_data.py --device "64:E8:33:82:AF:6A" --service 0000ff02-0000-1000-8000-00805f9b34fb --data "b1e409e5a4eaf9fe5158"
2023-11-22 15:05:33,163 __main__ INFO: Connected: True
2023-11-22 15:05:33,537 __main__ INFO: Read[0000ff02-0000-1000-8000-00805f9b34fb]: bytearray(b'Write Flags Here')
2023-11-22 15:05:33,537 __main__ INFO: Write[0000ff02-0000-1000-8000-00805f9b34fb]: b'b1e409e5a4eaf9fe5158'
2023-11-22 15:05:33,617 __main__ INFO: Returned Data: None
>>> python service_explorer.py --address "64:E8:33:82:AF:6A" 2>&1 | grep Score
2023-11-22 15:08:29,981 __main__ INFO:   [Characteristic] 0000ff01-0000-1000-8000-00805f9b34fb (Handle: 41): Vendor specific (read), Value: bytearray(b'Score:15/20')