Skip to content

通过全员投票来攻击DAO合约的可能性 #11

@lurenpluto

Description

@lurenpluto
  1. 修改委员会的操作接口
addCommitteeMember
removeCommitteeMember
setCommittees

最后生成依赖的params列表基本都是一个或者多个number address,加上一个字符串,表示此次的proposal的目的
比如:

setCommittees([addr1, addr2, addr3])

那么生成的params列表如下:

params: [addr1, addr2, addr3, "setCommittees"]

但这个参数列表是可以直接通过其它的投票purposal来直接生成的,比如全员投票

  1. 全员投票
    发起全员投票的接口如下
function fullPropose(
        uint duration,
        bytes32[] memory params,
        uint threshold
    ) external override returns (uint proposalId) 

由于params是外部可以完全指定,那么可以指定成上面的setCommittees的参数列表,然后生成一个proposal
只要这个proposal被投票通过了,那么直接拿这个proposalId,来调用setCommittees,一样可以通过其中_takeResult的检查,因为参数一致,并且结果也是Accept

  1. 基于上述的情况,几乎所有的操作,包括修改委员会,合约升级,都可以通过全员投票来生成提案并通过,然后基于这个accept的提案,进行对应的操作

  2. 问题在于,现在全员投票的门槛极低,看fullPropose的接口:

  • duration 没有任何限制,所以可以设计一个极短的时间间隔
  • params 可以是任意参数列表,包括最后一个操作类型的字符串(很多操作都有附加)
  • threshold 最低可以设置10,这个是最大的问题所在

所以基于这个假设,只要有一个人掌握了大约10%的已释放token,就几乎获得了对合约所有的操作权:
可以创建一个duration极小,threshold=10的全员投票,指定好攻击参数,那么就可以自己立刻投票并结算,然后进行目标的攻击操作

通过上述操作,可以升级合约,把所有token都释放到自己名下,从而进一步控制整个DAO组织

  1. 所以上述的核心,就是需要改进全员投票的设计:
  • duration必须增加最短时间间隔
    对于全员投票这种重大事情,时间最少应该是一到两周

  • params 内部需要强制在后面追加一个字符串参数,用作操作类型
    也可以强制发起者最后一个字符串参数,并且不可以是特定的敏感操作比如升级合约之类

  • threshold 这个不可以小于50
    任何全员投票,都应该满足半数原则,避免类似区块链的50%攻击

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions