मुख्य सामग्री पर जाएं
यह उदाहरण संक्षेप में यह दिखाएगा कि Tenderly का उपयोग हैक्स का विश्लेषण करने और यहाँ तक कि रोकने के लिए कैसे किया जा सकता है, जैसा कि Cover Protocol के साथ हुआ था। Cover Protocol हैक हो गया क्योंकि code storage में cache को अपडेट करने में असमर्थ था। इसे deposit/withdrawal और विशेष state changes जैसे महत्वपूर्ण functions पर अलर्ट सेट करके संभावित रूप से रोका जा सकता था ताकि हमले को रोका जा सके। Hack का manually विश्लेषण करने के लिए, हमें उस smart contract Blacksmith को import करना होगा जिसमें भेद्यता पाई गई थी। यदि आप प्रभावित protocol या vulnerable contract का सटीक नाम जानते हैं तो आप उसे सीधे search भी कर सकते हैं।
नोट - आप केवल उन contracts को search कर सकते हैं जो Tenderly या Etherscan पर सार्वजनिक रूप से सत्यापित हैं। Tenderly पर contract को कैसे सत्यापित करें, इसके बारे में यहाँ अधिक पढ़ें।
यदि contract सार्वजनिक रूप से सत्यापित नहीं है, तो आप इसे Tenderly के साथ उपयोग करने में सक्षम होने के लिए स्वयं कर सकते हैं। यदि project open-source है (जैसे Cover), तो आप project के GitHub repo पर जा सकते हैं, वह contract खोजें जिसका आप उपयोग करना चाहते हैं (और उसका सोर्स कोड) और स्वयं इसे सत्यापित करें। अब जब हमारे पास वह contract है जिसे हम Tenderly में import करना चाहते हैं, तो अपने Dashboard पर जाएँ और बाईं ओर Contracts tab पर क्लिक करें, और फिर ऊपर दाईं ओर Add Contract बटन पर क्लिक करें: आप अपने Dashboard के माध्यम से, या CLI के माध्यम से contract के code को upload और सत्यापित कर सकते हैं [अधिक जानकारी यहाँ]: अब, contract को import और सत्यापित करने के बाद, आइए hack का विश्लेषण करें। हम जानते हैं कि bug deposit function के cache में मौजूद था, इसलिए हम उस function को simulate करते हैं। जैसा कि हम नीचे screenshot में देख सकते हैं, execution के बाद यह Blacksmith: pool does not exist के साथ विफल हो गया। अब, आइए Tenderly Debugger में इस error को खोलें। line 118 पर हम deposit function में Pool memory pool = pools[_lpToken]; देखते हैं जहाँ data cache होता है, और बाद में line 125 पर अपडेट होता है जहाँ हम _claimCoverRewards(pool, miner); देखते हैं Contract ने cache data का उपयोग किया क्योंकि data स्वयं memory में रहा; इसीलिए बाद में contract ने उस data का उपयोग करके function की गणना की। Cache को अपडेट नहीं किया गया जिसके परिणामस्वरूप यह hack हुआ:
example.sol
miner.rewardWriteoff = miner.amount.mul(pool.accRewardsPerToken).div(CAL_MULTIPLIER);
example.sol
miner.bonusWriteoff = miner.amount.mul(bonusToken.accBonusPerToken).div(CAL_MULTIPLIER);
example.sol
function deposit(address _lpToken, uint256 _amount) external override {
    require(block.timestamp >= START_TIME, "Blacksmith: not started");
    require(_amount > 0, "Blacksmith: amount is 0");

    Pool memory pool = pools[_lpToken];

    require(pool.lastUpdatedAt > 0, "Blacksmith: pool does not exists");
    require(IERC20(_lpToken).balanceOf(msg.sender) >= _amount, "Blacksmith: insufficient balance");

    updatePool(_lpToken);

    Miner storage miner = miners[_lpToken][msg.sender];
    BonusToken memory bonusToken = bonusTokens[_lpToken];

    _claimCoverRewards(pool, miner);
    _claimBonus(bonusToken, miner);

    miner.amount = miner.amount.add(_amount);

    // update writeoff to match current acc rewards/bonus per token
    miner.rewardWriteoff = miner.amount.mul(pool.accRewardsPerToken).div(CAL_MULTIPLIER);
    miner.bonusWriteoff = miner.amount.mul(bonusToken.accBonusPerToken).div(CAL_MULTIPLIER);

    IERC20(_lpToken).safeTransferFrom(msg.sender, address(this), _amount);
    emit Deposit(msg.sender, _lpToken, _amount);
}

  • Hack से कुछ घंटे पहले liquidity mining के लिए एक नया pool approved हुआ था। यह pool पूरी तरह से सामान्य है लेकिन चूँकि यह नया था, blacksmith contract के पास इस pool का कोई LP token नहीं था।
  • हमलावर ने इस pool के कुछ tokens को Blacksmith contract में जमा कर दिया।
  • Blacksmith contract प्रति-token के आधार पर rewards का ट्रैक रखता है। यदि बहुत सारे tokens locked हैं, तो प्रति-token reward छोटा होगा। यदि बहुत कम tokens locked हैं, तो प्रति-token reward बड़ा होगा। संबंधित variable को accRewardsPerToken कहा जाता है और इसकी गणना totalPoolRewards / totalTokenBalance के रूप में की जाती है।
  • हमलावर ने फिर Blacksmith contract से लगभग सभी LP tokens निकाल लिए, totalTokenBalance राशि को लगभग शून्य कर दिया।
  • हमलावर ने फिर इस pool के कुछ tokens को Blacksmith contract में फिर से जमा किया। यहीं पर bug ने अपने असली रंग दिखाए। चूँकि पिछले transaction में totalTokenBalance बहुत कम हो गया था, इसलिए नए-गणना किए गए accRewardsPerToken में तेज़ी से वृद्धि हुई। Contract accRewardsPerToken के प्रभाव को नियंत्रण में रखने के लिए rewardWriteoff का उपयोग करता है। हालाँकि, bug के कारण, rewardWriteoff मान की गणना करते समय accRewardsPerToken का पुराना (छोटा) मान उपयोग किया गया था। इसके कारण, accRewardsPerToken का बड़ा मान अनियंत्रित रहा।
  • हमलावर ने फिर अपने rewards निकाले। चूँकि accRewardsPerToken में एक बड़ा, अनियंत्रित मान था, system से भुगतान किया गया कुल reward inflate हो गया और contract ने अंततः 40,796,131,214,802,500,000 COVER tokens mint कर दिए
इस hack breakdown के अंतिम भाग के लिए Mudit Gupta को धन्यवाद - उनके ब्लॉग पर इसके बारे में अधिक पढ़ें :)