1.概述
- 假设写一个存钱罐合约,可以从合约中存入货币,提取货币,
- 存钱不控制权限,每个人都能存
- 只有拥有AdminCap对象的人才能取钱。
- 取钱函数有一个AdminCap参数,sui 判断谁拥有Admincap 对象才能调用 withdraw
- 合约构造者A可以将AdminCap对象 转让给B
- 表面上只有B 能获得权限取钱
- 风险
- 合约构造者还拥有升级合约的权限,可能通过升级合约来获取新的取钱权限
2. 合约代码
1 | module upgrade_demo::upgrade_demo ; |
3. 发布合约
- 获得对象
1 | sui move --skip-fetch-latest-git-deps publish |
1 | ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ |
- 对象说明
| 对象名 | 取值 | 说明 |
|---|---|---|
| package address | 0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89 | 合约包的地址 |
| upgradeCap | 0x67a5815e053ab8be8248c894ec288baf79bc6f323321812074cac70e63915bb0 | 控制升级的权限 |
| adminCap | 0x10ca605ce0437c10274bcc260901ef5b34bab91c747f2dc498b722ae151a5baf | 控制提取Treasury余额 |
| owner | 0x6560a053cd8d98925b33ab2b951d656736d0133734def0b5d679402fc555576c | 目前合约发布者拥有adminCap,upgradeCap |
| treasury | 0x830243c107ff3ad19e4506d9a61d04af4a0c35771445e468c3a7befbc6d9293c | 存钱罐 |
查看当前用户的coin
{.line-numbers} 1
2
3
4
5
6
7
8sui client gas
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x09119914c43f80a486b30cc5d4f1382d0b57929eabd70f116ae4e918d9c89bf0 │ 470927364 │ 0.47 │
│ 0xae3da3cb598610db16ea040ad6b09cfd324e146a2a7e5b18ccfa76b34b8d3b22 │ 497664604 │ 0.49 │
│ 0xeda78a0ba9b2ef153a209f51380d8e77af0f35c60eb8ea26d4239c639844680d │ 995399888 │ 0.99 │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
4. A用户存钱
- 配置一些变量,方便后面的脚本使用
1 | export PKG=0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89 |
1 | # 查看当前用户有哪些coin |
5. A 用户取钱
1 | $ sui client ptb --move-call $PKG::upgrade_demo::deposit "@$TREASURY @0xae3da3cb598610db16ea040ad6b09cfd324e146a2a7e5b18ccfa76b34b8d3b22" |
取回5000单位
1
2
3
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
1 | sui client gas $ADDR_A |
6. A 用户转移adminCap 给B
1 | sui client ptb --transfer-objects [@$ADMIN_CAP] @$ADDR_B |
7. A用户 再次取款,预期会失败
1 | sui client ptb --move-call $PKG::upgrade_demo::withdraw "@$TREASURY 50000 @$ADMIN_CAP" --assign coin1 --transfer-objects [coin1] @$ADDR_A --gas-budget 30000000 |
8. 切换B 用户,取款成功
1 | # 切换到ADDR_B |
9. A 用户发起攻击,升级合约包, 新建AdminCap对象adminCap2
9.1 修改合约代码,新构造AdminCap对象
1 | public fun mintCap(ctx: &mut TxContext): AdminCap { |
9.2 升级合约
1 | # 切换为A |
新发布的的包 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)
1 | $ export PKG2=0xc163e33f376cae7a90b0c56b49af9a0aa387c210792d6c76aba0a12a22de8869 |
1 | export PKG=0x38417a57a4f061487895b2c97b3e188e847c25de37762c5e0146bd6a32cd5a89 |
对策:
方法1
- 转移权限时,将UpgradeCap也转移
方法2
在 Treasury 中添加adminCap的id。
在取款的时候做校验
1 |
|
附录:
最近在参加HOH 共学活动,
📹 课程B站账号
💻 Github仓库 https://github.com/move-cn/letsmove