The Notorious Bug Digest #1

Table of contents 
Introduction
TheIdolsNFT Incident Due to Mismanagement of Identical Address Pair Parameters
Alien Base Dex Incident from a Sell-AddLiquidity-Buy Sandwich Attack
Exploitation of ERC-6492 Arbitrary Call via Precompile Contract

Introduction
Welcome to The Notorious Bug Digest, a short collection of insights about recent Web3 bugs and security incidents! When not auditing, some of our security researchers use their time to keep up-to-date with the security space, read bug reports and study on-chain incidents. We think such information is useful to the security community, and can help other researchers hone their skills or onboard to the security space. Hence, we decided to share this content externally.



TheIdolsNFT Incident Due to Mismanagement of Identical Address Pair Parameters

The NFT project features a yield and claim mechanism, as illustrated in Picture A. rewardPerGod tracks the cumulative amount of stETH awarded per NFT since the protocol began. claimedSnapshots is a mapping that records how much reward each user has claimed for each NFT. When users claim their rewards, the _claimEthRewards function calculates the rewards based on the number of NFTs held by the user and the difference between rewardPerGod and the user's claimedSnapshots. After calculation, it updates the user's claimedSnapshots to match rewardPerGod.

As shown in Picture B, during a token transfer operation, the _beforeTokenTransfer hook automatically claims rewards for both the sender and receiver. If the sender ends up holding no NFTs after the transfer, their claimedSnapshots are reset to zero. However, when the sender and receiver are the same address, and they only hold one NFT, the receiver's claimedSnapshots are reset to zero before the rewards are claimed, allowing the receiver to claim more rewards.

Picture C illustrates how the attacker exploited this vulnerability by repeatedly transferring a single NFT between the same sender and receiver address, thus accumulating undue profits.

img-1Aimg-1B

img-1C



Alien Base Dex Incident from a Sell-AddLiquidity-Buy Sandwich Attack

As illustrated in Picture A, the contract features a compound function meant to collect fees from a liquidity pool and reinvest them. However, this function is permissionless; the tick range is determined by the caller, and there's no slippage protection (amountMin = 0). Picture B outlines the attack sequence: The attacker begins by selling flash-loaned ALB tokens, which drives down the ALB/WETH price. They then trigger the compound function, adding liquidity at this artificially low price. Following this, the attacker repurchases ALB with WETH, taking advantage of the increased liquidity at the lower price to end up with more ALB than they started with. Finally, the attacker converts these ALB tokens back into WETH in another pool to secure their profits.

img-2A

img-2B



Exploitation of ERC-6492 Arbitrary Call via Precompile Contract

A project using ERC-6492 was hacked, resulting in a loss of $50k. While the root cause—an arbitrary call—is straightforward, the exploitation was interesting as it used a precompile contract to bypass normal interface calls. ERC-6492 extends ERC-1271 by allowing signature validation for contracts not yet deployed, known as counterfactual contracts. The process involves wrapping signatures with additional deployment data when the contract isn't on-chain, using a format like concat(abi.encode((create2Factory, factoryCalldata, originalSignature)), magicBytes), where magicBytes (0x6492...6492) signal this special verification. Verifiers first check for these magic bytes to see if deployment is needed before validation. If the contract is already deployed, standard ERC-1271 verification is used; otherwise, create2Factory is called with factoryCalldata to deploy it.

ERC-6492 also provides a reference implementation for verifiers, and the project used a simplified version. According to the code, if there's a specific suffix in the signature and the signer contract isn't deployed yet (code.length == 0), the verifier calls create2Factory. Note that both create2Factory and factoryCalldata come from an external user, which introduces an arbitrary call vulnerability. But is this actually exploitable?

img-3A

Let's assume isCounterfactual is true and _signer.code.length == 0, and we trigger a USDC.transfer call to drain USDC tokens from the verifier contract. The control flow then enters the // Try ERC-1271 verification branch and executes try IERC1271Wallet(_signer).isValidSignature(_hash, sigToValidate) returns (bytes4 magicValue). Since no contract is actually deployed at _signer's address, isValidSignature returns empty data, causing a decode revert, and the hack fails.The first workaround involves using tokens with hooks. For instance, ERC-1155 has a receiver hook onERC1155Received on transfer. If ERC-1155 tokens are left in the verifier contract, we could deploy a contract at _signer that returns data for the isValidSignature call. But what if the contract only holds ERC-20 tokens like USDC? Here, we can't manipulate the control flow during USDC.transfer, yet we need isValidSignature to succeed. Is there any contract where code.length == 0 but returns >= 4 bytes when called with isValidSignature(bytes32, bytes)?

  • Contracts with code.length == 0 are Precompiles! They don't have EVM code but Node code.
  • Precompiles "ignore" function selectors, focusing on raw calldata.

Looking at the current precompiles in EVM (Cancun), there are several potential candidates. For instance, the precompile at address 0x4 accepts bytes data and returns the exact input; since the input is larger than 4 bytes, the decoding succeeds! The attacker used this method to execute the hack.

As shown in the picture, they called isValidSigImpl with _signer as the 0x4 precompile, setting create2Factory with factoryCalldata to USDC.transfer(attacker, balance). This triggered the transfer and allowed the IERC1271Wallet(_signer).isValidSignature call to execute successfully at the end.

img-3CShortly after this incident, @pcaversaccio opened an issue suggesting to add warnings about potential vulnerabilities to the official ERC-6492 reference implementation, including the use of precompile addresses (0x04) in combination with arbitrary calls.


It's important to emphasise that the intent behind this content 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.