whad-team/whad-client

wble-spawn: how to debug failure to MitM?

Opened this issue ยท 27 comments

I was able to do ble-central -i hci0 -b <target bdaddr> profile mydevice.json to generate a JSON file that looks like it has correct data within it.

However, when I do the command mentioned for the ble-spawn documentation, wble-connect -i hci0 <target bdaddr>| ble-wireshark | wble-spawn -i hci1 -p mydevice.json I do not see a connectable instance of the original device.

Using Sniffle I can see that the first wble-connect is connecting to just fine. But, if I use Sniffle and run only wble-spawn -i hci1 -p mydevice.json | wshark, I don't see any advertisements as either the bdaddr of hci1, or the original target bdaddr (I don't think you're trying to spoof the original bdaddr right?)

So what's the right way to debug the fact that the wble-spawn isn't advertising?

This specific tool chain connects to a target BLE device and then spawns a similar device based on the cached profile, and yes it should work as stated in the documentation. You can use the --log debug and --logfile options to enable debug messages for both wble-connect and wble-spawn (and save them in two separate files). This is what we use to debug such issues, but it is quite difficult to interpret when you are not familiar with the framework internals.

I guess the problem may be due to the use of ble-wireshark, which is some old tool we used when we started writing the doc. The expected toolchain is the following: wble-connect -i hci0 <target bdaddr>| shark | wble-spawn -i hci1 -p mydevice.json. But I guess you already tried it, as ble-wireshark should not exist anymore in WHAD.

I think that ble-wireshark just got back in there because I copied from the docs instead of my CLI.

In any case, I wanted to try and create something replicable for you, rather than targeting random peripherals around the house.

I installed Zephyr on a Nordic nrf52840 USB board (from Nordic, not Maker Diary or whatever that other thing is).

I am at commit 8a88cd4805b0a1352e822cb707a1a0f453a8d9d7 in Zephyr, which is probably not the latest but it's because I did this a while back and just snapshotted the VM. These are the tl;dr summary steps I wrote for myself based on going through the Getting Started:

sudo apt install python3-venv
python3 -m venv ~/zephyrproject/.venv
source ~/zephyrproject/.venv/bin/activate
pip install west
west init ~/zephyrproject
cd ~/zephyrproject
west update
west zephyr-export
pip install -r ~/zephyrproject/zephyr/scripts/requirements.txt
cd ~
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz
wget -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/sha256.sum | shasum --check --ignore-missing
tar xvf zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz
cd zephyr-sdk-0.16.5-1
./setup.sh
cd ~/zephyrproject/zephyr

I'm also not 100% sure what steps I used on this Ubuntu 24.04 system (since I've been trying various OSes since various Bluetooth tools break on newer OSes), but you need to install nrfutil. What I have written down is:

sudo apt-get install libusb-1.0

Download nrfutil Linux binary from:
https://www.nordicsemi.com/Products/Development-tools/nrf-util

mkdir ~/nrf
mv ~/Downloads/nrfutil ~/nrf
nano ~/.bashrc
export PATH=$PATH:/home/user/nrf
bash
nrfutil install nrf5sdk-tools
nrfutil install completion
Now for all commands below, instead of doing nrfutil dfu ... do nrfutil nrf5sdk-tools dfu ...

Now I modified the peripheral_hr as follows:

  1. nano samples/bluetooth/peripheral_hr/prj.conf
  2. Add the following at the bottom of the file
CONFIG_BT_PHY_UPDATE=n
CONFIG_BT_CONN_DISABLE_SECURITY=y
  1. save and exit
  2. nano samples/bluetooth/peripheral_hr/src/main.c
  3. add #include <zephyr/bluetooth/controller.h> at the top
  4. add the following right before err = bt_enable(NULL);
        bt_addr_le_t bdaddr;
        bdaddr.a.val[0] = 0x66;
        bdaddr.a.val[1] = 0x55;
        bdaddr.a.val[2] = 0x44;
        bdaddr.a.val[3] = 0x33;
        bdaddr.a.val[4] = 0x22;
        bdaddr.a.val[5] = 0x11;
        bdaddr.type = BT_ADDR_LE_PUBLIC;
        bt_ctlr_set_public_addr(bdaddr.a.val);
  1. Build with west build -p always -b nrf52840dongle_nrf52840 samples/bluetooth/peripheral_hr
  2. Package with nrfutil pkg generate --hw-version 52 --sd-req=0x00 --application build/zephyr/zephyr.hex --application-version 1 peripheral_hr.zip
  3. Press the "bootloader" button on the dongle, and it should then flash red
  4. Flash the firmware with sudo nrfutil dfu usb-serial -pkg peripheral_hr.zip -p /dev/ttyACM0
  5. Connect to the Zephyr serial console with sudo screen /dev/ttyACM0 115200
  6. Confirm you see the BDADDR changed and output like this:
