보고서 매니페스트
| 항목 | 설명 |
|---|---|
| 클라이언트 | Magpie XYZ |
| 대상 | CakePie 컨트랙트 |
버전 이력
| 버전 | 날짜 | 설명 |
|---|---|---|
| 1.0 | 2023년 11월 30일 | 최초 배포 |
1. 소개
1.1 대상 컨트랙트 정보
| 정보 | 설명 |
|---|---|
| 유형 | 스마트 컨트랙트 |
| 언어 | Solidity |
| 접근 방식 | 반자동 및 수동 검증 |
이번 감사의 대상은 Magpie XYZ의 CakePie 컨트랙트^1 코드 저장소입니다. CakePie 컨트랙트는 CakeRush 캠페인을 운영하며, 사용자는 PancakeSwap에서 보유한 CAKE 토큰 또는 잠긴 CAKE 포지션을 CakePie로 전환할 수 있습니다. 감사 범위에는 CakeRush.sol과 PancakeStakingBNBChain.sol만 포함되며, 그 외 파일은 이번 감사 범위에 포함되지 않습니다.
감사 프로세스는 반복적으로 진행됩니다. 구체적으로, 발견된 이슈를 수정한 커밋을 감사합니다. 새로운 이슈가 발생할 경우 이 과정을 계속합니다. 감사 중 사용된 커밋 SHA 값은 아래 표에 나타나 있습니다. 본 감사 보고서는 초기 버전(버전 1)의 코드와 감사 보고서의 이슈를 수정하기 위한 새 코드(이후 버전)에 대해 책임집니다.

1.2 보안 모델
위험을 평가하기 위해, OWASP 위험 등급 방법론^2 및 공통 취약점 목록(CWE)^3을 포함하여 업계와 학계에서 널리 채택된 표준 또는 권고사항을 따릅니다. 위험의 전반적인 심각도는 가능성과 영향에 의해 결정됩니다. 구체적으로, 가능성은 특정 취약점이 공격자에 의해 발견되고 악용될 가능성을 추정하는 데 사용되며, 영향은 성공적인 악용의 결과를 측정하는 데 사용됩니다.
본 보고서에서 가능성과 영향은 각각 높음과 낮음 두 가지 등급으로 분류되며, 그 조합은 표 1.1에 나타나 있습니다.

이에 따라 본 보고서에서 측정된 심각도는 높음, 중간, 낮음의 세 가지 범주로 분류됩니다. 완전성을 위해, 위험을 명확히 판단할 수 없는 경우에는 미확정도 사용됩니다.
또한, 발견된 항목의 상태는 다음 네 가지 범주 중 하나에 해당합니다:
-
미확정 아직 응답 없음.
-
확인됨 항목이 클라이언트에 접수되었으나 아직 승인되지 않음.
-
승인됨 항목이 클라이언트에 의해 인식되었으나 아직 수정되지 않음.
-
수정됨 항목이 클라이언트에 의해 승인 및 수정됨.
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번 줄의 뺄셈이 정수 언더플로우로 인해 실패할 수 있습니다.
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 토큰을 보상으로 청구할 수 있습니다. 그러나 이 함수에는 사용자가 보상을 여러 번 청구할 수 있는 문제가 포함되어 있습니다. 다음 코드 세그먼트에서, 금액이 사용자의 금액보다 클 경우 사용자에게 총 금액을 전송하거나 예치합니다. 올바른 구현은 해당 금액만 반환해야 하므로, 현재 구현은 사용자가 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 컨트랙트에서 보상 분배와 관련된 여러 매개변수를 추가할 수 있습니다. 그러나 이러한 매개변수가 컨트랙트의 가정에 따라 올바르게 설정되었는지 확인하는 검사가 없습니다. 구체적으로, 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
영향 해당 없음
제안 매개변수를 설정하는 함수에서 매개변수를 확인하십시오.
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에서 언급된 코드로 제한됩니다. 명시적으로 지정되지 않는 한, 언어 자체의 보안(예: Solidity 언어), 기반 컴파일 툴체인 및 컴퓨팅 인프라는 범위에 포함되지 않습니다.
3.2 감사 절차
다음 절차에 따라 감사를 수행합니다.
-
취약점 탐지 먼저 자동 코드 분석기로 스마트 컨트랙트를 스캔한 후, 보고된 이슈를 수동으로 검증(거부 또는 확인)합니다.
-
의미 분석 스마트 컨트랙트의 비즈니스 로직을 연구하고, 자동 퍼징 도구(연구팀이 개발)를 사용하여 가능한 취약점에 대한 추가 조사를 수행합니다. 또한 독립적인 감사자와 함께 가능한 공격 시나리오를 수동으로 분석하여 결과를 교차 확인합니다.
-
권고사항 가스 최적화, 코드 스타일 등 좋은 프로그래밍 관행의 관점에서 개발자에게 유용한 조언을 제공합니다.
주요 구체적인 점검 항목은 다음과 같습니다.
3.2.1 소프트웨어 보안
-
재진입
-
서비스 거부(DoS)
-
접근 제어
-
데이터 처리 및 데이터 흐름
-
예외 처리
-
신뢰할 수 없는 외부 호출 및 제어 흐름
-
초기화 일관성
-
이벤트 운영
-
오류 발생 가능한 무작위성
-
프록시 시스템의 부적절한 사용
3.2.2 DeFi 보안
-
의미적 일관성
-
기능적 일관성
-
권한 관리
-
비즈니스 로직
-
토큰 운영
-
비상 메커니즘
-
오라클 보안
-
화이트리스트 및 블랙리스트
-
경제적 영향
-
일괄 전송
3.2.3 NFT 보안
-
중복 항목
-
토큰 수신자 검증
-
오프체인 메타데이터 보안
3.2.4 추가 권고사항
-
가스 최적화
-
코드 품질 및 스타일
참고 이전 점검 항목은 주요 항목입니다. 프로젝트의 기능에 따라 감사 과정에서 더 많은 점검 항목을 사용할 수 있습니다.



