New Smart Contract Weakness: Hash Collisions With Multiple Variable Length Arguments
A new smart contract weakness was recently discovered by Steve Marx. As per the title of this article, the bug can result in a hash collision on functions with multiple variable-length arguments. Let’s take a look at an example to see the vulnerability in action.
As we can see in the contract, if the
addUsers function is called by an admin, arrays of admins and
regularUsers are added to mappings of
isRegularUser. If the function is not called by an admin, it can be relayed with an admins signature.
The vulnerability can be found on line 15 with the use of
abi.encodePacked(). The problem lies in the way that
abi.encodePacked() manages its parameters. The following two statements return the same value, even though the parameters are unique.
Given that different parameters can return the same value, an attacker could exploit this by modifying the position of elements in a previous function call to effectively bypass authorization. For example, if an attacker saw
addUser([addr1, addr2], [addr3, <attacker’s address>, addr4], sig), they could call
addUser([addr1, addr2, addr3, <attacker’s address>], [addr4], sig). Since the return values are the same, the signature will still match, making the attacker an admin. Though the contract should have proper replay protection, an attacker can still bypass this by front-running.
There are a few different remediation's we can take to prevent this vulnerability. The first option is to not allow arrays as parameters, instead of passing a single value. Another option would be to only allow arrays of fixed lengths, so the positions cannot be modified. Finally, we can avoid this vulnerability by simply opting for the use of
abi.encode() instead of