第23节:安全事故13-encodePacked攻击
abi.encodePacked() 采用非填充序列化,当序列化参数包含多个变长数组时,攻击者可以在保持所有元素顺序不变的前提下,改变两个变长数组的元素,如此序列化的结果相同。
在下面的代码中:通过构造 addUser 的输入,攻击者可以将 regularUsers 的成员加入 admins 成员,但是构造的输入和原输入的签名相同。
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import "./ECDSA.sol";
contract AccessControl {
using ECDSA for bytes32;
mapping(address => bool) isAdmin;
mapping(address => bool) isRegularUser;
// Add admins and regular users.
function addUsers(
address[] calldata admins,
address[] calldata regularUsers,
bytes calldata signature
)
external
{
if (!isAdmin[msg.sender]) {
// Allow calls to be relayed with an admin's signature.
bytes32 hash = keccak256(abi.encodePacked(admins, regularUsers));
address signer = hash.toEthSignedMessageHash().recover(signature);
require(isAdmin[signer], "Only admins can add users.");
}
for (uint256 i = 0; i < admins.length; i++) {
isAdmin[admins[i]] = true;
}
for (uint256 i = 0; i < regularUsers.length; i++) {
isRegularUser[regularUsers[i]] = true;
}
}
}
解决方案:使用定长数组,或者不让调用者传入 abi.encodePacked() 的参数,或者使用 abi.encode()。