作者:Revan Zhang 来源:Medium近年来,硬件钱包在区块链行业中,被视为相对安全的私钥存储方式之一。私钥生成后,会存储在硬件钱包的安全芯片(Secure Element)中,仅用于对外部传入的信息进行签名。这种无法联网的封闭式硬件设计,配合禁止导出私钥的软件机制,构建起了强大的保护屏障。然而,这种安全架构也有其“软肋” — — 硬件钱包高度依赖外部客户端软件和通信通道。一旦这些外围环节被篡改,攻击者便可实施“中间人攻击”(MITM),在用户毫无察觉的情况下替换信息,造成资产损失。# 什么是中间人攻击中间人攻击是指攻击者秘密地介入通信双方之间,拦截、篡改或伪造双方的通信内容,而通信双方误以为他们在直接交互。这种攻击常见于网络监听、数据伪造、身份盗用和地址替换等场景。在加密资产领域尤其危险,因为攻击者只需替换一个地址,就可能造成巨大的资产损失。举个生活中的例子:你寄给朋友一封重要信件。一个“坏邮差”在途中截获,将信件内容进行替换,然后重新封好寄出。朋友收到信件后,此时获取的信息因为被邮差替换,已经不再准确了。在这个例子中,邮差扮演了中间人的角色,通过替换信件内容实施了中间人攻击。在区块链系统中,由于大多数公链转账只需要收款地址,而不需要额外的诸如姓名的验证。这种场景的中间人攻击更容易得手,且造成的损失往往更大,更难追回。  # 硬件钱包通信流程:看不见的邮差市面上主流的硬件钱包大致采用四种通信方式:* USB:最常见且稳定,通过数据线实现双向传输* 蓝牙:低功耗蓝牙 BLE,常用于移动端* 二维码:airgap,较为新型的方式,物理隔离,通过互相扫码达到通信的目的* NFC:近场通信,暂时较少使用其中,USB 与蓝牙最为常见。无论采用哪种方式,通信流程大致相同:1. “钱包端”发起连接,例如浏览器插件钱包或者手机 APP 钱包2. 向“硬件钱包”发送请求,例如获取硬件内部的地址,发起签名等等3. 请求经上述“通信通道”送达硬件设备4. 设备完成处理并返回响应5. “钱包端”接收并显示结果尽管硬件钱包自身是安全的“保险库”,但通信过程却依赖客户端、通信通道这一“邮差链条”。一旦“邮差”遭到劫持或者作恶,传递的数据信息就可能早已被调包。这正是“达摩克利斯之剑”悬于硬件钱包之上的隐秘威胁 — — 中间人攻击。## 实战案例:基于恶意脚本的中间人攻击## 声明* 所有代码和操作均发生在 2023 年 9 月,距本文发布时间(2025 年 6 月)已超过 20 个月。* 所有演示基于当时公开版本的官方软件,具体版本信息已在文中注明。* 所涉及的攻击方式,已于当时向相关团队披露并确认。* 本文所有内容仅供学习与安全研究之用,观点仅代表作者本人。* 本文不对由此引发的任何行为或后果承担责任。## Trezor 通信的基本流程Trezor 通过 USB 连接 Metamask 浏览器钱包时可选择通过 Trezor Bridge 进行通信,Trezor Bridge 的基本流程* 安装软件之后,会在用户的电脑中,在本地 21325 端口启动 HTTP 服务器* Metamask 连接 Trezor 硬件钱包时,会通过弹出 网页来加载 JS SDK* Trezor 的 SDK 会尝试的将基于 protobuf 的序列化数据,发送向这个本地服务器* Trezor Bridge 接收到数据后,通过 lib-usb 库查找本地满足条件的 PID VID Trezor 设备并传输数据当检测到本地已安装 Trezor Bridge 时,所有通过 网页,也就是 Trezor SDK 的序列化数据都会经由本地 Trezor Bridge 服务器转发给设备,而不使用 Web USB。## Trezor Bridge 的基本信息Trezor Bridge 使用 Golang 开发,当前用户版本为 v2.0.27。从 GitHub 开源仓库可见,v2.0.27 使用 xgo 进行跨平台编译,从而生成 MacOS,Windows 和 linux 的安装包。以 MacOS 为例,安装时会在 /Applications/Utilities/TREZOR\ Bridge/trezord 目录创建 trezord 服务器二进制文件,并在用户本地创建 com.bitcointrezor.trezorBridge.trezord.plist 文件,通过 KeepAlive 实现开机自启和进程守护。## 攻击的基本流程在 Metamask 连接 Trezor 设备时,立刻就会读取设备内部的 ETH 公钥,然后基于派生地址序号在 Metamask 软件端计算出不同路径的地址。在这个过程中,没有任何的硬件确认或者提示,这也为一个简单的中间人攻击,提供了可实现的路径。一旦 Trezor Bridge 被本地恶意软件控制,整个通信链路上就会出现一个“坏邮差”。攻击者便可将其变为一个中转服务器,劫持所有与硬件钱包的通信数据。这样,发送给硬件的数据和从硬件返回的数据都可能被篡改,造成界面显示信息与实际硬件信息不一致,一旦软件流程有漏洞或者硬件上的信息没有认真确认,中间人攻击就能操作成功。## 攻击测试* 首先安装 Trezor Suite v23.8.1、Trezor Bridge v2.0.27 和 Metamask v11.0.0* 准备两台 Trezor Model T:一台按正常流程操作,另一台用于中间人攻击测试* 首先展示正常流程:两台设备在 Trezor Suite 和 Metamask 中均能正确读取地址* 执行恶意脚本替换 trezord 进程* 第一台设备在 Metamask 中仍能正确读取地址,流程正常* 第二台设备在 Metamask 中读取的地址被中间人替换,与 Trezor Suite 和硬件显示的地址不一致## 代码分析在 Trezor Bridge 关键位置添加日志标记,记录设备通信过程中的请求体和响应体数据:SDK 读取公钥阶段的 hexbodyStr 和 hexres 在多次调用时,对同一设备的不同时间操作参数和返回结果完全相同。通过日志筛选,发现 Metamask 读取 ETH 地址时的 call 函数调用数据如下:根据日志比对,003700000000 和 000b0000001f08ac8080800808bc80808008088080808008080008002207426974636f696e 等数据,均为发送、接收报文的序列化结果。经过重放测试,在信息构建过程中并没有时间戳等参数,可以非常简单轻易的模拟攻击手法,即针对部分发送报文,硬编码返回的结果,进行结果替换:在这个例子中,我们针对性的将数据进行了替换,当接收到 SDK 获取 ETH 公钥的请求时,不再使用硬件返回的结果,而是直接使用上述代码中硬编码的结果。重新通过文档编译后制作二进制文件,同时制作守护进程等攻击必要组件。准备工作完成后,当用户不慎安装和运行恶意软件后。每当用户使用 Metamask 连接 Trezor 并且发送数据时,通过 bridge 通信的数据不再是硬件读取出来的信息,而是上述代码中硬编码的序列化数据,再基于业务 SDK 的反序列化,读取地址的信息即替换成功。完整的攻击如上述视频所示。## 与团队的沟通整理好问题及操作之后,我第一时间已与 Metamask 和 Trezor 团队进行了沟通,以下是所有沟通信息及邮件信息* Metamask 沟通记录:* Trezor 邮件沟通信息:# 问题的核心及建议## Metamask 读取完整地址,无需硬件确认硬件整体可以作为一个封闭的安全环境,所以所有信息都应以硬件为准。但是在 Metamask 的产品设计流程当中,在硬件没有任何显示的情况下,直接读取完整地址的明文信息并进行展示。Trezor SDK ethereumGetAddress 方法提供了 showOnTrezor 参数,他的作用是读取硬件的地址,并在硬件当中展示,以供用户二次确认。这是一个产品设计的取舍问题,如果每次都需要用户确认过才能读取地址,那么在批量添加地址的场景用户会非常痛苦。所以在主流的支持硬件钱包的 APP 内,同时支持静默读取地址,但是如果想要接收资产,都需要先进行地址的硬件确认,就是为了防止上述攻击场景出现。Metamask 静默读取地址之后,没有让用户确认,就直接展示了完整地址,那么首次使用的用户,一旦被攻击,就很容易中招。## 盲签的威胁,可能比我们想象中的更大按照这种攻击手段和攻击方式,我们可以得出结论:所有硬件非确定性的信息,都是理论上不安全的。所有的盲签同理,虽然盲签硬件上有确认,但是这个确认的信息对于用户来说并不可读,所以他也满足硬件非确定性的信息,无法确保不被中间人攻击。safe 多签钱包的案件就是一个典型,钱包构造的签名信息被服务器恶意 javascript 代码替换,同时因为 ledger 钱包是盲签,导致这一次攻击的发生。## 通信通道的中间人攻击,方式众多除了用户本地有恶意软件以外,一些钱包的通信和构建流程可能涉及到服务器(例如一些钱包 encodeTx 需要通过服务端进行构建)。这些都是潜在的攻击对象和被攻击点。通信通道当然还包括硬件层面,例如不安全的电脑摄像头外设,不安全的 USB 数据线等等。从物理层到应用层,在通信层面,所有环节都有被攻击的可能。## 主动提升安全意识,任重而道远客户端到硬件钱包的完整通信,核心流程理论上是不需要联网的。所以即使有端到端的加密通信,也无法 100% 确保流程安全。增加端到端的加密之后,对于真正的黑客来说,增加的只是逆向客户端,对通信信道的破解成本。而对于攻击本身,从来都不是难度的较量,而是黑客付出的成本和法律后果的考量。同时,大部分区块链的产品团队,一般在面对这类中间人攻击的威胁时,会将这部分的优化放在整个迭代中处于低优先级,变相等于允许一部分中间人攻击的风险出现。如上面的团队沟通邮件,如 Metamask 的 bug bounty 界面,就将 MITM 中间人攻击排除在外:所以对于用户来说,更需要主动我们自身提高防范意识。对于重要资产,即使是使用硬件钱包,也最好隔离电脑,不连接不可信的网络,不访问不可信的网页等等等等。# 写在最后硬件钱包的“安全神话”并非来自单一设备本身,而是建立在整个生态系统的协作安全。当我们忽视客户端的可信性、放松对通信通道的防护,那把“达摩克利斯之剑”便悄然悬于我们的资产之上。真正的安全,从不止于硬件,而在于每一个你以为“无关紧要”的细节。
硬件钱包上的达摩克利斯之剑:隐秘的中间人威胁
作者:Revan Zhang 来源:Medium
近年来,硬件钱包在区块链行业中,被视为相对安全的私钥存储方式之一。私钥生成后,会存储在硬件钱包的安全芯片(Secure Element)中,仅用于对外部传入的信息进行签名。这种无法联网的封闭式硬件设计,配合禁止导出私钥的软件机制,构建起了强大的保护屏障。
然而,这种安全架构也有其“软肋” — — 硬件钱包高度依赖外部客户端软件和通信通道。一旦这些外围环节被篡改,攻击者便可实施“中间人攻击”(MITM),在用户毫无察觉的情况下替换信息,造成资产损失。
什么是中间人攻击
中间人攻击是指攻击者秘密地介入通信双方之间,拦截、篡改或伪造双方的通信内容,而通信双方误以为他们在直接交互。
这种攻击常见于网络监听、数据伪造、身份盗用和地址替换等场景。在加密资产领域尤其危险,因为攻击者只需替换一个地址,就可能造成巨大的资产损失。
举个生活中的例子:你寄给朋友一封重要信件。一个“坏邮差”在途中截获,将信件内容进行替换,然后重新封好寄出。朋友收到信件后,此时获取的信息因为被邮差替换,已经不再准确了。
在这个例子中,邮差扮演了中间人的角色,通过替换信件内容实施了中间人攻击。
在区块链系统中,由于大多数公链转账只需要收款地址,而不需要额外的诸如姓名的验证。这种场景的中间人攻击更容易得手,且造成的损失往往更大,更难追回。
硬件钱包通信流程:看不见的邮差
市面上主流的硬件钱包大致采用四种通信方式:
其中,USB 与蓝牙最为常见。无论采用哪种方式,通信流程大致相同:
尽管硬件钱包自身是安全的“保险库”,但通信过程却依赖客户端、通信通道这一“邮差链条”。一旦“邮差”遭到劫持或者作恶,传递的数据信息就可能早已被调包。
实战案例:基于恶意脚本的中间人攻击
声明
Trezor 通信的基本流程
Trezor 通过 USB 连接 Metamask 浏览器钱包时可选择通过 Trezor Bridge 进行通信,Trezor Bridge 的基本流程
当检测到本地已安装 Trezor Bridge 时,所有通过 网页,也就是 Trezor SDK 的序列化数据都会经由本地 Trezor Bridge 服务器转发给设备,而不使用 Web USB。
Trezor Bridge 的基本信息
Trezor Bridge 使用 Golang 开发,当前用户版本为 v2.0.27。
从 GitHub 开源仓库可见,v2.0.27 使用 xgo 进行跨平台编译,从而生成 MacOS,Windows 和 linux 的安装包。
以 MacOS 为例,安装时会在 /Applications/Utilities/TREZOR\ Bridge/trezord 目录创建 trezord 服务器二进制文件,并在用户本地创建 com.bitcointrezor.trezorBridge.trezord.plist 文件,通过 KeepAlive 实现开机自启和进程守护。
攻击的基本流程
在 Metamask 连接 Trezor 设备时,立刻就会读取设备内部的 ETH 公钥,然后基于派生地址序号在 Metamask 软件端计算出不同路径的地址。
在这个过程中,没有任何的硬件确认或者提示,这也为一个简单的中间人攻击,提供了可实现的路径。
一旦 Trezor Bridge 被本地恶意软件控制,整个通信链路上就会出现一个“坏邮差”。攻击者便可将其变为一个中转服务器,劫持所有与硬件钱包的通信数据。这样,发送给硬件的数据和从硬件返回的数据都可能被篡改,造成界面显示信息与实际硬件信息不一致,一旦软件流程有漏洞或者硬件上的信息没有认真确认,中间人攻击就能操作成功。
攻击测试
代码分析
在 Trezor Bridge 关键位置添加日志标记,记录设备通信过程中的请求体和响应体数据:
SDK 读取公钥阶段的 hexbodyStr 和 hexres 在多次调用时,对同一设备的不同时间操作参数和返回结果完全相同。通过日志筛选,发现 Metamask 读取 ETH 地址时的 call 函数调用数据如下:
根据日志比对,
003700000000 和 000b0000001f08ac8080800808bc80808008088080808008080008002207426974636f696e 等数据,均为发送、接收报文的序列化结果。
经过重放测试,在信息构建过程中并没有时间戳等参数,可以非常简单轻易的模拟攻击手法,即针对部分发送报文,硬编码返回的结果,进行结果替换:
在这个例子中,我们针对性的将数据进行了替换,当接收到 SDK 获取 ETH 公钥的请求时,不再使用硬件返回的结果,而是直接使用上述代码中硬编码的结果。重新通过文档编译后制作二进制文件,同时制作守护进程等攻击必要组件。
准备工作完成后,当用户不慎安装和运行恶意软件后。每当用户使用 Metamask 连接 Trezor 并且发送数据时,通过 bridge 通信的数据不再是硬件读取出来的信息,而是上述代码中硬编码的序列化数据,再基于业务 SDK 的反序列化,读取地址的信息即替换成功。完整的攻击如上述视频所示。
与团队的沟通
整理好问题及操作之后,我第一时间已与 Metamask 和 Trezor 团队进行了沟通,以下是所有沟通信息及邮件信息
问题的核心及建议
Metamask 读取完整地址,无需硬件确认
硬件整体可以作为一个封闭的安全环境,所以所有信息都应以硬件为准。但是在 Metamask 的产品设计流程当中,在硬件没有任何显示的情况下,直接读取完整地址的明文信息并进行展示。
Trezor SDK ethereumGetAddress 方法提供了 showOnTrezor 参数,他的作用是读取硬件的地址,并在硬件当中展示,以供用户二次确认。
这是一个产品设计的取舍问题,如果每次都需要用户确认过才能读取地址,那么在批量添加地址的场景用户会非常痛苦。所以在主流的支持硬件钱包的 APP 内,同时支持静默读取地址,但是如果想要接收资产,都需要先进行地址的硬件确认,就是为了防止上述攻击场景出现。
Metamask 静默读取地址之后,没有让用户确认,就直接展示了完整地址,那么首次使用的用户,一旦被攻击,就很容易中招。
盲签的威胁,可能比我们想象中的更大
按照这种攻击手段和攻击方式,我们可以得出结论:所有硬件非确定性的信息,都是理论上不安全的。
所有的盲签同理,虽然盲签硬件上有确认,但是这个确认的信息对于用户来说并不可读,所以他也满足硬件非确定性的信息,无法确保不被中间人攻击。
safe 多签钱包的案件就是一个典型,钱包构造的签名信息被服务器恶意 javascript 代码替换,同时因为 ledger 钱包是盲签,导致这一次攻击的发生。
通信通道的中间人攻击,方式众多
除了用户本地有恶意软件以外,一些钱包的通信和构建流程可能涉及到服务器(例如一些钱包 encodeTx 需要通过服务端进行构建)。这些都是潜在的攻击对象和被攻击点。
通信通道当然还包括硬件层面,例如不安全的电脑摄像头外设,不安全的 USB 数据线等等。从物理层到应用层,在通信层面,所有环节都有被攻击的可能。
主动提升安全意识,任重而道远
客户端到硬件钱包的完整通信,核心流程理论上是不需要联网的。所以即使有端到端的加密通信,也无法 100% 确保流程安全。
增加端到端的加密之后,对于真正的黑客来说,增加的只是逆向客户端,对通信信道的破解成本。而对于攻击本身,从来都不是难度的较量,而是黑客付出的成本和法律后果的考量。
同时,大部分区块链的产品团队,一般在面对这类中间人攻击的威胁时,会将这部分的优化放在整个迭代中处于低优先级,变相等于允许一部分中间人攻击的风险出现。如上面的团队沟通邮件,如 Metamask 的 bug bounty 界面,就将 MITM 中间人攻击排除在外:
所以对于用户来说,更需要主动我们自身提高防范意识。对于重要资产,即使是使用硬件钱包,也最好隔离电脑,不连接不可信的网络,不访问不可信的网页等等等等。
写在最后
硬件钱包的“安全神话”并非来自单一设备本身,而是建立在整个生态系统的协作安全。
当我们忽视客户端的可信性、放松对通信通道的防护,那把“达摩克利斯之剑”便悄然悬于我们的资产之上。
真正的安全,从不止于硬件,而在于每一个你以为“无关紧要”的细节。