撰文:Chen Bo Yu、Hsu Tzu Hsiu
在讨论智能合约缝隙解析之前,咱们先从一个基本的典范来了解一个智能合约会具备哪些元素。
● 变数:即此典范中的 balances,在这个合约中负责存储运用者地址在合约中对应的存款余额
● 函数:即此典范中的 getBalance,运用者呼叫此函数时,会回传运用者在合约中的存款余额
● 接纳函数:即此典范中的 receive,这是一个内建的函数。当合约收到运用者传入 ETH 且无呼叫其他函示时会触发,此典范在触发接纳函数时,会变更变数 balances 的状况,而函数中的 msg.sender 代表的是买卖的发送者地址
● 回退函数:即此典范中的 fallback,这也是一个内建的函数。当运用者呼叫了不存在的函数时触发,能够理解为破例处理函数。在此典范中,触发时把买卖回退,亦即让买卖失效。
了解问题产生的原因,而且归纳问题的类别能够协助咱们更好的防范。DASP (Decentralized Application Security Project)收录了十种智能合约缝隙,下面咱们整理了其中最常见的合约缝隙以及新型态的进犯形式。
重入缝隙
重入缝隙是最著名的智能合约缝隙,从前提到 The DAO 事情中也是为此原因而被骇客进犯,该缝隙原理是经过循环调用一个函数而到达进犯目的。
这边展现的是一个简略的提款函数,让运用者能够根据合约里的余额(userBalances)取走存款。能够注意到的是,当这个函数的调用者为一智能合约的时候,提款操作(msg.sender.call)将会触发该智能合约的 receive 函数,并把剩余的 gas 传入。而此刻还未把运用者在原先智能合约中记录的余额归零(userBalances[msg.sender] = 0),进犯者即可在 receive 函数里再次调用 withdrawBalance 函数,并经过余额状况没有批改的缝隙到达重复取款的目的,直到 gas 耗尽或合约被掏空。
进犯流程图展现
防范方法也很简略,只要先把智能合约纪录的余额做清空,再做转帐动作,即可防止进犯产生。
整数溢位缝隙
在以太坊智能合约中,uint256 是常见的整数型别,这意味着此变数能够贮存的整数规模为 0 ~ 2^256 - 1,存储上限大约是一个 78 位数的值,你或许会觉得这个数现已够大了,但它依然可被用来达成溢位,也便是说当一个变数的值为 2^256 - 1,而对这个变数的值又再进行加一的操作时,他的值会由于超越存储上限而变为 0。要防止此缝隙,咱们需求在整数运算前针对整数的规模去做检查,并在侦测到溢位运算时即时抛出反常。
阻断服务进犯
智能合约服务中断是一个严重的问题,由于有些缝隙形成的服务中断是永久性的,无法康复。进犯原理包含了:意外履行 SELFDESTRUCT 指令、拜访控制权限犯错、Gas limit 到达区块上限使合约无法正常运作、以及咱们这边展现的运用反常抛出,形成合约永久性瘫痪。
这是一个简略的拍卖合约示例,出价高者能够成为 currentLeader,并记录该次竞标出价为 highestBid,一起把从前的出价金额还给前一个竞标领先者。进犯者能够布置一个智能合约,在正常出价后让该合约成为 currentLeader,并在合约内负责收款的 receive 函数中运用 revert 函数来抛出反常,让买卖失效。当其他运用者想出价竞标时,会由于合约无法转钱给 currentLeader (即进犯者从前布置的合约),而形成买卖失利,拍卖合约的功用也因而永久失效,进犯者得以赢下此次的拍卖竞标。
进犯者合约示例
Tx origin 缝隙进犯
当开发者运用 solidity 中内建的 tx.origin 变数来验证权限时,会让进犯者有进犯的时机。在进入示例之前,须先了解 tx.origin 返回的是原始发送买卖的地址,而 msg.sender 返回的是当前买卖的发送者。以下示意图情形为:
用户 A 呼叫了合约 B 内部的函数,并在函数内又再呼叫了合约 C。能够观察 tx.origin 与 msg.sender 的差异。
接下来来看看实践的进犯场景,上图智能合约中的 sendTo 函数必须符合 tx.origin 与 owner 持平的条件才会被履行,可是进犯者能够经过下图的智能合约,运用上述提过 tx .origin 与 msg.sender 的差异,巧妙地绕过验证,并触发 sendTo 函数。具体细节是当进犯者诱导上图合约的 owner 去触发了下图合约的 fallback 函数时,若进犯合约在 fallback 函数内去调用 sendTo 函数,就能够得到 owner 的权限去履行。
未适当处理 external call 的回传值
在智能合约中,运用到低层级调用函数指令时,如:address.call()、address.callcode()、address.delegatecall() 和 address.send() 等等,假如调用失利并不会抛出反常,仅会回传调用成果的布林值,合约将能继续往下履行。若未对调用成果的回传值做检查,或许将会使智能合约无法正常运作。
咱们以一个简略的取款函数作为示例,当运用一合约呼叫上图的 withdraw 函数,且若该合约不能接纳 ETH 转入(例如无设置 payable)时,会形成呼叫方无法收到 ETH ,但因合约会继续往下履行,导致其在合约中 balances 的状况纪录被改变。批改写法如下:
短地址进犯
此进犯手法大多出现在 ERC-20 (代币规范协定)智能合约中,须先了解到,当咱们呼叫一个函数时,在 EVM (以太坊虚拟机)里实践上是在解析一堆 ABI (Application Binary Interface)字符。而一般 ERC-20 规范的代币都会完结用来转帐的 transfer 函数,当咱们调用 transfer 函数时,买卖的调用内容由 3 个部分组成:
● 4 字节,函数名的哈希值,例如:a9059cbb
● 32 字节,以太坊地址(地址为 20 字节,高位补零),例如:
00000000000000000000000011223344556677889900aabbccddeeff11223344
● 32 字节,代表需求转送的代币数量:
0000000000000000000000000000000000000000000000000de0b6b3a7640000
若进犯者地址为:0x1234567890123456789012345678901234567800 (尾数为零),且在呼叫 transfer 时刻意舍去尾数零,若合约内没有对内容格式做检查,EVM 读取时会从第三个参数(代币数量)的高位拿 00 来弥补,这将形成实践想要转送的代币数量短少一个字节,即向左移位了 8 个比特,数值瞬间扩大 256 倍,进犯者成功盗取代币合约中的代币。
闪电贷进犯
闪电贷,望文生义便是快速贷款,那这个速度有多快呢?官方的解说是,贷款发行和偿还的买卖必须在以太坊上同一个区块内完结。能够说闪电贷是一种借助于区块链技术的推翻式创新,它与传统的假贷有两个首要的不同,一个是它无需抵押品,第二个是它要求要在履行借出的同一笔买卖中履行还款操作,因而对于出借资金的那方来说是不用承担违约风险的,由于只有当区块链上假贷方履行的借出与还款操作都的确被履行了,这笔买卖才有效。也便是说咱们能够设计一个智能合约来借出资金,接着履行一些资金操作,最终在将资金归还,而这些操作都会在同一笔买卖中完结。这就给黑客们带来了运用闪电贷建议进犯的时机,由于它大大的降低了黑客的进犯本钱,近期在 DeFi 范畴的多数进犯都是运用闪电贷来完结,首要都是黑客经过借出的巨额资金来对协议制造价差并从中套利,还款后再带着不妥获利逃之夭夭。咱们从 bZx 进犯事情来了解黑客的进犯思路:
-
黑客经过闪电贷从去中心化数字财物衍生买卖平台 dYdX 借出了一万枚 ETH
-
运用其中的 5000 枚 ETH 抵押在去中心化假贷平台 Compound 以借出 112 枚 wBTC (Wrapped BTC,即以太坊上的 BTC 跨链财物)
-
剩余的的 5000 枚 ETH 到去中心化假贷平台 bZx 上开了 wBTC 的空单
-
用借出的 112 枚 wBTC 到去中心化买卖所 Uniswap 砸盘,让 wBTC 价格快速跌落
这一系列操作让黑客在 bZx 上开的空单仓位大赚,接着归还闪电贷借出的一万枚 ETH,并在这个过程中获得了价值 35 万美元的收益。此次进犯的首要原因是由于 Uniswap 的价格的剧烈变化最终导致财物的丢失,这本该是正常的商场行为,可是黑客经过歹意操作商场,使项目方形成丢失。bZx 合约被操作一事,开始让闪电贷进入了更多开发者的视野,一方面许多聪明的开发者开发出了全新的去中心化金融应用,一起也让开发者更为警觉或许的逻辑进犯。
定论
智能合约的运作为被动的,一切的合约动作均须由运用者建议买卖、呼叫合约中的函数函数才会履行动作,而合约履行根据区块链的特性是不可逆的,且当合约布置上区块链后,一切材料都是公开透明的,即使代码不开源,也可运用反组译东西回推合约内容。因而,开发者需熟悉缝隙原理并防止之,运用者也应了解合约安全议题,维护自身权益。
视野开拓
投行的客户交易涉及交易员、专业销售员和研究员。交易员负责从投资机构和个人投资者手上购买证券,并在未来某一时刻(几分钟、几小时、或几个月),以更高的价格将这些证券出售给其他投资者。交易受多种因素影响,包括研究、监管机构、诉讼等数不清的变量,一个优秀的交易员有能力跟踪并综合大量信息,迅速做出决策。不论交易员专注于何种行业,对于全球经济、利率、货币、信用风险、估值技术甚至政治的深入理解,都是至关重要的。 不管投资何种时限,交易员必须每天记录所持有证券和衍生品头寸的市场价值,这些价值的变化表现为每天的盈利或者亏损。 卖空:在股票卖空中,交易者借入股票并卖掉。如果股票下跌,卖空方可以从公开市场低价买入股票来偿还,从中赚取差价。卖空的股票会产生空头净额,空头净额比率是指上市公司股票被卖空的数量除以每日平均交易量。高空头净额比率意味着该股票处于熊市期,但这具有误导性,需要和已发行可转换债券结合想考虑。 FICC交易:FICC通常关注利率产品、信用产品和大宗商品。 信用违约互换(CDS):交易对手中的一方定期支付一定款项,以获取在基础证券或贷款违约时得到补偿。CDS本质上是对冲违约风险的保险合同,但签订此类合约不要求拥有真实的基础证券或贷款,许多信用保护买方会出于纯粹投机目的购买。 风险价值(VaR)是衡量投行交易风险的一个重要工具,它是在一定的置信度和期限内,由于不利的市场变动造成交易头寸的可能损失。例如,一个投行报告某笔利率交易业务的风险价值是5000万美元,则意味着,在正常交易条件下,银行有95%的信心,使得利率产品投资组合的单日价值变化所造成的损失不会超过5000万美元。-《投资银行、对冲基金和私募股权投资》