Open code423n4 opened 1 year ago
I don't really see what the issue is. The report is describing token holders colluding to fund themselves and split the proceed, but it does seem like a totally legit way of using the protocol considering it is "their" treasury if they have the required voting power.
Picodes changed the severity to QA (Quality Assurance)
Picodes marked the issue as grade-b
Lines of code
https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/GrantFund.sol#L13 https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L56 https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L85 https://github.com/code-423n4/2023-05-ajna/blob/main/ajna-grants/src/grants/base/ExtraordinaryFunding.sol#L173
Vulnerability details
Impact
Most of the treasury funds are emptied and the funds sent to the malicious users pro-rata to their voting power. This is possible both for standard and extraordinary funding. It would make it very hard if not impossible to give legit standard or extraordinary grants.
Proof of Concept
ExtraordinaryFunding
ExtraordinaryFunding let's users execute any proposal provided the received votes reach a threshold of
which totals
1_000_000_000
at the beginning (using the numbers from the tests), increasing with each funded proposal.Thus, users can create a contract which receives the funds and distributes to users according to their votes, such as the following contract
MaliciousGrantReceiver
.Add the following test to
ExtraordinaryFunding.t.sol
.In the test, users were able to go through 3 funding rounds with the initial votes, by claiming the rewards of the last round, waiting 33 blocks and repeating. Many variations of this could be performed, it's just an example.
In the test there were 24 users with
50_000_000
ajna tokens each, but more users could participate with less tokens each.An approximation of the reward per vote can be given by (according to the tested example):
rewardPerVote = treasuryStolenFunds/initialTokensNeeded = 417_500_000 / 1_200_000_000 = 0.4175 = 34.8%
. This is a significant incentive to perform the attack.StandardFunding
In standard funding, users have to beat the votes on other proposals, so the exploit and profit calculations are dependent on the behaviour of the other users.
The example malicious contract code
MaliciousGrantReceiver
is shown below.Add the following test to
StandardFunding.t.sol
.In this tests, 6 of the 24 users collaborated to sends funds to the MaliciousGrantReceiver. Each user received
2_500_000
rewards, having an initial balance of50_000_000
tokens, a 5% return. The numbers can vary a lot depending on the other users votes, but this example ilustrated the danger.Tools Used
Vscode, Foundry
Recommended Mitigation Steps
A possible (but limiting) solution would be to check if the target is a contract, but it's possible to dodge this by calling
GrantFund
while the malicious contract is in the constructor or by using create2 to predict an address. Still makes the attack harder.The most robust solution is only letting the recipient of the
transfer(...)
callexecuteExtraordinary(...)
and adding the checkrequire(tx.origin == msg.sender)
. This is the only way to make sure that the recipient is an EOA, which makes coordinating this attack almost impossible, given that users would have to trust someone.Assessed type
Other