作者:xxxeyJ
本文为作者投稿,Seebug Paper 期待你的分享,凡经采用即有礼品相送! 投稿邮箱:[email protected]
前言
最近我搞了一个小蜜圈,星球主攻区块链安全方向,但是目前由于圈内的小伙伴扎根在传统安全行业久已,对于区块链安全没有进一步的了解(经过调研发现大多数小伙伴只停留在了晓得区块链,比特币等几个基础概念),在此之前发在小蜜圈的文章也并没有很好的带动学习气氛,我想主要原因是由于小伙伴们对区块链安全不甚了解才导致了这幅局面,于是本文便应运而生,由于本文是出于教学的目的编写,故内容较为细化,适合对区块链安全感兴趣但目前处于新手村并且可以静下心来阅读学习的小伙伴食用,本文将会围绕着遵循 附录1
ERC20
标准的 SlowMist Zone Token 展开审计,希望本文的诞生会对后续小蜜圈以及各位小伙伴在区块链这一领域的发展起到良好的铺垫作用
前期准备工作
- SlowMist Zone Token 智能合约流程图
审计智能合约之前,需要先对审计对象有个基础了解,比如这里的慢雾区通证
Smart Contract: 0xcb0797aaca2fd4edd3bf07e7157d40322629af8b (Contract Account)
Holders: 99 (持币者数量)
Max Total Supply: 102,400,000 (最大发行总量)
Decimals: 18 (精度)
Name: SlowMist Zone Token (Token 全称)
Symbol: SLOWMIST (Token 符号)
Owner Account: 0x06a3f099e75720fd7415f87edc8cd9953b36d171 (Ownership) 附录2
CODE REVIEW
SlowMist Zone Token
采用单源文件多合约的形式部署智能合约,.sol
源文件内部合计共 9 个 Smart Contract,其在源文件内实现 SafeMath Library
Line 5 标识使用 Solidity 编译器版本号不低于 0.4.23
,且不高于下个大版本(0.5.0
)
Line 12 - Line 52 在源文件内部实现了 SafeMath Library
,将其作用于智能合约内部的数学运算以防止整型溢出漏洞的产生,在 SlowMist Zone Token 内部使用到的 SafeMath Library
只有sub()
以及 add()
这两个function
,我想这也是为什么 div()
功能代码被注释掉的原因,这里值得一提的是,附录3 Solidity ^0.8.0
版本之后编译器内部默认集成了 SafeMath Library
,因此,在后续的版本直接使用 Solidity 默认的数学运算符
即可避免整型溢出漏洞的产生
Line 59 - Line 64 实现了一个更为精简的 ERC20
标准,其中声明了 3 个 function,以及定义了一个 Transfer
事件,Line 70 BasicToken
子合约继承 ERC20Basic
父合约,Line 71 表示将 SafeMath Library
中的 function
应用于 uint256
整数类型,Line 73 构造一个名为 balances
的映射(Mapping)
,在当前智能合约中给它的定位是将此映射表示为指定账户持有的 Token
数量,Line 75 定义了一个 totalSupply_
状态变量,在当前 Token 中,使用到该状态变量的功能共有三处,其一是 Line 80 的 totalSupply()
function,可见性为 public
,表示可公开调用,函数返回值为 totalSupply_
,用以获取该 Token 中的发行总量,其二被作用在了 MintableToken
合约内的mint()
function,用来与 tmpTotal
变量联动判断当前已发行的代币数量,以确保其发行数量不超过最大发行总量,其三是这个变量(最大发行总量)
会在后续主合约中的构造函数中定义下来,Line 89 - Line 97 实现了 Transfer
转账功能,检查to
接收者地址不为 0x00
并检查转账金额小于等于发起人余额,通过两步检查后,使用 SafeMath
的 sub()
function 给调用者uint256 balances(address(msg.sender)
扣除转账金额,然后使用 SafaMath
的 add
function 给to
目标地址增加相应的转账金额(Token),而后触发 Transfer
事件,返回 True
表明调用成功
通过 balanceOf()
function 实现获取给定地址账户的余额(Token数量)
ERC20
合约继承自 ERC20Basic
合约,合约内部实现了三个方法,定义了一个 Approval
事件
StandardToken 这一合约实现了标准化的 ERC20
,其继承自 ERC20, BasicToken 两个合约,合约内部定义了一个 allowd
映射(Mapping),allowd 在此合约内表示_owner
这一账户授权给 _spender
账户可使用的金额(Token), 映射分为两部分,mapping(keyType => valueType),其中 keyType
存在一定的限制,keyType 不能为 映射
, 变长数组
, 合约
, 枚举
, 结构
,而 valueType
则无任何限制,可以为任意类型,包括(Mapping)自身类型,transferFrom()
和 approve()
这两个方法需要组合使用,首先通过 approve() 授权指定账户(_spender
)可以使用相应金额的 Token
, 然后被授权者通过 transferFrom
实现转账逻辑,而后给 allowd[_from][msg.sender]
减去相应已使用的金额,由于approve()
在早期版本存在条件竞争的风险,OpenZeppelin 提出了一种解决方案 详情见 附录4,慢雾在此沿用了这种方案,使其在 Token
内部实现了 increaseApproval()
用以追加支付授权额度,以及实现了 decreaseApproval()
用以撤销指定额度的支付授权,其中 decreaseApproval()
需要传递两个参数,被授权者地址以及想要撤销的金额,逻辑如下,将已授权的金额赋值给 oldValue
以便后续比对金额,如果想要削减的金额比被授权者持有使用权的金额大的情况下,则将授权金额直接清零,否则,根据其(_subtractedValue
)指定的撤销金额对被授权者减去相应的金额使用权,最后,触发 ERC20
合约内的 Approval
事件
Line 255 实现了 Ownable Library,其中定义了一个名为owner
的状态变量,表明合约所有者(Ownership),定义了两个事件,OwnershipRenounced
以及 OwnershipTransferred
,尽管合约内并没有写关于放弃所有权的代码,紧接着是一个构造函数,用于初始化 Ownable
合约,将调用者 address(msg.sender) 赋值给 owner 这一状态变量,下面是一个修饰器(modifier),在当前 Token 中用于校验调用者是否为所有者身份(Ownership
),transferOwnership()
方法用于移交所有权
MintableToken
是一个专门用于铸币的合约,继承自 StandardToken
,Ownable
这两个合约,继承 Ownable 合约的原因是为了使用其 owner
状态变量以便于确认所有者身份,mint() 使用了 hasMintPermission
和 canMint
修饰器加以修饰,hasMintPermission
用于确认铸币者是否为 owner
账户,canMint
用于检查是否允许铸币,Line 301 定义了一个名为 mintingFinished
的 bool 类型的状态变量,其值为 false
,修饰器内进行了!
取反操作,则通过检查,函数体内首先将当前已铸币量和待铸币数量相加并赋值给一个临时变量以便检验发行量是否小于 1.024 亿
这个数字 (从这点可以看出此Token不可增发!),条件判断成立后给 mintTotal
变量增加发行的金额以及给指定账户铸造了指定 Token
数量,最后,触发两个事件以确认成功铸币
Pausable 继承自 Ownable
Contract,合约内部定义了两个用于暂停/取消暂停的事件,Line 348 定义了一个名为 paused
的 bool 类型状态变量,可见性为 public
,此合约将该变量作用于 whenNotPaused
和 whenPaused
修饰器,paused
初始状态为 false
,表示为暂停状态,这时只能够调用unpause()
以取消其暂停状态,而后 paused
变量被赋值为 false
,这时则可以通过由 whenNotPaused
修饰的 pause()
方法将其暂停
PausableToken 继承自 StandardToken, Pausable
,使用到了Pausable
的修饰器whenNotPaused
,whenNotPaused
修饰器 !paused
执行取反操作则为 false
,得出 Token 目前处于暂停状态,可以将 super.function 关键词理解为继承关系的最上游,比如说 Line 399 的 return super.transfer(_to, _value)
,调用到的 function()
来自于最后一个实现此方法的合约,继承关系如下:
最后是 SlowMistToken 这一主合约,定义了三个用于标识 Token
信息的状态变量,以及 Line 455 的构造函数设定最大发行总量为 102,400,000 枚 Token
,Line 458 的 fallback()
回退函数不可用于接收 ETH,强行向该合约账户转账将会导致状态回滚
后记
总体看下来,慢雾科技的 SlowMist Zone Token
在合约代码满足了其业务需求的同时尽可能将攻击面降至最低,就目前市面上公开的攻击手法来讲,慢雾区 Token
的安全性毋庸置疑,回归审计文章原题,在我看来,代码层面没有漏洞,不代表其它层面没有问题,漫雾科技给SlowMist Zone Token
的定位是社区激励代币,它没有在二级市场流动所以不具备金融属性,即便劫持了该 Token 的 Ownership
账户 ,在我看来也做不了什么大的文章,目前来讲,该 Token 直面的威胁主要有两点,一个是防范 Ownership
账户沦陷的风险(比如说通过钓鱼等手段劫持其 Owner
账户),另一个则是目前藏匿于区块链生态中那片光亮照不到的 0day
总结
近年来,区块链生态安全形势愈演愈烈,区块链自身的金融属性是其它行业所不具备的,据统计加密货币市值已超过 1.66 万亿美金$,区块链中的链上安全形势尤为严峻,且伴随着DApp
(去中心化应用), DeFi
(去中心化金融),NFT
(非同质化代币) 等底层架构依附于区块链技术的新兴概念诞生,区块链也将会给一众从业者们带来前所未有的机遇,如果你对区块链安全感兴趣,并且想要加入知识星球 区块危机 共同学习,可以扫描下方二维码添加我的微信,星球主体内容为当下较为冷门的区块链安全(Blockchain Security),相信加入本星球的小伙伴们能够有所收获
附录
- 1 ) ERC20 Token Standard - ERC20 是当前最为常见的 Token 标准
- 2 ) Ownership - OpenZeppelin 的 Ownable Library 是当下最为流行的实现访问控制的 Library
- 3 ) Solidity v0.8.0 Changes - Solidity v0.8.0 更新日志
- 4 ) 条件竞争概述 - Approve() 存在的事务顺序依赖性问题解决方案
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1664/