前言

6月28日,慢雾科技发布了一条针对 USDT 的预警和漏洞分析,提醒各大交易所尽快暂停 USDT 充值功能,并自查代码是否存在该逻辑缺陷。全文如下:

#预警# #漏洞分析# 交易所在进行 USDT 充值交易确认是否成功时存在逻辑缺陷,未校验区块链上交易详情中 valid 字段值是否为 true,导致“假充值”,用户未损失任何 USDT 却成功向交易所充值了 USDT,而且这些 USDT 可以正常进行交易。

很遗憾,经过笔者的一番查找发现,网上的很多资料都倾向于描述 USDT 的资本意义,而对于技术上原理的文章却寥寥无几。于是经过一些调查,笔者发现这个漏洞实际上并不能归因于 USDT 本身,而是交易所一方的问题导致。这种漏洞起因可以说非常简单,但一旦利用成功,造成的后果却难以估算。区块链开发中,各种低级错误,诸如大小写拼错导致的智能合约漏洞也是数不胜数(如:http://www.freebuf.com/vuls/175904.html),仅以本文抛砖引玉。本文将回答以下问题:

USDT 是什么?USDT 的转账是怎么实现的?USDT 的转账/提现手续费给了谁?

这次漏洞是怎么回事?受害者是谁?怎么防御?

1. 背景知识

染色币

ETH 在近一年来因为智能合约系统而受到了广泛地关注,大家可以用 5 分钟时间就发出自己的第一个代币,而代币的发行、转账则都依赖于智能合约,底层则是由 ETH 公链承载。事实上,远在 ETH 诞生之前,大家就想用比特币主链做点啥。其中,一个重要的概念被提出来了:染色币。具体来说,染色币是指在普通的比特币交易中附上一些信息,借助比特币底层基础设施来记录。然而,比特币官方开发组(Core)对这种方式颇有争议,将用于存储信息的  OP_RETURN字段从 80 字节骤然缩小到了 40 字节( https://github.com/bitcoin/bitcoin/pull/3737)。

安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

Omni Layer(原名 Mastercoin)

Omni Layer 也属于染色币,其核心思想是将 Omni Protocol 层的数据用某种方式写入比特币区块链中,目前在协议定义中有三种, Class AClass B和  Class C 。

1. Class A  – 这种方法是利用了比特币每个地址本质上都是大小为 20 bytes 的二进制字符串。先将数据分割成 20 bytes 为一组的数据块,然后使用 Base58 编码作为目标地址发送即可。当然这些被发送的 UTXO 将永远丢失。使用这项技术的还有 cryptograffiti。

2. Class B  – 这种方法利用了比特币的 multisig ,即多签特性。发送一笔  1 of n的多重签名交易(即,n 个地址中,任何一个地址签名即可花费这笔 UTXO)。当前版本的 Omni Layer 协议中最大支持 n = 3。

3. Class C  – 这种方法则利用了 OP_RETURN操作码储存数据,目前来看,基本上所有的 Omni layer 层的交易都采用了这种方式。使用这项技术的还有 CoinSpark 。

Omni Protocol 能让用户发行自己的数字资产,其中资产编号为 31 的就是被广泛使用的 USDT 。而日常中,发送 USDT 使用的交易类型是  Simple Send

下面是对  Simple Send交易的定义。

安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

Simple Send 是一项将 Omni Layer 层中特定的数字资产从原地址转移到目标地址的操作,注意原地址和目标地址均使用比特币的地址。

可以看到上面的交易并没有任何的余额信息,这就是说,原地址的 Omni Layer 层数字资产,由 Omni Core 自己维护一个额外的账本并校验

Omni core 的实现是遍历交易,自己维护一个 tally map,下图所示是 Omni core 正在逐笔扫描并构建账簿。安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

以随机抽取的某笔交易为例(b364bea8e4a9c9aeefda048df6f71dd62dc39d07a2286340e7c83c19b7ffd895):

输入 (1) 0.00500000 BTC

1NJdUJGCJgZ4YEwthHD1skdHPsr5TxHbpq 0.00500000

源地址输出 (3) 0.00498879 BTC

地址解析失败 – (解码) 0.00000000

1FoWyxwPXuj4C6abqwhjDWdz6D4PZgYRjA 0.00000546 转账目标

1NJdUJGCJgZ4YEwthHD1skdHPsr5TxHbpq 0.00498333 找零

其 OP_RETURN 字段为  6f6d6e69000000000000001f0000001ac68eac4b

6f6d6e69 0000 0000 0000001f 0000001ac68eac4b

6f6d6e69 => omni

0000 => 版本:0

0000 => 交易类型:Simple Send(0)

0000001f = 31 => 货币:USDT

0000001ac68eac4b = 115000388683 = 1150.00388683 => 转账金额

与 Omni 浏览器的内容一致。

安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

而 Omni 浏览器 中的 Raw Data,则是由 Omni Core 自己根据扫描比特币区块链并重新构筑账本后输出的内容,这其中就有我们今天的主角  valid了。 安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

2. 漏洞分析及实操

从背景知识中我们可以看出,实际上对于余额的校验是通过客户端来进行的,但很遗憾的是,与比特币不同,Omni Layer 并没有 UTXO 机制,这也就导致了无效交易也能被广播。

正常的转账流程如下:

用户发起 USDT 转账行为。

Omni Layer 的一个正常实现客户端生成交易。

客户端广播交易。

比特币区块链确认交易。

交易所确认交易数足够。

入帐交易所。

恶意攻击流程如下:

黑客发起一笔恶意转账行为。

黑客重新编译客户端,绕过余额检查,或采用其他方式生成恶意交易。

黑客广播恶意交易。

比特币区块链确认交易。

交易所确认交易数足够,没有校验交易合法性。

入帐交易所。

黑客提走资产。

以下几种情况均会导致交易状态不合法: 

地址被冻结。

交易类型不允许。(目前只有当 property = 0 ,即为比特币时会出现这种情况)

交易金额超限,或小于零。

交易资产无效。

余额不足。

对交易合法性的定义在  omnicore/src/monicore/tx.cppCMPTransaction::logicMath_SimpleSend中。

下面我们亲自来构建一笔恶意交易。

为了方便起见,使用的是 Electrum 轻节点钱包。准备工具:

1. 一个有一点余额的比特币钱包

2. 一笔 Simple Send 的原始 tx

3. https://brainwalletx.github.io/#tx

步骤:

先使用 Electrum 发送一笔交易,目标地址随便写一个。

点击预览交易,将未签名的原始交易复制。安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

打开工具3

把原始交易粘贴上去,复制 JSON 交易。

out中 添加如下字段 安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

然后把 JSON 粘贴回去,原始交易粘贴回来,签名广播(工具->加载交易->从文本),搞定。

https://www.omniexplorer.info/tx/6791b04ff752bffc9251910fa58bda5c85f28dabb4f2651dd1b9830037a2c1da

安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

凭空产生的 USDT。

当然,之后这笔交易会被判定为 invalid。安全预警 | 空手套白狼?USDT 假充值逻辑缺陷漏洞利用分析-孤独常伴

3.总结与反思

本文首先对 USDT 进行了基础建设上的分析,然后讨论了这次漏洞出现的原因,最后,本文复现了被攻击场景。

实际上,这个逻辑漏洞的主要原因还是在交易所方面没有做好处理,面对这种涉及到金钱的事情,小心一些总是好的。

Tether 公司在以太坊上也发行了基于 ERC20 的 USDT,不知道会不会也出现类似的问题。

*本文原创作者:umiiii