*** Booting Zephyr OS build v3.6.0-1976-g8a88cd4805b0 ***
[00:00:00.311,401] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
[00:00:00.311,431] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
[00:00:00.311,462] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 3.6 Build 99
[00:00:00.312,042] <inf> bt_hci_core: Identity: 11:22:33:44:55:66 (public)
[00:00:00.312,103] <inf> bt_hci_core: HCI: version 5.4 (0x0d) revision 0x0000, manufacturer 0x05f1
[00:00:00.312,133] <inf> bt_hci_core: LMP: version 5.4 (0x0d) subver 0xffff
Bluetooth initialized
Advertising successfully started

Then I went and confirmed I see advertisements with Sniffle:

sudo python3 sniff_receiver.py -s /dev/ttyUSB0 -m 11:22:33:44:55:66
Timestamp: 0.064792  Length: 11  RSSI: -32  Channel: 37  PHY: 1M  CRC: 0xB14A5C
Ad Type: ADV_IND
ChSel: 1 TxAdd: 0 RxAdd: 0 Ad Length: 9
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  20 09 66 55 44 33 22 11  02 01 06                  .fUD3"....

So at this point I have a known-state peripheral named "Zephyr Heartrate Sensor" that I can connect to with nRF Connect phone app and enumerate the GATT services.

Now I'd like to profile it. so I restart the Zephyr peripheral. I have a CSR USB dongle which has its BDADDR set to AA:DD:AA:DD:AA:DD thanks to the bdaddr tool bundled with GATTacker. So I issue the following to profile:

(venv-root) root@VM:/home/user/whad-client# hciconfig
hci0:	Type: Primary  Bus: USB
	BD Address: AA:DD:AA:DD:AA:DD  ACL MTU: 310:10  SCO MTU: 64:8
	UP RUNNING 
	RX bytes:1029362 acl:0 sco:0 events:229 errors:0
	TX bytes:13699 acl:33 sco:0 commands:284 errors:0

(venv-root) root@VM:/home/user/whad-client# wble-central -i hci0 --log debug --log-file ./profiling.log -b 11:22:33:44:55:66 profile ZP_HR.json
Service Generic Attribute (0x1801)  (handle 1 to 8)
  Service Changed (0x2A05) I, handle 2, value handle: 3
    Descriptor type Client Characteristic Configuration (0x2902), handle: 4
  Client Supported Features (0x2B29) RW, handle 5, value handle: 6
  Database Hash (0x2B2A) R, handle 7, value handle: 8

Service Generic Access (0x1800)  (handle 9 to 15)
  Device Name (0x2A00) R, handle 10, value handle: 11
  Appearance (0x2A01) R, handle 12, value handle: 13
  Peripheral Preferred Connection Parameters (0x2A04) R, handle 14, value handle: 15

Service Battery (0x180F)  (handle 16 to 20)
  Battery Level (0x2A19) RN, handle 17, value handle: 18
    Descriptor type Client Characteristic Configuration (0x2902), handle: 19

Service Device Information (0x180A)  (handle 21 to 25)
  Model Number String (0x2A24) R, handle 22, value handle: 23
  Manufacturer Name String (0x2A29) R, handle 24, value handle: 25

Service Heart Rate (0x180D)  (handle 26 to 33)
  Heart Rate Measurement (0x2A37) N, handle 27, value handle: 28
    Descriptor type Client Characteristic Configuration (0x2902), handle: 29
  Body Sensor Location (0x2A38) R, handle 30, value handle: 31
  Heart Rate Control Point (0x2A39) W, handle 32, value handle: 33

Writing profile JSON data to ZP_HR.json ...

So now I've got a successful profile capture, and I want to start the MitM process. So I power up my second HCI interface, which is spoofed to be 11:22:33:44:55:66 like the target peripheral and I run:

(venv-root) root@VM:/home/user/whad-client# wble-connect --log debug --log-file ./wble-connect.log -i hci0 11:22:33:44:55:66 | wshark | wble-spawn --log debug --log-file ./wble-spawn.log -i hci1 -p ZP_HR.json

There is no output (feature request: there should be), but I know it connected to the true Zephyr HR peripheral fine because I see the following "Connected" line show up in the Zephyr serial output:

