Hack the TON
0. INTRODUCTION¶
-
可以在控制台中使用
help()
查看可以使用的程序功能(index) description fromNano(nano) "convert nano units to ton" toNano(ton) "convert ton units to nano" contract "current level contract instance (if created)" player "current player (if wallet connected)" Address.parse(addressString) "parse Address from string" tonConnectUI.sendTransaction(tx, options) "send custom transaction to arbitrary address" beginCell() "start building a cell" -
在连接钱包后,点击
GET NEW INSTANCE
获取一个题目实例 -
与 Ethernaut 类似,可以在控制台使用
contract
获取信息或与合约交互 -
完成后点击
CHECK SOLUTION
验证
References¶
1. DEPOSIT¶
You will beat this level if:
Claim ownership of the contract
Reduce its balance to 0
DepositLevel
-
只有所有者才能取出合约持有的 TON,首先向合约发送 TON 以成为所有者
-
使用
withdraw
取出合约中所有的资金
2. SCANNER¶
Claim ownership of the contract below to complete this level.
ScannerLevel
- 要成为合约的所有者需要知道
Child
合约的地址,可以通过 Tonviewer 查看ScannerLevel
部署的交易来获知 -
发送
Child
合约地址
3. BOUNCE¶
Claim ownership of the contract below to complete this level.
BounceLevel
- 合约
BounceLevel
收到弹回的消息后就会将玩家设为合约所有者 -
只需要在发送完
start
的三分钟后向BounceLevel
发送finish
,让合约Timer
抛出错误弹回消息即可- 或直接向
Timer
发送start
设置自定义开始时间
- 或直接向
References¶
4. INTRUDER¶
Claim ownership of the contract below to complete this level.
IntruderLevel
- 只有
manager
能设置合约的所有者,而初始manager
为Manager
合约 -
合约
Manager
不检查ChangeClientOwner
消息的发送者 -
可以向
Manager
合约发送ChangeClientOwner
消息来设置IntruderLevel
合约的所有者
5. PARTIAL¶
The goal of this level is to hack the vault contract below.
You are given 100 tokens to start with and you will beat the level if you manage to acquire 1000 or more.
PartialLevel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
|
-
通过
WithdrawFromVault
可以增加合约的余额,但若WithdrawInternal
执行失败,将回弹消息并回滚余额 -
在 TON 中, 如果支付的费用不足以完成执行,则不会创建回弹消息。因此只需要支付仅供
WithdrawFromVault
执行的费用,使余额增加即可
References¶
6. PEEK¶
Unlock the contract below to complete this level.
PeekLevel
- 提供正确的密码即可解锁,需要解析初始化消息
- 部署 Tact 编写的合约,函数
init()
的参数包含在 init data 中,并将在部署附带的第一次合约调用中根据 init data 更新存储 -
使用
pytoniq-core
解析初始数据- 尽管可以使用较小的
Int
表示形式来减少存储开销,但 TVM 仅对 257 位整型进行操作。因此,init data 中的整型参数均为 257 位有符号整数
- 尽管可以使用较小的
-
发送解锁消息
References¶
7. SWAP¶
You will beat the level if you manage to acquire tokens amount equivalent to 1000 TON or more.
SwapLevel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
|
-
操作
swap ton to tokens
每次能使Token
的balance
增加myBalance() - context().value
,即合约SwapLevel
持有的 TON 越多,每次的增加量越大。向合约SwapLevel
发送 TON 增加其余额以减少操作的次数 -
合约
Token
执行操作SwapTonToTokens
时,会向合约SwapLevel
发送send ton
消息,使其将所持有的全部 TON 发送给合约Token
。需要控制swap ton to tokens
消息附带的 TON,使合约SwapLevel
无法将余额发送给合约Token
。经测试,可以使用 0.008 TON -
使用 TON Web IDE 部署辅助合约,批量发送消息
- 全局设置中可以修改消息附带的 TON 数量
-
检查结束后取回合约中的 TON
8. COIN¶
To complete the level, guess 10 times in a row which side the coin will land on.
CoinLevel
-
需要连续猜对 10 次,借助辅助合约预测结果并发送消息
- 题目合约使用的 Tact 编译器版本为 1.4.4。不同编译器版本可能产生不同的编译结果,最好使用相同版本的编译器,保证
initOf
在辅助合约中的计算结果与实例合约相同
- 题目合约使用的 Tact 编译器版本为 1.4.4。不同编译器版本可能产生不同的编译结果,最好使用相同版本的编译器,保证
9. GATEKEEPER¶
Unlock the contract below to complete this level.
GatekeeperLevel
- 解锁需要
sender().asSlice().asCell().hash() ^ ((msg.a << 2) + msg.b)
的值与myAddress().asSlice().asCell().hash()
相等 -
获取实例地址的哈希值
-
由于
a
和b
没有限制范围,可以直接将a
设置为 0,b
为发送者地址哈希值与实例地址哈希值异或的结果
10. BRUTE-FORCE¶
Unlock the contract below to complete this level.
BruteforceLevel
- 解锁需要提供满足特定条件的四个整数
(self.x + self.y) == 2
即msg.a + msg.b + msg.c + msg.d == 2
,说明正负整数的绝对值之差为 2,且msg.a + msg.c
和msg.b + msg.d
的结果在 8 位无符号整型的范围内(((pow(msg.a, 25) + pow(msg.b, 25)) + pow(msg.c, 25)) + pow(msg.d, 25))
的结果是一个正整数1968172103452999492963878188028555943794336458502883276710491621054698698752
-
由此推测出两种可能的情况
self.x
为 2,self.y
为 0(反之亦同)self.x
和self.y
同为 1
-
发送结果到实例合约
11. TOLK¶
Unlock the contract below to complete this level.
Tolk
发送 OP_UNLOCK
对应的操作码即可解锁。
12. UPGRADE¶
Unlock the contract below to complete this level.
Upgrade
-
向合约发送
OP_UPGRADE
消息可以更新合约的代码 -
可以在新代码中增加更新存储的逻辑
-
更新代码并解锁
13. ACCESS¶
Unlock the contract below to complete this level.
Access
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
|
-
解锁需要
ctx_owner
是ctx_player
-
操作
op::change_owner
调用函数check_owner()
检查调用者是否为ctx_owner
,但由于未使用impure
标识符且没有检查函数调用的结果,该函数调用会在编译时被移除 -
因此,先修改
ctx_nonce
再更新ctx_owner
,即可解锁
14. DONATE¶
You will beat this level if you manage to reduce its balance to 0.
Donate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
|
- 操作
op::withdraw
可以将合约所持有的所有 TON 都发送给调用者destination
- 修改
destination
的操作op::change_destination
只有owner
可以调用 -
由于全局变量不能被重定义,当合约余额大于
donation_goal
时,操作op::donate
实际上更新的是全局变量destination
,而不是其定义的本地变量 -
捐款并设置
destination
,随后发送op::withdraw
消息
References¶
15. LOGICAL¶
Unlock the contract below to complete this level.
Logical
-
当当前交易的逻辑时间和上一交易的逻辑时间之差为
ctxLogicalTimeDiff
时,可以解锁 -
获取
ctxLogicalTimeDiff
的值 -
由于要求逻辑时间差仅为 1,可以从同一个合约中发出两条消息
16. SEED¶
Unlock the contract below to complete this level.
Seed
-
当
ctxSeed
不为 0 时,将直接设置 seed 并获取随机数作为下一个 seed -
发送一条消息初始化 seed
-
在已知 seed 的情况下,可以通过辅助合约获取随机的结果
References¶
17. TOKEN¶
You will beat this level if you manage to acquire tokens amount equivalent to total token supply.
Token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
|
-
需要让代币余额与总供应量相同
-
获取初始数据
-
初始代币余额为 0。操作
OP_TRANSFER
使用loadInt()
解析要转移的代币数量,即amount
可以为负数,能够增加发送者的代币余额 -
进行代币转移
18. JACKPOT¶
You will beat this level if you manage to reduce its balance to 0.
Jackpot
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
|
-
操作
withdraw
使用了非修改方法udict_delete_get?
获取了旧的余额,但修改后的字典没有赋值给ctx_balances
。如果withdraw_amount
恰好等于balance
,ctx_balances
将不会被更新 -
可以在一次 deposit 之后,进行多次 withdraw
19. PROXY¶
You will beat this level if you manage to disable this proxy contract.
Proxy
-
题目要求将
ctxEnabled
设置为false
,但只有合约所有者能够设置,而所有者为零地址 -
在
ctxEnabled
为true
时,操作0
可以向任意地址发送任意消息,可以用于直接发送check
消息 - 可以先执行一次
CHECK SOLUTION
,以确定目标地址 -
发送
check
消息 -
另外,由于
onInternalMessage()
没有检查收到的消息是否是弹回消息,也可以向零地址发送消息,并借助弹回消息设置ctxEnabled
References¶
20. EXECUTION¶
You will beat this level if you manage to reduce its balance to 0.
Execution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
|
- 通过操作
0
中的检查即能清空合约所持有的 TON - 操作
0
能够将发送者提供的代码作为无输入返回值类型为int
的函数执行,且要满足返回值与randomNumber
相同 -
但是操作
0
已经调用一次randomizeByLogicalTime()
随机化了种子,因而无法通过执行相同的代码来获取相同的随机数。不过,尽管safeExecute()
在调用完tryExecute()
后会重置寄存器c4
和c5
,但由于没有commit
,后续执行如果抛出错误,修改将会被回滚。因此,用户自定义的guesser()
函数实际上可以直接发送模式为 128 的消息,并调用commit()
提交当前c4
、c5
寄存器的状态即可 -
编译代码
-
发送交易
References¶
bless
- Introduction To Fift
- Fift deep dive
- Fift: A Brief Introduction
- Telegram Open Network Virtual Machine