Analysis of Build Finance attack events

创宇区块链安全实验室
本文约2492字,阅读全文需要约10分钟
The attacker created a low-threshold proposal, maliciously took over governance authority, and minted 1.1 million BUILD to sell for arbitrage.

secondary title

Build Finance, a venture capital DAO organization, posted on social media that the project encountered a malicious governance attack. The attacker maliciously minted 1.1 million BUILD and sold them for arbitrage. It is known that Chuangyu Blockchain Security Laboratory will follow up and analyze this incident in depth for the first time.

secondary title

0x02: event details

The attacker Suho.eth (0xD6dBed6297B539A11f1aAb7907e7DF7d9FFeda7e) attempted a malicious takeover at block height 14169198, and the vote failed, and then the proposal initiated at block height 14175830 was successfully passed:https://snapshot.org/#/buildThe online proposal voting address of the contract

, the most recent proposal was on November 24, 2021, and the wallet address participating in the online proposal was not found to interact with the governance contract. It is speculated that the online proposal does not directly interact with the contract on the chain:

function propose(address _target, bytes memory _data) public lockVotes returns (uint) {
require(balanceOf[msg.sender] >= proposalThreshold, "Governance::propose: proposer votes below proposal threshold");
bytes32 txHash = keccak256(abi.encode(_target, _data));
proposalCount++;
Proposal memory newProposal = Proposal({
id: proposalCount,
proposer: msg.sender,
startTime:   block.timestamp,
forVotes:     0,
againstVotes: 0,
txHash:       txHash,
executed:     false
});
proposals[newProposal.id] = newProposal;
return proposalCount;
}
function vote(uint _proposalId, bool _support) public lockVotes {
require(state(_proposalId) == ProposalState.Active, "Governance::vote: voting is closed");
Proposal storage proposal = proposals[_proposalId];
Receipt storage receipt = receipts[_proposalId][msg.sender];
require(receipt.hasVoted == false, "Governance::vote: voter already voted");
uint votes = balanceOf[msg.sender];
if (_support) {
proposal.forVotes += votes;
} else {
proposal.againstVotes += votes;
}
receipt.hasVoted = true;
receipt.support = _support;
receipt.votes = votes;
}

The contract code related to the governance contract 0x3157439c84260541003001129c42fb6aba57e758 proposal is as follows:

This function method allows any user who owns a certain amount of assets to initiate a proposal, and other users who hold the asset can vote. There is no security problem found in the function code, so we speculate that the attacker may initiate the proposal through the contract. After the proposal passed, the attacker minted 1 million BUILD tokens, draining most of the Balancer and Uniswap liquidity pools:

Subsequently, the balance pool was controlled through the governance contract, and other digital assets including 130,000 METRIC tokens were exhausted:

In the end, 100 million Builds were frantically minted and sold to any pools that still have liquidity:

address public governance;
constructor () public ERC20Detailed("BUILD Finance", "BUILD", 18) {
governance = msg.sender;
}
function mint(address account, uint amount) public {
require(msg.sender == governance, "!governance");
_mint(account, amount);
}

function setGovernance(address _governance) public {
require(msg.sender == governance, "!governance");
governance = _governance;
}

The content of the proposal initiated by the attacker has not yet been determined, but according to the minting behavior after the proposal was passed, the token contract 0x6e36556b3ee5aa28def2a8ec3dae30ec2b208739 was followed up:

When the contract is initialized, the contract owner will be set as the governor, and only the governor can initiate a minting request, and only the governor can call the setGovernance function to replace the governor, so it can be determined that the specific proposal initiated by the attacker is to replace the governor.

When creating the contract, the manager is 0x2Cb037BD6B7Fbd78f04756C99B7996F430c58172, which is the contract deployer. After deploying the contract, he replaces the manager with the Time Lock contract 0x38bce4b45f3d0d138927ab221560dac926999ba6:

Finally, in February 2022, Suho.eth (0xD6dBed6297B539A11f1aAb7907e7DF7d9FFeda7e) initiated a proposal to use the low voting threshold to replace the governor with 0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28, and mint coins to cash out after malicious takeover.

secondary title

0x03: Summary