fortress.loans 攻击学习

5月8日 fortress.loans 受到攻击损失 总计1,048.1 ETH 和 400,000 DAI
攻击交易:
https://bscscan.com/tx/0x13d19809b19ac512da6d110764caee75e2157ea62cb70937c8d9471afcb061bf
合约代码:
Chain:https://bscscan.com/address/0xc11b687cd6061a6516e23769e4657b6efa25d78e#code
FortressPriceOracle:https://bscscan.com/address/0x00fcf33bfa9e3ff791b2b819ab2446861a318285#code


合约Chain中,函数submit没有权限验证(票数验证),导致任何人可以提交并修改任意代币价格,

 function submit(
    uint32 _dataTimestamp,
    bytes32 _root,
    bytes32[] memory _keys,
    uint256[] memory _values,
    uint8[] memory _v,
    bytes32[] memory _r,
    bytes32[] memory _s
  ) public { // it could be external, but for external we got stack too deep
    uint32 lastBlockId = getLatestBlockId();
    uint32 dataTimestamp = squashedRoots[lastBlockId].extractTimestamp();

    require(dataTimestamp + padding < block.timestamp, "do not spam");
    require(dataTimestamp < _dataTimestamp, "can NOT submit older data");
    // we can't expect minter will have exactly the same timestamp
    // but for sure we can demand not to be off by a lot, that's why +3sec
    // temporary remove this condition, because recently on ropsten we see cases when minter/node
    // can be even 100sec behind
    // require(_dataTimestamp <= block.timestamp + 3,
    //   string(abi.encodePacked("oh, so you can predict the future:", _dataTimestamp - block.timestamp + 48)));
    require(_keys.length == _values.length, "numbers of keys and values not the same");

    bytes memory testimony = abi.encodePacked(_dataTimestamp, _root);

    for (uint256 i = 0; i < _keys.length; i++) {
      require(uint224(_values[i]) == _values[i], "FCD overflow");
      fcds[_keys[i]] = FirstClassData(uint224(_values[i]), _dataTimestamp);
      testimony = abi.encodePacked(testimony, _keys[i], _values[i]);
    }
    bytes32 affidavit = keccak256(testimony);
    uint256 power = 0;
    uint256 staked = stakingBank.totalSupply();
    address prevSigner = address(0x0);
    uint256 i = 0;
    for (; i < _v.length; i++) {
      address signer = recoverSigner(affidavit, _v[i], _r[i], _s[i]);
      uint256 balance = stakingBank.balanceOf(signer);

      require(prevSigner < signer, "validator included more than once");
      prevSigner = signer;
      if (balance == 0) continue;
      emit LogVoter(lastBlockId + 1, signer, balance);
      power += balance; // no need for safe math, if we overflow then we will not have enough power
    }
    require(i >= requiredSignatures, "not enough signatures");
    *********************************👇************
    // we turn on power once we have proper DPoS
    // require(power * 100 / staked >= 66, "not enough power was gathered");
	*********************************👆*************
    squashedRoots[lastBlockId + 1] = _root.makeSquashedRoot(_dataTimestamp);
    blocksCount++;

    emit LogMint(msg.sender, lastBlockId + 1, staked, power);
  }

同时借贷需要根据FortressPriceOracle . getUnderlyingPrice计算价格

function getPrice(address underlying) internal view returns (uint) {
        if (prices[underlying] != 0) {
            // return v1 price.
            return prices[address(underlying)];
            ********************👇
        } else if (underlying == FTS_ADDRESS) {
            // Handle Umbrella supported tokens.
            return getUmbrellaPrice(ftsKey);
            ********************👆
        } else if (areLPs[underlying]) {
            // Handle LP tokens.
            return getLPFairPrice(underlying);
        } else {
            // Handle Chainlink supported tokens.
            return getChainlinkPrice(getFeed(underlying));
        }
    }
    function getUmbrellaPrice(bytes32 _key) public view returns (uint256) {
        (uint256 value, uint256 timestamp) = _chain().getCurrentValue(_key);
        require(timestamp > 0, "value does not exists");
        return value;
    }

如果满足目标代币为FTS_ADDRESS则直接调用 _chain().getCurrentValue获得目标价格,
通过调用Chain中函数submit设置FTS代币价格,并质押即可借取大量其他代币,完成攻击.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值