From 0f326c1de7d31c461118a4f124583365a6426be8 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sat, 11 May 2019 19:50:19 +0200 Subject: WiP: wifi config is untested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- dcs8000lh-configure.py | 142 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 42 deletions(-) diff --git a/dcs8000lh-configure.py b/dcs8000lh-configure.py index 6dda245..131462f 100755 --- a/dcs8000lh-configure.py +++ b/dcs8000lh-configure.py @@ -1,65 +1,91 @@ #!/usr/bin/python3 +# +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2019 Bjørn Mork import sys -import hashlib +import argparse import base64 +import hashlib from bluepy.btle import Peripheral +VERSION = "0.01" + +# helper converting "K=V;L=W;.." strings to { "K": "V", "L": "W". ...} dicts +def kv2dict(kvstr, sep=";"): + result = {} + for x in kvstr.split(sep, 50): + (k, v) = x.split("=", 2) + result[k] = v + return result + class BleCam(object): + locked = True + def __init__(self, address, pincode): + print("Connecting to %s..." % address) self.pincode = pincode self.periph = Peripheral(address) - self.ipcamservice() + self._ipcamservice() self.name = self.periph.getCharacteristics(uuid=0x2a00)[0].read().decode() # wellknown name characteristic - self.dumpchars() - self.unlock() + print("Connected to '%s'" % self.name) - def ipcamservice(self): + def _ipcamservice(self): try: - print("getting IPCam service") + print("Verifying IPCam service") self.service = self.periph.getServiceByUUID(0xd001) + self.handles = self.service.getCharacteristics() except BTLEEException: print("no IPCam service found for %s" % periph.address) def dumpchars(self): - handles = self.service.getCharacteristics() print("%s supports these characteristics:" % self.name) - for h in handles: + for h in self.handles: print("%s - Handle=%#06x (%s)" % (h.uuid, h.getHandle(), h.propertiesToString())) def unlock(self): + if not self.locked: + return True auth = self.service.getCharacteristics(0xa001)[0] - for t in auth.read().decode().split(";", 10): - if t.startswith("C="): - self.challenge=t.split("=",2)[1] + state = kv2dict(auth.read().decode()) + + # already unlocked? + if state["M"] == 0: + self.locked = False + return True + + self.challenge = state["C"] hashit = self.name + self.pincode + self.challenge self.key = base64.b64encode(hashlib.md5(hashit.encode()).digest())[:16] try: auth.write("M=0;K=".encode() + self.key, True) + self.locked = False except: - print("write failed") + print("ERROR: failed to unlock %s - wrong pincode?" % self.name) + return not self.locked def get_ipconfig(self): - return self.service.getCharacteristics(0xa104)[0].read() + if not self.unlock(): return + return kv2dict(self.service.getCharacteristics(0xa104)[0].read().decode()) + def setup_wifi(self, essid, passwd): + for net in self.wifi_scan(): + if net["I"] == essid: + cfg = "M=" + net["M"] + ";I=" + essid + ";S=" + net["S"] + ";E=" + net["E"] + ";K=" + passwd + print("Will configure: %s" % cfg) + return True + self.service.getCharacteristics(0xa101)[0].write(cfg.encode(), True) + self.service.getCharacteristics(0xa102)[0].write("C=1".encode(), True) + return True + print("%s cannot see the '%s' network" % (self.name, essid)) + return False def wifi_scan(self): - - def _wifi_network(netstr): - keymap = { - "I": "essid", - "M": "authalg", # ?? - "C": "channel", - "S": "key_mgmt", # ?? /auth_alg/proto? - "E": "proto", # ?? - "P": "rssi", # ?? - } - result = {} - for x in netstr[2:].split(",", 10): - (k, v) = x.split("=", 2) - result[keymap[k]] = v - return result - + def _wifi2dict(wifistr): + return kv2dict(wifistr[2:], ",") + + if not self.unlock(): return + print("%s is scanning for WiFi networks..." % self.name) scan = self.service.getCharacteristics(0xa100)[0] p = -1 n = 0 @@ -71,26 +97,58 @@ class BleCam(object): return n = int(t[0].split("=",2)[1]) p = int(t[1].split("=",2)[1]) - print("read page %d of %d" % (p, n)) - return map(_wifi_network, result.split("&", 50)) + # print("read page %d of %d" % (p, n)) + return map(_wifi2dict, result.split("&", 50)) def run_command(self, command): + if not self.unlock(): return run = "P=" + self.pincode + ";N=" + self.pincode + "&&(" + command + ")&" + if len(run) > 128: + print("ERROR: command is too long") + return + print("Attempting to run '%s' on %s by abusing the 'set admin password' request" % (command, self.name)) try: self.service.getCharacteristics(0xa201)[0].write(run.encode(), True) except: - print("failed") + print("ERROR: Failed - is the admin password different from the pincode?") if __name__ == '__main__': - if len(sys.argv) < 3: - print("Usage: {} ".format(sys.argv[0])) - sys.exit(1) - - cam = BleCam(sys.argv[1], sys.argv[2]) - print("ip config is: %s" % cam.get_ipconfig()) - for network in cam.wifi_scan(): - print(network) - if len(sys.argv) > 3: - cam.run_command(sys.argv[3]) + parser = argparse.ArgumentParser(description="IPCam Bluetooth configuration tool.") + parser.add_argument("address", help="IPCam Bluetooth MAC address (01:23:45:67:89:AB)") + parser.add_argument("pincode", help="IPCam PIN Code (6 digits)") + #parser.add_argument("-v", "--verbose", help="Verbose output", action="store_true") + parser.add_argument("--essid", help="Connect to this WiFi network") + parser.add_argument("--wifipw", help="Password for ESSID") + parser.add_argument("--survey", help="List WiFi networks seen by the IPCam", action="store_true") + parser.add_argument("--ipconfig", help="Print current IP configuration", action="store_true") + parser.add_argument("--command", help="Run command on IPCam") + parser.add_argument("--telnetd", help="Start telnet server on IPCam", action="store_true") + parser.add_argument("--lighttpd", help="Start web server on IPCam", action="store_true") + parser.add_argument("--attrs", help="Dump IPCam GATT characteristics", action="store_true") + parser.add_argument("-V", "--version", action="version", version="%(prog)s " + VERSION) + args = parser.parse_args() + cam = BleCam(args.address, args.pincode) + if args.essid: + wifiok = cam.setup_wifi(args.essid, args.wifipw) + if args.ipconfig: + print("ip config is: %s" % cam.get_ipconfig()) + if args.survey: + for network in cam.wifi_scan(): + print(network) + if args.command: + cam.run_command(args.command) + if args.telnetd: + print("Adding the 'admin' user as an alias for 'root'") + cam.run_command("grep -Eq ^admin: /etc/passwd||echo admin:x:0:0::/:/bin/sh >>/etc/passwd") + print("Setting the 'admin' user passwrd to '%s'"% args.pincode) + cam.run_command("echo admin:" + args.pincode + "|chpasswd") + print("Starting telnetd") + cam.run_command("pidof telnetd||telnetd") + if args.lighttpd: + cam.run_command("/etc/rc.d/init.d/extra_lighttpd.sh start") + if args.attrs: + cam.dumpchars() + + print("Done.") -- cgit v1.2.3