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 優化

  • 程式碼品質與風格

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

Sign up for the latest updates

Best Security Auditor for Web3

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

BlockSec Audit