securing/gattacker

TypeError on connecting a target device because subscriptions is null

sh-noma opened this issue · 1 comments

Hi

Here is a issue report.

  • Description:

    • When advertise.js connects a target devices, node crashes because the subscriptions is null.
    • I'm not sure that I followed proper steps to set up.
      • What kind of timing should I operate to call the onSubscribe event before notify is called ?
      • Is this correct behavior for any devices?
    • Hook function is not defined.
  • Workaround:

    • To check if subscriptions is null in advertise.js, but this is just workaround.
  • Version: current version (git clone)

  • OS: Ubuntu 16.04 LTS (on Virtual Box)

  • Here is the console logs:

$ sudo node ws-slave.js 
GATTacker ws-slave
ws -> connection
ws -> send: {"type":"stateChange","state":"poweredOn"}
ws -> message: {"action":"macAddress"}
ws -> send: {"type":"macAddress","macAddress":"00:1a:7d:da:71:13"}
ws -> message: {"action":"initialize","peripheralId":"b4994c3a2823","servicesJsonData":[{"uuid":"1800","name":"Generic Access","type":"org.bluetooth.service.generic_access","startHandle":1,"endHandle":11,"characteristics":[{"uuid":"2a00","name":"Device Name","properties":["read"],"value":"53656e736547697a2d46494e4439313936","descriptors":[],"startHandle":2,"valueHandle":3,"asciiValue":"SenseGiz-FIND9196"},{"uuid":"2a01","name":"Appearance","properties":["read"],"value":"0000","descriptors":[],"startHandle":4,"valueHandle":5,"asciiValue":"  "},{"uuid":"2a02","name":"Peripheral Privacy Flag","properties":["read","write"],"value":"00","descriptors":[],"startHandle":6,"valueHandle":7,"asciiValue":" "},{"uuid":"2a03","name":"Reconnection Address","properties":["write"],"value":"","descriptors":[],"startHandle":8,"valueHandle":9},{"uuid":"2a04","name":"Peripheral Preferred Connection Parameters","properties":["read"],"value":"5000a0000000e803","descriptors":[],"startHandle":10,"valueHandle":11,"asciiValue":"P       "}]},{"uuid":"1801","name":"Generic Attribute","type":"org.bluetooth.service.generic_attribute","startHandle":12,"endHandle":15,"characteristics":[{"uuid":"2a05","name":"Service Changed","properties":["indicate"],"value":"","descriptors":[{"handle":15,"uuid":"2902","value":""}],"startHandle":13,"valueHandle":14}]},{"uuid":"180a","name":"Device Information","type":"org.bluetooth.service.device_information","startHandle":16,"endHandle":34,"characteristics":[{"uuid":"2a23","name":"System ID","properties":["read"],"value":"23283a00004c99b4","descriptors":[],"startHandle":17,"valueHandle":18,"asciiValue":"#(:  L  "},{"uuid":"2a24","name":"Model Number String","properties":["read"],"value":"00","descriptors":[],"startHandle":19,"valueHandle":20,"asciiValue":" "},{"uuid":"2a25","name":"Serial Number String","properties":["read"],"value":"46696e64203931393600","descriptors":[],"startHandle":21,"valueHandle":22,"asciiValue":"Find 9196 "},{"uuid":"2a26","name":"Firmware Revision String","properties":["read"],"value":"312e3000","descriptors":[],"startHandle":23,"valueHandle":24,"asciiValue":"1.0 "},{"uuid":"2a27","name":"Hardware Revision String","properties":["read"],"value":"302e3900","descriptors":[],"startHandle":25,"valueHandle":26,"asciiValue":"0.9 "},{"uuid":"2a28","name":"Software Revision String","properties":["read"],"value":"312e3000","descriptors":[],"startHandle":27,"valueHandle":28,"asciiValue":"1.0 "},{"uuid":"2a29","name":"Manufacturer Name String","properties":["read"],"value":"53656e736547697a00","descriptors":[],"startHandle":29,"valueHandle":30,"asciiValue":"SenseGiz "},{"uuid":"2a2a","name":"IEEE 11073-20601 Regulatory Certification Data List","properties":["read"],"value":"fe006578706572696d656e74616c","descriptors":[],"startHandle":31,"valueHandle":32,"asciiValue":"  experimental"},{"uuid":"2a50","name":"PnP ID","properties":["read"],"value":"010d0000001001","descriptors":[],"startHandle":33,"valueHandle":34,"asciiValue":"       "}]},{"uuid":"1803","name":"Link Loss","type":"org.bluetooth.service.link_loss","startHandle":35,"endHandle":37,"characteristics":[{"uuid":"2a06","name":"Alert Level","properties":["read","write"],"value":"00","descriptors":[],"startHandle":36,"valueHandle":37,"asciiValue":" "}]},{"uuid":"1802","name":"Immediate Alert","type":"org.bluetooth.service.immediate_alert","startHandle":38,"endHandle":40,"characteristics":[{"uuid":"2a06","name":"Alert Level","properties":["writeWithoutResponse"],"value":"","descriptors":[],"startHandle":39,"valueHandle":40}]},{"uuid":"1804","name":"Tx Power","type":"org.bluetooth.service.tx_power","startHandle":41,"endHandle":44,"characteristics":[{"uuid":"2a07","name":"Tx Power Level","properties":["read","notify"],"value":"00","descriptors":[{"handle":44,"uuid":"2902","value":""}],"startHandle":42,"valueHandle":43,"asciiValue":" "}]},{"uuid":"180f","name":"Battery Service","type":"org.bluetooth.service.battery_service","startHandle":45,"endHandle":49,"characteristics":[{"uuid":"2a19","name":"Battery Level","properties":["read","notify"],"value":"10","descriptors":[{"handle":48,"uuid":"2902","value":""},{"handle":49,"uuid":"2908","value":""}],"startHandle":46,"valueHandle":47,"asciiValue":" ","hooks":{"dynamicWrite":"hookDynamicWrite","dynamicNotify":"hookDynamicNotify"}}]},{"uuid":"f000ffc004514000b000000000000000","name":null,"type":null,"startHandle":50,"endHandle":65535,"characteristics":[{"uuid":"f000ffc104514000b000000000000000","name":null,"properties":["writeWithoutResponse","write","notify"],"value":"","descriptors":[{"handle":54,"uuid":"2901","value":"Img Identify"},{"handle":53,"uuid":"2902","value":""}],"startHandle":51,"valueHandle":52},{"uuid":"f000ffc204514000b000000000000000","name":null,"properties":["writeWithoutResponse","write","notify"],"value":"","descriptors":[{"handle":58,"uuid":"2901","value":"Img Block"},{"handle":57,"uuid":"2902","value":""}],"startHandle":55,"valueHandle":56}]}],"keepConnected":true}
ws -> send: {"type":"initializeStatus","peripheralId":"b4994c3a2823","status":"JSON services received"}
ws -> send: {"type":"initializeStatus","peripheralId":"b4994c3a2823","status":"start scanning for target peripheral"}
ws -> message: {"action":"macAddress"}
ws -> send: {"type":"macAddress","macAddress":"00:1a:7d:da:71:13"}
ws -> send: {"type":"startScanning"}
ws -> send: {"type":"stopScanning"}
noble: unknown peripheral b4994c3a2823 connected!
noble: unknown peripheral b4994c3a2823 handle notify!
ws -> send: {"type":"read","peripheralId":"b4994c3a2823","serviceUuid":"180f","characteristicUuid":"2a19","data":"54","isNotification":true}
noble: unknown peripheral b4994c3a2823 disconnected!
ws -> close
ws -> send: {"type":"stopScanning"}
$ sudo node advertise.js -a devices/b4994c3a2823.adv.json
Ws-slave address: 127.0.0.1
peripheralid: b4994c3a2823
advertisement file: devices/b4994c3a2823.adv.json
EIR: 0201060702031802180418
scanResponse: 120953656e736547697a2d46494e4439313936020a00
on open
poweredOn
Noble MAC address : 00:1a:7d:da:71:13
BLENO - on -> stateChange: poweredOn

<< Notify: 180f (Battery Service) -> 2a19 (Battery Level ) : 54 (T)
/home/iotsec/node_modules/gattacker/advertise.js:254
              subscriptions[serviceUuid][uuid](modifiedData);          
                                        ^

TypeError: Cannot read property '2a19' of undefined
    at /home/iotsec/node_modules/gattacker/advertise.js:254:41
    at Object.hookDynamicNotify (/home/iotsec/node_modules/gattacker/hookFunctions/find.js:32:2)
    at wsClient.<anonymous> (/home/iotsec/node_modules/gattacker/advertise.js:251:44)
    at emitMany (events.js:127:13)
    at wsClient.emit (events.js:201:7)
    at wsClient.onRead (/home/iotsec/node_modules/gattacker/lib/ws-client.js:201:10)
    at emitMany (events.js:127:13)
    at wsClient.emit (events.js:201:7)
    at wsClient.onMessage (/home/iotsec/node_modules/gattacker/lib/ws-client.js:141:10)
    at emitTwo (events.js:106:13)
  • Here is the patch:
diff --git a/advertise.js b/advertise.js
index aec4ca0..3f9bf39 100644
--- a/advertise.js
+++ b/advertise.js
@@ -250,7 +250,9 @@ wsclient.on('notification', function(peripheralId, serviceUuid, uuid, data) {
           hookFunctions[hook.dynamicNotify](peripheralId, serviceUuid, uuid, 'notify', data , wsclient, function(err, modifiedData){
             if (modifiedData) {
               console.log('<< Notify DATA hook                                                             : '.yellow + modifiedData.toString('hex').yellow.inverse + ' (' + utils.hex2a(modifiedData.toString('hex'))+ ')');
-              subscriptions[serviceUuid][uuid](modifiedData);              
+              if (subscriptions[serviceUuid] && subscriptions[serviceUuid][uuid]) {
+                  subscriptions[serviceUuid][uuid](modifiedData);
+              }
             } else {
               console.log('<< Notify DATA hook: '.yellow + 'intercept, not forwarding'.yellow);
             }

Hi,
thanks for the report and PR!

I'm not sure I follow your setup. You wrote that "hook function is NOT defined", but the error log shows you have defined it in hookFunctions/find.js, and besides the exception occurs in if (hook.dynamicNotify) block. I will assume this was a typo and you meant "hook function IS defined".

As you have noticed, this TypeError is an effect of the fact that subscriptions table does not contain expected values. This table contains active notify subscriptions. After a client/"central device" (e.g. mobile app) which is connected to your simulated device (advertise.js) subscribes to notification of a given characteristic, the subscriptions are filled with specific callback functions to invoke every time we receive notification from original device (see onSubscribe in advertise.js).

I suppose in your case:

  • ws-client was still connected to original device, and kept active subscription to notifications (probably from previous advertise.js connections?)
  • the "central device" (mobile app?) that (re?)connected to your simulated device did not manage to subscribe yet
  • we received notification from original device
  • advertise.js did not know where to "route" the received notification - the subscriptions table was empty because the mobile app did not subscribe yet
  • ... and we have the exception

I think we can quite safely discard such notification - which came from original device, but the target mobile app did not expect it yet anyway. We may optionally display an information of this fact in console. So I would call your PR not just "workaround" but a patch. I have merged, thank you!