查看原文
其他

智能家居设备的另一种打开方式——如何控制局域网中的小米设备

物联网安全 绿盟科技研究通讯 2021-03-12


近两年,物联网技术发展迅猛,各样的智能设备渐渐地走进了我们的家居生活。在众多的智能设备厂商中,小米是较早的布局智能家居生态的厂商,购买智能家居设备的用户几乎都会有一到两个小米设备。那么是否可以控制这些小米设备呢,其中过程是否会有安全风险呢?本文接下来会主要介绍这些内容。


具体地,除了米家app控制小米设备外,小米还提供一种局域网控制的方式,但前提是要获得用于设备认证一串字符串(即token),所以接下来主要介绍如何获取设备token,以及如何实现局域网控制设备。

一.  总体流程介绍

在同一局域网下,小米设备可以使用专有的加密UDP网络协议miio协议通信控制。在网络可达的前提下,向小米设备发送一串hello bytes即可获得含有token的结构体数据。之后,构造相应的结构体,并以同样的方式发送给设备即可实现控制。具体流程如下图所示:

 

二.  小米设备token获取

小米设备的token获取有三种途径,如下所述:

1miio获取token

miio有基于Python实现的库,其Github项目地址为:https://github.com/rytilahti/python-miio。该项目支持所有兼容miio协议的设备,并将设备发现、识别和控制的方法进行了分类。

>>>>

1.1  环境安装

python-miio需要Python3.5以上版本上才能运行,所以首先搭建Python环境。下面,我们在操作系统为Ubuntu的电脑或者树莓派中安装Python3.5:

  • 安装Python3.5依赖(本机存在的会忽略)

sudo apt-get install build-essential libsqlite3-dev sqlite3 bzip2 libbz2-dev libssl-dev openssl libgdbm-dev liblzma-dev libreadline-dev libncursesw5-dev

  • 编译安装Python3.5

wget https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tgz

tar zxvf Python-3.5.2.tgz

cd ./Python-3.5.2

./configure --prefix=/usr/bin/python3.5

sudo make

sudo make install

  • 编译后运行一下python3.5,结果如下证明安装成功

sean@ubuntu:~/Desktop/week/ProcessAndDeadline$ python3.5

Python 3.5.2 (default, Nov 23 2017, 16:37:01) 

[GCC 5.4.0 20160609] on linux

Type "help", "copyright", "credits" or "license" for more information.

>>> 

  • 安装miio库,下载库代码到本地并安装

git clone https://github.com/rytilahti/python-miio

cd python-miio/

python3.5 setup.py install



>>>>

1.2  通过脚本获取token

下面就以小米智能插座为例,说明如何获取该设备的token。

  • 脚本编写

首先要保证获取token的客户端要与插座网络可达。为了显示直观,我们将主要实现代码从库中提取出来(如下)。将文件放在python-miio/miio目录下(该脚本主要就是使用socket向设备ip的54321端口发送固定字符串,返回值即为设备token):

#-*-coding:utf8-*-

import codecs

import socket

from protocol import Message

helobytes = bytes.fromhex('21310020ffffffffffffffffffffffffffffffffffffffffffffffffffffffff')

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

s.sendto(helobytes, ('192.168.42.17', 54321))#插座ip,端口54321

data, addr = s.recvfrom(1024)

m = Message.parse(data)

tok = codecs.encode(m.checksum, 'hex')

print(m)

print(tok)

  • 运行:python3.5 miio_test.py

返回如下消息结构,其中checksum即为设备的token,解码之后为:'1d0616858062b8f836ebcacc98e62dd2'。

root@raspberrypi:~/python-miio/miio# python3.5 miio_test.py

Container:

    data = Container:

        offset2 = 32

        offset1 = 32

        length = 0

        value =  (total 0)

        data =  (total 0)

    header = Container:

        offset2 = 16

        offset1 = 0

        length = 16

        value = Container:

            length = 32

            unknown = 0

            device_id = \x03\xa9\x84\xb6 (total 4)

            ts = 1970-01-22 22:10:05

        data = !1\x00 \x00\x00\x00\x00\x03\xa9\x84\xb6\x00\x1c\xe7= (total 16)

    checksum = \x1d\x06\x16\x85\x80b\xb8\xf86\xeb\xca\xcc\x98\xe6-\xd2 (total 16)

 b'1d0616858062b8f836ebcacc98e62dd2'

支持这种方式拿token的还有小米的空气净化器、净水器、扫地机器人、智能插座插线板等。具体列表见https://github.com/rytilahti/python-miio。

2从米家app获取token

如果能用上述的探测方法获取token还是比较便捷的,但目前只有部分小米设备支持。接下来还有一种方法可以直接从app获取token。以小米绿米网关为例:首先下载米家app,将绿米网关配置入网后,点击网关设备。接下来步骤如下组图,最后的密码即为网关的token。

 

 目前绿米的这种设计模式是用户友好的,而且设备的所有者还可以选择是否开放局域网控制以及刷新控制token的有效性,比较安全。个人还是很希望小米的其他设备同样开放app侧获取设备token,因为毕竟获取需要搭建复杂的环境以及调试代码,大部分使用者应该不能接受的。

