DEV Community

ohmygod
ohmygod

Posted on

Anatomy of a Solana Wallet Drainer: Owner Reassignment, Durable Nonces, and Blinks Phishing

The Anatomy of a Solana Wallet Drainer: Owner Reassignment, Durable Nonce Tricks, and Blinks Phishing

Solana users lost over $90 million to phishing in the first half of 2025. By early 2026, the drainer kits have evolved again — and this time, they're bypassing wallet simulations entirely.

This isn't about smart contract bugs. It's about weaponizing legitimate Solana features against users who trust what their wallet shows them.

Let's dissect the three most dangerous wallet-draining techniques active on Solana right now, and what developers and users can do to defend against them.


Attack #1: The assign Instruction — Silent Owner Reassignment

Why Solana's Account Model Makes This Possible

On Ethereum, your EOA is controlled by whoever holds the private key. Period. On Solana, every account has an explicit Owner field that determines which program has write access to it. By default, your wallet account is owned by the System Program.

Here's the critical detail most users don't know: the assign instruction can change the Owner field of any account the signer controls.

// The Solana System Program's assign instruction
pub fn assign(
    pubkey: &Pubkey,        // Account to reassign
    owner: &Pubkey,         // New owner program
) -> Instruction
Enter fullscreen mode Exit fullscreen mode

How Attackers Exploit This

  1. Phishing entry point: Victim clicks a link to "claim an airdrop" or "verify for an allowlist"
  2. Benign-looking transaction: The wallet simulation shows no token transfers, no balance changes — everything looks safe
  3. Hidden assign instruction: Buried in the transaction is a System Program assign call that transfers the account's Owner field to an attacker-controlled program
  4. Delayed drain: The attacker now controls the account. They can drain SOL, SPL tokens, and NFTs at any time — the victim's private key is irrelevant

Why Wallet Simulations Miss It

Most wallet simulations focus on balance changes. The assign instruction doesn't move tokens — it changes who controls the account. The simulation shows:

✅ No SOL transferred
✅ No tokens transferred  
✅ No approvals granted
Enter fullscreen mode Exit fullscreen mode

The user signs with full confidence. Their account is already gone.

The SlowMist Analysis

SlowMist's research revealed that this attack mirrors the "malicious multisig" pattern previously seen on TRON, where administrative rights are silently reconfigured. On Solana, it's even more dangerous because:

  • No multisig UI exists to show ownership changes
  • The assign instruction is a core System Program feature, not an exotic edge case
  • Once reassigned, the original private key holder has zero recovery options

Attack #2: Durable Nonce + Contract Upgrade — The Time Bomb

The Setup

Normal Solana transactions expire after ~60-90 seconds (the blockhash lifetime). But durable nonces allow transactions to remain valid indefinitely. They're designed for legitimate use cases — offline signing, multi-party approvals, scheduled execution.

Attackers have weaponized this feature into a two-phase attack:

Phase 1: The Innocent Signature

User signs a durable-nonce transaction that:
├── Advances the nonce (required for durable nonce txs)
├── Calls a contract method: mint_nft()
└── The contract's mint_nft() is genuinely benign at signing time
Enter fullscreen mode Exit fullscreen mode

The wallet simulates the transaction. It calls mint_nft() on the current contract code. Everything checks out — the user gets an NFT, no funds leave.

Phase 2: The Upgrade

The attacker doesn't broadcast the transaction yet. Instead, they:

  1. Upgrade the target contract (Solana programs are upgradeable by default)
  2. The new mint_nft() function now includes transfer instructions that drain the caller's wallet
  3. Now they broadcast the pre-signed transaction

When the transaction executes, it runs against the new, malicious contract code. The simulation the user saw was accurate at the time of signing — but the contract changed underneath.

Why This Is Almost Undetectable

Timeline:
T+0s    User signs transaction → Simulation: ✅ Safe
T+30min Attacker upgrades contract
T+31min Attacker broadcasts pre-signed transaction
T+31min Transaction executes against malicious contract → 💀 Drained
Enter fullscreen mode Exit fullscreen mode

No wallet can simulate what a contract will become. The simulation was truthful — it just became stale.

Mitigation for Developers

If you're building a program that interacts with user funds:

// Always verify the program hasn't been upgraded since the user's intent
// Use immutable programs where possible
// If upgradeability is required, implement a timelock

#[account(
    constraint = program_data.upgrade_authority_address == Some(EXPECTED_AUTHORITY)
        @ ErrorCode::UnexpectedUpgradeAuthority,
    constraint = program_data.slot == EXPECTED_DEPLOYMENT_SLOT
        @ ErrorCode::ProgramUpgraded,
)]
pub program_data: Account<'info, ProgramData>,
Enter fullscreen mode Exit fullscreen mode

For users: never sign durable-nonce transactions from untrusted sources. If a dApp asks you to sign something that doesn't expire, treat it like handing someone a signed blank check.


Attack #3: Malicious Blinks — One-Click Wallet Drains on Social Media

What Are Blinks?

Solana Actions and Blinks (Blockchain Links) let developers embed executable transactions directly in social media posts, websites, and messaging apps. Click a Blink, approve in your wallet, and the action executes on-chain.

The UX is incredible. The phishing surface is terrifying.

The Attack Flow

