Submitted by ladboy233, also found by seyni, slvDev, minhquanym, gzeon, karanctf, Matin, 0x1f8b, yixxas, UniversalCrypto, obront, and rvierdiiev
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/LPDA.sol#L71
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/LPDA.sol#L82
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/LPDA.sol#L101
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/FixedPrice.sol#L62
Unsafe downcasting operation truncates users input.
There are a few unsafe downcasting operation that truncates users input. The impact can be severe or minimal.
In FixedPrice.sol,
The amount is unsafely downcasted from uint256 to uint48, note the code:
The upper limit for uint48 is 281474976710655,
If user wants to buy more than 281474976710655 amount of NFT and pay the 281474976710655 * sale price amount, the user can only receive 281474976710655 amount of NFT because of the downcasting.
In LPDA.sol, we are unsafely downcasting the price in the buy function:
Note the line:
uint80(msg.value) and uint80(price)
In LPDA.sol, same issue exists in the refund function:
Note the downcasting: uint80(getPrice())
This means if the price goes above uint80, it will be wrongly truncated to uint80 for price.
The Downcasting in LPDA.sol is damaging because it truncated users fund.
Below is the POC:
Add test in LPDA.t.sol
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/test/LPDA.t.sol#L167
And import forge-std/console.sol in LPDA.sol:
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/LPDA.sol#L3
And add console.log in LPDA.sol buy function.
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/LPDA.sol#L69
We run our test:
The output is:
As we can see, user uses 1208935819614629174706175 to buy in LPDA.sol but the balance is truncated to 9999999999999999999, later user is not able to get the refund they are entitled to because the msg.value is unsafely downcasted.
Also note the downcasting for getPrice in LPDA.sol is also a issue:
The getPrice in LPDA.sol returns a uint256
But this is downcasted into uint80 in function buy and refund.
We recommend the project handle downcasting and use safe casting library to make sure the downcast does not provide an unexpected truncate value.
https://docs.openzeppelin.com/contracts/3.x/api/utils#SafeCast
stevennevins (Escher) confirmed, but disagreed with severity and commented:
berndartmueller (judge) commented:
