Open c4-bot-4 opened 10 months ago
141345 marked the issue as primary issue
141345 marked the issue as sufficient quality report
Alec1017 (sponsor) acknowledged
Dubious this should qualify as M. Its an expensive attack vector with limited economic value to be gained. Users would certainly begin to leverage more private transaction methods to avoid the front running. Welcome more discussion during PJQA, but may decide to downgrade.
0xean marked the issue as satisfactory
0xean marked the issue as selected for report
This is invalid. We can see that the incremented value of Store.totalSafes()
is passed as part of the saltNonce to createProxyWithNonce:
safe = address(
safeProxyFactory.createProxyWithNonce(
address(safeSingleton),
initializerPayload,
// @audit totalSafes() + 1 included in saltNonce
uint256(keccak256(abi.encode(STORE.totalSafes() + 1, block.chainid)))
)
);
And STORE.addRentalSafe, called in deployRentalSafe, increments totalSafes:
function addRentalSafe(address safe) external onlyByProxy permissioned {
// Get the new safe count.
uint256 newSafeCount = totalSafes + 1;
// Register the safe as deployed.
deployedSafes[safe] = newSafeCount;
// Increment nonce.
totalSafes = newSafeCount;
}
We can see in createProxyWithNonce that the saltNonce changes the salt and thus the deployed address:
function createProxyWithNonce(address _singleton, bytes memory initializer, uint256 saltNonce) public returns (SafeProxy proxy) {
// If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
proxy = deployProxy(_singleton, initializer, salt);
emit ProxyCreation(proxy, _singleton);
}
Therefore, frontrunning deployRentalSafe will not cause any problems since both transactions will have different saltNonce's and therefore different deployment addresses.
Hey @0xean! I'd like to add that this issue is not only about the frontrunning, griefer can pass the same salt to the gnosis safe factory as the reNFT factory. If we know the victim's address and the current total number of safes, it's quite easy to predict the salt and create multiple safes in advance. For example Alice wants to create a reNFT safe, salt is calculated with: address - 0xa11ce, threshold - 1, totalSafes - 10, other parameters are always the same. The attacker can use these parameters and create 5 safes in the gnosis safe factory, therefore blocking Alice from creation of the new safe until other reNFT users will create 5 safes in the protocol to set totalSafes counter to 15.
All in all, the feasibility of this attack vector depends on the popularity of the protocol, how many new safes will be created every day; if totalsSafes counter moves slow enough, it can be pretty significant.
@kadenzipfel hi, the idea is to call gnosis safe factory directly, bypassing the reNFT factory
No loss of funds, no permanent loss of protocol functionality (only temporary), high cost of attack, no profit. Seems a Low/QA.
Also I'd argue about the cost since the sponsor plans to deploy on polygon network and transactions there are quite cheap, here is the hash of Create proxy with nonce
of the local factory (only $0.01)
https://polygonscan.com/tx/0xdf0ebb56d6698d95a326e48ca68d92c318ce2b01cc1d246da83fe5d215ac6388
We have an open org issue on this type of attack: https://github.com/code-423n4/org/issues/143 I personally believe it should be a low (I've also reported it as a low at #330)
Hi @0xean ,
A similar finding, https://github.com/code-423n4/2023-04-caviar-findings/issues/419, is considered as a medium risk in a previous contest though such DOS issue could be sidestepped according to https://github.com/code-423n4/2023-04-caviar-findings/issues/419#issuecomment-1528974537. Due to the similarity, would this finding be considered as a medium issue as well? Thanks again for judging!
gonna stand with @dmvt on this one. Downgrading all of these to QA. Final ruling.
0xean changed the severity to QA (Quality Assurance)
Hi @0xean,
After this finding is downgraded to QA, 7 findings of mine are considered as QA now, which are this one (#443), #436, #438, #446, #447, #452, and #455. Comparing to some other QA reports that have A grades, such as #293, my number of QA items is higher. Hence, would it be possible for my QA items to be graded A instead of B? Thanks!
@rbserver - @141345 will recheck your score based on downgrades.
@rbserver - @141345 will recheck your score based on downgrades.
Yes, 7 low should be ranked 1st for QA
0xean marked the issue as grade-a
0xean marked the issue as not selected for report
Lines of code
https://github.com/re-nft/smart-contracts/blob/cbf5ff74e40576be72090afd99bf6f0c366bd315/src/policies/Factory.sol#L138-L193 https://github.com/safe-global/safe-contracts/blob/eb93dbb0f62e2dc1b308ac4c110038062df0a8c9/contracts/proxies/SafeProxyFactory.sol#L52-L57 https://github.com/safe-global/safe-contracts/blob/eb93dbb0f62e2dc1b308ac4c110038062df0a8c9/contracts/proxies/SafeProxyFactory.sol#L26-L44
Vulnerability details
Impact
The
Factory.deployRentalSafe
transaction can be frontrun by calling theSafeProxyFactory.createProxyWithNonce
function with the sameaddress(safeSingleton)
,initializerPayload
,uint256(keccak256(abi.encode(STORE.totalSafes() + 1, block.chainid)))
being the inputs. Since this frontrunning attack can be repeated, none of the rental safes can be deployed.Proof of Concept
When the following
Factory.deployRentalSafe
function is called,address(safeSingleton)
,initializerPayload
,uint256(keccak256(abi.encode(STORE.totalSafes() + 1, block.chainid)))
, which are used for calling theSafeProxyFactory.createProxyWithNonce
function, are all known.https://github.com/re-nft/smart-contracts/blob/cbf5ff74e40576be72090afd99bf6f0c366bd315/src/policies/Factory.sol#L138-L193
After detecting a
Factory.deployRentalSafe
transaction in the mempool, a malicious actor can frontrun such transaction by directly calling theSafeProxyFactory.createProxyWithNonce
function with the sameaddress(safeSingleton)
,initializerPayload
,uint256(keccak256(abi.encode(STORE.totalSafes() + 1, block.chainid)))
being the inputs on the same chain. After the frontrunning, the address for the rental safe to be deployed is already taken, andaddress(proxy) != address(0)
would become false in theSafeProxyFactory.deployProxy
function to revert theFactory.deployRentalSafe
transaction. Hence, the rental safe deployment fails. This frontrunning attack can be repeated each time theFactory.deployRentalSafe
function is called, which means no rental safes can be deployed at all.https://github.com/safe-global/safe-contracts/blob/eb93dbb0f62e2dc1b308ac4c110038062df0a8c9/contracts/proxies/SafeProxyFactory.sol#L52-L57
https://github.com/safe-global/safe-contracts/blob/eb93dbb0f62e2dc1b308ac4c110038062df0a8c9/contracts/proxies/SafeProxyFactory.sol#L26-L44
Tools Used
Manual Review
Recommended Mitigation Steps
The
Factory.deployRentalSafe
function can be updated to additionally hashmsg.sender
as a part of thesaltNonce
input for calling theSafeProxyFactory.createProxyWithNonce
function.Assessed type
DoS