【本篇内容仅用作学习 科研所需 禁止用作任何违法行为 学安全为安全】
作为区块链方向的学生 最近在学合约审计 总结一下
访问控制缺陷漏洞:
漏洞简介:
即某些对权限有要求的方法的修饰符逻辑错误造成合约中的某些私有函数可以被非法调用
常出现的地方:
在function修饰符modifier上 或者访问权限 private public internal external 调用方法:call delegatecall中
防范方法:
1.在编写合约的时候 务必检查好函数调用权限 调用逻辑一定要清晰 否则一旦部署到区块链上 则不可更改 容易造成经济损失
2.避免disable enable在合约逻辑中存在
3.敏感变量必须通过函数修饰符进行权限控制 对可以操纵合约内部敏感变量的函数 应该用assert if-else等进行权限限制
漏洞举例:
IcxToken 合约中的
modifier onlyFromWallet {
require(msg.sender != walletAddress);
_;
}
function disableTokenTransfer()
external
onlyFromWallet {
tokenTransfer = false;
TokenTransfer();
}
function IcxToken( uint initial_balance, address wallet) {
require(wallet != 0);
require(initial_balance != 0);
_balances[msg.sender] = initial_balance;
_supply = initial_balance;
walletAddress = wallet;
}
在 disableTokenTransfer方法中 关闭合约交易权限应该只能由wallet执行
否则 如果被恶意调用disableTokenTransfer函数 那么该合约中被isTokenTransfer修饰的函数均不可正常使用
即这里的msg.sender != walletAddress 应该为 msg.sender == walletAddress
利用方法:
只要调用disableTokenTransfer的地址不是walletaddress即可
在remix实验时,可以直接点击函数进行调用
智能合约
跨合约调用漏洞:
漏洞简介:
由call系列函数引起的外部合约注入,即外部合约A调用B合约中的私有 || 具有权限限制的函数
原理解释:
solidity中 合约相互调用有两种:
对象: 合约地址当作合约对象适用 然后调用
用call() delegatecall() callcode()
方法解释:
call函数与delegatecall函数:
比如:
B中的function名字为 Bfunc
如果 A合约中以call方式调用B合约的Bfunc 那么Bfunc会在B合约上下文中执行 结果返回给A合约
如果用delegatecall调用Bfunc 那么会将Bfunc方法以“复制粘贴”的方式放在A合约中(A中需要包含Bfunc所需函数与变量),然后在A合约中调用
安全漏洞主要是由call()方法调用引起的:
call():
调用方法:
address.call(function_name, args[])
|| address.call(bytes)
防范方法:
出现这类漏洞的根本在于滥用call系列方法,防范自然是减少此类方法使用 比如交易可以用transfer()实现
实例:
漏洞代码:
contract sample{
function a(byte data) {
this.call(data)
}
function secret public{
require(this == msg.sender)
//code
}
}
在这个合约中 secret函数要求调用方必须为合约自己
如果在函数a中 合约byte码指向的合约构造了一个调用:调用secret函数 那么根据call函数机制 会将此代码“粘贴”到secret函数中
那么通过a函数 就可以调用secret函数 达到注入攻击目的
关于callcode的漏洞:
EVM中,对callcode方法:传参时,不会验证参数的个数,只要找到了需要的参数,其他参数就会忽略 不会产生影响
漏洞地址:https://etherscan.io/address/0x461733c17b0755ca5649b6db08b3e213fcf22546#code
漏洞处代码:
function transferFrom(address _from, address _to, uint256 _amount, bytes _data, string _custom_fallback)
public
returns (bool success)
{
// Alerts the token controller of the transfer
if (isContract(controller)) {
if (!TokenController(controller).onTransfer(_from, _to, _amount))
throw;
}
require(super.transferFrom(_from, _to, _amount));
if (isContract(_to)) {
ERC223ReceivingContract receiver = ERC223ReceivingContract(_to);
receiver.call.value(0)(bytes4(keccak256(_custom_fallback)), _from, _amount, _data);
}
ERC223Transfer(_from, _to, _amount, _data);
return true;
}
代码解释:
漏洞核心部分为
receiver.call.value(0)(bytes4(keccak256(_custom_fallback)), _from, _amount, _data);
在这里 如果目标地址为合约 就调用_custom_fallback 回退函数 并且依次填入from amount data
漏洞利用:
如果将_to的地址写为该合约本身,那么就可以实现以owner身份,即该合约自身的权限调用该合约,那么就可以调用合约中的setOwner方法,那么就可以将任意address设置为owner,进而进行其他非法操作。
P.S.
跨合约调用漏洞代码:
```typescript
```typescript
/**
*Submitted for verification at Etherscan.io on 2017-11-17
*/
pragma solidity ^0.4.13;
contract DSAuthority {
function canCall(
address src, address dst, bytes4 sig
) public view returns (bool);
}
contract DSAuthEvents {
event LogSetAuthority (address indexed authority);
event LogSetOwner (address indexed owner);
}
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
function DSAuth() public {
owner = msg.sender;
LogSetOwner(msg.sender);
}
function setOwner(address owner_)
public
auth
{
owner = owner_;
LogSetOwner(owner);
}
function setAuthority(DSAuthority authority_)
public
auth
{
authority = authority_;
LogSetAuthority(authority);
}
modifier auth {
require(isAuthorized(msg.sender, msg.sig));
_;
}
function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, this, sig);
}
}
}
contract DSNote {
event LogNote(
bytes4 indexed sig,
address indexed guy,
bytes32 indexed foo,
bytes32 indexed bar,
uint wad,
bytes fax
) anonymous;
modifier note {
bytes32 foo;
bytes32 bar;
assembly {
foo := calldataload(4)
bar := calldataload(36)
}
LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
_;
}
}
contract DSStop is DSNote, DSAuth {
bool public stopped;
modifier stoppable {
require(!stopped);