Cork Finance
Total Losses
$7.2M+
Date
Network
Categories
business logic price manipulation access controlStep-by-step Overview
The Cork Finance exploit unfolded in two primary, interconnected phases, orchestrated through a series of precise on-chain interactions:
- Rollover Pricing Manipulation (Cover Token Acquisition):
-
The attacker initiated a minimal
swapRaforDs
transaction shortly before the expiry of a targetedwstETH:weETH
market (specifically 19 minutes prior to expiry). -
This transaction, involving a small amount of
RA
forDS
, drastically inflated the market’s Historical Implied Yield Average (HIYA) due to the exponential sensitivity of the risk premium calculation as time to maturity approached zero. -
Upon rollover, this inflated HIYA value led to the new market’s Automated Market Maker (AMM) being initialized with an extremely low price for Cover Tokens (CT) relative to
wstETH
. -
The attacker then acquired a substantial quantity of
CT
from this manipulated AMM with a comparatively negligible amount ofwstETH
.
- Depeg Swap Extraction (Value Siphoning):
-
Following the CT acquisition, the attacker deployed a malicious smart contract (
CorkMaliciousHook
in our reporoduction) designed to function as a malicious callback. -
This malicious contract initiated a new Cork market, registering itself as the
exchangeRateProvider
for this new market’saugDS/wstETH
pair, thereby gaining direct control over its oracle feed and price rate. -
The attacker invoked Uniswap V4’s
unlock
function on thePoolManager
, directing the callback to their malicious smart contract (CorkMaliciousHook
). Within this callback,CorkMaliciousHook
invoked the legitimate CorkHook’sbeforeSwap
function, leveraging the absence of access control, supplying spoofed transaction parameters. -
These custom-made parameters bypassed insufficient internal validation within the
FlashSwapRouter
’scorkCall
function. -
This caused the
FlashSwapRouter
to transfer its unsoldaugDS
reserves into the attacker’s newly created “Exploiter’s Proxy Market,” where they were used to mintpDS
andpCT
directly for the attacker. -
Simultaneously, through AMM pricing manipulation within the
beforeSwap
context, an “over-borrowed” state was created, leading to a refund of excesspCT
to the attacker’s designated address. -
Finally, the accumulated
pCT
andpDS
were redeemed forwstETH
from the Peg Stability Module (PSM), effectively draining the Liquidity Vault.
Detailed Description
The Cork Finance exploit exemplifies a multi-stage attack that abused a combination of economic oracle manipulation and access control bypasses within a complex DeFi protocol architecture. The attacker showed understanding of the interdependencies between Cork’s core modules, specifically the ModuleCore
, FlashSwapRouter
, and the custom Cork Hook implementation alongside their business logic related to the pricing mechanisms.
Phase 1: Cover Token Extraction via Rollover Pricing Exploit
This initial vector capitalized on a subtle but critical flaw in Cork Protocol’s rollover mechanism, which aimed to ensure liquidity permanence across expiring markets. The Historical Implied Yield Average (HIYA) formula, designed to derive the historical risk premium from DS
trades, proved vulnerable to manipulation under specific, low-volume conditions.
The HIYA calculation is fundamentally influenced by the risk premium (rT
), which itself is an inverse function of the time to maturity (T
). As T
approaches zero (i.e., market expiry), rT
exhibits an exponential increase due to the 1/T
term. The attacker exploited this sensitivity by executing a timed, relatively small trade (initialBorrowAmount = 2e18 DS
with 3.5e15 RA
paid) just 19 minutes prior to the wstETH:weETH
market’s expiry. This single transaction, within a low-volume context, disproportionately skewed the volume-weighted HIYA, inflating it to an astronomical 1,779.7
quadrillion percent.
// AttackerSC_1.sol (Simplified relevant snippet)
// Guesses made with offchain calculations to inflate the risk premium
// over 1,779.7 quadrillion percent by effectively buying 2.5e18 about-to-expire
// Depeg Swap (DS) tokens
IPSMProxy.OffchainGuess memory offchainGuess;
offchainGuess.initialBorrowAmount = 2e18;
offchainGuess.afterSoldBorrowAmount = 2.55e18; // Corresponds to ~2.5 DS received
// The attacker paid only ~0.003407 wstETH for the requested DS (RA)
flashSwapProxy.swapRaforDs(PAIR_ID_FOR_RATE, 1, 3.5e15, 0, buyParams, offchainGuess);
The offchainGuess
parameters (initialBorrowAmount
, afterSoldBorrowAmount
) were pre-calculated by the attacker. While the reproduction utilized values within the same order of magnitude (specifically, initialBorrowAmount = 2e18
and afterSoldBorrowAmount = 2.55e18
), the attacker’s original exploit employed the precise values of 2035043806577874200
and 2554953564824393000
respectively. These figures were not arbitrary but were precisely calculated to achieve the desired HIYA inflation and optimize the subsequent swapRaforDs
call, thereby minimizing gas costs and ensuring exploit success by bypassing complex on-chain price calculations. The post-mortem’s statement that the attacker “purchased 2.5 DS
approximately 19 minutes prior to expiry” directly correlates with these carefully chosen offchainGuess
values.
Upon the market’s rollover, the protocol’s logic utilized this artificially inflated HIYA to initialize the new AMM. This resulted in an exceptionally low post-rollover price for Cover Tokens (CT), effectively rendering them drastically undervalued. The attacker capitalized on this by acquiring over 3,760 CT
with a minuscule amount of wstETH
, thereby establishing the foundation for the second phase of the attack.
// ModuleCore.sol
/**
* @dev Issues new assets, will auto assign amm fees from the previous issuance
* for first issuance, separate transaction must be made to set the fees in the AMM
*/
function issueNewDs(Id id, uint256 ammLiquidationDeadline) external whenNotPaused {
moduleCore.issueNewDs(id, defaultDecayDiscountRateInDays, rolloverPeriodInBlocks, ammLiquidationDeadline);
_autoAssignFees(id);
_autoAssignTreasurySplitPercentage(id);
}
Phase 2: Depeg Swap Extraction via Cork Hook and Router Exploit
This phase exploited a series of authorization and validation deficiencies within Cork’s integration layers, particularly centered around the custom Cork Hook and its interactions with FlashSwapRouter
.
The attacker’s key strategy was to deploy their own malicious contract, CorkMaliciousHook
. This contract was designed to serve as the recipient of Uniswap V4 callbacks, specifically by implementing the unlockCallback
function. This capability allowed it to receive execution control from the Uniswap V4 Pool Manager when unlock
was called with its address. This design choice enabled the attacker to establish a tightly controlled execution context. From within this callback environment, the CorkMaliciousHook
then proceeded to interact with and exploit the legitimate CorkHook
.
A critical step involved CorkMaliciousHook
initiating a new Cork market by calling corkConfig.initializeModuleCore
. Crucially, the attacker passed address(this)
(the CorkMaliciousHook
contract itself) as the exchangeRateProvider
parameter for this new market:
// CorkMaliciousHook.sol (Relevant snippet)
// 4.8 Initializes a new module core, setting self as the ExchangeRateProvider
corkConfig.initializeModuleCore(address(wstETH), _weETH8DS, 1, 100, address(this));
This call effectively registered the CorkMaliciousHook
as the authoritative oracle for the newly created augDS/wstETH
pair within Cork’s ecosystem. Consequently, any subsequent operations within Cork Protocol that queried the exchange rate for this specific market ID
would directly invoke the rate()
functions of the CorkMaliciousHook
(which are hardcoded to return 0
or 1
), granting the attacker full and unilateral control over the perceived asset pricing. This mechanism, where arbitrary contracts can dictate market-critical parameters without sufficient vetting or robust safeguards, introduces a significant area of risk within the market creation process.
Subsequently, the attacker triggered a Uniswap V4 unlock call on the PoolManager
, directing the callback
to CorkMaliciousHook
’s unlockCallback
. Within this callback, the malicious hook proceeded to invoke the legitimate CorkHook’s beforeSwap
function (which was public and lacked access control), but with crafted, spoofed hookData
. This hookData
contained manipulated parameters for caller, provided, borrowed, and reserveId
, designed to misrepresent the true state and origin of the transaction.
// CorkMaliciousHook.sol (Relevant snippet)
// Callback data passed by the attacker used when UniV4 calls this contract back
struct MaliciousCallbackData {
address weETH8DS;
address wstETH5CT;
bytes32 pairId;
address wstETH5DS;
}
// ... inside unlockCallback ...
// 4.12 Retrieves necessary information to craft the beforeSwap call
// ...
CallbackData memory flashSwapCallbackData = CallbackData({
buyDs: true,
caller: address(this), // Spoofed caller to receive refunds
borrowed: 0,
provided: amountToSkim,
reserveId: newPairIdStorage,
dsId: 1
});
bytes memory hookData = abi.encode(flashSwapCallbackData);
corkHook.beforeSwap(address(flashSwapProxy), poolKey, swapParams, hookData);
Another vulnerability lies in FlashSwapRouter.CorkCall
’s validation logic. While it correctly asserted that msg.sender
(the immediate caller, which would be CorkHook
or its HookForwarder
after the beforeSwap
call) was legitimate, it failed to perform sufficient contextual validation on the parameters embedded within the hookData
itself. This allowed the attacker’s spoofed caller address to be passed through, ultimately leading to unauthorized funds being transferred.
// CorkHook.sol, logic inside beforeSwap() call.
// call the callback
CorkSwapCallback(sender).CorkCall(sender, hookData, paymentAmount, paymentToken, address(poolManager));
// FlashSwapRouter.sol, logic inside corkCalll() call.
{
// make sure only hook and forwarder can call this function
assert(msg.sender == address(hook) || msg.sender == address(hook.getForwarder()));
assert(sender == address(this));
}
This deception caused the FlashSwapRouter
to:
-
Blindly trust the spoofed provided and borrowed amounts from the
hookData
. -
Transfer its existing unsold
augDS
reserves (assets that a dispatcher contract should ideally not hold) into the attacker’s “Exploiter’s Proxy Market” via adepositPsm
call. This resulted in the minting ofpDS
andpCT
tokens, which were then transferred to the attacker’s control. -
Simultaneously, by manipulating the AMM pricing during the
beforeSwap
execution, an “over-borrowed” scenario was engineered. This subtle economic imbalance triggered a refund of excesspCT
directly to the attacker’s spoofed caller address, further siphoning legitimate assets.
The culmination of these two attack vectors — the acquisition of highly discounted CT
and the unauthorized extraction of DS
— allowed the attacker to possess a significant amount of both pCT
and pDS
. These were then redeemed for 3,761 wstETH
from the Peg Stability Module, leading to the complete depletion of the wstETH:weETH
Liquidity Vault.
Conclusions beyond the Post-Mortem
Explicit Confirmation of exchangeRateProvider
Hijacking:
The direct mechanism for ExchangeRateProvider
subversion, achieved by registering CorkMaliciousHook
during initializeModuleCore
, represents a critical aspect of the exploit. The post-mortem alludes to “bypassing authorization checks,” but the precise method of becoming the active oracle for a new market goes beyond a simple access control bypass. This constituted a full oracle hijacking for newly created pairs, fundamentally undermining the integrity of price discovery within those specific markets. The attacker’s ability to arbitrarily define the rate()
function for newly provisioned markets enabled the complete dictation of perceived asset pricing, a powerful primitive for economic manipulation within the protocol.
Attacker’s Rationale: “The Cork Hook is Not the Problem”:
The attacker’s assertion that CorkHook
itself was “not the problem” is a key insight into their technical understanding. This statement suggests that the attacker viewed CorkHook
as functioning correctly according to its role as a Uniswap V4 callback recipient, executing the provided logic. The true flaw resided in Cork Protocol’s broader design, specifically the insufficient validation and access control within CorkHook
’s implementation, but also across Cork’s infrastructure as a whole, which collectively failed to properly validate the origin and legitimacy of the hookData
and its embedded parameters. The problem was not confined to the hook’s mechanism, but rather Cork’s trusting implementation around that mechanism. Furthermore, the protocol’s rollover pricing mechanism is also a significant concern, as it allows for the skewing of a market’s risk premium under specific conditions, leading to economic vulnerabilities that can be exploited for substantial gains.
Possible Mitigations
Based on the identified vulnerabilities and the post-mortem’s action plan, several mitigations can be proposed:
- Access Control:
- Uniswap V4 Hook Authorization: Adopt the explicit authorization checks introduced in later Uniswap V4 periphery contracts. This upstream change, missing in Cork’s deployed version, would have provided a direct mechanism for developers to verify and whitelist legitimate callers. Uniswap’s V4 sample hooks have an
onlyPoolManager
modifier.
- Architectural Best Practices and Asset Management:
-
Stateless Dispatchers: Ensure that dispatcher or routing contracts, such as
FlashSwapRouter
, are designed to be entirely stateless and do not hold significant token balances. Any necessary temporary asset transfers should be handled atomically within a single transaction and reconciled immediately. -
Segregation of Duties: Clearly define and enforce the responsibilities of each contract. A contract responsible for routing should not inadvertently become a treasury.
- Economic Security and Parameter Controls:
-
HIYA Formula Review: Re-evaluate and potentially re-engineer the HIYA formula, particularly its sensitivity to low-volume trades and close-to-expiry conditions. Consider introducing minimum volume thresholds or time-weighted average price (TWAP) mechanisms for historical data inclusion to mitigate sudden spikes in risk premium calculations.
-
Circuit Breakers: Implement circuit breakers for large or anomalous trades/movements of assets based on a deviation from a predefined threshold. This could temporarily pause problematic functions or markets.