智能合约安全性问题CheckList
根据腾讯腾讯安全2018上半年区块链安全报告,智能合约引发的安全问题以成为区块链自身机制安全性的主要问题,本文就目前文献中提到的主流安全性问题做出总结,并列出目前的相关研究。
整形溢出(Arithmetic Issues)
如下代码,如果没有assert判断,那么sellerBalance+value可能会超出uint上限制导致溢出。
pragma solidity ^0.4.15;
contract Overflow {
uint private sellerBalance=0;
function add(uint value) returns (bool, uint){
sellerBalance += value; // complicated math with possible overflow
// possible auditor assert
assert(sellerBalance >= value);
}
}
危险的delegatecall(dangerous delegatecall)[contractfuzzer]
首先需要了解call和delegatecall的区别:call和delegatecall都为合约相互调用时的函数,假设A调用B函数,call方法结果展示到B中,delegatecall方法结果展示到A中。
在如下示例中,Mark如果用delegatecall调用了恶意合约Steal,那么Mark合约会被删除。
复现:
1.用A账户部署Steal,用B账户部署Mark合约,并在部署时为合约附加10个ether。
2.账户B调用Mark.call(address(Steal)),即用B调用Steal的Innocence方法,实际上innocence会在Mark的上下文环境运行,发现账户B收到合约的10 ether(注意不是A账户)
3.用C账户执行Mark.deposit()方法,并附加10ether,再调用destruct方法,发现B无法收到10ether,说明合约确实已经在第二步被销毁。
pragma solidity ^0.4.2;
contract Steal{
address owner;
constructor () payable {
owner = msg.sender;
}
function innocence() {
log0("123");
selfdestruct(owner);
}
}
contract Mark {
address owner;
constructor () payable {
owner = msg.sender;
}
function Deposit() payable {}
function call(address a) {
a.delegatecall(bytes4(keccak256("innocence()")));
}
}
无Gas发送(Gasless Send)[contractfuzzer]
合约C调用合约D1时,由于fallback函数修改了storage变量——这是一个消耗大量gas的操作——导致了超过fallback的gas上限(2300gas)导致fallback失败,调用D2时,由于没有超过上限,调用成功。
复现:
1.用10ether部署C合约,0ether部署D1合约,0ether部署D2合约
2.调用C.pay(1000000000000000000, address(D1)),D1的count值仍为0
3.调用D1.kill(),以太币不增加。2,3两步说明了D1的fallback调用失败
4.调用C.pay(1000000000000000000,address(D2))
5.调用D2.kill(),发现账户增加1ether,说明D2的fallback调用成功
pragma solidity ^0.4.2;
contract C {
address owner;
constructor () payable{
owner=msg.sender;
}
function pay(uint n, address d){
d.send(n);
}
function kill() {
if (owner == msg.sender) {
selfdestruct(owner);
}
}
}
contract D1 {
address owner;
uint public count = 0;
constructor () payable{
owner=msg.sender;
}
function() payable {
count = count+1;
}
function kill() {
if (owner == msg.sender) {
selfdestruct(owner);
}
}
}
contract D2 {
address owner;
constructor () payable{
owner=msg.sender;
}
function() payable {}
function kill() {
if (owner == msg.sender) {
selfdestruct(owner);
}
}
}
依赖于交易顺序/条件竞争(TOD/Front Running)[smarter]
由于:
1.只有当交易被打包进区块时,他才是不可更改的
2.区块会优先打包gasprice更高的交易
所以攻击者可以恶意操控交易顺序从而使合约对自己有利。如图,出题人和做题人同时发起合约,那么做题人得到的奖励因合约执行顺序不同而不同。
根据腾讯腾讯安全2018上半年区块链安全报告,智能合约引发的安全问题以成为区块链自身机制安全性的主要问题,本文就目前文献中提到的主流安全性问题做出总结,并列出目前的相关研究。
整形溢出(Arithmetic Issues)
如下代码,如果没有assert判断,那么sellerBalance+value可能会超出uint上限制导致溢出。
pragma solidity ^0.4.15;
contract Overflow {
uint private sellerBalance=0;
function add(uint value) returns (bool, uint){
sellerBalance += value; // complicated math with possible overflow
// possible auditor assert
assert(sellerBalance >= value);
}
}
危险的delegatecall(dangerous delegatecall)[contractfuzzer]
首先需要了解call和delegatecall的区别:call和delegatecall都为合约相互调用时的函数,假设A调用B函数,call方法结果展示到B中,delegatecall方法结果展示到A中。
在如下示例中,Mark如果用delegatecall调用了恶意合约Steal,那么Mark合约会被删除。
复现:
1.用A账户部署Steal,用B账户部署Mark合约,并在部署时为合约附加10个ether。
2.账户B调用Mark.call(address(Steal)),即用B调用Steal的Innocence方法,实际上innocence会在Mark的上下文环境运行,发现账户B收到合约的10 ether(注意不是A账户)
3.用C账户执行Mark.deposit()方法,并附加10ether,再调用destruct方法,发现B无法收到10ether,说明合约确实已经在第二步被销毁。
pragma solidity ^0.4.2;
contract Steal{
address owner;
constructor () payable {
owner = msg.sender;
}
function innocence() {
log0("123");
selfdestruct(owner);
}
}
contract Mark {
address owner;
constructor () payable {
owner = msg.sender;
copyright 无奈人生
}
function Deposit() payable {}
function call(address a) {
a.delegatecall(bytes4(keccak256("innocence()")));
}
}
无Gas发送(Gasless Send)[contractfuzzer]
合约C调用合约D1时,由于fallback函数修改了storage变量——这是一个消耗大量gas的操作——导致了超过fallback的gas上限(2300gas)导致fallback失败,调用D2时,由于没有超过上限,调用成功。
复现:
1.用10ether部署C合约,0ether部署D1合约,0ether部署D2合约
2.调用C.pay(1000000000000000000, address(D1)),D1的count值仍为0
3.调用D1.kill(),以太币不增加。2,3两步说明了D1的fallback调用失败
4.调用C.pay(1000000000000000000,address(D2))
5.调用D2.kill(),发现账户增加1ether,说明D2的fallback调用成功
pragma solidity ^0.4.2;
contract C {
address owner;
constructor () payable{
owner=msg.sender;
}
function pay(uint n, address d){
d.send(n);
}
function kill() {
if (owner == msg.sender) {
selfdestruct(owner);
}
}
}
contract D1 {
address owner;
uint public count = 0;
constructor () payable{
owner=msg.sender;
}
function() payable {
count = count+1;
}
function kill() {
if (owner == msg.sender) {
selfdestruct(owner);
} copyright 无奈人生
}
}
contract D2 {
address owner;
constructor () payable{
owner=msg.sender;
}
function() payable {}
function kill() {
if (owner == msg.sender) {
selfdestruct(owner);
}
}
}
依赖于交易顺序/条件竞争(TOD/Front Running)[smarter]
由于:
1.只有当交易被打包进区块时,他才是不可更改的
2.区块会优先打包gasprice更高的交易
所以攻击者可以恶意操控交易顺序从而使合约对自己有利。如图,出题人和做题人同时发起合约,那么做题人得到的奖励因合约执行顺序不同而不同。
copyright 无奈人生
无奈人生安全网