
Execution of the exploit in a local Foundry environment. The 'PASS' status confirms that the skim() function is indeed vulnerable to unauthorized collateral draining.
๐ง Introduction
In the fast-paced world of smart contract audits, being right is only half the battle. You also have to be first. Recently, during the V11 Finance audit on Cantina, I discovered a critical flaw that allowed anyone to drain wrapped NFT collateral.
Although my finding was marked as a Duplicate of #1, the technical journey and the resulting PoC (Proof of Concept) are worth sharing.
๐ The Vulnerability: Public skim() and Untrusted Collateral
The core of the issue was in the wrapper contract's interaction with the underlying liquidity pool.
The Flaw
The contract exposed a public skim() function. In Uniswap-style pools, skim() is used to recover tokens that are "stuck" in the contract (balance > reserve). However, in V7's implementation, there were no access controls or internal checks to ensure the caller was authorized to touch the "excess" tokens.
The Attack Vector:
An attacker monitors the contract holding wrapped NFT collateral.
By manipulating the internal state or simply catching a moment when the balance exceeds the tracked reserve, the attacker calls skim(attackerAddress).
The contract blindly transfers the excess tokens (representing NFT ownership or collateral) to the attacker.
๐ The Proof of Concept (Foundry)
To confirm this, I wrote a custom test case: test_SkimAttack_Theft().
Solidity
function test_SkimAttack_Theft() public {
// 1. Setup: User deposits NFT collateral
vm.prank(user);
v7Contract.deposit(nftId);
// 2. The Attack: Unauthorized caller triggers skim()
vm.prank(attacker);
v7Contract.skim(attacker);
// 3. Verification: Attacker now owns the collateral
assertEq(nftToken.ownerOf(nftId), attacker);
console.log("Attack Successful: Collateral Stolen!");
}
Terminal Output:
[PASS] test_SkimAttack_Theft() (gas: 45231)
The test confirmed that the vulnerability was live and easily exploitable.
๐ The "Duplicate" Verdict
When I submitted the report, it was linked to Finding #1.
In big contests, the most "obvious" critical bugs are often caught within the first few hours. While it's a bit disappointing to miss out on the primary reward, itโs a massive confidence booster.
Lesson Learned: If you find a bug that looks "too easy," submit it immediately. Don't wait to polish the reportโspeed is a feature in competitive auditing.
๐ก Conclusion
Even though it's a duplicate, this finding confirms that my "security radar" is calibrated to the same frequency as the top researchers in the space.
Key Takeaways:
Public functions are dangerous: Always ask "Who can call this?"
PoC is King: Having a working Foundry test makes your report undeniable.
Stay Fast: The difference between a $5k reward and a Duplicate is often just a few minutes.
Top comments (0)