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会留在它自己这儿,就好像双花一样,代币被重复使用。