Platform: Code4rena
Start Date: 17/02/2023
Pot Size: $38,600 USDC
Total HM: 5
Participants: 5
Period: 5 days
Judge: GalloDaSballo
Total Solo HM: 2
Id: 217
League: ETH
Rank: 5/5
Findings: 2
Award: $0.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: bin2chen
Also found by: 0xsomeone, AkshaySrivastav, hihen
Data not available
https://github.com/code-423n4/2023-02-kuma/blob/main/src/kuma-protocol/KIBToken.sol#L276-L292
The KIBToken._transfer
function overrides the ERC20Upgradeable._transfer
function and adds custom logic.
The modified function looks like this:
function _transfer(address from, address to, uint256 amount) internal override { // ... uint256 startingFromBalance = this.balanceOf(from); if (startingFromBalance < amount) { revert Errors.ERC20_TRANSFER_AMOUNT_EXCEEDS_BALANCE(); } uint256 newFromBalance = startingFromBalance - amount; uint256 newToBalance = this.balanceOf(to) + amount; uint256 previousEpochCumulativeYield_ = _previousEpochCumulativeYield; uint256 newFromBaseBalance = WadRayMath.wadToRay(newFromBalance).rayDiv(previousEpochCumulativeYield_); uint256 newToBaseBalance = WadRayMath.wadToRay(newToBalance).rayDiv(previousEpochCumulativeYield_); if (amount > 0) { _totalBaseSupply -= (_baseBalances[from] - newFromBaseBalance); _totalBaseSupply += (newToBaseBalance - _baseBalances[to]); _baseBalances[from] = newFromBaseBalance; _baseBalances[to] = newToBaseBalance; } // ... }
It can be seen that while performing the transfer of amount
tokens, the function cache the token balances of from
and to
in temporary variables. These cached values do not represent the actual balances of accounts in an edge case.
This implementation of _transfer function can be exploited by any KIBToken holder by passing their own address as the to
parameter. When the from
and to
parameters are equal the function simply doubles the balance of that respective account. So any token holder holding x
token at the start of function invocation will have 2x
token at the end of invocation.
The bug can be repeated infinitely to gain a huge KIBToken token balance. This huge token balance can be used to drain assets from other contracts of the protocol, as well as to drain liquidity pools of KIBToken.
This test case was added to test/kuma-protocol/kib-token/KIBToken.transfer.t.sol
and ran using forge test -m test_audit
.
function test_audit_doubleMoneyBug() public { address alice = makeAddr("alice"); assertEq(_KIBToken.balanceOf(alice), 0); // Initial balance is 0 _KIBToken.mint(alice, 100e18); // Tokens minted assertEq(_KIBToken.balanceOf(alice), 100e18); // Balance is now 100e18 vm.startPrank(alice); _KIBToken.transfer(alice, 100e18); // Tokens transferred to self assertEq(_KIBToken.balanceOf(alice), 200e18); // Balance is doubled 200e18 }
Forge
Consider adding a check require(from != to)
in the _transfer
function. Or, always try to reference the storage parameters directly instead of storing values in temporary variables.
#0 - c4-judge
2023-02-23T13:42:52Z
GalloDaSballo marked the issue as primary issue
#1 - GalloDaSballo
2023-02-23T13:43:00Z
Example of a short and sweet description with good POC
#2 - c4-judge
2023-02-26T19:51:22Z
GalloDaSballo marked the issue as duplicate of #3
#3 - GalloDaSballo
2023-02-26T19:52:23Z
Removed primary due to inaccuracy in statements I will not penalize further though as I believe the POC offered covers the exploit sufficiently
#4 - c4-judge
2023-02-26T19:59:28Z
GalloDaSballo marked the issue as satisfactory
#5 - c4-sponsor
2023-02-28T03:11:22Z
m19 marked the issue as sponsor confirmed
🌟 Selected for report: hihen
Also found by: 0x52, 0xsomeone, AkshaySrivastav, bin2chen
Data not available
https://github.com/code-423n4/2023-02-kuma/blob/main/src/kuma-protocol/KUMAFeeCollector.sol#L152
In KUMAFeeCollector contract, the addPayee
validates that an already present payee cannot be added again to the _payees
set.
function addPayee(address payee, uint256 share) external override onlyManager { if (_payees.contains(payee)) { revert Errors.PAYEE_ALREADY_EXISTS(); } // ... }
However a similar check is not present in the changePayees
function.
https://github.com/code-423n4/2023-02-kuma/blob/main/src/kuma-protocol/KUMAFeeCollector.sol#L152
So a single payee can be added multiple times to the _payees
set using the changePayees
function.
Consider this scenario:
addPayee
function each account can be added only once.changePayees
function, Account1 can be added twice to the _payees
set.The final payees list looks like this:
Manual review
Consider adding a check in changePayees
function also which prevent addition of duplicate payee entries.
#0 - c4-judge
2023-02-26T19:53:40Z
GalloDaSballo marked the issue as duplicate of #13
#1 - GalloDaSballo
2023-02-26T19:53:48Z
Awarding 50% due to lack of detail in terms what goes wrong vs primary
#2 - c4-judge
2023-02-26T19:53:51Z
GalloDaSballo marked the issue as partial-50
#3 - akshaysrivastav
2023-03-01T04:58:25Z
Hey @GalloDaSballo I think this report also mentions all the necessary technical details as menttioned in #13. Just that, I intended the report to be precise and small.
As you can see, the example scenario above shows the exact _totalShares
and individual shares values.
#4 - c4-judge
2023-03-01T11:21:04Z
GalloDaSballo marked the issue as full credit
#5 - GalloDaSballo
2023-03-01T11:21:25Z
After re-reading, I agree that the finding shows the issue with accounting, restoring full credit
#6 - c4-judge
2023-03-01T16:44:45Z
GalloDaSballo marked the issue as satisfactory