Summary
Reference type argument (a string
in this case) set as indexed
causes loss of ensName
in createNewDAOMembership
event.
Vulnerability Details
Here is what Solidity v0.8.22 docs, says about indexed arguments:
If you use a reference type for an indexed argument, the Keccak-256 hash of the value is stored as a topic instead.
contracts/dao/MembershipFactory.sol#L28
event MembershipDAONFTCreated(string indexed ensName, address nftAddress, DAOConfig daoData);
function createNewDAOMembership(DAOInputConfig calldata daoConfig, ...)
external
returns (address)
{
emit MembershipDAONFTCreated(daoConfig.ensname, ...);
}
Impact
string
is a reference type, so a meaningless hash of the ensName
is stored as a topic instead of the intended ensName
itself. Apps subscribing to the event will not be able to read the intended ensName
from the event logs.
Proof of Concept
Here is a simple contract that demonstrates the issue:
pragma solidity 0.8.22;
contract C {
string ensName = "ens.eth";
event A(string indexed ensName);
event B(string ensName);
function foo() external {
emit A(ensName);
emit B(ensName);
}
}
Call C.foo()
and check the event logs, you will see that the ensName
is stored as a hash in the A
event, but not in the B
event:
[
{
"from": "0xf8e81D47203A594245E36C48e151709F0C19fBe8",
"topic": "0xdf7fc143d886e00f490c1e53e68812e6bd49fc1a78485bbc2b04b6e7397ffd79",
"event": "A",
"args": {
+ "0": {
+ "_isIndexed": true,
+ "hash": "0x92f6719d5f98fbf25cfb5d3ad72c9007713dcaae1f94a75e61afb09d5b649fdd"
+ }
}
},
{
"from": "0xf8e81D47203A594245E36C48e151709F0C19fBe8",
"topic": "0xd35cb0d88bd19af6c5256b642973a9ba0b893024f725ea8ea69487971ab5d1ff",
"event": "B",
"args": {
+ "0": "ens.eth",
"ensName": "ens.eth"
}
}
]
Tools Used
Remix IDE, Solidity docs.
Recommendations
Consider removing the indexed
keyword from the ensName
argument:
- event MembershipDAONFTCreated(string indexed ensName, address nftAddress, DAOConfig daoData);
+ event MembershipDAONFTCreated(string ensName, address nftAddress, DAOConfig daoData);
function createNewDAOMembership(DAOInputConfig calldata daoConfig, ...)
external
returns (address)
{
// ...
emit MembershipDAONFTCreated(daoConfig.ensname, ...);
}