1. Attacker creates a malicious Action endpoint
2. Wraps it in a Blink that says "Claim your 500 SOL airdrop!"
3. Posts it on X/Twitter, Discord, Telegram
4. Victim clicks → wallet opens → transaction preview shows...
   what exactly?
Enter fullscreen mode Exit fullscreen mode

The problem: Blinks execute arbitrary transaction payloads returned by the Action endpoint. The endpoint can return any valid Solana transaction, including:

  • Token approvals to attacker addresses
  • assign instructions (Attack #1)
  • Durable nonce transactions (Attack #2)
  • Direct SOL transfers disguised in complex instruction bundles

The Registry Problem

Solana maintains a Blinks registry of verified Action providers. But:

  • Not all wallets enforce registry checks
  • Users often click first, verify never
  • Attackers register legitimate-looking domains that pass initial review
  • Social engineering ("Limited time! Only 100 claims left!") bypasses rational judgment

Real-World Example: The Fake Mint Blink

In Q1 2026, a Blink circulating on X promised "free mint" for an NFT collection. The Action endpoint returned a transaction with three instructions:

Instruction 1: Create associated token account (looks normal)
Instruction 2: Transfer 0.01 SOL for "gas" (seems standard)  
Instruction 3: assign(victim_account, attacker_program)
Enter fullscreen mode Exit fullscreen mode

Most users saw "Create account + small fee" and approved. Their wallets were drained within minutes.


Defense Playbook: What Actually Works

For Users

Risk Defense Difficulty
Owner reassignment Use a hardware wallet that shows raw instructions Medium
Durable nonce traps Never sign durable-nonce txs from unknown sources Easy
Blinks phishing Only interact with registry-verified Blinks Easy
All of the above Use a burner wallet for all interactions Easy

The Burner Wallet Strategy:

Main wallet (cold storage): 95% of assets
├── Hardware wallet or multisig
├── Never connects to dApps
└── Only receives transfers from hot wallet

Hot wallet (daily use): 5% of assets  
├── Connects to dApps, signs transactions
├── Funded with only what you can afford to lose
└── Regularly sweep profits back to cold storage

Burner wallet (disposable): Dust only
├── Used for airdrops, new mints, untrusted dApps
├── Contains <$50 at any time
└── Abandoned and replaced regularly
Enter fullscreen mode Exit fullscreen mode

For Developers

  1. Make your programs immutable once they're stable. If your program doesn't need upgrades, revoke the upgrade authority permanently:
solana program set-upgrade-authority <PROGRAM_ID> --final
Enter fullscreen mode Exit fullscreen mode
  1. If upgradeability is required, implement a timelock:
pub struct UpgradeTimelock {
    pub proposed_authority: Pubkey,
    pub proposal_time: i64,
    pub timelock_seconds: i64,  // Minimum 48 hours recommended
}
Enter fullscreen mode Exit fullscreen mode
  1. Implement transaction guards that check for suspicious instruction patterns:
// Reject transactions that include assign instructions
// alongside your program's instructions
fn validate_transaction_context(ctx: &Context) -> Result<()> {
    let instructions = get_processed_sibling_instruction(0);
    for ix in instructions {
        if ix.program_id == system_program::ID {
            // Check if it's an assign instruction (discriminator = 1)
            if ix.data.first() == Some(&1) {
                return Err(ErrorCode::SuspiciousInstruction.into());
            }
        }
    }
    Ok(())
}
Enter fullscreen mode Exit fullscreen mode
  1. Emit events for ownership changes so monitoring tools can flag suspicious activity:
#[event]
pub struct OwnershipAlert {
    pub account: Pubkey,
    pub old_owner: Pubkey,
    pub new_owner: Pubkey,
    pub slot: u64,
}
Enter fullscreen mode Exit fullscreen mode

For Wallet Developers

The biggest gap in current wallet UX is the failure to surface non-financial state changes. Wallets should:

  1. Flag assign instructions with a prominent red warning: "This transaction will change who controls your account"
  2. Detect durable nonce usage and warn: "This transaction does not expire — the contract could change before execution"
  3. Show program upgrade status: "This program was last upgraded 3 hours ago" or "This program is immutable ✅"
  4. Verify Blinks against the official registry and display clear trust indicators

The Bigger Picture

These attacks share a common theme: they exploit the gap between what users see and what actually happens on-chain. Solana's account model is powerful, but its flexibility creates attack surfaces that don't exist on simpler chains.

The $90M+ lost to Solana phishing in 2025 wasn't caused by protocol bugs. It was caused by:

  • Users trusting wallet simulations as absolute truth
  • Developers shipping upgradeable programs without timelocks
  • Wallets optimizing for "clean" UX over transparent disclosure
  • Social platforms failing to filter malicious Blinks

The tools to prevent every attack in this article already exist. The challenge is getting the ecosystem to adopt them before the next $100M disappears.


Key Takeaways

  1. The assign instruction can silently transfer control of your Solana account — and wallet simulations won't catch it
  2. Durable nonce + contract upgrade creates a time-bomb attack that defeats simulation-based protection entirely
  3. Blinks are phishing delivery mechanisms that embed transaction payloads directly in social feeds
  4. Burner wallets are the single most effective user defense — isolate assets from interaction risk
  5. Developers must choose immutability or implement timelocks — there's no safe middle ground

This article is part of the DeFi Security Research series. Follow @ohmygod for weekly deep dives into blockchain security vulnerabilities, audit techniques, and defense strategies.

Top comments (0)