sui存钱罐的权限控制和pacakge升级带来的风险

sui 存钱罐涉及的管理权限adminCap 和upgradeCap ,其中upgradeCap 可能绕过adminCap限制

1.概述

  • 假设写一个存钱罐合约,可以从合约中存入货币,提取货币,
    • 存钱不控制权限,每个人都能存
    • 只有拥有AdminCap对象的人才能取钱。
    • 取钱函数有一个AdminCap参数,sui 判断谁拥有Admincap 对象才能调用 withdraw
    • 合约构造者A可以将AdminCap对象 转让给B
    • 表面上只有B 能获得权限取钱
  • 风险

    • 合约构造者还拥有升级合约的权限,可能通过升级合约来获取新的取钱权限

    2. 合约代码

module upgrade_demo::upgrade_demo ;
    use sui::object::{Self, UID};
    use sui::transfer;
    use sui::balance::{Self, Balance};
    use sui::coin::{Self, Coin};
    use sui::sui::SUI;
    use sui::tx_context::{Self, TxContext};

    // 错误码
    const ENotAdmin: u64 = 0;
    const EInsufficientBalance: u64 = 1;

    // 管理员权限凭证
    public struct AdminCap has key, store {
        id: UID
    }

    // 金库结构
    public struct Treasury has key {
        id: UID,
        balance: Balance<SUI>
    }

    // 初始化函数 - 创建管理员凭证和金库
    fun init(ctx: &mut TxContext) {
        let admin_cap = AdminCap {
            id: object::new(ctx)
        };

        let treasury = Treasury {
            id: object::new(ctx),
            balance: balance::zero(),
        };

        // 将AdminCap转移给部署合约的地址
        transfer::transfer(admin_cap, tx_context::sender(ctx));
        // 共享金库对象
        transfer::share_object(treasury);
    }

    // 存入SUI代币
    public fun deposit(treasury: &mut Treasury, payment: Coin<SUI>) {
        sui::balance::join(&mut treasury.balance, payment.into_balance());

    }

     // 管理员提取SUI代币
    public fun withdraw(
        treasury: &mut Treasury, 
        amount: u64,
        _admin_cap: &AdminCap,
        ctx: &mut TxContext
    ): Coin<SUI> {
        // 检查余额是否充足
        assert!(balance::value(&treasury.balance) >= amount, EInsufficientBalance);

        // 从金库中提取代币
        coin::from_balance(balance::split(&mut treasury.balance, amount), ctx)
    }

    // 查询金库余额
    public fun balanceOf(treasury: &Treasury): u64 {
        balance::value(&treasury.balance)
    }

3. 发布合约

  • 获得对象
sui move   --skip-fetch-latest-git-deps  publish
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes                                                                                             │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects:                                                                                           │
│  ┌──                                                                                                       │
│  │ ObjectID: 0x10ca605ce0437c10274bcc260901ef5b34bab91c747f2dc498b722ae151a5baf                            │
│  │ Sender: 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c                              │
│  │ Owner: Account Address ( 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c )           │
│  │ ObjectType: 0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89::upgrade_demo::AdminCap  │
│  │ Version: 245323791                                                                                      │
│  │ Digest: BLp2DjwNMD1fZ4Y4HxNMRz1AQq28afgMQ57pNFt4BZoa                                                    │
│  └──  

│  ┌──                                                                                                       │
│  │ ObjectID: 0x67a5815e053ab8be8248c894ec288baf79bc6f323321812074cac70e63915bb0                            │
│  │ Sender: 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c                              │
│  │ Owner: Account Address ( 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c )           │
│  │ ObjectType: 0x2::package::UpgradeCap                                                                    │
│  │ Version: 245323791                                                                                      │
│  │ Di

 ┌──                                                                                                       │
│  │ ObjectID: 0x830243c107ff3ad19e4506d9a61d04af4a0c35771445e468c3a7befbc6d9293c                            │
│  │ Sender: 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c                              │
│  │ Owner: Shared( 245323791 )                                                                              │
│  │ ObjectType: 0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89::upgrade_demo::Treasury  │
│  │ Version: 245323791                                                                                      │
│  │ Digest: Enq9zenKrY4r3fXYEWr9XJCcA8FixSTZhKbVqXqDueNM 
  • 对象说明
