If the underlying asset amount in the IBT changes without Spectra's awareness, it could affect ibtRate without triggering Spectra's rate update logic.m The assumption that is violated is that the only way ibtRate will change is based on user interactions with the Spectra protocol. The ibtRate relies on previewing redemptions from the IBT using IERC4626(ibt).previewRedeem(ibtUnit), as shown here:
However, if assets are deposited/withdrawn directly into the IBT without Spectra's awareness, the total assets backing the IBT can change without impacting the previewRedeem view.
Proof of Concept
This allows attackers to artificially inflate yields for Spectra users by injecting assets into the IBT vault without Spectra's awareness. Users could coordinate deposits to quickly compound yields, draining value from the protocol at the expense of other users.
The _getCurrentPTandIBTRates() function relies on previewRedeem() to get the IBT rate. However, previewRedeem() only provides a view into the contract balance - it does not track deposits/withdrawals.
An attacker could:
Deposit assets into a Spectra vault, minting 1,000 IBT at a 1:1 rate with 1,000 DAI (for example).
Later, directly deposit 100 DAI into the IBT vault from an external account.
Spectra would still expect 1 IBT = 1 DAI based on previewRedeem(). But with 1,100 DAI in the vault, the true rate is 1 IBT = 1.1 DAI.
This means users can artificially inflate yields by injecting assets into the IBT vault, as Spectra cannot accurately detect the rate change.
The key issue is the assumption previewRedeem() provides an accurate view into the IBT rate at Line 902
It has no way to account for changes in assets that happen outside Spectra deploys/redeems.
Tools Used
Manual review
Recommended Mitigation Steps
Directly integrate with the IBT vault to track total asset balances rather than rely on previews. This will give it an authoritative measure of the true backing assets and rates.
Lines of code
https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L901-L914 https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L902 https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L901-L914 https://github.com/code-423n4/2024-02-spectra/blob/383202d0b84985122fe1ba53cfbbb68f18ba3986/src/tokens/PrincipalToken.sol#L902
Vulnerability details
Impact
If the underlying asset amount in the IBT changes without Spectra's awareness, it could affect
ibtRate
without triggering Spectra's rate update logic.m The assumption that is violated is that the only wayibtRate
will change is based on user interactions with the Spectra protocol. TheibtRate
relies on previewing redemptions from the IBT usingIERC4626(ibt).previewRedeem(ibtUnit)
, as shown here:However, if assets are deposited/withdrawn directly into the IBT without Spectra's awareness, the total assets backing the IBT can change without impacting the
previewRedeem
view.Proof of Concept
This allows attackers to artificially inflate yields for Spectra users by injecting assets into the IBT vault without Spectra's awareness. Users could coordinate deposits to quickly compound yields, draining value from the protocol at the expense of other users.
An attacker could:
Deposit assets into a Spectra vault, minting 1,000 IBT at a 1:1 rate with 1,000 DAI (for example).
Later, directly deposit 100 DAI into the IBT vault from an external account.
Spectra would still expect 1 IBT = 1 DAI based on
previewRedeem()
. But with 1,100 DAI in the vault, the true rate is 1 IBT = 1.1 DAI.This means users can artificially inflate yields by injecting assets into the IBT vault, as Spectra cannot accurately detect the rate change.
The key issue is the assumption
previewRedeem()
provides an accurate view into the IBT rate at Line 902It does not account for changes in the total assets that could happen without user interactions through Spectra. This assumption needs to be fixed.
To exploit this:
ibtRate
is 1 IBT = 1 DAIIERC4626.deposit()
ibtRate
is 1 IBT = 1.1 DAIpreviewRedeem
This works because _getCurrentPTandIBTRates() relies solely on previewRedeem():
It has no way to account for changes in assets that happen outside Spectra deploys/redeems.
Tools Used
Manual review
Recommended Mitigation Steps
Directly integrate with the IBT vault to track total asset balances rather than rely on previews. This will give it an authoritative measure of the true backing assets and rates.
Assessed type
Oracle