Back to Blog

Cakepie 合約安全審計報告

Code Auditing
November 30, 2023
10 min read

報告清單

項目 描述
客戶 Magpie XYZ
目標 CakePie 合約

版本歷史

版本 日期 描述
1.0 2023 年 11 月 30 日 初次發布

1. 簡介

1.1 關於目標合約

資訊 描述
類型 智能合約
語言 Solidity
方法 半自動化與人工驗證

本次審計的目標是 Magpie XYZ 的 CakePie 合約^1程式碼儲存庫。CakePie 合約運行一項 CakeRush 活動,用戶可以在 CakePie 上將其 CAKE 代幣或鎖倉的 CAKE 部位轉換。請注意,僅 CakeRush.sol 和 PancakeStakingBNBChain.sol 包含在審計範圍內,其他檔案則不在本次審計範圍之內。

審計過程是迭代的。具體而言,我們將審計修復已發現問題的提交內容。如果出現新問題,我們將繼續此過程。審計期間的提交 SHA 值如下表所示。我們的審計報告對初始版本(版本 1)的程式碼,以及後續版本中修復報告內問題的新程式碼負責。

1.2 安全模型

為了評估風險,我們遵循產業界和學術界廣泛採用的標準或建議,包括 OWASP 風險評級方法論 ^2 和通用弱點枚舉 ^3。風險的整體嚴重程度可能性影響決定。具體而言,可能性用於估計攻擊者發現並利用特定漏洞的可能性,而影響用於衡量成功利用後的後果。

在本報告中,可能性和影響均分為兩個等級,即,其組合如表 1.1 所示。

因此,本報告中的嚴重程度分為三類:高 (High)中 (Medium)低 (Low)。為了完整起見,未定 (Undetermined) 也用於涵蓋無法明確界定風險的情況。

此外,已發現項目的狀態將屬於以下四類之一:

  • 未定 (Undetermined):尚未收到回應。

  • 已獲悉 (Acknowledged):客戶已收到該項目,但尚未確認。

  • 已確認 (Confirmed):客戶已認可該項目,但尚未修復。

  • 已修復 (Fixed):客戶已確認並修復該項目。

2. 審計發現

總體而言,我們發現了 兩個 潛在問題。此外,我們還提出了 三個 建議和 一個 備註。

  • 高風險:1

  • 低風險:1

  • 建議:3

  • 備註:1

ID 嚴重程度 描述 類別 狀態
1 參數重置後潛在的狀態不一致 軟體安全 已修復
2 重複領取 mCake 獎勵 軟體安全 已修復
3 - 檢查初始化函數中的參數 建議 已獲悉
4 - 檢查 CakeRush 合約中的參數 建議 已修復
5 - 修飾符中的額外條件 建議 已獲悉
6 - 潛在的中心化風險 備註 -

詳細內容請參閱下文。

2.1 軟體安全

2.1.1 參數重置後潛在的狀態不一致

項目 描述
嚴重程度
狀態 已於版本 2 修復
引入版本 版本 1

描述 CakeRush 合約根據多個參數分發獎勵。以下函數允許項目維護者重置部分參數:

function resetMultiplier() external onlyOwner {
        uint256 len = rewardMultiplier.length;
        for (uint8 i = 0; i < len; ++i) {
            rewardMultiplier.pop();
            rewardTier.pop();
        }

        tierLength = 0;
    }

    function resetTimeWeighting() external onlyOwner {
        uint256 len = weightedTime.length;
        for (uint8 i = 0; i < len; ++i) {
            weightedTime.pop();
            weighting.pop();
        }

        weightLength = 0;
    }

表 2.1:CakeRush.sol

然而,這些函數僅重置了參數,但未重置儲存在 userInfos 狀態變數中的用戶資訊。因此,CakeRush 合約中的計算可能會因為狀態不一致而失敗。例如,如果參數被重置並設定為錯誤的值,第 155 行的減法可能會因整數下溢(integer underflow)而失敗。