对象名 取值 说明
package address 0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89 合约包的地址
upgradeCap 0x67a5815e053ab8be8248c894ec288baf79bc6f323321812074cac70e63915bb0 控制升级的权限
adminCap 0x10ca605ce0437c10274bcc260901ef5b34bab91c747f2dc498b722ae151a5baf 控制提取Treasury余额
owner 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c 目前合约发布者拥有adminCap,upgradeCap
treasury 0x830243c107ff3ad19e4506d9a61d04af4a0c35771445e468c3a7befbc6d9293c 存钱罐
  • 查看当前用户的coin

    sui client gas
    ╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
    │ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │
    ├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
    │ 0x09119914c43f80a486b30cc5d4f1382d0b57929eabd70f116ae4e918d9c89bf0 │ 470927364          │ 0.47             │
    │ 0xae3da3cb598610db16ea040ad6b09cfd324e146a2a7e5b18ccfa76b34b8d3b22 │ 497664604          │ 0.49             │
    │ 0xeda78a0ba9b2ef153a209f51380d8e77af0f35c60eb8ea26d4239c639844680d │ 995399888          │ 0.99             │
    ╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯

4. A用户存钱

  • 配置一些变量,方便后面的脚本使用
export PKG=0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89
export ADMIN_CAP=0x10ca605ce0437c10274bcc260901ef5b34bab91c747f2dc498b722ae151a5baf
export UPGRADE_CAP=0x67a5815e053ab8be8248c894ec288baf79bc6f323321812074cac70e63915bb0
export TREASURY=0x830243c107ff3ad19e4506d9a61d04af4a0c35771445e468c3a7befbc6d9293c     
export ADDR_A=0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c
export ADDR_B=0x7cbe5e6596e23266dd5763dd89b4ab1195516908ecde8febfe96685c7cbe6432
# 查看当前用户有哪些coin
sui client switch --address $ADDR_A
sui client gas
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x09119914c43f80a486b30cc5d4f1382d0b57929eabd70f116ae4e918d9c89bf0 │ 470927364          │ 0.47             │
│ 0xae3da3cb598610db16ea040ad6b09cfd324e146a2a7e5b18ccfa76b34b8d3b22 │ 497664604          │ 0.49             │
│ 0xeda78a0ba9b2ef153a209f51380d8e77af0f35c60eb8ea26d4239c639844680d │ 995399888          │ 0.99             │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯

# 根据前面的输出,设置需要存入的coin
export COIN_A=0xae3da3cb598610db16ea040ad6b09cfd324e146a2a7e5b18ccfa76b34b8d3b22
# 存入货币
sui client ptb --move-call $PKG::upgrade_demo::deposit   "@$TREASURY,@$COIN_A"

5. A 用户取钱

$ sui client ptb --move-call $PKG::upgrade_demo::deposit   "@$TREASURY @0xae3da3cb598610db16ea040ad6b09cfd324e146a2a7e5b18ccfa76b34b8d3b22"
  • 取回5000单位

    sui client ptb --move-call $PKG::upgrade_demo::withdraw "@$TREASURY 50000 @$ADMIN_CAP"
    sui client ptb --move-call $PKG::upgrade_demo::withdraw "@$TREASURY 50000 @$ADMIN_CAP" --assign coin1 --transfer-objects [coin1] @$ADDR_A
  • 查看当前A用户拥有的coin

sui client gas $ADDR_A
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x09119914c43f80a486b30cc5d4f1382d0b57929eabd70f116ae4e918d9c89bf0 │ 466808880          │ 0.46             │
│ 0xd91123cfc325ba1eff95e839837a1bf443046cc00a72da5192a005ffea1a7117 │ 50000              │ 0.00             │
│ 0xeda78a0ba9b2ef153a209f51380d8e77af0f35c60eb8ea26d4239c639844680d │ 995399888          │ 0.99             │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯

6. A 用户转移adminCap 给B

sui client ptb --transfer-objects [@$ADMIN_CAP] @$ADDR_B

# 显示ADMIN_CAP 对象的owner是0x7cbe5e6596e23266dd5763dd89b4ab1195516908ecde8febfe96685c7cbe6432

│  ┌──                                                                                                       │
│  │ ObjectID: 0x10ca605ce0437c10274bcc260901ef5b34bab91c747f2dc498b722ae151a5baf                            │
│  │ Sender: 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c                              │
│  │ Owner: Account Address ( 0x7cbe5e6596e23266dd5763dd89b4ab1195516908ecde8febfe96685c7cbe6432 )           │
│  │ ObjectType: 0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89::upgrade_demo::AdminCap  │
│  │ Version: 245323796                                                                                      │
│  │ Digest: 6QpfZqgrTFjTcaySHPrfS5cDE1K1L5xvuWDQRxh1vumS                                                    │
│  └── 

7. A用户 再次取款,预期会失败

sui client ptb --move-call $PKG::upgrade_demo::withdraw "@$TREASURY 50000 @$ADMIN_CAP" --assign coin1 --transfer-objects [coin1] @$ADDR_A --gas-budget 30000000
RPC call failed: ErrorObject { code: ServerError(-32002), message: "Transaction validator signing failed due to issues with transaction inputs, please review the errors and try again:\n- Transaction was not signed by the correct sender: Object 0x10ca605ce0437c10274bcc260901ef5b34bab91c747f2dc498b722ae151a5baf is owned by account address 0x7cbe5e6596e23266dd5763dd89b4ab1195516908ecde8febfe96685c7cbe6432, but given owner/signer address is 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c\n- Could not find the referenced object 0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89 at version None", data: None }

8. 切换B 用户,取款成功

# 切换到ADDR_B
$ sui client switch --address $ADDR_B
Active address switched to 0x7cbe5e6596e23266dd5763dd89b4ab1195516908ecde8febfe96685c7cbe6432
# 调用withdraw取款
$ sui client ptb --move-call $PKG::upgrade_demo::withdraw "@$TREASURY 50000 @$ADMIN_CAP" --assign coin1 --transfer-objects [coin1] @$ADDR_B

# 查看获得50000 mist的coin
$ sui client gas
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x015a3cb7a04cdcd7631e4056f56e8a55b77a1a7d05dc7da6f30e9ac705d19b7c │ 50000              │ 0.00             │
│ 0x418760c10d6fe9ecaa2594803848412c834f58983f1be03b26962a22dd84f9e5 │ 995949216          │ 0.99             │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯

9. A 用户发起攻击,升级合约包, 新建AdminCap对象adminCap2

9.1 修改合约代码,新构造AdminCap对象

    public fun mintCap(ctx: &mut TxContext): AdminCap {
        AdminCap{id: object::new(ctx)}
    }

9.2 升级合约

# 切换为A
sui client switch --address $ADDR_A
Active address switched to 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c
# A 升级合约
sui client upgrade  --upgrade-capability $UPGRADE_CAP  --gas-budget 100000000  --skip-fetch-latest-git-deps
# 升级对象变更,package版本增长 ,版本id 变成了 0xc163e33f376cae7a90b0c56b49af9a0aa387c210792d6c76aba0a12a22de8869
老的版本id是0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89
│  ┌──                                                                                             │
│  │ ObjectID: 0x67a5815e053ab8be8248c894ec288baf79bc6f323321812074cac70e63915bb0                  │
│  │ Sender: 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c                    │
│  │ Owner: Account Address ( 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c ) │
│  │ ObjectType: 0x2::package::UpgradeCap                                                          │
│  │ Version: 245323797                                                                            │
│  │ Digest: 64rX9ChLmX5mupim2UqRGUoKnHD3wTiiYg4uXxzjvppc                                          │
│  └──                                                                                             │
│ Published Objects:                                                                               │
│  ┌──                                                                                             │
│  │ PackageID: 0xc163e33f376cae7a90b0c56b49af9a0aa387c210792d6c76aba0a12a22de8869                 │
│  │ Version: 2                                                                                    │
│  │ Digest: 8aWACYMu9nLjAG3MHQGteXMsNiBUY5xCt6KhKmMEmrTX                                          │
│  │ Modules: upgrade_demo                                                                         │
│  └──                   
  • 新发布的的包 id发生改变

    0x67a5815e053ab8be8248c894ec288baf79bc6f323321812074cac70e63915bb0

9.3 发起攻击

用户用新创建的adminCap2 来取钱

初始的package

image.png

  • 1 pacakge object id
  • 2 版本
  • 3 代码中的包地址。

升级后的packageid发生变更

image.png

  • 1 package object id
  • 2 版本
  • 3 代码中的package地址

但是从代码看,package地址 还是最初发布的地址,version=2, 代码module后面的地址不变。 但是objectid发生变更(图1)

$  export PKG2=0xc163e33f376cae7a90b0c56b49af9a0aa387c210792d6c76aba0a12a22de8869
$  sui client ptb --move-call $PKG2::upgrade_demo::mintCap --assign new_cap --transfer-objects ["new_cap" ] @$ADDR_A

 Created Objects:                                                                                           │
│  ┌──                                                                                                       │
│  │ ObjectID: 0xb71d4b11e35d769bcdefa42f9706af118b76b2ab2896fe2a294639e19f52d816                            │
│  │ Sender: 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c                              │
│  │ Owner: Account Address ( 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c )           │
│  │ ObjectType: 0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89::upgrade_demo::AdminCap  │
│  │ Version: 245323798                                                                                      │
│  │ Digest: 8xio19V93oLgmF8jLxWKoCfgCpSitFJqYLLLTbN9Wc77                                                    │
│  └──  
$ export  ADMIN_CAP2=0xb71d4b11e35d769bcdefa42f9706af118b76b2ab2896fe2a294639e19f52d816
$ sui client ptb --move-call $PKG2::upgrade_demo::withdraw "@$TREASURY 6000 @$ADMIN_CAP2" \
--assign coin2 --transfer-objects [coin2] @$ADDR_A
# 偷取成功,获得一个6000mist的coin
$ sui client gas $ADDR_A
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId                                                          │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x09119914c43f80a486b30cc5d4f1382d0b57929eabd70f116ae4e918d9c89bf0 │ 450920664          │ 0.45             │
│ 0x33233ad9e71a8ef079b7c1c58ac9c99a263d04620f585d37e1ae94b4bd64d538 │ 6000               │ 0.00             │
│ 0x64502383e84a24dda2e531ec0cac4d8123c9c4d7820451a1b42a02eae1888bea │ 50000              │ 0.00             │
│ 0xd91123cfc325ba1eff95e839837a1bf443046cc00a72da5192a005ffea1a7117 │ 50000              │ 0.00             │
│ 0xeda78a0ba9b2ef153a209f51380d8e77af0f35c60eb8ea26d4239c639844680d │ 995399888          │ 0.99             │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
export PKG=0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89
export UPGRADE_CAP=0x67a5815e053ab8be8248c894ec288baf79bc6f323321812074cac70e63915嗯嗯

$ sui client upgrade  --upgrade-capability $UPGRADE_CAP --package-id $PKG --gas-budget 100000000

对策:

方法1

  • 转移权限时,将UpgradeCap也转移

方法2

  • 在 Treasury 中添加adminCap的id。

  • 在取款的时候做校验

    // 金库结构
    public struct Treasury has key {
        id: UID,
        balance: Balance<SUI>,
    +   admin_id : ID,
    }

    // 初始化函数 - 创建管理员凭证和金库
    fun init(ctx: &mut TxContext) {
        let admin_cap = AdminCap {
            id: object::new(ctx)
        };

        let treasury = Treasury {
            id: object::new(ctx),
            balance: balance::zero(),
   +        admin_id: *admin_cap.id.as_inner(),
        }
        ...

                 // 管理员提取SUI代币
    public fun withdraw(
        treasury: &mut Treasury, 
        amount: u64,
        admin_cap: &AdminCap,
        ctx: &mut TxContext
    ): Coin<SUI> {
   +     assert!(admin_cap.id.as_inner() == treasury.admin_id, ENotAdmin);
      ...

<!--StartFragment-->

 附录:

最近在参加HOH 共学活动,

💧 HOH水分子公众号

🌊 HOH水分子X账号

📹 课程B站账号

💻 Github仓库 <https://github.com/move-cn/letsmove>

<!--EndFragment-->

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
科学减肥
科学减肥
0xf54D...aDcd
http://github.com/nextuser