CTF-governance-shenamigans
题目源码:点击
要求是;
NotSushiToken 治理代币合约已经上线,旨在决定谁是最佳寿司厨师。谁不想要这样的荣耀呢?
该合约只允许 WLed 地址投票。幸运的是,你的 Sybil 攻击让你获得了 3 个可以投票的 WLed 地址。
你的目标是获得最多的委托投票,成为真正的寿司之王。你手中有 500 个代币,而你的竞争对手有 2000 个。
从题目中就可以得到一点方向,就是有3个可以投票的地址,说明我们肯定要运用起来
整个合约的逻辑就是委托投票,那么如果,我们将原有的500 个代币,委托给那3个地址,给我们投票,那么我们不就有很多票了吗
看向代码,允许我们这么做吗
function _delegate(address delegator, address delegatee) internal { address currentDelegate = _delegates[delegator]; uint256 delegatorBalance = balanceOf(delegator); // balance of underlying SUSHIs (not scaled); _delegates[delegator] = delegatee;
emit DelegateChanged(delegator, currentDelegate, delegatee);
_moveDelegates(currentDelegate, delegatee, delegatorBalance); }
/// @dev votes are transfered from delegatee `srcRep` to `dstRep` function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal { if (srcRep != dstRep && amount > 0) { if (srcRep != address(0)) { // decrease old representative uint32 srcRepNum = numCheckpoints[srcRep]; uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; uint256 srcRepNew = srcRepOld.sub(amount); _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); }
if (dstRep != address(0)) { // increase new representative uint32 dstRepNum = numCheckpoints[dstRep]; uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; uint256 dstRepNew = dstRepOld.add(amount); _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); } } }
|
发现没有什么限制,就能更新 _delegates[delegator] = delegatee
代表者, 也就是说刚刚那个想法就可实现
攻击思路:
三个地址,attracker,B,C,
attracter发送500代币给B,B在委托给attracker, 此时attracker的投票数500;
B将500代币发送给C,C在委托给attracker,此此时attrcaker的投票数为1000;
C将500代币发送给attracker,attracker在再重复前面的工作,直至投票数超过2000;
个人认为,这个合约漏洞是,可以随意更新代表者,并且委托人的token会留在它自己这儿,就好像双花一样,代币被重复使用。