1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| // SPDX-License-Identifier: MIT pragma solidity ^0.8.17;
/** * 存在重放漏洞的支付合约 * 攻击者可以重复使用已签名的交易来多次提取资金 */ contract VulnerablePayment { mapping(address => uint256) public balances; mapping(bytes32 => bool) public usedSignatures;
address public owner;
constructor() { owner = msg.sender; }
// 存款函数 function deposit() external payable { balances[msg.sender] += msg.value; }
// 存在重放漏洞的提现函数 function withdraw( uint256 amount, uint256 nonce, bytes memory signature ) external { bytes32 messageHash = keccak256(abi.encodePacked( msg.sender, amount, nonce, address(this) ));
bytes32 ethSignedMessageHash = keccak256(abi.encodePacked( "\x19Ethereum Signed Message:\n32", messageHash ));
// 验证签名 address recovered = recoverSigner(ethSignedMessageHash, signature); require(recovered == owner, "Invalid signature");
// 但没有检查nonce是否已被使用! // 攻击者可以重复使用相同的签名数据
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount; payable(msg.sender).transfer(amount); }
// 签名验证函数 function recoverSigner( bytes32 ethSignedMessageHash, bytes memory signature ) internal pure returns (address) { (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature); return ecrecover(ethSignedMessageHash, v, r, s); }
function splitSignature(bytes memory sig) internal pure returns (bytes32 r, bytes32 s, uint8 v) { require(sig.length == 65, "Invalid signature length");
assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) }
if (v < 27) v += 27; require(v == 27 || v == 28, "Invalid signature version"); } }
|