裂纹在PVC板上蔓延。
轰!
吊顶连同积水一起砸落,在卫生间里激起一人高的水花。
水流瞬间冲出卫生间,涌入客厅。
我的小腿被一股冰冷的水流包裹。
客厅里所有放在地上的东西都漂浮起来。
我踉跄着穿过积水,冲到玄关,拉开电闸箱的盖子。
里面一片漆黑,总开关已经跳闸。
我尝试将它推上去,但开关立刻又弹了回来,发出一声沉闷的“咔嗒”声。
线路里有短路。
水已经没过我的膝盖。
我放弃了电闸,转身拉开房门。
楼道里的声控灯应声亮起,惨白的光照亮了门外的一切。
走廊干干净净,没有一滴水。
对门302的红色地垫,邻居放在门口的鞋架,都安然无恙。
仿佛我的房子,处于另一个被水淹没的世界。
我回到屋内,从漂浮的杂物中捞起我的笔记本电脑和手机,把它们放在唯一还干燥的餐桌上。
我拨打了物业的电话。
“您好,您拨打的电话正在通话中,请稍后再拨。”
冰冷的系统女声重复了三遍。
我挂断,再次拨打。
结果一样。
我又拨打了小区公布的24小时紧急维修电话。
这次接通了。
“喂?”一个含混不清的男声。
“你好,我是8栋901的住户,我家水管爆了,整个房子都被淹了,电也断了。”
“8栋901?”对方顿了一下,“系统里没查到你报修啊。”
“我刚打物业电话打不通。”
“哦,这样。水管爆了是吧,你先去把入户总阀关了。”
“哪个是总阀?”
“就在你家水表旁边,楼道管道井里,你自己找不到吗?”
“我不知道管道井在哪里。”
“啧,行吧,我记下了,你等着,我派人过去。”
电话被挂断。
我站在餐桌旁,水已经快要漫到桌沿。
墙上的插座不时冒出电火花,发出“噼啪”的声响。
我打开笔记本电脑,连接手机热点。
网络连接成功。
我打开浏览器,输入了“幸福一家人团购”小程序的名称。
搜索结果寥寥无几,只有一个官方的介绍页面和一个下载链接。
我点开介绍页面。
开发商是一家名为“邻里智联”的科技公司。
网站做得很简陋,只有一些宣传语和功能介绍。
“打造新型智慧社区生态。”
“一键团购,便捷生活。”
“智能家居联动,安全有保障。”
我点击“关于我们”,页面跳转到一个地址和联系方式。
地址在城西的一个软件园。
我打开地图软件,搜索这个地址。
距离我们小区三十公里。
我回到团购群。
群里还在热火朝天地讨论着今天新上架的水果。
【这个新疆的哈密瓜看着不错啊,有人买吗?】
【我买了,上次买过,特别甜。】
【张阿姨选品,质量肯定没问题!】
张惠兰没有再说话。
我点开她的头像,进入她的朋友圈。
最新一条是十分钟前发的。
九张图片,都是各种美食和笑脸。
配文是:【邻里和睦,万事兴。】
下面一排点赞和评论。
302王哥:【张阿姨说得对!】
401刘姐:【没错,远亲不如近邻。】
我关掉朋友圈,视线回到笔记本屏幕上。
我需要找到这个小程序的后台。
或者说,找到张惠兰控制这一切的那个“系统”。
我打开了浏览器的开发者工具。
切换到网络监视标签。
在手机上,我重新打开了团购小程序。
笔记本屏幕上,一连串的数据请求开始滚动。
GET, POST, PUT…
各种发往服务器的请求日志,像瀑布一样刷新。
我找到了用户登录的API接口。
api.linlizhilian.com/v1/login
返回的数据包里,包含了我的用户信息。
userID: “u-1b9d7a8c”
username: “李明”
role: “user”
communityID: “CMT-0034”
这个CMT-0034应该就是我们小区的代号。
而我的角色,只是一个普通user。
我继续在手机上操作,点开团购清单,模拟下单。
一个新的API请求出现在日志里。
POST api.linlizhilian.com/v1/order/create
请求体里,是我刚刚选择的商品ID和数量。
我往下翻看日志,试图找到与“规则”或“惩罚”相关的接口。
一条不起眼的日志引起了我的注意。
GET api.linlizhilian.com/v1/community/rules?communityID=CMT-0034
我复制了这个URL,在浏览器里打开。
屏幕上显示出一串JSON格式的数据。
{ “rules”: [ {“id”: 1, “description”: “必须参与每日团购。”}, {“id”: 2, “description”: “必须购买清单上评价最高的商品。”}, {“id”: 3, “description”: “绝对不能质疑群主。”} ], “enforcement_module”: “smart-home-control-v2.3” }
执法模块:智能家居控制。
这就是答案。
这个小程序,连接着小区的智能家居中控系统。
张惠兰的权力,就来自于这个enforcement_module。
可她是怎么获得权限的?
我继续翻找网络请求日志。
当我点开群成员列表,查看张惠兰的个人资料时,一条新的请求出现了。
GET api.linlizhilian.com/v1/user/profile?userID=u-00000001
返回的数据里,她的role字段写着一个我从未见过的词。
role: “initial_administrator”
初始管理员。
不是admin,不是super_admin,而是initial。
这个词意味着,她的权限是系统被创建时就赋予的。
唯一的,最原始的管理员。
我尝试着构造一个请求,去修改我自己的role。
我打开一个API测试工具,输入了修改用户信息的接口地址。
PUT api.linlizhilian.com/v1/user/profile/update
请求体里,我填上了我的userID,然后把role字段修改为admin。
点击发送。
服务器返回了错误信息。
{ “error”: “Permission Denied”, “code”: 403 }
权限被拒绝。
很正常的防御机制。
普通用户不能修改自己的权限。
我陷入了沉思。
既然无法从外部提升自己的权限,那有没有可能,让系统本身出错?
或者说,找到一个不需要高权限就能操作的致命漏洞。
我重新梳理着刚才看到的所有API接口。
登录,获取信息,创建订单,查看规则……
我的目光停留在了一个非常常见的接口上。
POST api.linlizhilian.com/v1/user/register
用户注册。
任何一个开放系统,注册功能都是对所有人都开放的,不需要任何权限。
问题是,注册一个新用户对我毫无帮助。
我需要的是修改我现有账户的权限。
除非……
除非注册的过程中,能做点什么。
我点开注册接口的详细信息,查看它的请求参数。
username, password, phoneNumber……
都是些常规的参数。
我往下滚动,一个可选参数引起了我的注意。
invitationCode。
邀请码。
很多系统都有这个功能,通过邀请码注册可以获得一些奖励。
但在这里,它或许有别的用处。
一个念头在我脑中闪过。
如果……这个invitationCode不只是一个普通的字符串,而是与其他用户的userID相关联呢?
如果我用张惠兰的userID作为邀请码去注册一个新用户,会发生什么?
这是一种非常古老的系统漏洞,叫做“关联账户权限提升”。
如果程序员在设计时偷懒,可能会让被邀请的新账户,继承邀请者的一部分属性。
通常是积分、会员等级之类的东西。
但万一,它继承了role呢?
我感到心跳在加速。
这是一个疯狂的猜测,但值得一试。
我立刻打开API测试工具,填好注册接口的地址。
username我随便填了一个“testuser”。
password也是。
然后在invitationCode字段,我小心翼翼地填入了张惠兰的ID。
u-00000001
我的手指悬在“发送”按钮上。
门外传来一阵脚步声,由远及近,停在了我的门口。
然后是敲门声。
咚,咚,咚。
“小李啊,在家吗?阿姨过来看看你。”
是张惠兰的声音。