Description: The AaveDIVAWrapperCore::_registerCollateralToken
function improperly uses abi.encodePacked()
with dynamic types when concatenating strings.
WToken _wTokenContract = new WToken(
string(abi.encodePacked("w", _collateralTokenContract.symbol())),
_collateralTokenContract.decimals(),
address(this)
);
Impact: This can result in potential hash collisions that could be exploited to disrupt the system's integrity.
Proof of Concept:
string(abi.encodePacked("0x123", "456")) => "0x123456"
which is exactly identical to:
string(abi.encodePacked("0x1", "23456")) => "0x123456"
Proof of Code:
contract Exploit {
function demonstrateCollision() external pure returns (bool) {
bytes memory collision1 = abi.encodePacked("0x123", "456");
bytes memory collision2 = abi.encodePacked("0x1", "23456");
console.log("Collision 1: %s", string(collision1));
console.log("Collision 2: %s", string(collision2));
console.log("Hash 1: %s", toHexString(keccak256(collision1)));
console.log("Hash 2: %s", toHexString(keccak256(collision2)));
return keccak256(collision1) == keccak256(collision2);
}
function toHexString(bytes32 data) internal pure returns (string memory) {
bytes memory hexChars = "0123456789abcdef";
bytes memory str = new bytes(64);
for (uint256 i = 0; i < 32; i++) {
str[i * 2] = hexChars[uint8(data[i] >> 4)];
str[1 + i * 2] = hexChars[uint8(data[i] & 0x0f)];
}
return string(str);
}
}
contract ExploitTest is Test {
Exploit exploit;
function setUp() public {
exploit = new Exploit();
}
function testHashCollision() public view {
bool result = exploit.demonstrateCollision();
assertTrue(result, "Hash collision did not occur as expected");
}
}
Result:
$ forge test --mt testHashCollision -vvv
[PASS] testHashCollision() (gas: 89574)
Logs:
Collision 1: 0x123456
Collision 2: 0x123456
Hash 1: 5462d984a8e2b55d8deb1f69505cec3a1118749768d005cc3792f6f32dfd78ee
Hash 2: 5462d984a8e2b55d8deb1f69505cec3a1118749768d005cc3792f6f32dfd78ee
Tools Used: [Aderyn](https://github.com/Cyfrin/aderyn)
Recommended Mitigation: Use abi.encode()
instead which will pad items to 32 bytes, which will [prevent hash collisions](https://docs.soliditylang.org/en/v0.8.13/abi-spec.html#non-standard-packed-mode). Unless there is a compelling reason, abi.encode
should be preferred.
WToken _wTokenContract = new WToken(
string(abi.encode("w", _collateralTokenContract.symbol())),
_collateralTokenContract.decimals(),
address(this)
);