首先复习一下solidity中的4种编码函数
1、abi.encode:用于将数据编码为可以在合约之间传递的二进制格式。
abi.encodePacked(123, "Hello, World!");
2、abi.encodePacked:将数据编码为紧凑的 ABI 格式,不包含填充字节。
abi.encodePacked(123, "Hello, World!");
3、abi.encodeWithSelector
abi.encodeWithSelector(bytes4(keccak256("transfer(address,uint256)")), address(0x123...), 100);
4、abi.encodeWithSelector,但使用完整的函数签名而不是函数选择器。
abi.encodeWithSignature("transfer(address,uint256)", address(0x123...), 100);
这里使用的abi.encodePacked编码能够将给定参数根据其所需最低空间编码,主要用于节省空间,但是节省空间的同时可能也会带来安全问题。
pragma solidity ^0.8.0;
contract HashCollisionBug {
mapping(bytes32 => uint256) public balances; //创造balances数组,内容为uint256
//创造一个hash
functioncreateHash(
string memory _string1,
string memory _string2
) publicpurereturns (bytes32) {
return keccak256(abi.encodePacked(_string1, _string2));
}
//存款方法
functiondeposit(
string memory _string1,
string memory _string2
) externalpayable{
//存入ETHER
require(msg.value > 0, "Deposit amount must be greater than zero");
//获取hash值
bytes32 hash = createHash(_string1, _string2);
// createHash(AAA, BBB) -> AAABBB
// createHash(AA, ABBB) -> AAABBB
// Check if the hash already exists in the balances mapping
//如果该hash键的值为0,才能存入以太坊,不然被视为碰撞
require(balances[hash] == 0, "Hash collision detected");
//生成hash键,值为传入的ether值
balances[hash] = msg.value;
}
}
传入值,生成hash,然后调用deposit进行转账ether,该转账ether对应的值记录在对应hash上,可以理解为"[订单编号(hash):ETH value]"
但是由于采用了encodePacked进行压缩,encodePacked是最低编码,会省略其中的所有的0单位,尤其还是字符串都是可控的情况下,所以会产生问题,如下图所示
使用abi.encodePacked生成的编码值为最低编码值
所以计算出来的值为keccak256(0x31323334)
可以看到,传入值12,34 和1,234所产生出来的值是一样的,自然所计算出来的hash也是一样的。
使用abi.encode替换abi.encodePacked即可,它会为每个参数添加长度前缀,并确保编码结果是固定长度的
原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(三十七)不正确的abi.encodePacked编码导致哈希一致
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论