*** Booting Zephyr OS build v3.6.0-1976-g8a88cd4805b0 ***
[00:00:00.311,401] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
[00:00:00.311,431] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
[00:00:00.311,462] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 3.6 Build 99
[00:00:00.312,042] <inf> bt_hci_core: Identity: 11:22:33:44:55:66 (public)
[00:00:00.312,103] <inf> bt_hci_core: HCI: version 5.4 (0x0d) revision 0x0000, manufacturer 0x05f1
[00:00:00.312,133] <inf> bt_hci_core: LMP: version 5.4 (0x0d) subver 0xffff
Bluetooth initialized
Advertising successfully started
Connected

And there's no further output until I ctrl-c the WHAD chain. Unfortunately though that's not very telling, because it seems this sample isn't particularly verbose. Even when I connect with nRF Connect, I don't see any further output after "Connected", even when GATT enum/reads succeed.

I also now see with Sniffle that wble-spawn is advertising the fake peripheral from HCI1 interface 11:22:33:44:55:66, and I can see the device in the nRF Connect phone app.

So then I connect to the fake peripheral with nRF Connect, but it fails to enumerate GATT. I captured a Sniffle pcap off to the side and it looks to me like the nRF Connect is perhaps not proceeding because the fake peripheral isn't responding to the MTU exchange that the legitimate central is trying to do:

image

Those are all the packets that are seen, and then the nRF Connect times out.

I've attached the log files for this sequence too
wble-connect.log
wble-spawn.log

I think I've managed to reproduce the problem you're facing, and figured out what's happening here. I still did not have time to test the exact same setup you mention in your previous comment (and jeez, thank you anyway to provide such details and procedure to reproduce this issue) but I guess I stumbled upon the same problem with a device of mine that sends PDUs right after a Central device connects to it.

The root cause was that once wble-connect connects to a target device, this device sends some PDUs (MTU exchange or even GATT requests in my case) that comes just before the second tool (chained with a pipe, say wble-spawn for instance) connects to the Unix server socket created by the first one. Problem is (or was) that wble-connect uses its own BLE stack to handle these PDUs while it should relay these to the second tool directly. I made some modifications to wble-connect and to WHAD's default connector class (whad.device.WhadDeviceConnector) to avoid this behavior and it seems to have greatly improved the stability of this Man-in-the-Middle attack.

I've tested it against the same device I own and I had no more issues while performing a MitM in both ways (wble-connect | wble-spawn and wble-spawn | wble-connect). Like I said, I've only tested it with some of my devices, I should find some time to flash a nRF52 with Zephyr and check that everything works fine with my fix.

However, I had some other issues when the target device disconnects as my ButteRFly dongle started sending BLE advertisements caught by my wireshark instance, polluting everything... Maybe some further improvements are needed in this specific case to handle it properly.

