TimeswapPair.sol#borrow() takes a user input value of yIncrease, and the BorrowMath.check() at L316 only checks for a minimal yIncrease, which means that it allows the state of pool.state.y to increase by the value of the user's input as long as it's larger than minimal.
Even though a large number of yIncrease means that the user will need to pay more interest, the attacker can use a small amount xDecrease (1 wei for example) so that the collateral can be a small amount and simply default the loan at minimal cost (dust amount of collateral).
As a result, pool.state.y can be set to an arbitrary target value by the attacker.
When pool.state.y is extremely large, many core features of the protocol will malfunction, as the arithmetic related to state.y can overflow. For example:
An attacker can set state.y to a near overflow value, then lend() to get a large amount of extra interest (as Bond tokens) with a small amount of asset tokens. This way, the attacker can steal funds from other lenders and liquidity providers.
PoC
Near the maturity time, the attacker can do the following:
borrow() a dust amount of assets (xDecrease = 1 wei) and increase pool.state.y to an extremely large value;
lend() a regular amount of state.x, get a large amount of bond token;
burn() the bond token and get a large portion of the assets.
Recommendation
Consider making pair.borrow() to be onlyConvenience, so that yIncrease will be a computed value (based on xDecrease and current state) rather than a user input value.
Handle
WatchPug
Vulnerability details
TimeswapPair.sol#borrow()
takes a user input value ofyIncrease
, and theBorrowMath.check()
at L316 only checks for a minimalyIncrease
, which means that it allows the state ofpool.state.y
to increase by the value of the user's input as long as it's larger than minimal.Even though a large number of
yIncrease
means that the user will need to pay more interest, the attacker can use a small amountxDecrease
(1 wei for example) so that the collateral can be a small amount and simply default the loan at minimal cost (dust amount of collateral).As a result,
pool.state.y
can be set to an arbitrary target value by the attacker.https://github.com/code-423n4/2022-01-timeswap/blob/bf50d2a8bb93a5571f35f96bd74af54d9c92a210/Timeswap/Timeswap-V1-Core/contracts/TimeswapPair.sol#L299-L338
Impact
pool.state.y
is extremely large, many core features of the protocol will malfunction, as the arithmetic related tostate.y
can overflow. For example:LendMath.check(): https://github.com/code-423n4/2022-01-timeswap/blob/bf50d2a8bb93a5571f35f96bd74af54d9c92a210/Timeswap/Timeswap-V1-Core/contracts/libraries/LendMath.sol#L28-L28
BorrowMath.check(): https://github.com/code-423n4/2022-01-timeswap/blob/bf50d2a8bb93a5571f35f96bd74af54d9c92a210/Timeswap/Timeswap-V1-Core/contracts/libraries/BorrowMath.sol#L31-L31
state.y
to a near overflow value, thenlend()
to get a large amount of extra interest (as Bond tokens) with a small amount of asset tokens. This way, the attacker can steal funds from other lenders and liquidity providers.PoC
Near the maturity time, the attacker can do the following:
borrow()
a dust amount of assets (xDecrease
= 1 wei) and increasepool.state.y
to an extremely large value;lend()
a regular amount ofstate.x
, get a large amount of bond token;burn()
the bond token and get a large portion of the assets.Recommendation
Consider making
pair.borrow()
to beonlyConvenience
, so thatyIncrease
will be a computed value (based onxDecrease
and current state) rather than a user input value.