function quoteConvert(
        uint256 _amountToConvert,
        address _account
    )
        external
        view
        returns (
            uint256 newUserFactor,
            uint256 newTotalFactor,
            uint256 newUserWeightedFactor,
            uint256 newWeightedTotalFactor
        )
    {
        if (_amountToConvert == 0 || rewardMultiplier.length == 0 || weighting.length == 0)
            return (0, 0, 0, 0);

        UserInfo storage userInfo = userInfos[_account];
        uint256 accumulated = _amountToConvert + userInfo.converted;

        uint256 factorAccuNoWeighting = 0;
        uint256 i = 1;
        while (i < rewardTier.length && accumulated > rewardTier[i]) {
            factorAccuNoWeighting += (rewardTier[i] - rewardTier[i - 1]) * rewardMultiplier[i - 1];
            i++;
        }
        factorAccuNoWeighting += (accumulated - rewardTier[i - 1]) * rewardMultiplier[i - 1];

        uint256 factorToEarnNoWeighting = (factorAccuNoWeighting / DENOMINATOR) - userInfo.factor;

表 2.2:CakeRush.sol

更糟的是,如果用戶在參數重置後立即(在新參數設定前,例如通過搶先交易)呼叫 convert 或 convertWithCakePool,可能會因為第 141-142 行的邏輯導致合約內記錄的總因數和加權因數被重置。

影響 重置參數可能導致狀態不一致和錯誤。

建議 在清除舊參數後設定新參數。

項目方回應 一旦 Cake Rush 活動開始,乘數將不再重置。

2.1.2 重複領取 mCake 獎勵

項目 描述
嚴重程度
狀態 已於版本 3 修復
引入版本 版本 2

描述 用戶在合約中鎖定 CAKE 代幣後,可以通過該函數領取 mCake 代幣獎勵。然而,該函數包含一個允許用戶多次領取獎勵的問題。在以下程式碼片段中,如果金額大於用戶的金額,將會向用戶轉移或存入總金額。正確的實現應該僅返回金額,因此目前的實現實際上允許用戶重複領取 mCake 獎勵。

function claim(bool _isStake) external nonReentrant {
        UserInfo storage userInfo = userInfos[msg.sender];
        if (claimedMCake[msg.sender] >= userInfo.converted) revert AlreadyClaimed();
        if (_isStake && userInfo.converted > 0) {
            if (masterCakepie == address(0)) revert MasterCakepieNotSet();
            IERC20(mCakeOFT).safeApprove(address(masterCakepie), userInfo.converted);
            IMasterCakepie(masterCakepie).depositFor(
                address(mCakeOFT),
                address(msg.sender),
                userInfo.converted
            );
        } else if (userInfo.converted > 0) {
            IERC20(mCakeOFT).transfer(msg.sender, userInfo.converted);
            emit Claim(msg.sender, userInfo.converted);
        }

        claimedMCake[msg.sender] = userInfo.converted;
    }

表 2.3:CakeRush.sol

影響 用戶能夠重複領取 mCake 獎勵。

建議 修訂獎勵領取邏輯。

2.2 其他建議

2.2.1 檢查初始化函數中的參數

項目 描述
狀態 已獲悉
引入版本 版本 1

描述 在 CakeRush 和 PancakeStakingBNBChain 合約的初始化函數中,有些參數在初始化後無法更改。建議應在初始化函數中檢查這些參數。

function __CakeRush_init(
        address _cake,
        address _mCakeOFT,
        address _masterCakepie
    ) public initializer {
        __Ownable_init();
        __ReentrancyGuard_init();
        __Pausable_init();
        cake = _cake;
        mCakeOFT = _mCakeOFT;
        masterCakepie = _masterCakepie;
    }

表 2.4:CakeRush.sol

影響 N/A

建議 在初始化函數中檢查參數。

2.2.2 檢查 CakeRush 合約中的參數

項目 描述
狀態 已於版本 2 修復
引入版本 版本 1

描述 在 CakeRush 合約中,可以新增多個有關獎勵分發的參數。然而,沒有檢查這些參數是否按照合約假設正確設定。具體而言,在 setMultipler 和 setTimeWeighting 函數中,必須檢查額外條件(即 rewardTier 和 weightedTime 陣列的單調遞增特性)。

function setMultiplier(
        uint256[] calldata _multiplier,
        uint256[] calldata _tier
    ) external onlyOwner {
        if (_multiplier.length == 0 || (_multiplier.length != _tier.length)) revert LengthInvalid();

        for (uint8 i = 0; i < _multiplier.length; ++i) {
            if (_multiplier[i] == 0) revert InvalidAmount();
            rewardMultiplier.push(_multiplier[i]);
            rewardTier.push(_tier[i]);
            tierLength += 1;
        }
    }

表 2.5:CakeRush.sol

影響 N/A

建議 在設定參數的函數中檢查參數。

2.2.3 修飾符中的額外條件

項目 描述
狀態 已獲悉
引入版本 版本 1

描述 在 CakeRush 合約中,_onlyPancakeStaking 修飾符具有外加條件。根據此修飾符的語義,檢查 msg.sender != pancakeStaking 就已足夠。

modifier _onlyPancakeStaking() {
        if (pancakeStaking == address(0) || msg.sender != pancakeStaking)
            revert OnlyPancakeStaking();
        _;
    }

表 2.6:CakeRush.sol

影響 N/A

建議 移除修飾符中冗餘的條件。

2.3 備註

2.3.1 潛在的中心化風險

描述 CakeRush 的所有者擁有修改關鍵配置的重大特權,這導致了單點故障。如果攻擊者破壞了所有者權限,他們可能會使整個系統癱瘓。

此外,未明確處理合約中的 CAKE 代幣以將其鎖定在 VECake 合約中。相反地,CakeRush 允許所有者提取所有這些 CAKE,這意味著所有者必須在提取後鎖定 CAKE 代幣。然而,程式碼層級無法保證此邏輯,這也帶來了中心化疑慮。

項目方回應 團隊將所有者權限移交給多簽錢包以降低風險。

3. 注意事項與備註

3.1 免責聲明

本審計報告不構成投資建議或個人推薦。它並未考量,也不應被解釋為考量或對代幣、代幣銷售或任何其他產品、服務或其他資產的潛在經濟效益產生影響。任何實體不應以任何方式依賴本報告,包括用於做出購買或出售任何代幣、產品、服務或其他資產的決策。

本審計報告並非對任何特定項目或團隊的背書,報告也不保證任何特定項目的安全性。本審計不保證能夠發現智能合約的所有安全問題,即評估結果不保證未來不會發現進一步的安全問題。由於一次審計不能被視為全面性的,我們始終建議進行獨立審計並建立公開漏洞懸賞計畫,以確保智能合約的安全性。

本次審計範圍僅限於第 1.1 節中提到的程式碼。除非明確說明,否則語言本身(例如 Solidity 語言)、底層編譯工具鏈和計算基礎設施的安全性均不在審計範圍內。

3.2 審計程序

我們按照以下程序進行審計:

  • 漏洞檢測 我們首先使用自動程式碼分析器掃描智能合約,然後手動驗證(拒絕或確認)其報告的問題。

  • 語義分析 我們研究智能合約的業務邏輯,並使用自動模糊測試工具(由我們的研究團隊開發)對潛在漏洞進行進一步調查。我們還與獨立審計員手動分析可能的攻擊場景,以交叉檢查結果。

  • 建議 我們從良好程式設計實踐的角度為開發人員提供一些有用的建議,包括 Gas 優化、程式碼風格等。

我們在下方展示了主要的檢查項目。

3.2.1 軟體安全

  • 重入 (Reentrancy)

  • 拒絕服務 (DoS)

  • 存取控制

  • 資料處理與資料流

  • 異常處理

  • 不安全的外部呼叫與控制流

  • 初始化一致性

  • 事件操作

  • 容易出錯的隨機性

  • 不當使用代理系統

3.2.2 DeFi 安全

  • 語義一致性

  • 功能一致性

  • 權限管理

  • 業務邏輯

  • 代幣操作

  • 緊急機制

  • 預言機安全

  • 白名單與黑名單

  • 經濟影響

  • 批次轉帳

3.2.3 NFT 安全

  • 重複項目

  • 代幣接收者驗證

  • 鏈下元資料安全

3.2.4 其他建議

  • Gas 優化

  • 程式碼品質與風格

備註 上述檢查項目為主要檢查項。根據項目的功能,我們在審計過程中可能會使用更多檢查項目。

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit