Co-authors: FRANK LEI & YUGUANG IPSEN
Welcome to the Web3 Security Auditor's 2024 Rewind, a collection of succinct technical breakdowns of notable security incidents and vulnerabilities from the past year.
Previously known as the Ethereum Smart Contract Auditor's Rewind
, the article series was originally authored by Patrick Drotleff, an Information Security and Privacy advocate, independent security researcher and mentor in the Secureum community. After connecting at TrustX Istanbul, Patrick entrusted us with continuing this tradition while he shifts focus to new endeavors.
We've adhered to the original format, delivering concise, actionable insights for each incident—highlighting the vulnerabilities and offering just enough context to identify these issues during security reviews or bug hunting. To increase the diversity of issues, this year's compilation expands beyond exploits to include bugs uncovered through traditional audits, contests, and bug bounty platforms. Additionally, as auditing increasingly encompasses off-chain infrastructure, we've included vulnerabilities from Rust and Golang systems like blockchain nodes or CEX on-ramping infrastructure.
Table of Contents
- EIP-1153: Transient Storage
- Uniswap V4 Double-Entrypoint Tokens Critical
- Polygon PoS Voting Takeover and Infinite Mint Bug
- Compound DAO Governance Attack
- Kraken Exchange Balance Printing Bug
- Radiant Capital Hack
- Missing Input Validations
- Missing Access Control
- Underflows and Overflows
- Missing Validations in Flashloan Callback
- Incomplete Signature Schemes
- Unsafe Casts
- Reentrancies
- Price Manipulations
- Rounding Errors
- Arbitrary Calls
- Faulty Business Logic
- Locked Funds
- Signature Malleability
- Lingering Approvals
- Faulty Array Handling
- Weak RNG
- Self Transfers
- Unsafe Storage Use
- Flawed Reward Systems
- Transaction Timing Attacks
- Forgetting to Blind Polynomials In Zero-Knowledge Protocols
- Protocol Misconfigurations
- Two Parser Bugs
- Incorrect VM Gas Charge
- Bridge Status Mismatch
- LayerZero Denial of Service
- LayerZero Integration Denial Of Service
- Faulty Upgrades
- Unset Initialization Flags
- Insufficient Verification of Proposer Response
- Others
The Highlights
EIP-1153: Transient Storage
The Ethereum Cancun-Deneb upgrade introduced transient storage, a short-lived data space similar to persistent storage but lasting only through a transaction. Developers can declare a variable transient
or leverage tstore
and tload
assembly instructions to interact with transient storage, benefiting from its cost-efficiency. At roughly 100 gas per operation, it is significantly cheaper than traditional storage, which typically requires around 20,000 gas for reads and writes. However, with increased complexity comes increased risk, and transient storage is no exception:
-
Due to the low gas cost of accessing transient storage, reentrancy attacks with low gas stipends (like the 2300 gas allowance through
address.transfer
) are now possible. If a code block does not follow the checks-effects-interactions pattern nor usesReentrancyGuard
, security researchers should ensure that transient storage variables are safe from reentrancy-based modifications, as such changes could lead to malicious or unintended contract behaviors. -
Currently, transient storage supports only reading and writing value types. Because data structures are reference-types, developers that want to use them must implement workarounds that sacrifice type-safety. This Solidity Underhanded Contest submission demonstrates how the absence of type-safety can lead to accidental decoding of data into a structure with a similar layout but different meaning, which can introduce subtle and potentially exploitable errors.
-
Lastly, while transient storage is automatically cleared at the end of a transaction, developers should explicitly clear it to prevent possible interactions with the contract in a "dirtied" transient state. The following article details how reverts or incorrect behavior can occur during
multicalls
or complex interactions between contracts within the same transaction.
Uniswap V4 Double-Entrypoint Tokens Critical
In 2024, Uniswap V4 has gone through several thorough audits, one of which uncovered a critical issue involving the deployment of the protocol on chains where the native tokens also have an ERC20 representation.
In Uniswap V4, the settle
function manages debt by increasing the account delta for the specified token. It uses msg.value
for native token deltas and the balance change since the last sync
or settle
call for ERC-20 tokens. This mechanism can be exploited on chains where native tokens, like CELO
on Celo, also have ERC-20 representations: After syncing to load current balances, an attacker can first call settle
with msg.value > 0
to increase the delta for the native token, then call settle
with the ERC-20 token's address. Since the balance of the ERC-20 token has increased from the first call, the attacker can inflate the delta without transferring actual tokens in the second call, allowing them to later withdraw more tokens than they deposited, effectively draining the pool.
The fix ensures that the settle
call must settle the previously synced token, otherwise the transaction will revert.
Polygon PoS Voting Takeover and Infinite Mint Bug
The following research article uncovers how the Polygon bridge could have been drained by combining a rogue validator with two issues found in the Heimdall validator code.
The Polygon Proof-of-Stake network is made of three layers:
-
Ethereum Contracts: Manage staking, checkpoint submissions and validator rewards.
-
Consensus Layer (Heimdall): Determines block producers based on Ethereum's staking state and pushes checkpoint updates.
-
Execution Layer (Geth fork): Produces new blocks.
When users stake MATIC
on Ethereum, a StakeUpdate(uint256 validatorId, uint256 nonce, uint256 newAmount)
event is emitted. Off-chain components notify Heimdall, which increases the validator's voting power for consensus. To verify the legitimacy of such events, Heimdall queries Ethereum to ensure the event indeed exists, the field values match and the nonce
aligns with the one stored on Polygon.
The first issue arises because Heimdall fails to validate event types. If the Ethereum contracts emit a similarly structured event like SignerChange(uint256 validatorId, address oldSigner, address newSigner)
, Heimdall could misinterpret it as StakeUpdate
. In this scenario, Heimdall would parse oldSigner
as nonce
and newSigner
as newAmount
. A rogue validator could emit a SignerChange
event with an oldSigner
address matching their Polygon nonce
and a newSigner
that parses into a massive newAmount
, artificially inflating their voting power.
The second issue lies in how Heimdall validates nonce
. The event's 256-bit nonce is truncated to uint64
, dramatically lowering the computational difficulty of generating an oldSigner
address matching the nonce on Polygon.
An attacker needs only the last 64 bits of oldSigner
to align. Although quite limited, the attacker can further reduce the necessary computational effort by altering their nonce through on-chain actions.
With overwhelming voting power, the attacker could have manipulated consensus to accept fake deposit events, arbitrarily minting funds on Polygon. By bridging the funds back to Ethereum, they could have drained the Polygon bridge. Fortunately, the bug was responsibly disclosed and fixed without any incident and loss of funds.
Compound DAO Governance Attack
In July, the Compound DAO faced an attack aiming to pass a malicious governance proposal. This proposal sought to transfer 499K COMP tokens — 5% of the total supply, valued at $25 million at the time — from the treasury to an investment vault. Besides the transfer, the tokens' voting power would also be delegated to the proposers. Given that Compound's voting turnout is usually 4-5% of the total token supply, the attackers could have controlled the governance process by passing this proposal. By being able to pass any malicious proposals, the Compound TVL and treasury funds would be put at risk.
The attack involved three proposals:
-
Proposal 247 (May 6th): was created directly on-chain, without a corresponding forum post detailing it, process which is customary within the Compound community. The proposal meant to send 92K
COMP
tokens from the treasury to a multisig owned by the “GoldenBoyz”, promising the tokens would be further invested into a vault that deposits them for yield into Balancer. This raised suspicions within OpenZeppelin and one of our colleagues promptly notified the community about the possibility of malicious activity. The proposal was subsequently cancelled after being heavily voted down. -
Proposal 279 (July 15th): This time accompanied by a forum post, it intended to transfer funds via a
TrustSetup
smart contract directly into the vault, bypassing the "GoldenBoyz" multisig. While this was an improvement, new concerns were raised that the tokens' voting power was delegated to the "GoldenBoyz" multisig before investment. This proposal was heavily voted down and did not pass. -
Proposal 289 (July 24th): Identical to the secondary proposal, except for the amount now being 499k tokens instead of 92k. Despite the community being promptly made aware, the "GoldenBoyz" mustered enough votes to pass the proposal. It is suspected that the first two proposals were only created to gauge the usual voting power of the community, only to accumulate more funds in the meantime and overwhelm the opposition at the vote of the third proposal. Moreover, most "for" votes were cast right at the end of the voting period (See "Timeline" in Tally), restricting the community's capacity to respond.
Given the previous voting power of the "GoldenBoyz" and the newly granted 499K COMP tokens, the succesful execution of Proposal 289 could have given the attackers control over Compound Governance, rendering other large delegates powerless against any malicious proposals.
The community counteracted with Proposal 290, aiming to transfer the timelock admin to the Community Multisig and restrict the "GoldenBoyz" from executing malicious actions against the DAO treasury and the protocol. Thanks to rapid action by Compound delegates in collaboration with OpenZeppelin and other parties, this emergency proposal neutralized the threat by preventing the misuse of the newly acquired voting power.
Ultimately, the "GoldenBoyz" reached an agreement with the DAO and canceled Proposal 290 before its execution. While they and affiliated accounts retain significant voting power, it currently doesn't pose an immediate threat to the DAO but warrants ongoing vigilance.
As an additional security measure, the Compound protocol added a temporary proposal admin which has the power to cancel malicious proposals.
Kraken Exchange Balance Printing Bug
Centralized exchanges typically maintain user balances off-chain, updating them based on observed on-chain deposits and exchange activity. Users are assigned deposit addresses, and once a transfer to these addresses is detected, their exchange account is credited with the deposited tokens.
In June, a critical bug in the Kraken Centralized Exchange was reported, allowing malicious users to inflate their account balances without completing the actual on-chain deposit process. While user funds remained safe, this flaw endangered Kraken's treasury by enabling attackers to create and withdraw fake balances as real on-chain funds, totaling ~3M USD.
Details from DanielVF's analysis and his proof of concept suggest that if crafted correctly, a reverting token transfer would be interpreted by the Kraken system as a valid deposit. It is important to note we could not find an official post-mortem detailing the exact technical details behind it.
A transaction containing an inner call that transferred funds and subsequently reverted was mistakenly considered as valid deposit, without taking into consideration the rollback of the inner call. The credited exchange amount matched the token transfer in the reverted transaction, and hence attackers could amplify their gains using flash loans to inflate the apparent deposit value.
Ongoing investigations aim to clarify whether this was a proof-of-concept demonstration by researchers or a malicious attempt at fund theft.
Radiant Capital Hack
In October, Radiant Capital suffered a security breach resulting in the loss of approximately $50 million. At least three developer devices were compromised through a sophisticated malware injection further detailed in an incident update. The compromise allowed the attackers to intercept authentic transaction data from the Safe UI and tamper with it by the time it reached the signature process done by hardware wallets, ultimately resulting in gathering signatures of different, malicious on-chain actions.
The attack involved altering transaction details sent to the hardware wallet. While the Safe frontend displayed legitimate transaction data, the actual transactions being signed were malicious, enabling attackers to execute a transferOwnership
call after a quorum of 3/11 authorized signers was reached.
By becoming the owner of Radiant's LendingPoolAddressesProvider
contract, the attackers changed the implementation behind lending pools proxies and stole all funds and previously given token approvals. The discrepancy between the signature expected by Safe and the signature given by the hardware wallet caused errors within the Safe UI and during on-chain execution, but this did not cause immediate suspicions as such errors were common when submitting meta-transactions and hence, were not subject to detailed analysis.
For a deeper understanding of how Safe interacts with hardware wallets and the risks of blind signing, refer to this analysis.
The Usual
Missing Input Validations
- In the Beanstalk stablecoin protocol, users could supply
BEAN
tokens as liquidity intoWell
contracts (similar to liquidity pools) in exchange for LP tokens. When withdrawing liquidity, users would specify the address of theWell
they wanted to withdraw from and the amount of LP tokens to burn. TheWell
contract was responsible for burning the LP tokens and calculating how many BEAN tokens to return to the user from the Beanstalk contract. However, theWell
contract was neither validated nor subject to any whitelist. An attacker could create a contract that when called, would falsely report that any amount of burned LP tokens entitled the user to the entire balance ofBEAN
tokens held in the Beanstalk contract.
-
The
BankrollNetworkStack
contract functions as a vault in which users depositWBNB
through thebuyFor
function, in exchange for Bankroll shares. Part of the deposited amount is distributed asWBNB
dividends to each shareholder. After accumulating sufficient dividends, a user can sell their shares back into the vault and withdraw all the accumulated dividends. However, thebuyFor
function was missing a check for the_customerAddress
parameter and would not validate that it is not set to the Vault's own address. This oversight allowed an attacker to call the function repeatedly with the_customerAddress
as the Vault address and thebuy_amount
as the total amount ofWBNB
in the vault. This would transfer theWBNB
from the vault to itself, while distributing part of the amount as dividends, a slice of which were accumulated for the attacker. After calling the function sufficient times, the attacker sold their shares and withdrew an inflated amount of dividends, to the loss of other users.
Missing Access Control
- When the
TSURU
system received anERC1155
token, it would call theonERC1155Received
function of theTsuruWrapper
contract, minting correspondingTSURU
tokens to the depositor. However, theonERC1155Received
function lacked proper access control, allowing an attacker to call it directly and mint unlimited tokens to themselves. The attacker then used these tokens to drain theTSURU-ETH
Uniswap pool.
-
The Galaxy Fox token airdrop worked by storing a Merkle Tree root on-chain, allowing participants to claim tokens by submitting a valid path with their address as the leaf. However, the
setMerkleRoot
function lacked access control, allowing an attacker to set a maliciously crafted root and claim enoughGFOX
tokens to drain theWETH-GFOX
Uniswap pool. -
The Alchemix project enabled users to deposit yield-bearing assets such as
yvDAI
orwstETH
, which served as collateral for borrowing synthetic assets likealDAI
oralETH
, thereby providing immediate access to liquidity. Periodically, yield was collected via theharvest
function and sometimes required unwrapping through swaps on Balancer, creating opportunities for sandwich attacks. To mitigate these risks, theharvest
function included aminimumAmountOut
parameter and could only be called by the Gelato Automate contract — a system designed to schedule recurrent on-chain calls with correct parameters, similar to traditional cron jobs.However, Alchemix integrated with the general, non-custom version of Gelato, which does not react to only dedicated
msg.senders
and allows anyone to trigger calls from theAutomate
contract. This lack of customization would allow an attacker to create call chains with arbitrary values forminimumAmountOut
, facilitating significant sandwiching during the unwrapping of harvested yield. -
The
CoreBranchRouter
contract in Maia DAO's system lacked necessary access control due to a virtual function not being overridden with proper caller restrictions. This oversight would allow an attacker to act as a trustedCoreBranchRouter
and drain funds through a sequence of calls. As highlighted in the post-mortem, developers and security researchers should carefully review all inherited contracts for unguarded or hidden code to prevent similar vulnerabilities.
Underflows and Overflows
- On Mantle,
MNT
andETH
transfers between two L2 addresses can be initiated on L1 by specifying the amount to transfer. Without validating the balances, aTransactionDeposited
event is emitted and is picked up by the Mantle Node which ensures thefrom
address has enough funds to transfer. However, whenETH
transfers were involved, the Mantle node did not ensure thefrom
address had enough funds and directly updated the balances. Due to the use of Golang’sBigInt
type and theBigToHash
function, underflows were not detected, and a negative value would be converted to a positive. This would allow an attacker to transferETH
they did not have to another address they controlled, increasing both balances at the same time and hence, infinitely minting.
-
An attacker stole multiple users'
LW
tokens by exploiting a missing underflow check in the token'stransferFrom
function. The function failed to verify that the transfer amount was within the approved amount, assuming the subtraction would automatically revert if it went negative. However, the contract was compiled with Solidity0.7.6
which lacks automatic underflow checks. This allowed the attacker to transfer tokens from users who hadn't provided approval, bypassing the intended restrictions.For another example with the same vulnerability pattern, see this writeup on the
Scroll
token exploit.
Missing Validations in Flashloan Callback
- Prisma Finance's
MigrateTroveZap
contract was designed to help users migrate theirTroves
(positions with collateral and debt) by using a flashloan to close and reopen them in the new contract. However, theonFlashLoan
method did not properly validate the input. An attacker bypassed themigrateTrove
process and initiated a flashloan directly, invoking theonFlashLoan
method with unexpected data. This allowed the attacker to close other users' troves and reopen them with the bare minimum collateral required to keep the positions healthy. The attacker then opened their own trove and was able to inherit the remaining collateral from the compromised positions, ultimately withdrawing the excess funds.
-
Similarly, Dough Finance's
ConnectorDeleverageParaswap
contract facilitated users in obtaining flashloans from AAVE through theflashloanReq
function. Users could provide aswapData
parameter that was intended to define the details for swapping the flashloaned tokens via specified routes. However, there was no validation on theswapData
parameter, allowing an attacker to invoke aWETH transferFrom
instead of a token swap and steal assets that were approved to the contract.
Incomplete Signature Schemes
- The
isValidSignature
function in ERC1271 provides a standard way for contracts to verify whether a signature on behalf of a given contract is valid. A common use case is smart accounts, which rely on signatures from their owners to authorize actions. Unfortunately, the reference implementation does not inherently tie themsg.sender
or the target contract address to the signature. This omission can lead to signature replay attacks, where a signature intended for smart account A could be reused for account B, provided both accounts have the same owner and message format.
-
To receive a Taiko grant, a
TimelockTokenPool
contract was deployed, allowing recipients to withdraw tokens after a time delay and with a valid signature. The signature was validated against thekeccak256(abi.encodePacked("Withdraw unlocked Taiko token to: ", _to))
digest, missing a few key fields:-
without a nonce, the signature could be replayed for the same
TimelockTokenPool
contract. -
without the verifying contract's address, the signature could be replayed between different
TimelockTokenPool
contracts. -
lastly, without the
chainId
, the signature could be replayed on other chains.
-
-
In the Phi off-chain system, a signer authority would issue signatures for users to claim rewards on-chain. While the signatures were constructed with all the necessary security fields, the on-chain validation mechanism did not check if the
chainId
matched the current chain. As a result, signatures generated for one chain could be reused on another chain to claim additional rewards. By using theEIP712
abstract contract, developers can enforce that all security fields are both included in the signature and validated against the context of execution (eg.version
,address(this)
,block.chainid
). -
In the Phi system, the signer authority would issue off-chain signatures using parameters validated by the frontend, for users to submit on-chain and create NFT art. However, not all parameters were included in the signature, allowing an attacker to frontrun the signature submission and alter certain fields, and thus change the artist, maximum supply or royalty receiver of the NFT.
Unsafe Casts
- In Uniswap v4, an unsafe cast from
int128
touint128
in thevalidateMinOut
function can allow slippage checks to be bypassed. This happens because a hook might return a negativeliquidityDelta
, which, when cast touint128
, turns into a very large positive number that exceeds user-specified minimum outputs. The fix involves usingSafeCast.toUint128
, which will revert the transaction whenliquidityDelta
is negative.
-
A vulnerability was identified in the Lotus and Venus clients of the Filecoin network, which could allow an attacker to remotely crash nodes. The root cause was due to an unsafe cast from an unsigned to a signed integer, combined with subsequent out-of-bounds access. In the
validateCompressedIndices
function, message indices, which are unsigned integers, were unsafely cast to signed integers for validation. This validation checked if the index was less than the length of theBls
slice, but since the peer could control the index value, it was possible to set an index larger than the maximum value for a signed integer. When cast to a signed integer, such a large value would wrap around to become negative, thereby bypassing the validation check. This error allowed for out-of-bounds access after the validation, leading to a panic and causing the node to crash. -
A vulnerability was identified in the Sei Node within the
AsEthereumData
function, which is used to decode Ethereum transaction data. The vulnerability arises because the function does not validate thev
,r
, ands
signature values (typebig.Int
) before casting them touint256
withuint256.MustFromBig
. If thesebig.Int
values are maliciously crafted to be overly large, theMustFromBig
function can panic due to an overflow, leading to the Sei Node shutting down.
Reentrancies
- When depositing
ERC20
tokens with sender hooks on Scroll, a misplaced reentrancy guard could have allowed an attacker to receive inflated amounts and potentially drain the contract. ThenonReentrant
modifier was placed on the internal_deposit
function which did not contain the token transfer, instead of the externaldepositERC20
which contained thesafeTransferFrom
call.
-
The SumerMoney project, a Compound fork supporting assets like
ETH
andUSDC
, differed in its loan repayment logic: it refunded any excess before updatingtotalBorrows
, potentially handing execution to the caller while the exchange rate between the underlying asset andcToken
was inflated. An attacker exploited this by borrowingETH
, repaying it with a 1 wei surplus, and redeeming collateral at the inflated rate. By doing so, the attacker recuperated their initial collateral by redeeming lesscTokens
and used the leftover amount to further borrow more underlying tokens without repaying, draining funds from the market. -
The
ETH/OETH
Curve pool'swithdraw_admin_fees
function sent excess funds determined by the difference between internal (self.balance; OETH.balanceOf(address(this))
) and accounting balances (self.balances[0]; self.balances[1]
) to a fee-receiver. Because the behavior of the function was not dependent on themsg.sender
, the function lacked access control.The
remove_liquidity_imbalance
function allowed users to withdraw liquidity in disproportionate amounts, transferingETH
and handing execution to the caller before transferingOETH
. This would temporarily leaveOETH.balanceOf(address(this))
in the initial, and now inconsistent, state. By callingwithdraw_admin_fees
at the right time, an attacker could reduce the pool’s internalOETH
balance below the accounting balance, temporarily breaking pool accounting. -
Onyx, a lending platform forked from Compound V2, implemented its own liquidation flow. A low-liquidity market within the platform allowed an attacker to exploit the system by continuously minting and redeeming a single LP token, gradually decreasing its exchange rate. This process reduced the collateral value until it was marginally underwater, allowing the attacker to initiate a profitable self-liquidation.
Furthermore, a missing input validation in Onyx’s liquidation logic enabled the attacker to use a fake token as repayment, increasing the profitability of the attack.
-
The Penpie project, offering yield-boosting services for Pendle Finance users, allowed users to collect rewards from specific Pendle
markets
via thebatchHarvestMarketRewards
function. This function records token balances before, redeems rewards and records token balances after, with the differences representing the rewards to distribute.However, an attacker exploited this by attaching a valid Pendle market with a malicious custom token that handed over execution during token transfer inside
IPendleMarket.redeemRewards()
. The attacker used this execution window to calldepositMarket
, depositing tokens to mint LP tokens while simultaneously inflating the "after" token balances used to calculate rewards.
Price Manipulations
-
In the HYDT token system, the
InitialMint
contract let users depositBNB
in exchange for newly mintedHYDT
tokens. The minted amount was based on theWBNB/USDT
PancakeSwap pool spot price. An attacker flashloanedUSDT
, traded it for nearly all theWBNB
in the pool to manipulate the exchange rate and then minted themselves an inflated amount ofHYDT
tokens. -
PolterFinance used the average of two spot prices - one from a Uniswap V2 pool and one from a Uniswap V3 pool — to value
BOO
tokens as collateral for borrowing. Although aggregating prices can reduce manipulation risk, relying entirely on spot prices leaves the system vulnerable to on-chain price manipulation. By leaving small amounts ofBOO
in these Uniswap pools, an attacker artificially inflated the spot prices, causing the aggregated price to rise sharply. This enabled the attacker to borrow most of PolterFinance’s assets using the overvaluedBOO
as collateral. -
The
AIZPT314
system facilitated users to tradeBNB
forAIZPT
tokens, using the formulatoken0Out = token1In * token0InContract / (token1In + token1InContract)
. Unlike constant-product formulas, the formula above allows value extraction through back and forth trades of big amounts. An attacker exploited this by depositing a large amount ofBNB
to receive most of the contract’sAIZPT
tokens, then recovering theirBNB
by trading back small amounts ofAIZPT
, effectively draining the contract. -
The WOOFI decentralized exchange used a spot pricing mechanism that was not time-weighted and could be manipulated in cases of low liquidity and large-value trades. An attacker identified a low-liquidity market on Arbitrum and through a sequence of flashloans and trades, caused one
WOO
to be valued at 0.00000009USDC
. Normally, WOOFI would fallback to a Chainlink oracle in case of extreme price fluctuations, but the oracle was not properly set up. Because of this oversight, the system used the manipulated price and the attacker subsequently drained all theWOO
tokens out of the market.
Rounding Errors
-
In The Graph system, staking tokens incurred a 1% curation fee. However, due to the fee calculation function rounding down, a curator could pay no fee by repeatedly staking only 99 tokens at once. The potency of the attack could be amplified using multicall on a low-cost network like Arbitrum.
-
The article Precision Loss Accumulation: The “Two Parser Bug” Lurking in the Shadows explores how small, repeated precision losses can accumulate, potentially leading to denial-of-service or broken invariants.
The first example describes a reward system where each reward is rounded up while fees are rounded down, resulting in cumulative over-distribution that may prevent the last user from claiming their reward.
The second example discusses a token sale with public and private participants, where accumulated precision loss in exchange rate calculations can allow participants to claim slightly more tokens than the sale cap allows.
Arbitrary Calls
-
In Mantle, bridging
ETH
orMNT
locks the tokens on the source chain and mints them to theCrossDomainMessenger
contract on the destination chain. Then, theCrossDomainMessenger
callsrelayMessage
on thetarget
, forwarding the funds and executing user-defined arbitrary logic. If the call fails, the message is stored for replay and the funds remain in the contract until then. Due to insufficient validation of thetarget
address, an attacker could craft and send a malicious cross-chain message which would trigger anapprove
call. Since the transaction is executed with theCrossDomainMessenger
asmsg.sender
, it would grant the attacker approval over the funds locked for failed messages. -
Li.Fi Dex Aggregator introduced a new diamond facet that allowed users to deposit tokens and specify a route for swapping them. However, insufficient validation of the target and function selector in the route enabled arbitrary calls. A malicious attacker exploited this vulnerability by encoding the target as token addresses and the function selector as
transferFrom
, ultimately draining tokens from all users who had previously approved the vulnerable Li.Fi contract. -
Spectra Finance allowed users to choose from various routes for executing token trades, one of which was through KyberSwap. However, the
kyberRouter
andtargetData
parameters were not properly validated. Similar to the Li.Fi exploit, this vulnerability allowed an attacker to encode theasdCRV
token address and thetransferFrom
selector, draining tokens from addresses who had previously granted approvals to the Spectra Finance contract. -
Similarly, Socket's Bungee Bridge was exploited using an arbitrary execution bug. Because of a missing validation on the
swapExtraData
parameter, an attacker would be able to encode atransferFrom
and call it from the bridge on theWETH
address, draining all lingering approvals.
Faulty Business Logic
-
The
uniBTC
system allowed mintinguniBTC
tokens againstBTC
orBTC-equivalent
tokens, at a 1-to-1 exchange rate. To restrict minting againstnon-BTC-equivalent
tokens, the system used a capping mechanism, where a deposit reverts if the vault's holdings (returned by thetotalSupply
function) exceed a set cap (set to 0 fornon-BTC-equivalent
tokens). On non-BTC-native chains, theNATIVE_BTC
address represented the chain’s native token which might not be equivalent toBTC
. For such tokens, thetotalSupply
function mistakenly returned 0, practically always passing the cap check:(totalSupply == 0) <= (cap == 0)
. This oversight enabled an attacker to mintuniBTC
against the native token, which was not BTC-equivalent on several chains. -
The Zyfy project's
PermissionlessPaymaster
contract allowed gas fee sponsorship by enabling a user to set a manager address that funds their transactions. Following a sponsored transaction, leftover gas was credited back to thepreviousManager
state variable, which kept track of the sponsor of the last transaction. This refund could later be withdrawn by the respective manager. However, a vulnerability arose from theselfRevokeSigner
function, which let users revoke their manager while also updating thepreviousManager variable
.An attacker could exploit this by backrunning a sponsored transaction with a call to
selfRevokeSigner
, updating thepreviousManager
to their own address. As the refund for the previous transaction was processed using this variable, the attacker could steal gas credits intended for the legitimate sponsor.
Locked Funds
-
In Renzo Finance,
ETH
withdrawals were sent to claimers usingtransfer
, which forwards only 2300 gas. If the claimer was a smart contract with a non-trivialreceive
function, the gas limit would have been insufficient, causing the transaction to revert and effectively lock the funds in an unclaimable state. -
The Bridged USDC Standard is Circle’s recommended approach for bridging
USDC
to new blockchains, with an option to later transition into native USDC, reducing liquidity fragmentation. The DeFi Wonderland opUSDC system bridgesUSDC
to OP stack-based chains, allowing for contract ownership transfer to Circle for nativeUSDC
transition. After lockingUSDC
on L1, the L2 counterpart contract had the permission and would mintUSDC
to the receiver. If minting failed temporarily on L2 (due to out-of-gas errors or bridgedUSDC
being paused), the transaction could be replayed on L2. However, if the transition to nativeUSDC
occurred in the meantime, minting permissions would be transferred to Circle, causing any replayed transaction to revert indefinitely, thereby locking L1 funds without recovery options.
Signature Malleability
-
The
TCH
system allowed token burning from a Uniswap pair using a signature from an authorized signer. However, it had two significant vulnerabilities: it usedecrecover
for signer recovery, which is susceptible to malleability, and it enforced the uniqueness of signatures rather than message digests. This combination allowed an attacker to modify and re-submit past signatures, enabling them to burn double the intended amount and inflate the value of theirTCH
tokens. -
Similarly, Ironblocks Onchain Firewall implemented a custom signer recovery process, where the signature was split into
v
,r
ands
and then fed intoecrecover
. However, it failed to validate that thes
value is within the secure bounds, creating the potential for signature malleability. To fully mitigate this attack vector, it is recommended to use the OpenZeppelinECDSA
library.
Lingering Approvals
-
Hedgey Finance allowed users to create vesting campaigns via the
createLockedCampaign
function, which transfered funds from the campaign creator (msg.sender
) and approved aClaimLockup
contract to manage these funds based on the campaign terms. When a campaign was canceled, the remaining funds were returned to the creator, but the approval for theClaimLockup
contract was not revoked. An attacker exploited this flaw by creating a maliciousClaimLockup
contract. The contract set up a campaign, canceled it to retrieve all deposited funds, and then calledtransferFrom
on the lingering approval to receive the funds a second time. -
The
OwnershipNFTs.sol
contract of the Superposition AMM implemented custom ERC721 logic which did not revoke token approvals upon transfer. This oversight would allow an NFT owner to approve the token to themselves, sell it to a victim and then use the lingering approval to reclaim the NFT without consent.
Faulty Array Handling
-
In the Alchemix Finance system, simultaneously creating multiple checkpoints would result in data points with duplicate timestamps within the
pointHistory
array. The binary search used to locate an entry by timestamp failed to handle duplicates properly, stopping at the first matching timestamp and potentially leading to stale or incorrect results. -
In the Royco system, offers were created to incentivize and reward arbitrary on-chain actions with incentive tokens. Similarly to the above, the system failed to account for and sanitize duplicate tokens in the reward array. An attacker could exploit this by creating an offer with duplicate reward tokens, completing the action themselves, and claiming a reward multiplied by the number of duplicate tokens included in the offer, draining the system.
Weak RNG
-
The
RedKeysGame
contract contract allowed users to place token bets, rewarding them if they correctly guessed the_betResult
. However, the result was derived from predictable on-chain values such as a block'stimestamp
,prevrandao
,gaslimit
,coinbase
andnumber
fields. This allowed an attacker to craft a contract that calculates and submits the winning number, guaranteeing consistent winnings. -
Similarly, the Boost AA Wallet determined the winner of its raffle using
block.prevrandao
andblock.timestamp
as sources of randomness. A validator with control over block production could influence the raffle outcome by selectively proposing or withholding blocks until the values ofblock.prevrandao
andblock.timestamp
aligned in their favor.
Self Transfers
-
The SuperSushiSamurai token's custom ERC20 logic failed to account for transfers where the sender (
from
) and receiver (to
) were the same. Additionally, instead of increasing the receiver's balance, it overwrote it. These flaws allowed an attacker to transfer tokens to themselves repeatedly, doubling their balance with each transaction. The key vulnerability is highlighted in the code snippet:uint256 toBalance = _postCheck(from, to, amountAfterTax)
_balances[from] = fromBalanceBeforeTransfer - amount;
_balances[to] = toBalance;For another example with the same vulnerability pattern, see this writeup on the
GPU
token exploit. -
The Hackathon token had custom logic for token transfers involving certain AMM pairs but failed to account for scenarios where a pair was both the
sender
andrecipient
. This oversight was exploited through the standard AMMskim
function, which forces token balances to match reserves by withdrawing surpluses. By sending excess tokens to the pair and callingskim
with the pool as receiver, the faulty logic doubled the excess token balance. Subsequently, the attacker calledskim
with their own address as receiver, collecting the excess tokens.
Unsafe Storage Use
-
The Delta Prime project was hacked due to a storage collision between its proxy and implementation contracts, along with a forgotten
init
function. The project used a pattern calledDiamondBeaconProxy
, where each user'sPrime Account
smart contract was deployed as an on-chain clone, reducing gas costs by reusing the same contract logic across accounts. Accounts served as storage, while execution wasdelegatecalled
to theDiamondBeacon
contract, which then delegated to various facets based on the function selector. This setup allowed updates to the diamond facets to simultaneously modify the functionality of all Prime Accounts.Each Prime Account was initialized upon deployment through the
initialize
function that set the account owner. Similarly, the diamond was initialized via aninit
function, which set the diamond's owner. However, both contracts used the same storage slot for the owner address but different storage slots for theirinitialized
flags. Moreover, the development team forgot to remove the diamond'sinit
function after deployment. A malicious exploited this by invoking theinit
function on other users' Prime Accounts, leading to adelegatecall
to the diamond’sinit
function rather than thefallback
. Theinitialized
flag check passed, as it relied on a different storage slot, allowing the attacker to change the Prime Account’s owner by exploiting the shared storage slot for ownership. This enabled the attacker to take control of multiple Prime Accounts and drain funds using administrative privileges. -
In Restake Finance, withdrawal requests store the amount of shares a user wants to burn in exchange for underlying tokens. Once processed, the protocol sends the tokens, deletes the request, and burns the shares. However, the burn operation retrieved the shares amount from the already-deleted request, resulting in a value of 0. Consequently, shares were never burned and remained stuck in the contract, diluting the shares-to-underlying conversion.
Flawed Reward Systems
-
The Pythia Finance staking contract allowed users to stake
Pythia
tokens in exchange for rewards. However, the rewards calculation only multiplied the user's balance by an accumulating rewards-per-share variable, without considering the duration of staking. This flaw allowed an attacker to deposit large amounts of Pythia tokens and repeatedly claim excessive rewards. -
The
BGM
token rewarded transfers by allocatingBGM
claims to the sender, recipient, and their referrers, with rewards sourced from aBGM/USDT
liquidity pool. An attacker exploited this system by creating numerous accounts and conducting artificial transfers to inflate rewards. These rewards were then withdrawn, depleting the pool ofBGM
and significantly increasing the token's value. Finally, the attacker leveraged the inflated price to drain the pool’sUSDT
. -
The Elfi protocol allowed users to stake tokens, minting
stakeToken
(st
) as proof of deposit while also recording the staked amount internally. Rewards were calculated based on thest.balanceOf(account)
instead of the internal accounting. This oversight, coupled with the transferability ofst
introduced a vulnerability: users could create multiple accounts, stake tokens, and transferst
back and forth between accounts. This would allow them to claim rewards from both accounts repeatedly, effectively doubling their earnings. -
The Ramses Exchange rewarded users based on the combination of reward periods and
veNFT
tokenId
, using thetokenTotalSupplyByPeriod
value. However, it failed to reducetokenTotalSupplyByPeriod
when rewards were claimed, allowing users to claim the same rewards multiple times. Additionally, it did not check if the current block timestamp matched the designated reward period, enabling claims for past periods. Lastly, to bypass the limit of reward claims for atokenId
, the attacker leveraged a system feature which allows mintingveNFT
with newtokenIds
.
Transaction Timing Attacks
-
EIP-2612 (Permit) allows users to sign a token approval off-chain and have it executed on-chain by another party, eliminating the need to hold native token for gas. The signature is single-use and immutable, ensuring consistent results regardless of who submits it. However, a denial-of-service and incomplete execution attack vector was identified. In this scenario, a user submits a transaction (T) to a smart contract that both approves funds via
permit()
and performs an additional action (A). An attacker can observe the submitted signature, frontrun transaction T with their own transaction that solely executes thepermit()
and succeeds. Due to signature replay protection, this causes transaction T to revert, resulting in a situation where the funds are correctly approved, but action A is never executed. -
Uniswap V4 Periphery Contracts used Permit2 to approve spenders in a function designed to be part of a
multicall
. Similarly to the above, an attacker could exploit this by frontrunning themulticall
and submitting the permits separately. This would cause the legitimate multicall transaction to fail, as it attempted to consume a permit with a signature that had already been used. -
The
SquidTokenSwap
contract allowed any user to trigger PancakeSwap trades betweensquidV1
andsquidV2
tokens held in the contract. Because of this and a set 5% slippage tolerance, an attacker repeatedly triggered and sandwiched these trades, taking the slippage margin as profit. -
When claiming rewards in Opal Omnipool, the contract used
Balancer.batchSwap
to perform token swaps without specifying any slippage protection. Similarly to the above, this oversight would allow an attacker to steal part of the rewards by sandwiching the swaps with two swaps if his own. -
Updating the Pyth price oracles for a Radiant market required sending
ETH
to the contract’sreceive
function, followed by callingupdateUnderlyingPrices
for the desired market. Since these steps were independent, an attacker could exploit this by backrunning thereceive
function and using the suppliedETH
to update a different market than the one intended by the original transaction sender.
Forgetting to Blind Polynomials In Zero-Knowledge Protocols
- In some zero-knowledge (ZK) protocols, particularly those using zk-SNARKs, provers demonstrate knowledge of a secret witness by encoding it as part of a polynomial. To verify correctness, the prover reveals evaluations of this polynomial at specific points, often determined by the protocol's setup. However, without protection, revealing sufficient evaluations could allow an attacker to reconstruct the polynomial and deduce the secret witness, compromising the zero-knowledge property. For example, a polynomial of degree
n
(i.e.,n+1
coefficients) can be fully recovered fromn+1
evaluations. - To counteract this, polynomial blinding is employed. This involves obscuring the polynomial by incorporating randomness, such as adding random coefficients to prevent reconstruction. For instance, a polynomial of degree
n
may be blinded to a degreen+r
polynomial when evaluated atr
points. This ensures that information about the polynomial cannot be inferred from its evaluations, preserving the secrecy of the witness and maintaining the protocol's zero-knowledge guarantees. -
The implementation of Linea's PLONK Go Prover did not apply proper blinding to polynomial shards, which could allow an attacker to statistically recover the witness.
Protocol Misconfigurations
-
The
VOW
token was exploited due to a non-atomic testing transaction being used in production. Changing an operational parameter, testing the behavior, and then reverting the parameter were done in separate transactions. This allowed an attacker to insert their own transaction between these steps, exploiting the contract due to the critical operational parameters being in an abnormal state. -
The UWU lending protocol set the liquidation threshold at 90% and the liquidation bonus at 10%, which were excessively high and provided little margin of safety. This setup made the protocol susceptible to even minor price fluctuations, potentially leading to bad debts. During the hack, an attacker manipulated the oracle price by just
4%
, creating a position with a Loan-to-Value (LTV) ratio of93%
. By doing this, the attacker could liquidate the position, earning a profit calculated as93% * 110% (liquidator) - 100% (liquidated) = 2%
from the protocol.
Two Parser Bugs
-
In Taiko, cross-chain messages are subject to an Ether rate limiting quota on the destination chain, which caps the value transferred in a given time frame. If a message exceeds the remaining quota for the time period, it is marked as
RETRIABLE
and can be re-executed later. Otherwise, the message undergoes validation checks that it does not target forbidden addresses and is then executed.A vulnerability was discovered in the message retry flow, where the implementation did not perform the forbidden address checks before executing the message. By intentionally exceeding the quota, a malicious attacker could execute a cross-chain message not bound to any targets with the
Bridge
asmsg.sender
, and potentially drain other users' assets.This vulnerability highlights a common issue where identical data should not but is processed differently depending on execution paths. Similar patterns have been exploited in other systems, as described in this critical vulnerability analysis.
Incorrect VM Gas Charge
- The Fuel VM's CCP (Code Copy) instruction copies code from a contract into memory, charging gas based solely on the size of the contract's bytecode rather than the actual number of bytes being copied into memory. Furthermore, when the destination region for copying exceeds the bytecode size, the instruction zero-fills the exceeded area. By setting an offset and copy length larger than the bytecode length, users can cause the function to zero-fill large memory regions without incurring the appropriate gas costs. This manipulation allows for performing expensive memory-clearing operations at a reduced cost, which could potentially lead to network resource exhaustion and denial-of-service attacks.
Bridge Status Mismatch
- The Fuel Layer 1 (L1) executor will include relayed messages even when the transaction is reverted on L2. This enables an attacker to perform fake token withdrawals repeatedly. For example, the attacker triggers two token withdrawals on L2; the first one reverts in the end but still sends a message to L1, while the second succeeds. As long as there are sufficient tokens on L1, the attacker receives double the amount of tokens he initially deposited, effectively allowing the attacker to steal funds from the bridge.
LayerZero Denial of Service
-
LayerZero V1 is a cross-chain protocol enabling message sending, verification, and execution across blockchains. When a message is sent on the source chain, off-chain
Relayer
andOracle
services are notified, triggering a consensus on message authenticity. TheOracle
submits a Merkle root on the destination chain, and theRelayer
submits a proof to execute the message.The first part of a research article series describes a DoS attack vector through message nonces overlap, which could prevent any cross-chain message from being delivered. Messages in LayerZero are ordered by a unique nonce based on the
srcChain
,srcAddress
, anddstChain
, without considering thedstAddress
. However, as applications can choose their ownRelayer
andOracle
, they can forge messages from anysrcChain
andsrcAddress
but not to anydstAddress
. Since the nonce did not account for thedstAddress
, an attacker could send a spoofed message with the same nonce, blocking legitimate messages with the same nonce from reaching the destination chain. -
LayerZero’s OFTs (Omnichain Fungible Tokens) and ONFTs (Omnichain Non-Fungible Tokens) allow bridging of ERC20 and ERC721 tokens, respectively, by locking them on one chain and minting them on another. The third part of the series shows an attack vector where when bridging ERC721 tokens, an attacker can cause a DoS by using the
onERC721Received
callback on the destination chain to consume all gas, blocking the message delivery and freezing the cross-chain transfer.
LayerZero Integration Denial Of Service
-
The second part of the series involves Stargate, a protocol that enables cross-chain asset transfers and swapping through LayerZero. When swapping tokens, a message is sent together with a payload, enabling the recipient of the funds to execute arbitrary logic upon receipt. If a message delivery fails, it is handled gracefully through a
try-catch
, an error and payload are stored and the system considers the message delivered, ensuring the process does not block subsequent messages due to the nonce ordering.By encoding a payload with a recipient address that is not a smart contract, a malicious attacker could force the
try-catch
clause to revert without going into thecatch
block. This would reserve the upcoming nonce while being unable to deliver the message, causing a freeze of the messaging channel.A second way to block the messaging channel is by making it revert through an out-of-gas error. Because the
catch
clause would store the payload in contract storage, a malicious attacker could send a payload so big that it would spend all remaining gas when being stored. -
Similarly to the Stargate vulnerabilities of reverting at the bridge level, the following thread uncovers a denial-of-service possibility in the Drips Network: if a message indefinitely reverts at the bridge level on the destination chain, the communication channel will be blocked and should ideally have a means to recover from this state. Unfortunately, a recovery mechanism was not present, and to upgrade the implementation on the destination chain, a successful cross-chain message was required but was not possible because of the above.
Faulty Upgrades
-
To approve withdrawals, the Ronin bridge required signatures from a validator set with a total weight above a certain threshold. However, during the upgrade from version 2 to version 4, the development team neglected calling the initialization function for version 3. This oversight left the threshold uninitialized and set to its default value of 0, allowing anyone to initiate withdrawals without submitting valid signatures. As a result, the bridge suffered a $12 million exploit.
-
Pike Finance’s protocol upgrade introduced a storage misalignment, mistakenly moving the
initialized
variable to a different slot. This allowed an attacker to reinitialize the proxy, upgrade it to a malicious implementation and ultimately steal funds.
Unset Initialization Flags
-
The Wrapped XETA contract failed to set the
initialized
flag in itsinitialize
function, allowing an attacker to reinitialize the contract. The attacker gave himself token minting permissions, which they used to drain theUSDT/WXETA
liquidity pools. -
Similarly, the
NFGSToken
contract failed to set theuniswapV2Dele
flag upon deployment, which was supposed to lock the privileged_uniswapV2Proxy
address from further modification. This oversight allowed an attacker to assign_uniswapV2Proxy
to their own address, giving themselves access to restricted operations such as the minting of unlimitedNFGS
tokens. Declaring critical state variables asimmutable
upon initialization, rather than relying on additional locking logic, would have provided stronger protection against unauthorized changes.
Insufficient Verification of Proposer Response
- Titan Relay's Helix implementation lacks integrity validation on KZG commitments of the proposer's response. This oversight allowed a malicious proposer to sign a blinded block with valid headers but invalid KZG commitments, receive the unblinded block, and then propose a profitable block with reordered transactions. The fix involved ensuring that the KZG commitments provided by the proposer matched those from the builder.
Others
-
The Munchables smart contract securely tracked user deposits, limiting withdrawals to each user's recorded balance. However, the proxy admin previously set an unverified and malicious implementation behind the proxy, assigning themselves a
1e24 ETH
balance. This inflated balance persisted in storage, allowing the admin to later drain funds through the verified, secure implementation. -
The research paper, Demystifying and Detecting Cryptographic Defects in Ethereum Smart Contracts, provides an analysis of cryptographic issues in smart contracts. The authors outline nine distinct categories of defects, which can serve as a checklist for security researchers:
-
Single and Cross-Contract Signature Replay
-
Signature Front-Running and Malleability
-
Insufficient Signature Verification
-
Merkle Proof Replay and Front-Running
-
Hash Collisions with Dynamic-Length Arguments
-
Weak Randomness from Hashing Chain Attributes
-
Conclusion
Similar to previous years, 2024 has been rich in valuable research, innovative vulnerabilities and creative exploits. It's important to emphasise that the intent behind the article is not to criticise or blame the affected projects, but rather provide objective overviews that serve as educational material for the community to learn from and better protect projects in the future.