지난 한 주 동안(2026/03/02 - 2026/03/08), BlockSec은 총 7건의 공격 사건을 탐지 및 분석하였으며, 총 예상 손실액은 약 $3.25M입니다. 아래 표는 각 사건을 요약한 것이며, 각 사례에 대한 상세 분석은 이후 소절에서 제공됩니다.
| 날짜 | 사건 | 유형 | 예상 손실 |
|---|---|---|---|
| 2026/03/01* | BUBU2 사건 | 토큰 설계 결함 | ~$19.7K |
| 2026/03/02 | ACPRoute 사건 | 잘못된 비즈니스 로직 | ~$58K |
| 2026/03/02 | sDOLA Llamalend 마켓 사건 | 가격 조작 | ~$239K |
| 2026/03/03 | z0r0z의 V4 Router 사건 | 잘못된 비즈니스 로직 | ~$42K |
| 2026/03/05 | BitcoinReserveOffering 컨트랙트 사건 | 잘못된 비즈니스 로직 | ~$2.7M |
| 2026/03/07 | MoltEVM 사건 | 잘못된 비즈니스 로직 | ~$127K |
| 2026/03/08 | LEDS 사건 | 잘못된 비즈니스 로직 | ~$64K |
*BUBU2 사건은 지난 주 보고서에서 누락되었으며, 완전성을 위해 여기에 포함되었습니다.
1. BUBU2 사건
간략한 요약
2026년 3월 1일, BNB Chain의 BUBU2 토큰이 익스플로잇되어 약 $19.7K의 손실이 발생했습니다. 근본 원인은 토큰 설계 결함으로, 컨트랙트에는 시간 누적 방식의 디플레이션 메커니즘이 내장되어 있어 전송 시 AMM 페어의 준비금에서 직접 토큰을 차감합니다. 컨트랙트 소유자는 공격 전에 트리거 간격을 비합리적으로 작은 값으로 단축하여 수백 회의 소각 라운드가 누적된 후 단일 호출에서 실행되도록 만들었습니다. 공격자는 플래시 론을 활용하여 이 메커니즘을 트리거하고, 페어의 준비금을 붕괴시켜 토큰 가격을 인위적으로 부풀린 다음, 왜곡된 환율로 역방향 스왑을 통해 이익을 취했습니다.
배경
BUBU2는 BNB Chain에 배포된 디플레이션 ERC-20 토큰 프로토콜입니다. 이 프로토콜에는 burnAndMintSwitch로 제어되는 주기적 디플레이션 엔진이 내장되어 있으며, 소유자가 setBurnAndMintSwitch(true)를 통해 활성화하면 면제 대상이 아닌 모든 전송이 _update() 훅을 트리거하여 _triggerDailyBurnAndMint()를 호출합니다. 이 함수는 마지막 트리거 이후 경과한 TRIGGER_INTERVAL 주기 수에 비례하여 거래 페어의 BUBU2 잔액에서 토큰을 소각하고, 준비금을 동기화합니다.
취약점 분석
근본 원인은 BUBU2 토큰 컨트랙트(0x3fF3...ee52)의 설계 결함입니다. 컨트랙트는 _update() 훅에 주기적 디플레이션 메커니즘을 내장하고 있으며, 트리거 시 `_triggerDailyBurnAndMint()`는 마지막 실행 이후 경과한 `TRIGGER_INTERVAL` 주기 수를 계산하여 거래 페어([0x7745...cd2f](https://bscscan.com/address/0x774547ea9d2a0cc79db3288f61e989f1b06bcd2f))에서 비례적인 양의 `BUBU2`를 직접 소각한 후 sync()를 통해 준비금을 업데이트합니다. 중요한 점은 소유자가 타임락이나 최솟값 보호 없이 TRIGGER_INTERVAL을 재설정할 수 있다는 것입니다.
공격 전에 소유자는 setBurnAndMintSwitch(true)를 호출한 후 setTriggerInterval(120)을 호출하여 기본값인 6시간에서 120초로 간격을 압축했습니다. lastTriggerTime이 여전히 몇 시간 전에 고정되어 있었기 때문에, 다음 트리거 시 수백 회의 누적 라운드가 계산되었고, 소각량은 라운드에 비례하여 선형적으로 증가했습니다. 이로 인해 단 한 번의 전송으로 페어에서 막대한 양의 BUBU2가 소진되어 준비금이 붕괴되고 토큰 가격이 약 11배 상승했습니다.

공격 분석
다음 분석은 트랜잭션 0x1bc0...141c,0x191c...1ee4,0xd6e5...51a6를 기반으로 합니다.
- 1단계: 토큰 소유자는 먼저
setBurnAndMintSwitch(true)를 통해 주기적 디플레이션 메커니즘을 활성화한 다음,setTriggerInterval(120)을 통해 트리거 간격을 기본값 6시간에서 120초로 단축했습니다. 이후 공격자는 플래시 론으로 18개의WBNB를 빌려 풀에서 약 18,715,856개의BUBU2로 스왑했습니다.


- 2단계: 공격자는 소량의 전송을 시작하여
_triggerDailyBurnAndMint()를 트리거했습니다. 풀의BUBU2잔액은 약 1,025,988,664e18 토큰 감소하였으며,sync()실행 후 준비금에 남은BUBU2는 6,493,352e18뿐이었고, 이로 인해BUBU2가격이 약 200배 상승했습니다.



- 3단계: 공격자는 1단계에서 확보한
BUBU2를 부풀려진 가격으로 풀에 다시 매도하여 약 50개의WBNB를 받았습니다. 플래시 론을 상환한 후 순이익은 약 32개의WBNB였습니다.
결론
BUBU2 익스플로잇은 토큰 컨트랙트의 심각한 설계 결함에서 비롯되었습니다. 소유자가 지나치게 작은 TRIGGER_INTERVAL을 설정함으로써 경과 시간이 수백 라운드로 누적되어 단 한 번의 호출로 거래 페어의 준비금을 고갈시키고 BUBU2 가격이 급격히 급등하는 결과를 초래했습니다.
향후 유사한 위험을 줄이기 위해:
-
TRIGGER_INTERVAL과 같은 중요한 파라미터를 범위 제한 또는 타임락으로 보호하세요. -
누적 실행 라운드를 제한하거나 상한선을 설정하세요.
2. ACPRoute 사건
간략한 요약
2026년 3월 2일, Base의 ACPRoute 프로토콜이 익스플로잇되어 약 $58K의 손실이 발생했습니다. 근본 원인은 결제 관리자 컨트랙트의 잘못된 비즈니스 로직으로, job 상태가 스토리지 참조 대신 메모리 복사본으로 로드되어 누적 지급 추적이 온체인에 영구 저장되지 않았습니다. 이로 인해 공격자는 job을 생성하고 단계 전환 중 자동 결제 릴리즈를 트리거한 후, 권한 없이 접근 가능한 claim 함수를 통해 동일한 에스크로 자금을 두 번 청구할 수 있었습니다.
배경
ACP(Agent Commerce Protocol)는 클라이언트와 제공자 간의 상호작용을 위해 설계된 모듈식 온체인 상거래 프로토콜입니다. 이 프로토콜은 활동을 세 가지 계층(계정, Job, 메모)으로 구조화합니다. Job은 고정된 생명주기(REQUEST → NEGOTIATION → TRANSACTION → EVALUATION → COMPLETED)를 따르며, TRANSACTION 단계에서 결제는 PaymentManager 컨트랙트에 의해 에스크로로 보관됩니다. Job이 COMPLETED에 도달하면 프로토콜은 releasePayment() 함수를 통해 에스크로 자금을 제공자에게 릴리즈합니다. 이중 청구를 방지하기 위해 프로토콜은 Job 구조체의 amountClaimed 필드를 사용하여 job당 누적 지급액을 추적합니다. releasePayment() 함수의 각 호출은 요청 금액을 amountClaimed와 비교하여 자금이 한 번만 릴리즈되도록 보장해야 합니다.


취약점 분석
근본 원인은 PaymentManager 컨트랙트(0x56c3...0684)의 releasePayment() 함수에 있으며, job 상태가 스토리지 참조가 아닌 메모리 복사본으로 로드됩니다. 프로토콜이 이중 청구를 방지하기 위해 amountClaimed를 통해 누적 지급액을 추적하지만, job.amountClaimed += amount 증가 연산은 일시적인 로컬 복사본에서만 작동하며 온체인 스토리지에는 기록되지 않습니다. 결과적으로 releasePayment() 함수를 호출할 때마다 amountClaimed == 0이 관찰되어, 공격자가 단계 전환으로 releasePayment() 함수가 자동으로 트리거된 후에도 claimBudget() 함수를 호출하여 피해자 컨트랙트(0x307e...d6e8)를 두 번 탈취할 수 있었습니다.

공격 분석
다음 분석은 트랜잭션 0xe94a...f9a0을 기반으로 합니다.
-
1단계: 공격자는 이후 공격을 위한 자본으로 플래시 론을 통해 97,000e6
USDC를 빌렸습니다. -
2단계: 콜백 내에서 공격자는 ACPRouter의
createJob()함수를 호출하는 동시에 자금을 수령할 새로운 제공자 컨트랙트(0x1ee502dd...)를 배포했습니다. -
3단계: 공격자는
createMemo()함수를 반복적으로 호출하고 제공자 컨트랙트 자체의exec()를 실행하여 job 단계를 진행시키고,COMPLETED단계 전환이 트리거될 때까지 반복했습니다. 이 전환은 자동으로releasePayment()함수를 호출하여 전체 잔액을 제공자 컨트랙트에 릴리즈했습니다. 이 시점에서amountClaimed가 업데이트되었어야 했지만, 스토리지의 값은 0으로 남아 있었습니다. -
4단계: 공격자는
claimBudget()함수를 호출하여 에스크로된 97,000e6USDC를 두 번째로 성공적으로 청구하여 이익을 취했습니다.

결론
이 사건은 동일한 job 생명주기 내에서 에스크로 자금을 두 번 청구할 수 있게 한 상태 영속성 결함으로 인해 발생했습니다.
향후 유사한 위험을 줄이기 위해:
-
중요한 상태 변수(예:
amountClaimed)가 스토리지 참조를 사용하여 업데이트되도록 보장하세요. -
민감한 지급 함수를 제한하거나 실행 전에 엄격한 상태 검증을 강제하세요.
3. sDOLA Llamalend 마켓 사건
간략한 요약
2026년 3월 2일, 이더리움의 sDOLA Llamalend 마켓이 익스플로잇되어 약 $239K의 손실이 발생했습니다. 근본 원인은 가격 조작입니다. 구체적으로, sDOLA는 donate()를 통해 가격을 조작할 수 있는 ERC4626 토큰입니다. Llamalend는 AMM 기반 대출 마켓으로, LLAMMA의 메커니즘으로 인해 담보 가격이 상승하더라도 사용자가 청산 가능 상태가 될 수 있습니다. 따라서 공격자는 donate()를 사용하여 sDOLA 가격을 올리고, 사용자의 포지션 건전성을 0 아래로 낮춘 다음, 해당 사용자를 청산하여 이익을 취할 수 있습니다.
배경
LLAMMA(Lending-Liquidating AMM Algorithm)는 Curve가 사용하는 AMM 기반 대출 마켓입니다 [1]. 기존 대출 프로토콜과 달리, LLAMMA는 AMM 내의 가격 밴드에 사용자 담보를 배치합니다. 오라클 가격 이 움직이면, 담보는 밴드 전반에 걸친 차익 거래 주도 스왑을 통해 담보 토큰과 crvUSD 사이에서 점진적으로 전환되어(소프트 청산) 갑작스러운 청산을 트리거하는 대신 포지션 위험을 점진적으로 줄입니다.

소프트 청산으로 포지션 위험을 충분히 빠르게 줄이지 못하면 하드 청산이 발동됩니다 [2]. 하드 청산은 포지션의 건전성이 0 아래로 떨어질 때 트리거됩니다. 건전성은 get_x_down() [3]을 통해 계산되며, 단순히 담보를 시장 가격으로 평가하지 않습니다. 대신, 사용자 포지션의 왕복 전환을 시뮬레이션하여 회수 가능한 가치를 평가합니다. 이 왕복 전환에는 두 가지 다른 가격 앵커가 사용됩니다: 하나는 현재 에서 파생되어 라이브 오라클과 함께 움직이고, 다른 하나는 사용자의 밴드 경계에서 파생되어 포지션 생성 시 고정됩니다.

취약점 분석
버그가 있는 컨트랙트는 crvUSD Controller(0xad44...fb86)와 LLAMMA AMM(0x0079...a1f7)입니다. 피해자 컨트랙트는 sDOLA Llamalend 마켓(0x2b08...4fbe)입니다.
sDOLA는 총 자산 대 총 주식 비율로 주식 가격이 결정되는 ERC4626 볼트 토큰입니다. 누구나 donate()를 호출하여 볼트에 자산을 추가할 수 있기 때문에, 단일 트랜잭션 내에서 주식 가격을 인위적으로 부풀릴 수 있습니다. 이것이 LLAMMA의 건전성 함수가 의존하는 조작 가능한 오라클입니다.
배경에서 설명한 바와 같이, get_x_down()은 두 가지 가격 앵커를 통한 왕복 전환을 시뮬레이션하여 건전성을 계산합니다: 에서 파생된 동적 앵커(라이브 오라클과 함께 움직임)와 사용자의 밴드 경계에서 파생된 정적 앵커(포지션 생성 시 고정). 프로토콜은 을 읽을 때 지연이나 온전성 검사를 적용하지 않습니다. 따라서 오라클 가격이 인위적으로 부풀려지면 동적 앵커는 상승하지만 정적 앵커는 그대로 유지됩니다. 실질적으로 시뮬레이션은 부풀려진 오라클 가격으로 crvUSD를 담보로 전환하고 원래 밴드 가격으로 다시 전환하므로, 두 앵커 간의 격차가 커질수록 평가된 회수 가능 가치가 줄어듭니다. 이로 인해 담보 가격이 상승했음에도 불구하고 건전성이 0 아래로 떨어집니다.


공격 분석
다음 분석은 트랜잭션 0xb935...d8a4을 기반으로 합니다.
- 1단계: LLAMMA 상태 조작(대규모 스왑)을 통해
active_band를 이동시키고 많은 사용자를 x only 포지션(오직crvUSD만)으로 밀어넣습니다.

- 2단계: 기부를 통해
sDOLA의 가격을 조작한 다음, 제로 금액 스왑(swap(0))을 사용하여 조작된 가격을 AMM 풀에 기록합니다.


-
3단계:
users_to_liquidate()->_health()->get_x_down()을 통해 건전성 재계산을 트리거합니다. -
4단계:
get_x_down()이 두 개의 발산하는 가격 앵커(현재 부풀려진 동적 앵커 대 변경되지 않은 정적 앵커)를 통해 회수 가능 가치를 계산하기 때문에, 왕복 전환은 유효 가치에 헤어컷을 발생시키고 많은 포지션이 건전성 0 아래로 떨어집니다. -
5단계: 해당 사용자들을 일괄 하드 청산하여 이익을 취합니다.
또한, 트레이스에는 두 번의 create_loan() 호출이 포함되어 있습니다. 첫 번째 대출은 주로 대규모 crvUSD 스왑 작업에 자금을 조달하기 위해 사용되었습니다. 두 번째 대출은 첫 번째 포지션의 부채를 상환하고 자본을 재활용하기 위해 crvUSD를 확보하는 데 사용되었습니다. 이 두 대출은 주로 자금 조달/결제 단계이며 핵심 익스플로잇 단계 자체는 아닙니다.
결론
이 사건은 담보 가격 조작 공격입니다. 공격자는 트랜잭션 내에서 sDOLA 가격을 조작했으며, LLAMMA의 경로 기반 건전성 메커니즘으로 인해 사용자를 청산 조건으로 몰아넣었습니다. 핵심적인 결과는 담보 가격이 상승하더라도 포지션이 청산 가능 상태가 될 수 있다는 것입니다. 권장 강화 방향:
- 청산 시 지연 또는 TWAP 담보 가격을 사용하세요.
참고 자료
-
[1] Curve crvUSD LLAMMA 문서: [https://dev.curve.finance/crvUSD/amm/#bands](https://dev.curve.finance/crvUSD/amm/#bands)
-
[2] Curve LLAMMA 청산 설명: [https://dev.curve.finance/crvUSD/llamma-explainer/#liquidation](https://dev.curve.finance/crvUSD/llamma-explainer/#liquidation)
-
[3] Curve stablecoin 백서: [https://docs.curve.finance/pdf/whitepapers/whitepaper\_curve\_stablecoin.pdf](https://docs.curve.finance/pdf/whitepapers/whitepaper_curve_stablecoin.pdf)
4. z0r0z의 V4 Router 사건
간략한 요약
2026년 3월 3일, 이더리움의 z0r0z V4 Router가 익스플로잇되어 약 $42K의 손실이 발생했습니다. 이 공격은 swap()의 잘못된 인가 로직으로 인해 발생했으며, 라우터가 인라인 어셈블리에서 고정된 calldata 오프셋에 의존하여 지급자가 msg.sender와 일치하는지 확인했습니다. ABI 인코딩에서 동적 타입의 레이아웃이 고정되어 있지 않기 때문에, 공격자는 검사를 우회하는 비표준적이지만 유효한 calldata를 만들어 이전에 승인된 피해자를 지급자로 위장하고 스왑 출력을 공격자가 제어하는 주소로 리디렉션할 수 있었습니다.
배경
이 프로토콜은 Uniswap v4 Router에서 상속받아 일부 메서드를 오버라이드합니다. 본질적으로 스왑 라우터로 기능하며, 사용자가 해당 풀과 직접 상호작용하는 대신 라우터를 통합된 진입점으로 사용할 수 있게 합니다.
취약점 분석
근본 원인은 V4 Router 컨트랙트(0x0000...ce97)의 잘못된 비즈니스 로직입니다. 피해자 컨트랙트는 0x65a8...7675입니다. swap() 함수는 data(bytes)와 deadline(uint256) 두 파라미터를 받습니다. 함수 내에서 인가 검사는 인라인 어셈블리 블록을 사용하여 calldataload(164)를 caller()와 비교하고, data에 인코딩된 지급자가 msg.sender와 일치하는지 확인합니다. 프로토콜은 bytes 오프셋이 항상 0x40이라고 가정하여 지급자를 바이트 위치 164(0xa4)에 배치합니다.
그러나 ABI 인코딩은 이 레이아웃을 보장하지 않습니다: 동적 파라미터는 헤드에 오프셋만 저장하며, 해당 오프셋은 calldata의 임의의 정렬된 위치를 합법적으로 가리킬 수 있습니다. bytes 오프셋을 0xc0으로 이동시킨 유효하지만 비표준적인 인코딩을 만들어, 공격자는 헤드와 실제 테일 사이에 임의의 패딩을 삽입할 수 있습니다. 공격자는 어셈블리 검사를 통과하기 위해 바이트 위치 164에 자신의 주소를 배치하는 반면, 재배치된 테일의 실제 bytes 페이로드는 피해자의 주소를 지급자로 인코딩합니다. 이전에 라우터를 승인한 피해자를 선택하고 수신자를 자신의 주소로 설정함으로써, 공격자는 스왑 출력을 리디렉션하여 자금을 탈취할 수 있습니다.


공격 분석
다음 분석은 트랜잭션 0xfe34...466a을 기반으로 합니다.
-
1단계: 이전에 라우터를 승인한 사용자를 대상으로 선택합니다.
-
2단계: 인가 검사를 우회하도록 calldata를 조작합니다.
-
3단계: data에서 피해자를 지급자로 지정하고, 공격자 자신의 주소를 토큰 수신자로 설정한 다음, 피해자의 자산을 탈취합니다.

결론
이 사건은 스왑 경로에서 인가를 위해 하드코딩된 calldata 오프셋에 의존함으로써 발생했습니다. ABI 인코딩에서 동적 타입의 레이아웃이 고정되어 있지 않기 때문에, 공격자는 비표준적이지만 유효한 calldata로 검사를 우회하여 승인된 피해자를 지급자로 위장하고 스왑 출력을 리디렉션했습니다.
향후 유사한 위험을 줄이기 위해:
-
동적 ABI 인코딩 파라미터에 대해 하드코딩된 calldata 오프셋에 절대 의존하지 마세요.
-
수동 위치 가정 대신 표준 ABI 디코딩을 사용하여 구조화된 입력을 디코딩하고 검증하세요.
-
실제 지급자, 수신자 및 토큰 흐름이 의도한 호출자 및 실행 컨텍스트와 일관되게 검증되도록 보장하세요.
5. BitcoinReserveOffering 컨트랙트 사건
간략한 요약
2026년 3월 5일, 이더리움의 BitcoinReserveOffering 컨트랙트가 익스플로잇되어 약 $2.7M의 손실이 발생했습니다. 근본 원인은 잘못된 비즈니스 로직으로, 전체 ERC-3525 SFT 예치를 처리할 때 민팅 로직이 단일 호출 내에서 두 번 실행되었습니다. 한 번은 전송 콜백 중에, 한 번은 메인 플로우에서 실행되었습니다. 공격자는 이 이중 민팅 동작을 반복적인 소각-민팅 사이클을 통해 악용하여 BRO 토큰 잔액을 기하급수적으로 부풀린 다음, 초과분을 SolvBTC로 상환하여 이익을 실현했습니다.
배경
BitcoinReserveOffering은 ERC-3525 SFT 포지션을 전송 가능한 ERC-20 BRO 토큰으로 변환하는 래퍼 컨트랙트입니다. 사용자는 mint() 함수를 호출하여 적격 SFT를 예치할 수 있으며, 컨트랙트는 설정된 환율에 따라 상응하는 양의 BRO를 민팅합니다. 사용자가 SFT 가치의 일부만 예치하는 경우, 컨트랙트는 지정된 금액을 내부적으로 보유한 포지션으로 전송한 후 해당 가치의 BRO를 민팅합니다. 사용자가 전체 SFT를 예치하는 경우, 컨트랙트는 전체 토큰을 래퍼 컨트랙트로 전송하고 SFT의 총 가치를 기반으로 BRO를 민팅합니다. 사용자는 나중에 burn() 함수를 호출하여 BRO를 해당하는 ERC-3525 SFT 포지션으로 상환할 수 있으며, 래핑된 ERC-20 가치를 SFT 포지션으로 다시 전환합니다.
취약점 분석
근본 원인은 BitcoinReserveOffering 컨트랙트(0x15f7...cfcf)가 mint() 함수에서 전체 ERC-3525 SFT 예치를 처리할 때 민팅 로직을 두 번 실행한다는 것입니다. 구체적으로, amount_ == sftBalance 분기에서 mint()는 ERC3525TransferHelper.doSafeTransferIn()을 호출하여 전체 SFT를 컨트랙트로 안전하게 전송하며, 이는 onERC721Received() 콜백을 트리거합니다. 이 콜백 내에서 컨트랙트는 이미 SFT의 가치를 계산하고 발신자에게 BRO를 민팅합니다. 콜백이 반환된 후 mint()는 실행을 계속하여 동일한 amount_를 사용하여 가치를 재계산하고 두 번째 _mint(msg.sender, value) 작업을 수행합니다. 이 이중 민팅 동작을 통해 공격자는 소각-민팅 루프를 통해 BRO 잔액을 반복적으로 부풀릴 수 있습니다.


공격 분석
다음 분석은 트랜잭션 0x44e6...958d을 기반으로 합니다.
-
1단계: 공격자는 초기 자본으로 135e18개의
BRO토큰을 공격 컨트랙트로 전송합니다. -
2단계: 공격자는 다음 루프를 실행합니다:
-
135e18개의
BRO토큰을SFT토큰으로 래핑합니다. -
전체
SFT가치로mint()를 호출하면onERC721Received()콜백이 트리거되어 135e18개의BRO토큰이 민팅됩니다; 외부mint()는 계속 실행되어_mint()를 다시 호출하여 총 270e18개의BRO토큰이 이중 민팅됩니다. -
270e18개의
BRO토큰을 다시SFT토큰으로 래핑합니다.
- 3단계: 2단계를 22회 반복한 후 공격자는 약 567,758,816e18개의
BRO토큰을 축적하며, 이는 최종적으로 38e18개의SolvBTC로 상환되어 이익이 됩니다.
결론
이 사건은 전체 SFT 예치 중 이중 민팅으로 인해 발생했으며, 공격자가 반복적인 소각-민팅 사이클을 통해 BRO 잔액을 기하급수적으로 부풀리고 초과분을 SolvBTC로 상환할 수 있었습니다.
향후 유사한 위험을 줄이기 위해:
-
자산 회계가 예치 작업당 한 번만 발생하도록 보장하세요.
-
민팅 금액이 기초 예치 가치를 초과하지 않도록 불변 검사를 추가하세요.
6. MoltEVM 사건
간략한 요약
2026년 3월 7일, Base의 MoltEVM 프로토콜이 익스플로잇되어 약 $127K의 손실이 발생했습니다. 근본 원인은 토큰 컨트랙트의 잘못된 접근 제어 로직으로, 권한이 있는 민팅 함수가 호출자가 특정 인터페이스를 구현한 컨트랙트인지만 확인하는 모디파이어로 보호되었으며, 이는 쉽게 위장될 수 있었습니다. 공격자는 악의적인 컨트랙트를 배포하여 검사를 우회하고 대량의 토큰을 민팅한 다음, 유동성 풀을 통해 덤핑하여 이익을 취했습니다.
배경
MoltEVM은 Base에 배포된 실험적인 ERC-20 토큰 프로토콜로, 자가 복제 토큰 모델을 탐구합니다. 이 시스템은 특정 준비금 임계값에 도달하면 토큰이 새로운 자식 토큰("Moltling" 토큰)을 생성할 수 있습니다. 새로운 Moltling이 생성되면 초기 토큰 공급량이 mintFromSpawner() 함수를 통해 배포되고, 새 토큰의 거래 시장을 형성하기 위해 유동성이 자동으로 공급됩니다. 이 설계는 프로토콜이 자율적으로 토큰 계보를 확장할 수 있게 하며, 각 세대의 토큰이 추가 후손을 생성할 수 있습니다.
취약점 분석
취약점은 MoltEVM 컨트랙트(0x225d...501f)의 mintFromSpawner() 및 setExemptFromSpawner() 함수의 접근 제어 로직에 있습니다. 두 함수 모두 onlySpawnerToken 모디파이어에 의존하며, 이는 프로토콜이 생성한 합법적인 Moltling 컨트랙트로만 호출을 제한하려는 의도였습니다.
그러나 이 모디파이어는 두 가지 취약한 검사만 수행합니다: msg.sender가 컨트랙트인지 확인하고, 호출자에서 initialized()를 호출하면 true가 반환되는지 확인합니다. 이 조건들은 동일한 인터페이스를 구현하는 임의의 컨트랙트로 쉽게 충족될 수 있으므로, 이 검사는 합법적인 스포너 토큰의 의미 있는 인증을 제공하지 않습니다.
결과적으로, 공격자는 단순히 true를 반환하는 initialized() 함수를 구현한 최소한의 컨트랙트를 배포할 수 있습니다. 배포 후 악의적인 컨트랙트는 mintFromSpawner()를 자유롭게 호출하여 임의의 양의 토큰을 민팅하고, setExemptFromSpawner()를 호출하여 공격자가 제어하는 주소를 화이트리스트에 추가할 수 있습니다. 이는 공격자에게 민팅 경로에 대한 완전한 제어권을 부여하고 새로 민팅된 토큰을 유동성 풀에 덤핑하여 이익을 취할 수 있게 합니다.

공격 분석
다음 분석은 트랜잭션 0x10b7...e03d을 기반으로 합니다.
-
1단계: 공격자는
onlySpawnerToken모디파이어 검사를 통과하기에 충분한true를 반환하도록 하드코딩된 단일initialized()함수를 구현한 최소한의 컨트랙트를 배포했습니다. -
2단계: 공격자의 컨트랙트는 동일한 취약한
onlySpawnerToken모디파이어로 보호되는setExemptFromSpawner()함수를 호출하여 공격자의 주소를 세금 면제 화이트리스트에 추가했습니다. 이를 통해 이후 대규모 토큰 덤핑 시 매도 세금이나 내부 스왑 로직이 트리거되지 않아 이익이 극대화되었습니다.

- 3단계: 공격자는 먼저
mEVM에 대해setExemptFromSpawner()+mintFromSpawner()함수 순서를 반복하고, 이어서CSPAWN,CCUTTL,LWORM,NHYDRA를 포함한 여러 Moltling 자식 토큰에 대해 반복하여 모든 대상에 걸쳐 토큰을 일괄 민팅하고 각각의 유동성 풀에 덤핑하여 이익을 추출했습니다.

결론
이 사건은 권한이 있는 민팅 및 설정 함수에 대한 불충분한 접근 제어로 인해 발생했으며, 공격자가 합법적인 스포너를 위장하여 임의의 토큰을 민팅하고 이익을 취할 수 있었습니다.
향후 유사한 위험을 줄이기 위해:
-
권한이 있는 민팅 또는 설정 함수에 대해 엄격한 접근 제어를 강제하세요.
-
인가를 위해 쉽게 위장할 수 있는 컨트랙트 검사에 의존하지 마세요.
-
명시적인 화이트리스트 또는 신뢰할 수 있는 컨트랙트 레지스트리를 통해 호출자 신원을 검증하세요.
7. LEDS 사건
간략한 요약
2026년 3월 8일, BNB Chain의 LEDS 토큰이 익스플로잇되어 약 $64K의 손실이 발생했습니다. 근본 원인은 잘못된 비즈니스 로직으로, 토큰 컨트랙트가 여러 개의 독립적인 디플레이션 메커니즘을 노출시키며, 각각이 유동성 페어에서 직접 토큰을 소각할 수 있고 접근 제어나 쿨다운으로 보호되지 않습니다. 공격자는 단일 트랜잭션 내에서 모든 소각 경로를 연결하여 페어의 LEDS 준비금을 거의 0에 가깝게 붕괴시키면서 WBNB 준비금은 그대로 유지한 다음, 불균형한 풀을 역방향 스왑으로 탈취하여 이익을 취했습니다.
배경
LEDS는 전송 수수료 메커니즘이 내장된 BNB Chain의 디플레이션 토큰입니다. _transfer()는 공급을 점진적으로 줄이고 가격을 지지하도록 설계된 여러 소각 메커니즘을 구현합니다: 일일 소각 함수 triggerDailyBurnAndMint(), 매도 시 stor_18 누적을 통한 지연 소각, 스왑 수신자가 PancakeSwap Router로 설정된 경우 0xdead로 토큰을 소각하는 특수 분기. 토큰은 사용자가 BNB를 보내 LEDS를 민팅하고 유동성을 제공하여 배포자를 통해 청구 가능한 LP 보상을 얻는 deposit() 함수도 포함합니다.
취약점 분석
근본 원인은 LEDS 토큰 컨트랙트(0xfb62...a48f)의 잘못된 비즈니스 로직입니다. 피해자는 LEDS/WBNB 페어(0xd109...6f3f)입니다. 토큰은 여러 디플레이션 메커니즘을 구현합니다: triggerDailyBurnAndMint(), 매도 경로 stor_18 누적, 그리고 _transfer()의 to == router 소각 분기. 이 각각은 독립적으로 유동성 페어에서 LEDS를 제거하고 sync()를 호출하여 준비금을 업데이트합니다.
개별적으로는 이러한 메커니즘들이 점진적인 디플레이션 도구로 기능하지만, 단일 트랜잭션 내에서 연결하여 효과를 복합적으로 발생시킬 수 있습니다. 또한 공개 함수 0x17a06174()는 접근 제어나 속도 제한 없이 누구나 누적된 전체 stor_18 잔액을 즉시 플러시할 수 있습니다. 이러한 소각 경로들을 순차적으로 쌓음으로써, 공격자는 WBNB 준비금을 그대로 유지하면서 페어의 LEDS 준비금을 거의 0에 가깝게 줄여 역방향 스왑으로 악용 가능한 극단적인 가격 불균형을 만들 수 있습니다.
공격 분석
다음 분석은 트랜잭션 0x2608...79da을 기반으로 합니다.
-
1단계: 공격자는 플래시 론(Moolah + Venus 담보 차입 + Aave + PancakeSwap V4)을 통해 대량의
WBNB를 확보했습니다. -
2단계:
deposit()을 여러 번 호출하여 LP 보상을 축적한 다음,triggerDailyBurnAndMint()를 트리거했습니다. 이는 페어에서 일부LEDS를 소각하고 배포자에게 보상을 전송하여 풀의LEDS준비금을 감소시켰습니다.

- 3단계: 함수
0xde1b1942()를 호출하여 누적된LEDS토큰 보상을 청구했습니다.

- 4단계: 청구한
LEDS를WBNB로 매도했습니다. 매도 후 페어의LEDS잔액이 임계값을 초과했기 때문에 전송된 금액이stor_18(대기 중인 소각)에 누적되었습니다.

- 5단계: 수신자를 Router 주소로 설정하여 PancakeSwap을 통해
LEDS를 구매했습니다. 이는_transfer()에서 특수 분기를 트리거하여 구매한LEDS를 페어에서 0xdead로 직접 소각하여 풀의LEDS준비금을 더욱 감소시켰습니다.

- 6단계: 함수
0x17a06174()를 호출하여 페어에서 0xdead로 전체stor_18잔액(4단계에서 누적)을 소각하고sync()를 호출하여 풀의LEDS준비금을 단 2 wei로 붕괴시켰습니다.

- 7단계: 극도로 불균형해진 풀에서 역방향 스왑으로
WBNB를 탈취하고, 모든 플래시 론을 상환하여 104.56WBNB($64K)의 이익을 얻었습니다.
결론
이 사건은 단일 트랜잭션 내에서 연결될 수 있는 여러 개의 보호되지 않은 디플레이션 메커니즘으로 인해 유동성 페어의 준비금이 치명적으로 고갈되어 발생했습니다.
디플레이션 또는 자동 소각 메커니즘을 구현하는 모든 토큰 컨트랙트에 대해 개발자는 다음을 권장합니다:
-
적절한 접근 제어, 속도 제한, 또는 쿨다운 없이 페어에서 소각하는 함수를 절대 노출시키지 마세요.
-
여러 개의 독립적인 소각 메커니즘이 단일 트랜잭션 내에서 순차적으로 트리거되는 것을 허용하지 마세요.
BlockSec 소개
BlockSec은 풀스택 블록체인 보안 및 암호화폐 컴플라이언스 제공업체입니다. 저희는 고객이 코드 감사(스마트 컨트랙트, 블록체인 및 지갑 포함), 실시간 공격 차단, 사건 분석, 불법 자금 추적, 프로토콜 및 플랫폼의 전체 생명주기에 걸쳐 AML/CFT 의무 이행을 수행할 수 있도록 제품과 서비스를 구축합니다.
BlockSec은 저명한 학술 컨퍼런스에서 다수의 블록체인 보안 논문을 발표하고, DeFi 애플리케이션의 여러 제로데이 공격을 보고하였으며, 2천만 달러 이상을 구제하기 위한 여러 해킹을 차단하고, 수십억 달러의 암호화폐를 보호했습니다.
-
공식 웹사이트: https://blocksec.com/
-
공식 트위터 계정: https://twitter.com/BlockSecTeam
-
🔗 BlockSec 감사 서비스 : 요청 제출



