Bluetooth devices are all around us and a surprising number of them are left discoverable. In this post I describe techniques for finding discoverable Bluetooth devices and listing the services running on them. I will also cover basic BLE (Bluetooth Smart) reconnaissance.
This tutorial assumes you have a modern Linux system or VM running BlueZ. The commands below should work in BlueZ 4.101 and BlueZ 5.x.
Finding Bluetooth Devices
The most basic way to find local discoverable Bluetooth devices is to use
hcitool scan
. In the following example, there are two discoverable devices near my laptop: a Nexus 4 and a wireless speaker:
$ hcitool scan
Scanning ...
98:D6:XX:XX:XX:XX Nexus 4
00:0D:XX:XX:XX:XX Bluetooth Speaker
The left column is the device's BD ADDR (Bluetooth Device ADDRess). This address is unique to the device and is very similar to a WiFi or ethernet MAC address. The right column is the device's human-readable name, which the device sent us in response to our scan requests.
To get a little more information about a device, we can use
hcitool inq
, another subcommand of
hcitool
:
$ hcitool inq
Inquiring ...
98:D6:XX:XX:XX:XX clock offset: 0x0000 class: 0x5a020c
00:0D:XX:XX:XX:XX clock offset: 0x5a75 class: 0x240404
This output again includes the BD ADDR, but this time it also includes the clock offset and class of device. Clock offset is a low-level value that can be ignored. Class of Device (CoD) tells us what type of device we're talking to.
You can decode CoD using a tool I wrote called
btclassify. Running it with the above classes tells us these devices identify as a phone and a wearable headset:
$ ./btclassify.py 0x5a020c 0x240404
0x5a020c: Phone (Smartphone): Telephony, Object Transfer, Capturing, Networking
0x240404: Audio/Video (Wearable Headset Device): Audio, Rendering
Diving Deeper: Services
Now that we know we have a phone and a headset (which is actually a speaker), we'd like to find out what services they run.
In Bluetooth service information is available via the Service Discovery Protocol, or SDP. BlueZ ships with
sdptool
for querying SDP. The
browse
subcommand is typically the best tool for listing services. It has fairly verbose output, so it's useful to filter it using grep:
$ sdptool browse 98:D6:XX:XX:XX:XX | grep Service\ Name
Service Name: Headset Gateway
Service Name: Handsfree Gateway
Service Name: AV Remote Control Target
Service Name: Advanced Audio
Service Name: Android Network Access Point
Service Name: Android Network User
Service Name: OBEX Phonebook Access Server
Service Name: OBEX Object Push
Given the presence of "Android Network" services, we can conclude that this device is likely an Android smart phone. The other services are relatively typical for a phone. For instance, Headset and Handsfree are used with headsets and car audio systems, and the OBEX services are used for transferring contacts and sharing files.
Unfortunately when we try to run
sdptool browse
against the speaker, we do not receive any records. Instead we can use the
records
subcommand to receive these records. This command runs for 20 seconds before terminating, so don't worry if it appears to have hung:
$ sdptool records 00:0D:XX:XX:XX:XX
Service RecHandle: 0x10001
Service Class ID List:
"Audio Sink" (0x110b)
Protocol Descriptor List:
"L2CAP" (0x0100)
PSM: 25
"AVDTP" (0x0019)
uint16: 0x102
Profile Descriptor List:
"Advanced Audio" (0x110d)
Version: 0x0102
Service RecHandle: 0x10002
Service Class ID List:
"AV Remote Target" (0x110c)
Protocol Descriptor List:
"L2CAP" (0x0100)
PSM: 23
"AVCTP" (0x0017)
uint16: 0x103
Profile Descriptor List:
"AV Remote" (0x110e)
Version: 0x0103
Service Name: Hands-Free unit
Service RecHandle: 0x10004
Service Class ID List:
"Handsfree" (0x111e)
"Generic Audio" (0x1203)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 1
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Handsfree" (0x111e)
Version: 0x0105
Pay attention to the values after "Service Class ID List". This tells us the device has "Audio Sink", "AV Remote Target", "Handsfree", and "Generic Audio" services. These services make sense given that we know this is a speaker.
Both of these commands give a complete breakdown of all lower-level protocols used to implement the services. This information is not typically useful during general reconnaissance, but it can be useful when developing fuzzers.
One final thing to note is that
sdptool
works even if a device is not discoverable. If you know a device's BD ADDR you can try to connect to it using
sdptool browse
or
records
to find out if it's nearby but undiscoverable.
Information Hidden by BlueZ
Certain devices return a bit more information that BlueZ does not display in
hcitool
output. You can get at this information using
hcidump
or BlueZ 5's
btmon
. Since
hcidump
works on all recent versions of BlueZ, I will demonstrate its use.
In one terminal run
hcidump
and tell it to log to a file:
$ sudo hcidump -w inquriy.cap
In another terminal run
hcitool scan
:
$ hcitool inq
Inquiring ...
00:26:XX:XX:XX:XX Richard Gill's MacBook Pro
Yowza, we can already see the name of the owner of a nearby laptop. Could be useful for social engineering. This naming convention is fairly common among Mac laptops. Worse, on at least some versions of Mac OS X when you put Bluetooth into discoverable mode it will remain in that mode indefinitely.
Kill
hcidump
and load the file it created in Wireshark. Filter the output to just inquiry responses using
bthci.evt_code == 0x2f
. If the device is friendly, you will see information similar to the following screenshot:
Under "Extended Inquiry Response Data" you can see a list of service UUIDs, which will be fairly similar to the output of
sdptool
. On the right I've highlighted a "manufacturer specific" field. This field helpfully tells us the specific model number of the laptop! This information is also highly useful for social engineering.
BLE Recon
All the of the above applies to classic Bluetooth devices, such as phones, headsets, speakers, and so forth. BLE devices are becoming increasingly common, and they require their own set of tools. Some examples of BLE devices are fitness wristbands, heart rate monitors, smart watches, proximity sensors, and so on.
Listing BLE Devices
The most basic tool to list local BLE devices is
hcitool
's subcommand
lescan
:
$ hcitool lescan
LE Scan ...
DC:A0:F2:B9:4F:9E (unknown)
DC:A0:F2:B9:4F:9E Flex
00:22:XX:00:XX:XX (unknown)
00:22:XX:00:XX:XX Polar H7 00XXXX
I haven't censored the first device because it is using a random address that can vary over time, a BLE security feature that makes tracking devices more difficult.
Given a bit of a-priori knowledge, we can assume the first device is a FitBit Flex. The second device is a Polar H7 heart rate monitor.
Listing BLE Services
Recent versions of BlueZ ship with
gatttool
for querying BLE services over GATT. The
--primary
command line switch connects to the device and asks it for a list of services:
$ gatttool --primary -b 00:22:XX:00:XX:XX
attr handle = 0x0001, end grp handle = 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle = 0x000c, end grp handle = 0x000e uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle = 0x000f, end grp handle = 0x0014 uuid: 0000180d-0000-1000-8000-00805f9b34fb
attr handle = 0x0015, end grp handle = 0x0023 uuid: 0000180a-0000-1000-8000-00805f9b34fb
attr handle = 0x0024, end grp handle = 0x0026 uuid: 0000180f-0000-1000-8000-00805f9b34fb
attr handle = 0x0027, end grp handle = 0xffff uuid: 6217ff49-ac7b-547e-eecf-016a06970ba9
The service UUID defines the service. UUIDs of the form
000018xx-0000-1000-8000-00805f9b34fb
are generally 16-bit services that are assigned by the Bluetooth SIG. Information about these can be found on the
GATT assigned numbers page.
Based on the UUIDs, we can see this device has "Generic Access", "Generic Attribute", "Heart Rate", "Device Information", and "Battery" services. The last UUID is a manufacturer-specific UUID. If you see another device with this service, it's likely a Polar H7.
Given the presence of the "Heart Rate" service we can assume this device is probably a heart rate monitor. The "Device Information Service" is more interesting, and I'll describe some fun tricks with that in the next section.
One thing to note: if you're trying to connect to a device using a random address, such as the FitBit Flex, you need to add the
-t random
command line flag. Let's list the services for that device:
$ gatttool -t random --primary -b DC:A0:F2:B9:4F:9E
attr handle = 0x0001, end grp handle = 0x0007 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle = 0x0008, end grp handle = 0x0008 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle = 0x0009, end grp handle = 0x000e uuid: adabfb00-6e7d-4601-bda2-bffaa68956ba
attr handle = 0x000f, end grp handle = 0x0012 uuid: 558dfa00-4fa8-4105-9f02-4eaa93e62980
attr handle = 0x0013, end grp handle = 0x0018 uuid: 0000180a-0000-1000-8000-00805f9b34fb
attr handle = 0x0019, end grp handle = 0xffff uuid: 0000180f-0000-1000-8000-00805f9b34fb
This device has many services in common with the heart rate monitor, but there are two additional manufacturer-specific services. Seeing these services on another device indicates you're probably looking at a FitBit Flex.
Finally, to see a list of all characteristics you can use the
--characteristics
flag or
characteristics
command in interactive mode. I will omit its output, since it's generally very long. Once you've found a characteristic you're interested in, you can try reading it using
char-read-uuid
or
char-read-hnd
.
Sensitive Information in Device Information Service
The "Device Information Service" is very interesting. From the
SIG's page about it, we can see there is an optional field called "serial number". Clicking on the link brings us to the
serial number string characteristic, which is assigned number 0x2a25. Let's use
gatttool
's interactive mode to try to read it:
$ gatttool -I -b 00:22:XX:00:XX:XX
[ ][00:22:XX:00:XX:XX][LE]> connect
[CON][00:22:XX:00:XX:XX][LE]> char-read-uuid 2a25
[CON][00:22:XX:00:XX:XX][LE]>
handle: 0x001b value: 30 30 30 33 3X 3X 3X 3X 3X 3X 00
This value is an ASCII representation of the serial number, which in this case is 0003XXXXXX. This value is likely unique and trackable.
Let's try that on the FitBit:
$ gatttool -t random -I -b DC:A0:F2:B9:4F:9E
[ ][DC:A0:F2:B9:4F:9E][LE]> connect
[CON][DC:A0:F2:B9:4F:9E][LE]> char-read-uuid 2a25
[CON][DC:A0:F2:B9:4F:9E][LE]> Read characteristics by UUID failed: No attribute found within the given range
Too bad, it doesn't have it. One other interesting UUID to query is the
manufacturer name:
[CON][DC:A0:F2:B9:4F:9E][LE]> char-read-uuid 2a29
handle: 0x0015 value: 46 69 74 62 69 74 00 00 00 00 00 00 00 00 00 00 00 00 00
Converting this to ASCII gives us "FitBit", as expected.
One Last Thing
Using
gatttool
is nowhere near as reliable as the above makes it out to be. Connections will fail and drop all the time, even under controlled conditions. Just be patient and things generally work eventually.
Have fun doing Bluetooth recon!