某社区网络门禁协议解析

 

准备

  • HttpCanary
  • Frida
  • Mixplorer(用于浏览 SQLite 数据库)

思路

首先用 HttpCanary 抓包,过滤掉无用域名后,定位到一个 UDP 连接,在明文数据中可以明显看到 UNLOCK 字样,并得知采用 SIP 协议通信

逆向,根据 SIP 服务器地址找到相关类,使用 Frida 对该类 hook,可以得到该协议使用的用户名与密码

安全问题1:该协议使用的密码与 App 账户密码相同,因此可断定后端为明文存储密码

安全问题2:该 App 保留了调试用的 log 输出,只是通过两处 bool 值判断是否输出,存在暴露 log 的问题

开源问题1:该协议用到了开源库pjsip,但没有遵循 GPL 2 协议(就连署名都没有,你脸呢?)

然后就是踩坑环节了

由于这个协议不算罕见,开始时想到的是去找轮子,却发现没有支持 Python 3 的轮子,所以卡了一段时间,最后决定不用轮子直接当 UDP 协议来写,请求头之类的全部复制过来(面向过程😆)

其中一个比较麻烦的点就是 Digest 认证,虽然严格按照规范写一遍也不算难

首次发送后,没有像预期一样收到响应,看着每次请求都不同的 OperateId,我又走了一条死路想,这个 UUID 会不会是与时间相关的哈希之类的东西?

回到逆向那边,搜索,最终找到的却只有randomUUID

看回模拟的代码,这个 ID 用的是硬编码的固定值,那是不是——随机才行?

改成 uuid.uuid4(),发送,五秒后——门开了

🤔

由于这个 App 可以打开多处的门禁,下一个问题就是如何指定目标门禁

之前看 logcat 的时候,偶然发现了含有“lock number”的一行,对应的就是 SIP 协议中的号码(这个本来也是硬编码的固定值),又发现对于不同的门禁,该号码也不同

去翻/data/data/包名/databases,在某一个数据库里找到了完整的对应列表,复制,运行时读取,第二个问题也解决了

termux-api里的UI.radio装饰一下,搞个还过得去的用户界面,就能拿来用了

顺便吐槽下这个 App,肉眼可见的 SDK 就有至少十个,一半的内容都是购物,6KiB 就能实现的功能搞成了一个 60+ MiB 的软件

总结

走了一大堆弯路,但还是挺有意思的经历就是了

大概就是,抓住每一处细节,多去翻databases这类有敏感信息的文件,别在一些随机的东西上纠结太久

以及,没有轮子可以不用轮子,轮子不是用来给你制造麻烦的👎