I also noted your point that the MitM chain displays nothing in the terminal, and that it could be very helpful when debugging what's going on with a MitM attack. For now, I'd be very happy if my fix solves your MitM issues (you can check with the latest dev branch, commit e0ecf46, to see if it's better). Will have a look at a more verbose output and graceful exiting later ๐Ÿ˜„.

I took some time to test with Zephyr's peripheral_hr example and managed to reproduce the bug as well. I found out another bug in ble-spawn, it was supposed to put received packets in a temporary queue when no connection to the fake device is established but in fact it didn't. I suppose this missing feature has been left without a comment and we forgot to implement it.

I made some modifications to make it work, tried it with the Zephyr HR example peripheral and it worked well. Not sure how the other modifications I made previously impact this result, but things are it seems to work OK.

Modifications have been pushed into branch bugfix/issue-52, if you want to give it a try. I will also add some warnings in case a user tries to perform a MitM with two different devices, i.e. one that supports raw BLE packets while the other does not.

I've just added some code to display a warning message when ble-spawn is used in a configuration where BLE control PDUs may be lost due to the inability of one interface to send control PDUs (does not support raw BLE PDU sending/receiving). Code has been pushed into bugfix/issue-52.

jsmif commented

When I go to recreate this, I encounter a couple issues, though I must prefix them by saying that I updated my Zephyr to the latest version, and the HR example code is slightly different (it now blinks the LED while waiting for connect and stops when it's connected for instance. I don't know what else might have changed.)

*** Booting Zephyr OS build v3.7.0-3858-g44d101acabb5 ***
[00:00:00.315,032] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
[00:00:00.315,063] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
[00:00:00.315,093] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 3.7 Build 99
[00:00:00.315,734] <inf> bt_hci_core: Identity: 11:22:33:44:55:66 (public)
[00:00:00.315,765] <inf> bt_hci_core: HCI: version 5.4 (0x0d) revision 0x0000, manufacturer 0x05f1
[00:00:00.315,795] <inf> bt_hci_core: LMP: version 5.4 (0x0d) subver 0xffff
Bluetooth initialized
Starting Legacy Advertising (connectable and scannable)
Advertising successfully started
Checking LED device...done.
Configuring GPIO pin...done.
Start blinking LED...
Connected
Stop blinking LED.
Disconnected, reason 0x15 
Starting Legacy Advertising (connectable and scannable)
Start blinking LED...
Connected
Stop blinking LED.
Disconnected, reason 0x08 

Second, I'm in the correct commit according to git log:

commit 5a7c3a005eba2b0a6068bc3d966054437e17e96d (HEAD -> bugfix/issue-52, origin/bugfix/issue-52)

The first issue is that the connect didn't always work, as evidenced by the fact that I was sniffing with Sniffle and didn't see it stop advertising as I would expect. But let's set that aside for the moment, because that was transient and could be fixed with just trying again. (It actually makes me like the new peripheral_hr behavior, because I can quickly tell whether WHAD connected successfully based on whether the nrf52480 dongle stops blinking or not.)

The second issue is that I noticed in Sniffle that the wble-spawn fake advertisements didn't look like the real advertisements, despite getting a successful ZP_HR.json file with the latest version, in case that mattered. That file attached here:
(Logs won't attach for some reason...)

Example of what the ADV_IND and SCAN_RSP look like to Sniffle from wble-spawn:

Timestamp: 69.996364  Length:  8  RSSI: -32  Channel: 37  PHY: 1M  CRC: 0x5A3446
Ad Type: ADV_IND
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 6
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  00 06 66 55 44 33 22 11                           ..fUD3".

Timestamp: 69.996968  Length:  8  RSSI: -32  Channel: 37  PHY: 1M  CRC: 0x043264
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 6
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  04 06 66 55 44 33 22 11                           ..fUD3".

Example of the correct ADV_IND and SCAN_RSP:

Timestamp: 0.428161  Length: 19  RSSI: -32  Channel: 37  PHY: 1M  CRC: 0xBA733D
Ad Type: ADV_IND
ChSel: 1 TxAdd: 0 RxAdd: 0 Ad Length: 17
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  20 11 66 55 44 33 22 11  02 01 06 07 03 0d 18 0f   .fUD3".........
0x0010:  18 0a 18                                          ...

Timestamp: 0.428853  Length: 33  RSSI: -32  Channel: 37  PHY: 1M  CRC: 0x30A051
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 31
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  04 1f 66 55 44 33 22 11  18 09 5a 65 70 68 79 72  ..fUD3"...Zephyr
0x0010:  20 48 65 61 72 74 72 61  74 65 20 53 65 6e 73 6f   Heartrate Senso
0x0020:  72  

So basically it's like the packets are getting truncated.

And so the 3rd issue is that I consequently can't connect to the fake device. E.g. I can't connect in phone apps because they don't see a name, and bluetoothctl doesn't even register its existence via "scan on" for me to connect with "connect 11:22:33:44:55:66" (fun random fact: however WHAD is using the HCI adapters, it seems to be taking exclusive control of them somehow? I couldn't even do bluetoothctl until I connected a 3rd dongle since the first 2 were apparently taken by WHAD.)

Can you confirm you're not seeing truncated advertisements w/ Sniffle?

I've also attached my latest log files:
(Logs won't attach for some reason...)

jsmif commented

attaching logs for above from different VM...
logs.tar.gz

This Zephyr example is getting more and more interesting ๐Ÿ˜ƒ ! I managed to reproduce this issue with the advertising data sent by wble-spawn and identified its cause... Well, I might have forgotten to pass this information to the BLE peripheral class ๐Ÿ™„ (in wble-spawn). I've just pushed a fix into the bugfix/issue-52 branch to solve this.

The first bug you mention seems weird, could worth it to investigate it further (which device are you using to initiate a BLE connection ?). The third one looks normal to me, as HCI devices cannot be shared between WHAD and the Bluetooth service. WHAD takes full control of the HCI adapter while running, and control is given back to the Bluetooth service once the process is terminated. (This is quite annoying I agree, I did not count the numerous times I've disconnected my bluetooth headset while testing WHAD ...)

jsmif commented

git pulled to get latest, but I still only see truncated ADV_IND and SCAN_RSP

sudo python3 sniff_receiver.py -s /dev/ttyUSB1 -e -o bla1.pcap -A -m 11:22:33:44:55:66
[sudo] password for user: 
Timestamp: 0.711898  Length:  8  RSSI: -32  Channel: 37  PHY: 1M  CRC: 0x5A3446
Ad Type: ADV_IND
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 6
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  00 06 66 55 44 33 22 11                           ..fUD3".

Timestamp: 0.712503  Length:  8  RSSI: -32  Channel: 37  PHY: 1M  CRC: 0x043264
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 6
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  04 06 66 55 44 33 22 11                           ..fUD3".

This can also be confirmed by running spawn w/o the connect as wble-spawn --log debug --log-file ./wble-spawn.log -i hci1 -p ZP_HR.json | wshark:

(Note: I briefly got confused that it was working when I switched to only running wble-spawn, but that was because I was actually picking up the true device's advertisements. So to test just wble-spawn one needs to make sure to turn off the dongle before sniffing.)

I am using the CSR8510 A10 USB dongle with address AA:DD:AA:DD:AA:DD to initiate the connection.

That's weird. I tried exactly the same using wble-spawn to create a spoofed device and used nRF Connect on my smartphone to have a look at the advertising data and they are exactly the same (I'm using the old example from Zephyr).

Screenshot_20241009-154558_nRF Connect

In the screenshot above, 11:22:33:44:55:77 is the Zephyr HR example running on a dedicated nRF52 USB dongle while the 11:22:33:44:55:66 device is the one spawned with wble-spawn (using a CS4 USB dongle and a spoofed BD address).

jsmif commented

Do you have Sniffle (or other sniffer) capability in order to check the raw ADV_IND/SCAN_RSP are not truncated? It feels vaguely possible that there's caching going on when looking at phone apps, and only an OTA ground truth would eliminate that possibility.

Also, to update to the latest Zephyr you can run the below as described here.

cd zephyrproject/zephyr
git stash
git pull
west update

And then you can just re-add the bdaddr spoofing code (I'm not sure if a git stash pop will work or if the new sample is too different, but it's worth a try) and do the rebuild and reflash:

west build -p always -b nrf52840dongle_nrf52840 samples/bluetooth/peripheral_hr
nrfutil pkg generate --hw-version 52 --sd-req=0x00 --application build/zephyr/zephyr.hex --application-version 1 peripheral_hr.zip
sudo nrfutil dfu usb-serial -pkg peripheral_hr.zip -p /dev/ttyACM0

Yes, I recently bought some hardware compatible with Sniffle with the idea to port Sniffle's firmware to WHAD ๐Ÿ˜„. I get this with Sniffle:

Scan Results:
================================================================================
AdvA: 11:22:33:44:55:66 (Public) Avg/Min/Max RSSI: -31.0/-31/-31 Hits: 4

Advertisement:
Timestamp: 2.554643  Length: 39  RSSI: -31  Channel: 37  PHY: 1M  CRC: 0xC18AC8
Ad Type: ADV_IND
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 37
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  00 25 66 55 44 33 22 11  02 01 06 07 03 0d 18 0f  .%fUD3".........
0x0010:  18 0a 18 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0x0020:  00 00 00 00 00 00 00                              .......

Scan Response:
Timestamp: 2.555495  Length: 33  RSSI: -31  Channel: 37  PHY: 1M  CRC: 0x30A051
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 31
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  04 1f 66 55 44 33 22 11  18 09 5a 65 70 68 79 72  ..fUD3"...Zephyr
0x0010:  20 48 65 61 72 74 72 61  74 65 20 53 65 6e 73 6f   Heartrate Senso
0x0020:  72                                                r
================================================================================

================================================================================
AdvA: 11:22:33:44:55:77 (Public) Avg/Min/Max RSSI: -33.0/-33/-33 Hits: 57

Advertisement:
Timestamp: 3.022045  Length: 19  RSSI: -33  Channel: 37  PHY: 1M  CRC: 0xB14DA4
Ad Type: ADV_IND
ChSel: 1 TxAdd: 0 RxAdd: 0 Ad Length: 17
AdvA: 11:22:33:44:55:77 (Public)
0x0000:  20 11 77 55 44 33 22 11  02 01 06 07 03 0d 18 0f   .wUD3".........
0x0010:  18 0a 18                                          ...

Scan Response:
Timestamp: 3.022737  Length: 33  RSSI: -33  Channel: 37  PHY: 1M  CRC: 0x950A2F
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 31
AdvA: 11:22:33:44:55:77 (Public)
0x0000:  04 1f 77 55 44 33 22 11  18 09 5a 65 70 68 79 72  ..wUD3"...Zephyr
0x0010:  20 48 65 61 72 74 72 61  74 65 20 53 65 6e 73 6f   Heartrate Senso
0x0020:  72                                                r
================================================================================

The only difference I can notice is this weird data sent in ADV_IND (37 bytes ?) that has some null bytes padding, but SCAN_RSP is the same.

jsmif commented

OK, I need to set up WHAD on a separate VM anyway, so I'll re-set it up and see if that makes any difference. Thank you.

And when I use a nRF52 USB dongle, I get the exact same advertising data:

Scan Results:
================================================================================
AdvA: 11:22:33:44:55:66 (NRPA) Avg/Min/Max RSSI: -31.0/-31/-31 Hits: 67

Advertisement:
Timestamp: 2.062199  Length: 19  RSSI: -31  Channel: 37  PHY: 1M  CRC: 0x210FCF
Ad Type: ADV_IND
ChSel: 0 TxAdd: 1 RxAdd: 1 Ad Length: 17
AdvA: 11:22:33:44:55:66 (NRPA)
0x0000:  c0 11 66 55 44 33 22 11  02 01 06 07 03 0d 18 0f  ..fUD3".........
0x0010:  18 0a 18                                          ...

Scan Response:
Timestamp: 2.062883  Length: 33  RSSI: -31  Channel: 37  PHY: 1M  CRC: 0x60B82F
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 1 RxAdd: 1 Ad Length: 31
AdvA: 11:22:33:44:55:66 (NRPA)
0x0000:  c4 1f 66 55 44 33 22 11  18 09 5a 65 70 68 79 72  ..fUD3"...Zephyr
0x0010:  20 48 65 61 72 74 72 61  74 65 20 53 65 6e 73 6f   Heartrate Senso
0x0020:  72                                                r
================================================================================

================================================================================
AdvA: 11:22:33:44:55:77 (Public) Avg/Min/Max RSSI: -33.0/-33/-33 Hits: 37

Advertisement:
Timestamp: 2.054029  Length: 19  RSSI: -33  Channel: 37  PHY: 1M  CRC: 0xB14DA4
Ad Type: ADV_IND
ChSel: 1 TxAdd: 0 RxAdd: 0 Ad Length: 17
AdvA: 11:22:33:44:55:77 (Public)
0x0000:  20 11 77 55 44 33 22 11  02 01 06 07 03 0d 18 0f   .wUD3".........
0x0010:  18 0a 18                                          ...

Scan Response:
Timestamp: 2.054721  Length: 33  RSSI: -33  Channel: 37  PHY: 1M  CRC: 0x950A2F
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 31
AdvA: 11:22:33:44:55:77 (Public)
0x0000:  04 1f 77 55 44 33 22 11  18 09 5a 65 70 68 79 72  ..wUD3"...Zephyr
0x0010:  20 48 65 61 72 74 72 61  74 65 20 53 65 6e 73 6f   Heartrate Senso
0x0020:  72                                                r
================================================================================

We might have an issue with HCI adapters regarding advertising data. I'll dig into it.

jsmif commented

What were you using before you tried the nRF dongle, if not HCI adapters?

I only tried with HCI adapters and a nRF52 dongle.

jsmif commented

I set it up fresh on a separate Ubuntu 24.04 VM and I see mostly the same thing, except now the truncated ADV_IND are also padded out to 0x27 bytes too (max ADV_IND size.)

Timestamp: 7.894943  Length: 39  RSSI: -31  Channel: 37  PHY: 1M  CRC: 0xB2FF98
Ad Type: ADV_IND
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 37
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  00 25 66 55 44 33 22 11  02 01 06 00 00 00 00 00  .%fUD3".........
0x0010:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0x0020:  00 00 00 00 00 00 00                              .......

Timestamp: 7.895796  Length:  8  RSSI: -31  Channel: 37  PHY: 1M  CRC: 0x043264
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 6
AdvA: 11:22:33:44:55:66 (Public)
0x0000:  04 06 66 55 44 33 22 11                           ..fUD3".

I also change the Zephyr address to end in :77 instead of :66, like you had, to avoid confusion, and also just in case there was anything at issue with the real and fake peripheral sharing an address. But that didn't make any difference.

Out of suspicion of my HCI dongle, I went and plugged in a separate one, (which I don't have the capability to spoof the address for, so I've censored it.)

hci1:	Type: Primary  Bus: USB
	BD Address: 00:E0:4C:XX:XX:XX  ACL MTU: 1021:6  SCO MTU: 255:12
	UP RUNNING 
	RX bytes:4516 acl:1 sco:0 events:308 errors:0
	TX bytes:39535 acl:0 sco:0 commands:332 errors:0
	Features: 0xff 0xff 0xff 0xfe 0xdb 0xfd 0x7b 0x87
	Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 
	Link policy: RSWITCH HOLD SNIFF PARK 
	Link mode: PERIPHERAL ACCEPT 
	Name: 'VM #2'
	Class: 0x6c0000
	Service Classes: Rendering, Capturing, Audio, Telephony
	Device Class: Miscellaneous, 
	HCI Version: 5.1 (0xa)  Revision: 0xdfc6
	LMP Version: 5.1 (0xa)  Subversion: 0xd922
	Manufacturer: Realtek Semiconductor Corporation (93)

With that alternate adapter I'm Still just seeing truncated-to-address packets when I launch wble-spawn --log debug --log-file ./wble-spawn.log -i hci1 -p ZP_HR.json | wshark

Timestamp: 1.386447  Length: 39  RSSI: -31  Channel: 37  PHY: 1M  CRC: 0xA9A4E2
Ad Type: ADV_IND
ChSel: 1 TxAdd: 0 RxAdd: 0 Ad Length: 37
AdvA: 00:E0:4C:XX:XX:XX (Public)
0x0000:  20 25 XX XX XX 4c e0 00  02 01 06 00 00 00 00 00   %.&AL..........
0x0010:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0x0020:  00 00 00 00 00 00 00                              .......

Timestamp: 1.387300  Length:  8  RSSI: -31  Channel: 37  PHY: 1M  CRC: 0x71CF41
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 6
AdvA: 00:E0:4C:XX:XX:XX (Public)
0x0000:  04 06 XX XX XX 4c e0 00                           ...&AL..

And again just to confirm I'm on the correct commit: commit e06e7fb7e9874fbb8328fd827583f6586587dbcc (HEAD -> bugfix/issue-52, origin/bugfix/issue-52)

Also out of superstition I disconnected so that the Realtek was the only connected dongle and tried again as hci0 just in case it didn't like it as hci1, but that didn't make any difference.

I also see the same results with the main branch at HEAD. Which seems suspicious. Shouldn't that be breaking in a different way if these changes haven't been merged yet?

jsmif commented

I also set it up on an Ubuntu 20.04 VM just in case it was the Ubuntu version's issue, but I see the same results:

Timestamp: 22.270463  Length: 39  RSSI: -33  Channel: 37  PHY: 1M  CRC: 0x501F8D
Ad Type: ADV_IND
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 37
AdvA: AA:DD:AA:DD:AA:DD (Public)
0x0000:  00 25 dd aa dd aa dd aa  02 01 06 00 00 00 00 00  .%..............
0x0010:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0x0020:  00 00 00 00 00 00 00                              .......

Timestamp: 22.271316  Length:  8  RSSI: -33  Channel: 37  PHY: 1M  CRC: 0x56B953
Ad Type: SCAN_RSP
ChSel: 0 TxAdd: 0 RxAdd: 0 Ad Length: 6
AdvA: AA:DD:AA:DD:AA:DD (Public)
0x0000:  04 06 dd aa dd aa dd aa                           ........

The CSR dongles are these btw: https://www.amazon.com/gp/product/B08SW48TY8/ . But I'll note that I've used them successfully with btlejack and gattacker in the past...

I've just pushed some modifications into bugfix/issue-52 to fix the unexpected ADV_IND padding and it seems to work fine on my side.

Are you sure your JSON file holds the correct data (advertising data and scan response) ? Just to make sure, as you seem to use the correct commit. Can you share this file ? Here is what mine looks like:

{
  "services": [
    {
      "uuid": "1801",
      "type_uuid": "2800",
      "start_handle": 1,
      "end_handle": 8,
      "characteristics": [
        {
          "handle": 2,
          "uuid": "2803",
          "properties": 32,
          "security": 0,
          "value": {
            "handle": 3,
            "uuid": "2A05"
          },
          "descriptors": [
            {
              "handle": 4,
              "uuid": "2902"
            }
          ]
        },
        {
          "handle": 5,
          "uuid": "2803",
          "properties": 10,
          "security": 0,
          "value": {
            "handle": 6,
            "uuid": "2B29"
          },
          "descriptors": []
        },
        {
          "handle": 7,
          "uuid": "2803",
          "properties": 2,
          "security": 0,
          "value": {
            "handle": 8,
            "uuid": "2B2A"
          },
          "descriptors": []
        }
      ]
    },
    {
      "uuid": "1800",
      "type_uuid": "2800",
      "start_handle": 9,
      "end_handle": 15,
      "characteristics": [
        {
          "handle": 10,
          "uuid": "2803",
          "properties": 2,
          "security": 0,
          "value": {
            "handle": 11,
            "uuid": "2A00"
          },
          "descriptors": []
        },
        {
          "handle": 12,
          "uuid": "2803",
          "properties": 2,
          "security": 0,
          "value": {
            "handle": 13,
            "uuid": "2A01"
          },
          "descriptors": []
        },
        {
          "handle": 14,
          "uuid": "2803",
          "properties": 2,
          "security": 0,
          "value": {
            "handle": 15,
            "uuid": "2A04"
          },
          "descriptors": []
        }
      ]
    },
    {
      "uuid": "180F",
      "type_uuid": "2800",
      "start_handle": 16,
      "end_handle": 20,
      "characteristics": [
        {
          "handle": 17,
          "uuid": "2803",
          "properties": 18,
          "security": 0,
          "value": {
            "handle": 18,
            "uuid": "2A19"
          },
          "descriptors": [
            {
              "handle": 19,
              "uuid": "2902"
            }
          ]
        }
      ]
    },
    {
      "uuid": "180A",
      "type_uuid": "2800",
      "start_handle": 21,
      "end_handle": 25,
      "characteristics": [
        {
          "handle": 22,
          "uuid": "2803",
          "properties": 2,
          "security": 0,
          "value": {
            "handle": 23,
            "uuid": "2A24"
          },
          "descriptors": []
        },
        {
          "handle": 24,
          "uuid": "2803",
          "properties": 2,
          "security": 0,
          "value": {
            "handle": 25,
            "uuid": "2A29"
          },
          "descriptors": []
        }
      ]
    },
    {
      "uuid": "180D",
      "type_uuid": "2800",
      "start_handle": 26,
      "end_handle": 33,
      "characteristics": [
        {
          "handle": 27,
          "uuid": "2803",
          "properties": 16,
          "security": 0,
          "value": {
            "handle": 28,
            "uuid": "2A37"
          },
          "descriptors": [
            {
              "handle": 29,
              "uuid": "2902"
            }
          ]
        },
        {
          "handle": 30,
          "uuid": "2803",
          "properties": 2,
          "security": 0,
          "value": {
            "handle": 31,
            "uuid": "2A38"
          },
          "descriptors": []
        },
        {
          "handle": 32,
          "uuid": "2803",
          "properties": 8,
          "security": 0,
          "value": {
            "handle": 33,
            "uuid": "2A39"
          },
          "descriptors": []
        }
      ]
    }
  ],
  "devinfo": {
    "adv_data": "02010607030d180f180a18",
    "scan_rsp": "18095a6570687972204865617274726174652053656e736f72",
    "bd_addr": "11:22:33:44:55:77",
    "addr_type": 0
  }
}

zp_hr.json

jsmif commented

This JSON looks valid AFAICT. (BTW how did you pretty print it like that instead of having a single line entry?)

ZP_HR.json

This JSON is definitely valid, I've tested it with the latest version of wble-spawn and I saw the expected advertised values. I cannot reproduce this bug on my machine, advertising data and scan response look OK here. This is puzzling.

I used https://jsonformatter.org/ to format my JSON file, but I have no idea how it manages the data sent to this service so anonymize your data if privacy/tracking is a concern.

jsmif commented

Can you let me know the exact OS and dongles you're using? Maybe I can buy some to try and replicate your setup.

Successfully tested on Debian 12 (bookworm) with a NRF52840 USB dongle (Nordic), a CSR4 USB dongle and my laptop internal bluetooth adapter (Intel Wireless 8265).

jsmif commented

OK, since it was never tested with two CSRs, in the config where it was the Nordic and the CSR, which one was the fake central and which the fake peripheral? (Or did you try it and it worked both ways?) And is that the Nordic with the custom butterfly firmware on it, or something else?

I always rely on an HCI adapter for the fake device because the nRF52 WHAD firmware sometimes fail with my BLE5-enable smartphone, when performing a full man-in-the-middle.

I've tested wble-spawn with the following command line to check the advertised device:

$ wble-spawn -i hci0 -p zp_hr.json | wshark

I managed to get it working (advertising) with my CSR4 dongle, nRF52840 dongle running ButteRFly and my onboard Bluetooth HCI adapter. I didn't try to connect to it so far, just checking the advertised data was as expected.

Any update on this issue ? I would like to merge some bugfixes I've made in the dedicated issue branch (bugfix/issue-52) as they solve critical issues related to MitM that have been discovered while investigating this one.

jsmif commented

Sorry, I haven't had any time to get back to this (because I think I specifically need to test with ButteRFly), and I probably won't be able to for the next couple of weeks still.