Each snake is trained on its language and framework. Run them individually or deploy the full team for comprehensive cross-chain coverage.
🐍
RattlerAI
Solidity · EVM · DeFi
Scans Solidity and EVM contracts for reentrancy, flash loan attack surfaces, access control failures, oracle manipulation, and 20+ vulnerability classes. Powered by Claude Opus + Slither static analysis.
Audits Move smart contracts on Aptos and Sui. Finds resource leaks, capability confusion, object ownership violations, arithmetic overflow in fixed-point math, and access control bypasses unique to the Move resource model.
Resource LeaksCapability BugsOverflowObject OwnershipAccess Control
Pricing
Straightforward. No surprises.
All subscriptions are month-to-month. Pay in USDC. Cancel anytime.
All three snakes. One repository. Full report delivered within 48 hours. No commitment required — the right starting point before launching or before a fundraise.
These reports were generated against publicly known vulnerable contracts. The vulnerabilities are real — the exploits happened.
Euler Finance — EToken.sol + BaseLogic.sol$197M exploited March 2023Solidity · EthereumScanned by RattlerAI
## Executive Summary
The EToken contract is part of the Euler Finance protocol (proxy-based modular
architecture). The contract exhibits the well-known donation/inflation vulnerability
via donateToReserves, and critically, the mint function contains the exact bug that
led to the Euler Finance $197M hack in March 2023: a self-borrow operation that
doesn't verify liquidity correctly against the freshly minted self-collateralized
position, combined with donateToReserves allowing collateral destruction without
health check.
## Findings### [CRITICAL] H-01 — Self-borrow via mint enables under-collateralized leverage
exploitable through reserve donationSeverity: CRITICAL
Location: EToken.sol:mint / donateToReserves
Description:
The mint() function performs a "self-borrow": it mints eTokens (collateral) and
dTokens (debt) of the same underlying in equal underlying amounts to the same
account. Because the eToken is self-collateralizing the dToken, the account can
leverage up massively in a single call without any actual underlying being deposited.
The checkLiquidity call at the end relies on the collateral factor of the eToken
position being higher than the borrow factor of the dToken — true in normal
operation. However, if donateToReserves lets a user burn their eToken balance and
credit it to reserves WITHOUT a liquidity check, an attacker can:
1. Call mint(0, hugeAmount) to create equal eToken collateral and dToken debt.
2. Call donateToReserves(0, mostOfTheETokens) to destroy the collateral side
while leaving the debt intact.
3. Pass the liquidity check because donateToReserves did not call checkLiquidity
on the donating account.
4. Self-liquidate to extract value from the now-undercollateralized position,
draining the pool.
Impact: Complete drain of any liquid token market on the platform.
Proof of Concept:
// Attacker contract (sub-account A funds, sub-account B liquidates)
function exploit(address eToken, uint amount) external {
IEToken(eToken).deposit(0, smallSeed);
IEToken(eToken).mint(0, amount * 10); // 10x leverage
IEToken(eToken).donateToReserves( // destroy collateral
0, IEToken(eToken).balanceOf(address(this)) - dust
);
ILiquidation(euler).liquidate( // extract at discount
subAccount0, underlying, underlying, type(uint).max, 0
);
IEToken(eToken).withdraw(1, type(uint).max);
}
Recommendation:
Add checkLiquidity(account) inside donateToReserves (the fix Euler shipped
in eIP-14). Disallow donations from accounts holding any dToken debt in the
same underlying.
### [LOW] L-01 — decimals() hardcoded to 18 regardless of underlying
Location: EToken.sol:decimals
Returns 18 always; naive integrators pricing eTokens against underlying without
convertBalanceToUnderlying will misprice. Not exploitable in-contract.
## Summary
┌─────┬──────────┬─────────────────────────────────────────────────────┐
│ H-01│ CRITICAL │ Self-borrow + unchecked donateToReserves → pool drain│
│ L-01│ Low │ decimals() hardcoded to 18, mismatches underlying │
└─────┴──────────┴─────────────────────────────────────────────────────┘
Verification: 1 confirmed · 0 false positives removed
Wormhole Solana Bridge — post_vaa.rs + verify_signature.rs$323M exploited February 2022Rust · Anchor · SolanaScanned by CottonmouthAI
## Executive Summary
This is an audit of the Wormhole bridge's Solana program (built on the Solitaire
framework), specifically the post_vaa and verify_signatures instruction handlers.
These handlers are critical infrastructure responsible for accepting and validating
cross-chain VAAs (Verified Action Approvals) signed by the Wormhole guardian set.
The code contains several high-severity issues in verify_signatures involving
off-by-one signer index validation, missing duplicate signer prevention, and
untrusted message-source binding, as well as a critical quorum calculation flaw
in post_vaa that under-counts the required signatures threshold.
## Findings### [HIGH] H-02 — Off-By-One in Signer Index Check Allows Out-of-Bounds IndexingSeverity: HIGH
Location: verify_signature.rs:verify_signatures
The bounds check uses strict > instead of >=:
if s.signer_index > accs.guardian_set.num_guardians() {
return Err(ProgramError::InvalidArgument.into());
}
let key = accs.guardian_set.keys[s.signer_index as usize]; // OOB if == len
If num_guardians() returns keys.len(), then signer_index == keys.len() passes
the check but panics on the next line. Use >= and compare against keys.len().
Impact: DoS of the VerifySignatures instruction for crafted inputs.
### [HIGH] H-03 — Message Hash Not Validated Against secp256k1 InstructionSeverity: HIGH
Location: verify_signature.rs
The Wormhole signing scheme requires double-keccak: guardians sign
keccak256(keccak256(body)). This implementation stores only the single keccak
of the body. Signatures produced for the canonical scheme cannot be verified
here, creating a chain-divergence vector.
Recommendation: Hash the body twice in check_integrity to match the canonical
Wormhole spec:
let body_hash = keccak256(&body);
let body_hash = keccak256(&body_hash); // double-hash per spec### [HIGH] H-04 — Duplicate Signer Indices Not Rejected Across CallsSeverity: HIGH
Location: verify_signature.rs + post_vaa.rs
verify_signatures writes signatures[signer_index] = true with no check that
two sig_infos entries share the same signer_index. While idempotent within
one call, crafted multi-call sequences where the same guardian is counted
across separate verify_signatures invocations can corrupt consensus counting.
## Summary
┌─────┬──────┬──────────────────────────────────────────────────────────┐
│ H-02│ High │ Off-by-one signer index → OOB panic / guardian skip │
│ H-03│ High │ Single vs double keccak mismatch → cross-chain divergence│
│ H-04│ High │ Duplicate signer indices not rejected across calls │
└─────┴──────┴──────────────────────────────────────────────────────────┘
Verification: 3 confirmed · 1 false positive removed (H-01)
Cetus Protocol — integer-mate math_u256.move$223M exploited May 2025Move · SuiScanned by CopperheadAI
## Executive Summary
This is a Sui Move math utility module providing 256-bit integer operations.
The module contains a critical off-by-one bug in checked_shlw that completely
defeats the overflow protection it was designed to provide — this is the exact
bug pattern that caused the Cetus Protocol $223M exploit in May 2025.
Additional issues include unchecked division-by-zero and a logic flaw in div_round.
## Findings### [CRITICAL] H-01 — Broken Overflow Check in checked_shlwSeverity: CRITICAL
Location: integer_mate::math_u256::checked_shlw
The overflow guard uses an incorrectly constructed mask:
public fun checked_shlw(n: u256): (u256, bool) {
let mask = 0xffffffffffffffff << 192; // BUG: should be 1u256 << 192
if (n > mask) {
(0, true)
} else {
((n << 64), false) // overflows for n >= 2^192
}
}
The mask evaluates to ~2^256 - 2^192, which is vastly larger than the safe
threshold of 2^192. For any n in [2^192, mask], the check n > mask is false
and n << 64 executes anyway, silently truncating the high bits and returning
an attacker-controlled small number with overflow = false.
Impact: Any AMM or liquidity math built on this primitive can be tricked into
accepting an astronomically large notional with effectively zero collateral.
Attacker mints near-infinite LP shares for trivial input, then drains every
pool sharing the contract.
Proof of Concept:
let n: u256 = 1u256 << 200; // above 2^192, should overflow
let (val, overflowed) = checked_shlw(n);
// overflowed == false (BUG — should be true)
// val == truncated garbage — downstream credits attacker with huge liquidity
Recommendation:
public fun checked_shlw(n: u256): (u256, bool) {
let mask: u256 = 1u256 << 192; // correct threshold
if (n >= mask) {
(0, true)
} else {
(n << 64, false)
}
}### [HIGH] H-02 — Unchecked Division by Zero in div_mod and div_roundSeverity: HIGH
Location: integer_mate::math_u256::div_mod, div_round
Both functions perform num / denom without validating denom != 0. While Move
aborts on divide-by-zero, the opaque abort code cannot be caught by callers.
When denom is derived from pool state (e.g. price = 0 on empty tick), this
creates a DoS griefing vector that permanently aborts valid transactions.
### [MEDIUM] M-01 — div_round Can Overflow When p + 1 SaturatesSeverity: MEDIUM
When round_up is true and p * denom != num, returns p + 1. For edge-case
inputs where p approaches u256::MAX, this overflows. Use num % denom instead
of recomputing p * denom to eliminate the multiplication entirely.
### [LOW] L-01 — Unused Constants MASK_U128 / MASK_U64
Declared but never referenced. Dead constants suggest a planned safety mask
was forgotten — which correlates with the broken mask in checked_shlw.
## Summary
┌─────┬──────────┬─────────────────────────────────────────────────────┐
│ H-01│ CRITICAL │ Broken overflow check in checked_shlw → pool drain │
│ H-02│ High │ Division by zero → DoS griefing vector │
│ M-01│ Medium │ div_round overflow on p+1 saturation │
│ L-01│ Low │ Unused constants suggest incomplete implementation │
└─────┴──────────┴─────────────────────────────────────────────────────┘
Verification: 4 confirmed · 1 false positive removed
Process
How it works
Simple, transparent, no back-and-forth.
1
You email us
Send your GitHub repo URL and select a plan. We review scope and confirm within 24 hours.
2
Payment
Pay in USDC to our Base wallet. Scan runs after confirmation. No net-30. No invoices.
3
The Vipers run
RattlerAI, CottonmouthAI, and CopperheadAI audit your contracts. Findings verified before delivery.
4
Report delivered
Full report to your inbox. Critical findings flagged immediately. Remediation guidance included.
Get Started
Ready when you are.
Email us your repository URL, tell us which plan fits, and we'll get back to you within 24 hours. No pitch deck. No sales call required.