从 TON Hack 挑战赛中得出结论
TON Hack挑战赛于10月23日举行。在TON主网上部署了几个带有人为安全漏洞的智能合约。每个合约都有3000或5000 TON的余额,允许参与者攻击它并立即获得奖励。
源代码和比赛规则托管在Github 这里。
合约
1. 互助基金
安全规则
始终检查函数是否有impure
修饰符。
第一个任务非常简单。攻击者可以发现authorize
函数没有impure
。这个修饰符的缺失允许编译器在函数不返回任何内容或返回值未使用时跳过对该函数的调用。
() authorize (sender) inline {
throw_unless(187, equal_slice_bits(sender, addr1) | equal_slice_bits(sender, addr2));
}
2. 银行
安全规则
始终检查修改/非修改方法。
使用.
而不是~
调用了udict_delete_get?
,所以真正的 dict 没有被触及。
(_, slice old_balance_slice, int found?) = accounts.udict_delete_get?(256, sender);
3. DAO
安全规则
如果你真的需要,使用符号整数。
投票权在消息中以整数形式存储。 所以攻击者可以在转移投票权时发送一个负值,并获得无限投票权。
(cell,()) transfer_voting_power (cell votes, slice from, slice to, int amount) impure {
int from_votes = get_voting_power(votes, from);
int to_votes = get_voting_power(votes, to);
from_votes -= amount;
to_votes += amount;
;; No need to check that result from_votes is positive: set_voting_power will throw for negative votes
;; throw_unless(998, from_votes > 0);
votes~set_voting_power(from, from_votes);
votes~set_voting_power(to, to_votes);
return (votes,());
}
4. 彩票
安全规则
在执行rand()
之前,始终随机化seed。
seed来自交易的逻辑时间,黑客可以通过暴力破解当前区块中的逻辑时间来赢得比赛(因为lt在一个区块的边界内是连续的)。
int seed = cur_lt();
int seed_size = min(in_msg_body.slice_bits(), 128);
if(in_msg_body.slice_bits() > 0) {
seed += in_msg_body~load_uint(seed_size);
}
set_seed(seed);
var balance = get_balance().pair_first();
if(balance > 5000 * 1000000000) {
;; forbid too large jackpot
raw_reserve( balance - 5000 * 1000000000, 0);
}
if(rand(10000) == 7777) { ...send reward... }