Open hats-bug-reporter[bot] opened 1 month ago
Any reason why this issue being submitted twice? the core issue is same as #16 , the compound function can drain AZERO. The user can drain incentive which is AZERO, so in my opinion this is the same as #16
also, after calling compound
, isn't the nominator balance will 0, so there will be nothing to compound again (until it released to nominator pool again), so isn't it will just return 0 for the let (compounded, incentive) = self.data.delegate_compound()?;
?
Yes this is the same as issue #16, I tried to clarify by outlining that in the description. I was not satisfied with my outlining of the impact in the first issue so I wanted to describe it better in this issue. As shown in the PoC, it will not return 0 and just keep on paying out rewards. I was able to drain almost all pooled funds that way.
This is in case tokens are sent to the vault by transfer, not by stake. After this call the balance of the nomination agent is 0, since balance - incentive is bonded to the nomination pool and incentive is sent to the vault.
Thank you for your submission. the test is on mock example, in nomination_agent contract remain balance is sent to the nomination pool https://github.com/hats-finance/Kintsu-0x7d70f9442af3a9a0a734fa6a1b4857f25518e9d2/blob/c9bdc853b18c305de832307b91a9bca0f281f71e/src/nomination_agent/lib.rs#L158
This is in case tokens are sent to the vault by transfer, not by stake. After this call the balance of the nomination agent is 0, since balance - incentive is bonded to the nomination pool and incentive is sent to the vault.
This is correct
Github username: -- Twitter username: -- Submission hash (on-chain): 0x20e4fe2078ccb2d540378a522b0d994b3ba56593205a238211478864a563e726 Severity: high
Description: Description\ (More detailed impact for issue with submission hash 0xb0c97ae7e55a984c308ca229d1aa3d9b6f7e76545fe868818b7101f4127fd099)
Attack Scenario\ Assuming we take one of the already existing testcases for the
compound
function, which adds two agents, each with 10000 bonded AZERO each. This means we have 20000 AZERO bonded and our calculation for the incentive (happening indata.delegate_compound
) is therefore20000 * (100% - 0.05%)
. This is taking the standard incentive percentage of0.05%
. Calculating this, we get an incentive of10 AZERO
and after callingcompound
once, thetotal_pooled
went from20000
to19990
.If now a user goes ahead and just calls
compound
3 times we get the following calculations:20000 * (1 - 0.0005) = 19990
->incentive = 10
19990 * (1 - 0.0005) = 19980.005
->incentive = 10
19980 * (1 - 0.0005) = 19970.01xx
->incentive = 10
Looking at this we see that we can consistently withdraw funds from the pool. Therefore we can drain the pool and steal its funds.
Since the incentive is calculated as a percentage, this attack will be more effective if there are more assets in the pool or the owner decides to increase the incentives percentage.
Attachments
Add the following into
drink_test/lib.rs
comment out the line wherecharlie
gets initial funds and then execute it withpnpm test test_compound_drain_funds
NOTE: this test runs quite long. If it takes too long or you run out of RAM, please decrease the size of the for loop, it will still proof the concept. In that case the last assertion must be changed as it depends on the amount of iterations
Looking at this test, we were able to drain more than
75%
of the funds, possibly even more.