レポートマニフェスト
| 項目 | 説明 |
|---|---|
| クライアント | Magpie XYZ |
| 対象 | CakePie Contracts |
バージョン履歴
| バージョン | 日付 | 説明 |
|---|---|---|
| 1.0 | 2023年11月30日 | 初版リリース |
1. はじめに
1.1 対象コントラクトについて
| 情報 | 説明 |
|---|---|
| タイプ | スマートコントラクト |
| 言語 | Solidity |
| アプローチ | 半自動および手動検証 |
本監査の対象は、Magpie XYZのCakePieコントラクト^1のコードリポジトリです。CakePieコントラクトはCakeRushキャンペーンを実行しており、ユーザーはCakePie上でPancakeSwapからCAKEトークンまたはロックされたCAKEポジションを変換できます。なお、監査範囲にはCakeRush.solとPancakeStakingBNBChain.solのみが含まれ、その他のファイルは本監査の範囲外となります。
監査プロセスは反復的です。具体的には、発見された問題点を修正するコミットを監査します。新しい問題が発生した場合は、このプロセスを継続します。監査中のコミットSHA値は以下の表に示されています。本監査レポートは、初版(バージョン1)のコード、および監査レポートの問題点を修正するための新しいコード(後続バージョン)について責任を負います。

1.2 セキュリティモデル
リスクを評価するため、OWASPリスク評価方法論^2および共通脆弱性列挙(Common Weakness Enumeration)^3など、業界および学術界で広く採用されている基準または提案に従います。リスクの全体的な重大度は、発生可能性と影響度によって決定されます。具体的には、発生可能性は、特定の脆弱性が攻撃者によって発見および悪用される可能性を推定するために使用され、影響度は、成功した悪用の結果を測定するために使用されます。
本レポートでは、発生可能性と影響度の両方を2つの評価(高および低)に分類し、その組み合わせを表1.1に示します。

