-
Notifications
You must be signed in to change notification settings - Fork 288
/
Hash-collisions.sol
83 lines (65 loc) · 2.84 KB
/
Hash-collisions.sol
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
81
82
83
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "forge-std/Test.sol";
/*
Name: abi.encodePacked() Hash Collisions
Description:
Using abi.encodePacked() with multiple variable length arguments can,
in certain situations, lead to a hash collision.
Hash functions are designed to be unique for each input,
but collisions can still occur due to limitations in the hash function's size or the sheer number of possible inputs.
This is a known issue mentioned:
https://docs.soliditylang.org/en/v0.8.17/abi-spec.html?highlight=collisions#non-standard-packed-mode
In deposit function allows users to deposit Ether into the contract based on two string inputs: _string1 and _string2.
The contract uses the keccak256 function to generate a unique hash by concatenating these two strings.
If two different combinations of _string1 and _string2 produce the same hash value, a hash collision will occur.
The code does not handle this scenario properly and allows the second depositor to overwrite the previous deposit.
Mitigation:
use of abi.encode() instead of abi.encodePacked()
REF:
https://twitter.com/1nf0s3cpt/status/1676476475191750656
https://docs.soliditylang.org/en/v0.8.17/abi-spec.html?highlight=collisions#non-standard-packed-mode
https://swcregistry.io/docs/SWC-133
https://github.com/sherlock-audit/2022-10-nftport-judging/issues/118
*/
contract ContractTest is Test {
HashCollisionBug HashCollisionBugContract;
function setUp() public {
HashCollisionBugContract = new HashCollisionBug();
}
function testHash_collisions() public {
emit log_named_bytes32(
"(AAA,BBB) Hash",
HashCollisionBugContract.createHash("AAA", "BBB")
);
HashCollisionBugContract.deposit{value: 1 ether}("AAA", "BBB");
emit log_named_bytes32(
"(AA,ABBB) Hash",
HashCollisionBugContract.createHash("AA", "ABBB")
);
vm.expectRevert("Hash collision detected");
HashCollisionBugContract.deposit{value: 1 ether}("AA", "ABBB"); //Hash collision detected
}
receive() external payable {}
}
contract HashCollisionBug {
mapping(bytes32 => uint256) public balances;
function createHash(
string memory _string1,
string memory _string2
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_string1, _string2));
}
function deposit(
string memory _string1,
string memory _string2
) external payable {
require(msg.value > 0, "Deposit amount must be greater than zero");
bytes32 hash = createHash(_string1, _string2);
// createHash(AAA, BBB) -> AAABBB
// createHash(AA, ABBB) -> AAABBB
// Check if the hash already exists in the balances mapping
require(balances[hash] == 0, "Hash collision detected");
balances[hash] = msg.value;
}
}