3从数据库获取token

该方法是读取手机中米家的app中的数据记录来获取设备的token,具体步骤如下:

1.准备一部获取root权限的安卓手机

2.安装米家app并登录账号

3.进入/data/data/com.xiaomi.smarthome/databases/

4.拷贝miio2.db,下载到电脑

5.前往网站(http://miio2.yinhh.com/),上传miio2.db,点击提交,即可获得token。

因为没有root的安卓手机,笔者没有具体测试这种方式获取token的有效性,具体可以参考这篇文章(https://homekit.loli.ren/docs/show/12)

三.  控制小米WiFi插座

如果获得了token,就能对小米的设备进行操作,接下来介绍使用miio协议控制小米插座的主要步骤。

1控制脚本编写

基于1.1.2获取到的回传的token信息,构造如下数据结构,用来控制设备。

ts = m.header.value.ts+datetime.timedelta(seconds=1)

cmd = {'id':1,'method':'set_power', 'params': ['on']}

header = {'length': 0, 'unknown': 0x00000000,                   

          'device_id': device_id, 'ts': ts}

msg = {'data': {'value': cmd},

       'header': {'value': header},

       'checksum': 0}

其中:

token为获取到的设备token;

device_id为获取token返回结构中的device_id字段;

ts是一个时间结构,控制传的ts的需要在获取到ts基础上加1秒;

cmd中的method包括:set_power(控制开关)、get_prop(获取状态),控制的params是['on']/ ['off'],获取状态的params是['power', 'temperature']

下面的代码是实现打开插座的控制,其中插座的IP为192.168.42.17。

m0 = Message.build(msg, token=m.checksum)

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

s.sendto(m0,('192.168.42.17', 54321))

data, addr = s.recvfrom(1024)

m1 = Message.parse(data,token=tok)

print (m1)


2执行控制脚本

执行上面编写的控制代码,得到返回状态,如果返回状态中value变量中的result字段为[ 'ok'],即为控制成功。当cmd使用get_prop方法时,如果返回的value变量值为{'id': 1, 'result': ['on', 59]}时,表示插座状态是开的,温度为59℃。因为篇幅关系具体就不贴了。

root@raspberrypi:~/python-miio/miio# python3.5 miio_test.py

Container:

    data = Container:

        value = {'id': 1, 'result': ['ok']}

        data = \x03\x88T\x86\x1a\xbd\xb5\xb24.\xdcm\xdb\xc3\xb4\xdb\x0e7\x80JR\x0e\xda\xa987\x91Q\xd0\xee\x9bV (total 32)

        offset2 = 64

        offset1 = 32

        length = 32

    header = Container:

        value = Container:

            length = 64

            unknown = 0

            device_id = \x03\xa9\x84\xb6 (total 4)

            ts = 1970-01-22 20:40:38

        data = !1\x00@\x00\x00\x00\x00\x03\xa9\x84\xb6\x00\x1c\xd2F (total 16)

        offset2 = 16

        offset1 = 0

        length = 16

    checksum = \xfc\xe2\r\x8b\xd9\xb6\x1d\xfda\xd5\x11\x04\xe1b\xbe\xfd (total 16)


四.  总结

从目前的智能家居市场来看,用户不会只使用单个智能设备厂商的设备,所以对于厂商来说,通过开放接口给用户一些局域网的控制“自由”,实现不同厂商设备的联动是一个不错的选择。

从另外一个角度,本文中体现的安全问题我们也不容忽视。如果如2.1所示在局域网中不经过认证就能获取物联网设备的访问凭证,并进而进行控制,无形中给入侵者留了一扇门。例如,攻击者可经过扫描互联网发现家庭路由器,并利用弱口令或设备漏洞获得路由器的shell权限,接下来就可按照文中步骤就可以获得设备token进而控制。

在接下来的文章中,我们会给大家介绍一些智能家居的平台,以及家庭环境中智能设备的一些安全防护方法,让智能与安全同行。


内容编辑:物联网安全实验室  桑鸿庆  责任编辑:肖晴

期回顾

本公众号原创文章仅代表作者观点,不代表绿盟科技立场。所有原创内容版权均属绿盟科技研究通讯。未经授权,严禁任何媒体以及微信公众号复制、转载、摘编或以其他方式使用,转载须注明来自绿盟科技研究通讯并附上本文链接。

关于我们


绿盟科技研究通讯由绿盟科技创新中心负责运营,绿盟科技创新中心是绿盟科技的前沿技术研究部门。包括云安全实验室、安全大数据分析实验室和物联网安全实验室。团队成员由来自清华、北大、哈工大、中科院、北邮等多所重点院校的博士和硕士组成。

绿盟科技创新中心作为“中关村科技园区海淀园博士后工作站分站”的重要培养单位之一,与清华大学进行博士后联合培养,科研成果已涵盖各类国家课题项目、国家专利、国家标准、高水平学术论文、出版专业书籍等。

我们持续探索信息安全领域的前沿学术方向,从实践出发,结合公司资源和先进技术,实现概念级的原型系统,进而交付产品线孵化产品并创造巨大的经济价值。

长按上方二维码,即可关注我们

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存