したがって、本レポートで測定される重大度は、高、中、低の3つのカテゴリに分類されます。完全性を期すため、リスクを適切に判断できない状況をカバーするために、未決定も使用されます。
さらに、発見された項目のステータスは、以下の4つのカテゴリのいずれかに分類されます。
-
未決定 まだ応答がありません。
-
承認済み 項目はクライアントによって受信されましたが、まだ確認されていません。
-
確認済み 項目はクライアントによって認識されましたが、まだ修正されていません。
-
修正済み 項目はクライアントによって確認および修正されました。
2. 監査結果
合計で、2件の潜在的な問題を発見しました。さらに、3件の推奨事項と1件の注記があります。
-
高リスク: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行目の減算は整数アンダーフローにより失敗する可能性があります。
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行目のロジックにより、コントラクト内に記録されている合計および加重ファクターがリセットされる可能性があります。
影響 パラメータのリセットは、不整合で不正確な状態を引き起こす可能性があります。
提案 古いパラメータをクリアした後、新しいパラメータを設定してください。
プロジェクトからのフィードバック CakeRushキャンペーンが開始されたら、マルチプライヤーはリセットされません。
2.1.2 mCake報酬の繰り返し請求
| 項目 | 説明 |
|---|---|
| 重大度 | 高 |
| ステータス | バージョン3で修正済み |
| 導入元 | バージョン2 |
説明 コントラクトでCAKEトークンをロックした後、ユーザーは関数を通じてmCakeトークンを報酬として請求できます。しかし、この関数にはユーザーが報酬を複数回請求できる問題が含まれています。以下のコードセグメントでは、金額がユーザーの保有量よりも大きい場合、transferまたはdepositがユーザーに合計金額を渡します。正しい実装はclaimedMCake[msg.sender]を返すべきですが、現在の実装ではユーザーが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
影響 該当なし
提案 初期化関数でパラメータをチェックしてください。
2.2.2 CakeRushコントラクトでのパラメータチェック
| 項目 | 説明 |
|---|---|
| ステータス | バージョン2で修正済み |
| 導入元 | バージョン1 |
説明 CakeRushコントラクトでは、報酬分配に関するいくつかのパラメータを追加できます。しかし、これらのパラメータがコントラクトの仮定に従って正しく設定されているかどうかのチェックはありません。具体的には、setMultiplierおよび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
影響 該当なし
提案 パラメータを設定する関数でパラメータをチェックしてください。
2.2.3 修飾子での追加条件
| 項目 | 説明 |
|---|---|
| ステータス | 承認済み |
| 導入元 | バージョン1 |
説明 CakeRushコントラクトでは、_onlyPancakeStaking修飾子に余分な条件があります。この修飾子の意味するところによれば、msg.sender != pancakeStakingをチェックするだけで十分です。
modifier _onlyPancakeStaking() {
if (pancakeStaking == address(0) || msg.sender != pancakeStaking)
revert OnlyPancakeStaking();
_;
}
リスト2.6: CakeRush.sol
影響 該当なし
提案 修飾子から余分な条件を削除してください。
2.3 注記
2.3.1 潜在的な中央集権化リスク
説明 CakeRushのオーナーは、重要な設定を変更する広範な権限を保持しています。これは単一障害点となります。攻撃者がオーナーを侵害した場合、システム全体を無効化する可能性があります。
さらに、コントラクト内のCAKEトークンは、VECakeコントラクトにロックするために明示的に処理されていません。代わりに、CakeRushはオーナーにすべてのCAKEを引き出すことを許可しており、これはオーナーがCAKEトークンを引き出した後にロックする必要があることを意味します。しかし、このロジックはコードレベルでは保証されておらず、中央集権化の懸念も生じます。
プロジェクトからのフィードバック リスクを軽減するために、チームはオーナーをマルチシグにしました。
3. 通知と注記
3.1 免責事項
本監査レポートは、投資アドバイスまたは個人的な推奨を構成するものではありません。トークン、トークンセール、またはその他の製品、サービス、またはその他の資産の潜在的な経済性を考慮しておらず、考慮したとは解釈されず、またそれらに影響を与えるものでもありません。いかなる主体も、トークン、製品、サービス、またはその他の資産の売買の決定を行う目的を含め、いかなる方法においても本レポートに依存すべきではありません。
本監査レポートは、特定のプロジェクトまたはチームの推奨ではなく、レポートはいかなる特定のプロジェクトのセキュリティも保証するものではありません。本監査は、スマートコントラクトのすべてのセキュリティ問題を発見することを保証するものではありません。つまり、評価結果はいかなるさらなるセキュリティ問題の発見がないことを保証するものではありません。1回の監査では包括的とは見なされないため、スマートコントラクトのセキュリティを確保するために、独立した監査と公開バグバウンティプログラムの実施を常に推奨します。
本監査の範囲は、セクション1.1で言及されているコードに限定されます。明示的に指定されていない限り、言語自体のセキュリティ(例:Solidity言語)、基盤となるコンパイラツールチェーン、およびコンピューティングインフラストラクチャは範囲外です。
3.2 監査手順
以下の手順に従って監査を実行しました。
-
脆弱性検出 まず、自動コードアナライザーでスマートコントラクトをスキャンし、その後、それらが報告した問題を人間が検証(却下または確認)します。
-
意味解析 スマートコントラクトのビジネスロジックを調査し、自動ファジングツール(当社研究チームによって開発)を使用して、潜在的な脆弱性についてさらに調査します。また、独立した監査人と協力して、潜在的な攻撃シナリオを人間が分析し、結果を相互に確認します。
-
推奨事項 ガス最適化、コードスタイルなど、優れたプログラミングプラクティスの観点から、開発者に役立つアドバイスを提供します。
以下に、主な具体的なチェックポイントを示します。
3.2.1 ソフトウェアセキュリティ
-
再入可能性(Reentrancy)
-
DoS(サービス拒否)
-
アクセス制御
-
データ処理とデータフロー
-
例外処理
-
信頼できない外部呼び出しと制御フロー
-
初期化の一貫性
-
イベント操作
-
エラーが発生しやすい乱数
-
プロキシシステムの不適切な使用
3.2.2 DeFiセキュリティ
-
意味の一貫性
-
機能の一貫性
-
権限管理
-
ビジネスロジック
-
トークン操作
-
緊急メカニズム
-
オラクルセキュリティ
-
ホワイトリストとブラックリスト
-
経済的影響
-
バッチ転送
3.2.3 NFTセキュリティ
-
重複アイテム
-
トークン受信者の検証
-
オフチェーンメタデータセキュリティ
3.2.4 追加の推奨事項
-
ガス最適化
-
コード品質とスタイル
注記 上記のチェックポイントが主なものです。プロジェクトの機能に応じて、監査プロセス中にさらに多くのチェックポイントを使用する場合があります。



