Open sherlock-admin2 opened 4 weeks ago
yes this is valid and have been fixed prior to our trading competition. will provide links later
Escalate, This is gas optimization issue at best. The reverseSortWithAmount224() function has the following implementation:
function reverseSortWithAmount224( address token0, address token1, address ra, address ct, uint256 token0Amount, uint256 token1Amount ) internal pure returns (uint256 raAmountOut, uint256 ctAmountOut) { if (token0 == ra && token1 == ct) { raAmountOut = token0Amount; ctAmountOut = token1Amount; } else if (token0 == ct && token1 == ra) { raAmountOut = token1Amount; ctAmountOut = token0Amount; } else { revert InvalidToken();
You've deleted an escalation for this issue.
@AtanasDimulski, I think you misunderstood something.
The issue is simply due to the unnecessary application of the reverseSortWithAmount224()
function.
That function reorders the amounts, which were already well-ordered.
@KupiaSecAdmin you are correct, my mistake. Nice catch!
escalate
This issue is invalid because it requires the ra address to be bigger than the ct address. Ra and ct are picked by the admin, in which ct results from the create opcode, so the admin has complete control on them. Place where the admin picks ra. Ct is created here, which uses the create opcode, so the admin can create dummy ra,ct pairs just to get a ct address bigger than ra. As such, it should pick values that do not cause issues.
(External) Admin trust assumptions: When a function is access restricted, only values for specific function variables mentioned in the README can be taken into account when identifying an attack path.
If no values are provided, the (external) admin is trusted to use values that will not cause any issues.
escalate
This issue is invalid because it requires the ra address to be bigger than the ct address. Ra and ct are picked by the admin, in which ct results from the create opcode, so the admin has complete control on them. Place where the admin picks ra. Ct is created here, which uses the create opcode, so the admin can create dummy ra,ct pairs just to get a ct address bigger than ra. As such, it should pick values that do not cause issues.
(External) Admin trust assumptions: When a function is access restricted, only values for specific function variables mentioned in the README can be taken into account when identifying an attack path.
If no values are provided, the (external) admin is trusted to use values that will not cause any issues.
You've created a valid escalation!
To remove the escalation from consideration: Delete your comment.
You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.
@0xsimao
I don't agree with your insistence.
ct
is not a variable that the admin needs to monitor closely to avoid any issues. If the admin consistently chooses ct
to be greater than ra
, there is no need to implement mechanisms to sort raAmount
and ctAmount
.so the admin can create dummy ra,ct pairs just to get a ct address bigger than ra.
- Whenever
ct
is created, a Uniswap V2 pool (ra, ct) is established, which incurs significant costs. Therefore, creating dummy entries untilct > ra
is not a feasible action.
(ds, ct)
, there is an expiration time that prevents immediate issuance of new (ds, ct)
.@KupiaSecAdmin still, the admin picks the ra and ct address, so according to the rule, it is trusted to pick values that do not cause any issues.
@0xsimao
I totally disagree with your insistence.
- Additionally, when issuing new
(ds, ct)
, there is an expiration time that prevents immediate issuance of new(ds, ct)
.
When issuing a new (ds, ct)
, the previous (ds, ct)
must be expired. Therefore, once a new (ds, ct) is issued, it cannot be immediately canceled, even if ct < ra
. How can the admin pick the ct address?
This is a clear bug, and the sponsor has also confirmed it.
So, I believe the judge will make the correct decision.
It does not have to cancel it, it can create dummy ra, ct pairs. ct
is created via the create()
opcode, so we can always find ct addresses bigger than ra. The address used for the ra in the dummy can be another, just to increase the creation nonce of the contract and change the ct address.
Additionally, the admin can order the issuance of ra, ct pairs according to their addresses. If we have ra1 = 5, ra2 = 3, ct1 = 4, ct2 = 6, the admin can do pairs ra2,ct1 and ra1,ct2. If the ra address is bigger than the next ct, and the admin really wants to use this ra, he can create other pairs to increase the ct address and then create the pair with the correct ct.
Another option for the admin is to create a wrapper for ra that has a smaller address, if needed.
I am not saying the bug does not exist, it does, but it is invalid according to the sherlock rules, as it is picked solely by the admin and the admin is trusted to use values that do not cause issues.
The protocol team fixed this issue in the following PRs/commits: https://github.com/Cork-Technology/Depeg-swap/pull/71/commits/4944b7dbf56144d389fa578432536bf1c44539c2
(External) Admin trust assumptions: When a function is access restricted, only values for specific function variables mentioned in the README can be taken into account when identifying an attack path.
If no values are provided, the (external) admin is trusted to use values that will not cause any issues.
@0xsimao,
Your assertions do not align with the Sherlock rule you mentioned.
CT
, as mentioned in your earlier comment, is not intended and appears to be an overestimation of the protocol design to force it to align with the Sherlock rule.It does not have to cancel it, it can create dummy ra, ct pairs.
ct
is created via thecreate()
opcode, so we can always find ct addresses bigger than ra. The address used for the ra in the dummy can be another, just to increase the creation nonce of the contract and change the ct address.Additionally, the admin can order the issuance of ra, ct pairs according to their addresses. If we have ra1 = 5, ra2 = 3, ct1 = 4, ct2 = 6, the admin can do pairs ra2,ct1 and ra1,ct2. If the ra address is bigger than the next ct, and the admin really wants to use this ra, he can create other pairs to increase the ct address and then create the pair with the correct ct.
Another option for the admin is to create a wrapper for ra that has a smaller address, if needed.
I am not saying the bug does not exist, it does, but it is invalid according to the sherlock rules, as it is picked solely by the admin and the admin is trusted to use values that do not cause issues.
Your method does not focus on controlling specific function variables; rather, it centers on managing the create opcode nonce by modifying the protocol design. Therefore, it does not align with the Sherlock rule.
issueNewDs()
function are determined by the protocol strategy, not to prevent CT < RA
.(External) Admin trust assumptions: When a function is access restricted, only values for specific function variables mentioned in the README can be taken into account when identifying an attack path.
If no values are provided, the (external) admin is trusted to use values that will not cause any issues.
I believe you were pointing to the issueNewDs()
function as access restricted
. This function has the following parameters:
function issueNewDs(Id id, uint256 expiry, uint256 exchangeRates, uint256 repurchaseFeePrecentage)
These parameters do not have arbitrary values; each has a specific meaning. Therefore, the admin does not set these values to generate a CT
greater than RA
. Consequently, the Sherlock rule you mentioned does not apply to my issue.
The admin is trusted to use the right ra and ct values, and they are fully in their control. There are ra and ct values that the admin can use that do not cause any issues. Hence, the rule applies.
The id in the function points to a specific ra, so it can choose the ra value. The ct value comes from the nonce, as explained earlier, and can also be picked to not cause any issues.
The admin is trusted to use the right ra and ct values, and they are fully in their control. There are ra and ct values that the admin can use that do not cause any issues. Hence, the rule applies.
CT is not under the admin's control; it is clearly an internal address that the admin disregards.
The id in the function points to a specific ra, so it can choose the ra value. The ct value comes from the nonce, as explained earlier, and can also be picked to not cause any issues.
Id
denotes (ra, pa). Yes, we are concentrating on a specific fixed Id
.
For a given Id
, your method of controlling the generated CT is not feasible. It could only be possible if the admin creates a fake Id to increase the nonce. This approach does not involve controlling function variables; rather, it stems from an overestimation of the protocol design.
Therefore, the rule does not apply. I believe there may have been a misunderstanding.
Once again, I trust that the judge will make the right decision.
CT is not under the admin's control; it is clearly an internal address that the admin disregards. For a given Id, your method of controlling the generated CT is not feasible. It could only be possible if the admin creates a fake Id to increase the nonce. This approach does not involve controlling function variables; rather, it stems from an overestimation of the protocol design.
The admin would disregard it if it was not trusted. But he is trusted, so he should do the correct thing and use proper ra and ct values, which is possible, as mentioned before.
As per the argument that the admin should not create dummy ct addresses to get one higher than ra and prevent this issue, this is the reason #180 was invalidated, as the admin could do this to create another pool. So here the admin must also be able to use a proper ct and mitigate this issue.
KupiaSec
High
The
VaultLib.__liquidateUnchecked()
function unnecessarily reorders the already correctly ordered values ofraReceived
andctReceived
fromUniswapV2Router
Summary
The
__liquidateUnchecked()
function removes liquidity and receives the correspondingRA
andCT
from UniswapV2, returning the amounts ofRA
andCT
received. When removing liquidity, theremoveLiquidity()
function of theUniswapV2Router
returnsraReceived
andctReceived
, which are the exact amounts ofRA
andCT
corresponding to the removed liquidity, regardless of the order of tokensRA
andCT
in the Uniswap pool. However, in the__liquidateUnchecked()
function, these values are reordered as if they represent the received amounts oftoken0
andtoken1
, assuming thattoken0 = CT
andtoken1 = RA
.Vulnerability Detail
As noted in line 282 of the
__liquidateUnchecked()
function, theammRouter.removeLiquidity()
function returns two values:raReceived
andctReceived
. These values represent the exact amounts ofRA
andCT
received from the Uniswap pool, even in the case wheretoken0 = CT
andtoken1 = RA
(seeUniswapV2Router02.sol
). Therefore, there is no need to reorder these values. However, at line 284, the function reorders them, assumingtoken0 = CT
andtoken1 = RA
. As a result, whentoken0 = CT
andtoken1 = RA
, the values ofraReceived
andctReceived
will be interchanged. This leads to incorrect behavior in the _liquidatedLp() function, which processes expired states when issuing new DS. Consequently, an incorrectRA
amount is reserved in the reserve pool, potentially leading to a loss ofRA
for users.Impact
Loss of
RA
for users.Code Snippet
https://github.com/sherlock-audit/2024-08-cork-protocol/tree/main/Depeg-swap/contracts/libraries/VaultLib.sol#L270-L289
https://github.com/Uniswap/v2-periphery/blob/master/contracts/UniswapV2Router02.sol#L116
Tool used
Manual Review
Recommendation
Eliminate the reordering.