sui存钱罐的权限控制和pacakge升级带来的风险
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
- 1 pacakge object id
- 2 版本
- 3 代码中的包地址。
升级后的packageid发生变更
- 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-->
版权声明
本文仅代表作者观点,不代表区块链技术网立场。
本文系作者授权本站发表,未经许可,不得转载。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。