지난 한 주(2026/05/25 - 2026/05/31) 동안, BlockSec은 여러 블록체인 생태계에 걸쳐 다수의 공격 사건을 식별했습니다. 아래 표는 총 약 $16M의 피해가 발생한 5건의 주요 사건을 정리한 것입니다.
| 날짜 | 사건 | 유형 | 추정 피해액 |
|---|---|---|---|
| 2026/05/27 | SquidRouterModule | 부적절한 입력 유효성 검사 | ~$3.2M |
| 2026/05/29 | Stake DAO | 개인 키 탈취 | ~$91K* |
| 2026/05/29 | DxSale | 잘못된 비즈니스 로직 및 키 탈취 | ~$7.3M |
| 2026/05/30 | Gravity Bridge | 취약한 오프체인 서명 프로세스 | ~$5.4M |
| 2026/05/30 | Alephium | 취약한 오프체인 서명 프로세스 | ~$300K* |
*추가 참고사항:
- Stake DAO 사건에서 공격자는 배포자의 개인 키를 탈취하여 Arbitrum의
vsdCRV컨트랙트에서 LayerZero v2 OFT 피어를 재구성하는 데 사용했으며, 이를 통해 5.4조 개의vsdCRV를 발행했습니다. DEX 유동성이 제한되어 풀이 고갈되기 전 약 $91K만 현금화되었습니다. - Alephium 사건에서 공격자는 이더리움의
USDT,USDC,WETH,WBTC와 BNB Chain의USDT,WBNB등 약 $300K 상당의 브릿지 수탁 자산을 탈취한 것 외에도, 이더리움에서 담보 없는wALPH1,370만 개를 발행했습니다. 브릿지가 중단되어 공격자는 Alephium 브릿지를 통해 해당 토큰을 상환하거나 브릿징하지 못했습니다.
심층 분석을 위해 두 건의 사건이 선정되었습니다:
- SquidRouterModule: CrossCurveFi 익스플로잇과 마찬가지로 동일한 유형의 통합 실수가 반복되었으며, 깊은 이해와 엄격한 보안 검토 없이 크로스체인 로직을 포크할 경우 상속된 복잡성이 곧 상속된 위험으로 변할 수 있음을 보여줍니다.
- DxSale: 긴 공격 타임라인은 오랫동안 악용되지 않은 레거시 컨트랙트가 반드시 안전하지는 않다는 것을 보여주며, 프로토콜은 온체인 및 오프체인 활동을 지속적으로 모니터링하고 권한 접근을 최소화해야 하며, 수년간의 침묵을 보안의 증거로 착각해서는 안 됩니다.
Best Security Auditor for Web3
Validate design, code, and business logic before launch
주간 하이라이트: DxSale
이 사건은 수년간 패치되지 않은 로커 컨트랙트와 탈취된 배포자 키의 조합이 반복되는 패턴을 잘 보여주기 때문에 주간 하이라이트로 선정되었습니다: 지속적인 감사나 권한 순환 없이 배포 시점의 코드를 영구적으로 안전하다고 간주하는 프로토콜은 언제든지 무기화될 수 있는 숨겨진 위험을 내포하고 있습니다.
2026년 5월 29일, BNB Chain의 토큰 세일 및 유동성 잠금 플랫폼인 DxSale이 약 $7.3M의 피해를 입는 익스플로잇을 당했습니다 [1]. 토큰 출금 함수에 세 가지 상태 업데이트 누락이 있어 유효한 잠금 포지션을 보유한 모든 사용자가 반복적으로 출금하여 공유 풀 전체를 소진할 수 있었으며, 소유자 권한은 필요하지 않았습니다. EIP-7702 위임을 통한 DxSale 배포자 키의 별도 탈취는 익스플로잇의 전제 조건은 아니었지만, 수수료 조작 및 다른 사용자의 새 잠금 생성 차단을 가능하게 하여 공격자의 효율성을 높였습니다.
배경
DxSale은 BNB Chain의 토큰 세일 및 유동성 잠금 플랫폼입니다. DxLock 컴포넌트는 프로젝트 개발자가 LP 토큰을 시간 잠금 금고에 잠글 수 있도록 하여, 유동성이 러그풀 될 수 없다는 확신을 투자자에게 제공합니다.
핵심 잠금 메커니즘은 다음과 같이 작동합니다: 사용자는 로커 컨트랙트에 토큰을 예치하여, 호출자의 주소와 토큰 ID의 조합으로 인덱싱된 잠금 레코드를 생성합니다. 각 레코드에는 잠금 존재 여부, 토큰이 현재 잠겨 있는지 여부, 잠긴 금액, 잠금 해제 타임스탬프, 토큰 주소가 저장됩니다. 잠금 기간이 만료되면 사용자는 unlockToken(tokenId)를 호출하여 토큰을 출금합니다.
로커는 공유 잔액에서 많은 사용자의 토큰을 동시에 보유하기 때문에, 개별 할당은 별도의 잠금 레코드를 통해 정확하게 추적되어야 합니다. 전체 풀의 보안은 각 출금이 호출자 자신의 레코드를 올바르게 검증하고 전송 후 이를 업데이트하는 것에 달려 있습니다.
취약점 분석
버그가 있는 컨트랙트(0xeb3a...e449)의 unlockToken 함수에는 각각 상태 업데이트 또는 검사가 누락된 세 가지 복합적인 결함이 있었습니다:
function unlockToken(uint256 tokenId) public nonPayable {
require(_increaseLockTime[msg.sender][tokenId].field0_0_0); // locker exists
require(_increaseLockTime[msg.sender][tokenId].field0_1_1); // tokens are locked
require(_increaseLockTime[msg.sender][tokenId].field2); // amount > 0
if (block.timestamp > _increaseLockTime[msg.sender][tokenId].field3) {
_increaseLockTime[msg.sender][tokenId].field0_1_1 = 0;
}
v0, v1 = _increaseLockTime[msg.sender][tokenId].field5_0_19.balanceOf(this);
require(v1 >= _increaseLockTime[msg.sender][tokenId].field2);
v2, v3 = _increaseLockTime[msg.sender][tokenId].field5_0_19.transfer(
msg.sender, _increaseLockTime[msg.sender][tokenId].field2
);
}
-
잠금 기간 미적용. 잠금 기간이
require대신if문으로 처리되었습니다. 잠금 해제 타임스탬프 이전에 호출되면, 함수는 리버트 대신 잠금 플래그 해제를 단순히 건너뛰었습니다. 이는 잠금 기간에 관계없이 언제든지 토큰을 출금할 수 있음을 의미했습니다. -
잠긴 금액이 초기화되지 않음. 호출자에게 토큰을 전송한 후, 함수는
field2(잠긴 금액)를 0으로 초기화하지 않았습니다. 이후 모든 호출은 동일한 원래 값을 읽고 동일한 금액을 다시 출금하여, 무한 출금 루프를 만들었습니다. -
개별 할당 대신 공유 잔액 확인.
balanceOf(this)호출은 호출자의 개별 할당이 아닌 컨트랙트의 전체 토큰 잔액을 검증했습니다. 소규모 잠금 포지션을 가진 호출자도 컨트랙트에 충분한 전체 토큰이 남아 있는 한 공유 풀 전체를 소진할 수 있었습니다.
이 세 가지 상태 업데이트 누락이 합쳐져 함수에서 모든 종료 조건이 제거되었습니다: 호출은 조기에 리버트되지 않고, 출금 금액은 감소하지 않으며, 잔액 확인은 컨트랙트가 완전히 비워질 때까지 차단하지 않았습니다. 이 버그는 잠금 포지션을 생성한 모든 사용자가 악용할 수 있었으며, 핵심 소진을 위해 소유자 권한은 필요하지 않았습니다.
공격 분석
DxSale 배포자 계정(0x47bacf93)은 EIP-7702 위임을 통해 운영되며, 2026-04-15부터 다수의 DxSale 컨트랙트에 걸친 대량 소유권 이전, 수수료 변경, LP 잠금 해제 작업 등 비정상적인 활동을 보였습니다. 2026-05-26에 배포자는 피해자 로커(0xeb3a...e449)의 소유권을 공격자 컨트랙트(0xc457...fa69)로 이전했습니다. 이틀 후, 공격자는 5단계 소진을 실행했습니다.
아래 분석은 대표 트랜잭션 0x437b26...c1b303을 기반으로 합니다.
-
1단계: 공격자는 PancakeSwap에서
BNB를BNBC토큰으로 스왑하고 유동성을 제공하여 새로운Cake-LP (BNBC/WBNB)쌍을 생성하여 약 0.323개의 LP 토큰을 받았습니다. -
2단계: 이전 소유권 이전을 통해 얻은 소유자 권한을 사용하여, 공격자는
changeFees(1)을 호출하여 잠금 생성 수수료를 1 wei로 설정하고,createLocker를 호출하여 약 0.323개의 LP 토큰을 새 잠금 포지션(tokenId=131)에 예치했습니다. 직후, 공격자는changeFees(10^36)을 호출하여 수수료를 천문학적인 수치로 높여 다른 사용자가 새 잠금을 생성하지 못하도록 차단했습니다. -
3단계: 공격자는 익스플로잇 함수(
0x11b432b4)를 호출하여 단일 트랜잭션 내에서 반복적인unlockToken호출을 조율했습니다. 여러 트랜잭션에 걸쳐 공격자는 체계적으로 로커를 소진했습니다: 0x437b26...c1b303은 tokenId=131을 통해 161.4개의 LP 토큰을 소진하고, 0x82f541...a9fa73과 0x5dd61f...4298aa는 추가 잠금 포지션을 소진했으며, 0xac8f5e...d36df9는 478번의 호출을 통해 tokenId=319로 260.3개의 LP 토큰을 소진했습니다. -
4단계: 공격자는 PancakeSwap에서
removeLiquidity를 호출하여 소진된 LP 토큰을 기초 토큰(BNBC와WBNB)으로 분리했습니다. -
5단계: 공격자는 PancakeSwap을 통해 기초 토큰을
WBNB와BUSD로 스왑하고 수익금을 외부 주소로 전송했습니다. BSCScan은 이 공격자 컨트랙트에 대해 4일(2026-05-28 ~ 05-31) 동안 123,447건의 트랜잭션을 기록했습니다.
결론
이 사건의 근본 원인은 unlockToken의 세 가지 상태 업데이트 누락입니다. 각각에 대한 직접적인 수정 방법이 있습니다: if 대신 require로 잠금 기간을 강제하고, 각 출금 후 잠긴 금액을 0으로 초기화하며, 컨트랙트의 전체 잔액 대신 호출자의 개별 할당을 확인하는 것입니다. 이 결함들만으로도 악용에 충분했으며, 잠금 포지션을 생성한 모든 사용자가 반복적인 호출을 통해 소유자 권한 없이 공유 풀 전체를 소진할 수 있었습니다.
EIP-7702 위임을 통한 DxSale 배포자의 사전 탈취는 익스플로잇의 전제 조건은 아니었지만, 공격자의 효율성을 높였습니다. 소유권 이전과 수수료 조작을 통해 공격자는 거의 무비용으로 잠금 포지션을 생성하고 합법적인 사용자의 경쟁 잠금 생성을 차단했습니다. 운영 측면에서, 프로토콜은 사용자 자금을 보유하는 레거시 컨트랙트를 지속적으로 감사하고, 관리 기능의 단일 키 관리를 피해야 합니다. 수년간의 사고 없는 운영이 감사받지 않은 코드의 보안을 보장하지는 않습니다.
참고자료
- [1] DxSale 공지
이번 주 추가 사건
SquidRouterModule
2026년 5월 27일, SquidRouterModule이라는 이름의 알 수 없는 컨트랙트가 부적절한 입력 유효성 검사로 인해 이더리움에서 익스플로잇되어 [1] 약 $3.2M의 피해를 입었습니다. 근본 원인은 이전의 CrossCurveFi 공격 패턴 [2]과 유사한 Axelar Bridge 통합의 오용이었습니다. 컨트랙트는 Axelar Gateway를 통한 크로스체인 메시지를 제대로 검증하지 않아, 공격자가 피해자의 토큰을 사전에 구성된 악성 풀로 승인 및 스왑하도록 허용하는 페이로드를 위조할 수 있었습니다. 가짜 토큰을 배포하고 풀에 시드한 별도의 공격자 주소가 나중에 유동성을 제거하여 실제 자산을 추출했습니다. 이 공격은 86개의 Safe 지갑에 영향을 미쳤습니다. 이름에도 불구하고, SquidRouterModule은 Squid 프로토콜 팀이 구축, 배포 또는 운영하지 않았습니다 [3].
배경
이 컨트랙트는 Axelar 크로스체인 프로토콜을 기반으로 구축된 Safe 지갑용 크로스체인 거래소 서비스 모듈입니다. 컨트랙트는 크로스체인 메시지 확인 전 트랜잭션의 조기 실행을 지원하는 expressExecuteWithToken 함수를 제공합니다. 사용자는 이 서비스를 사용하기 전에 Safe 지갑에서 컨트랙트의 APPROVE 및 SWAP 권한을 사전 승인해야 합니다. expressExecuteWithToken이 호출되면, 컨트랙트는 페이로드의 작업 지시를 디코딩하고 Safe 지갑의 execTransactionFromModule을 호출하여 토큰 승인 및 거래소 작업을 실행합니다.
취약점 분석
취약한 컨트랙트(0x1f1d...23ca)의 _executeWithToken 함수는 srcAddress가 squidRouter 상수와 같은지만 확인했습니다. 그러나 srcAddress는 호출자가 제공하는 파라미터인 반면, squidRouter는 누구나 컨트랙트에서 읽을 수 있는 불변 문자열입니다. 이로 인해 공격자는 검사를 쉽게 우회하고 임의의 페이로드 내용으로 _processPayload 함수를 실행할 수 있었습니다.

반면, 합법적인 Squid Router의 executeWithToken 함수는 gateway.validateContractCallAndMint()를 호출하고 Axelar 검증자 네트워크가 트랜잭션을 확인하지 않은 경우 NotApprovedByGateway()로 리버트합니다.

취약한 컨트랙트는 expressExecuteWithToken 경로에서 이러한 게이트웨이 승인을 강제하지 않았습니다. Safe 지갑 사용자가 모듈에 사전 승인한 APPROVE 및 SWAP 권한과 결합하여, 위조된 페이로드가 피해자의 자금을 직접 조작할 수 있었습니다.
공격 분석
아래 분석은 트랜잭션 0xd29d1c...3bb854를 기반으로 합니다.
공격 전, 별도의 공격자 주소가 가짜 토큰(
0xe6Ff...3512)을 배포하고, 각각 가짜 토큰과 다른 가치 있는 토큰(예:USDC,ENA,USDT)을 페어링한 여러 Uniswap V3 풀을 생성하여 풀에 가짜 토큰 유동성을 시드했습니다.
-
1단계: 공격자는 악성 calldata를 위조하여 취약한 컨트랙트의
expressExecuteWithToken함수를 호출하고, 알려진squidRouter주소를sourceAddress로 사용하여 검증 검사를 우회했습니다. 피해자가 모듈에 사전 승인한 APPROVE 및 SWAP 권한을 통해, 위조된 페이로드는 피해자의 Safe 지갑에서 Uniswap 풀로 토큰 승인을 강제했습니다. -
2단계: 이 강제된 승인을 사용하여, 페이로드는 해당 악성 풀을 통해 피해자의 토큰을 가치 없는 가짜 토큰으로 스왑했습니다.
공격자는 취약한 컨트랙트를 승인한 총 86개의 Safe 지갑을 대상으로 여러 트랜잭션에 걸쳐 이 방법을 반복했습니다. 가짜 토큰을 배포하고 풀에 시드한 별도의 공격자 주소가 이후 유동성을 제거하여 실제 자산을 추출하고, 최종적으로 약 $3.2M의 이익을 얻었습니다.
결론
이 사건은 크로스체인 메시지 처리 경로에서의 부적절한 입력 유효성 검사로 인해 발생했습니다. 유일한 보안 검사는 호출자가 제어할 수 있는 파라미터를 공개적으로 읽을 수 있는 상수와 비교하는 것이었으며, 실질적인 보호를 제공하지 않았습니다. Safe 지갑 사용자의 사전 승인된 APPROVE 및 SWAP 권한과 결합하여, 공격자는 크로스체인 메시지를 위조하고 사용자 자금을 탈취할 수 있었습니다.
이 사건은 CrossCurveFi 익스플로잇 [2]과 유사합니다: 둘 다 수신 크로스체인 메시지를 제대로 검증하지 못한 Axelar Bridge 통합과 관련이 있습니다. 크로스체인 메시징 인프라를 통합할 때, 목적지 컨트랙트는 브릿지의 승인 메커니즘을 통해 메시지 진위를 독립적으로 검증해야 합니다. 이 검증을 건너뛰고 공격자가 제어할 수 있는 입력에 의존하면 통합이 개방된 공격 표면으로 변합니다.
참고자료
BlockSec 소개
BlockSec은 풀스택 블록체인 보안 및 암호화폐 컴플라이언스 제공업체입니다. 저희는 코드 감사(스마트 컨트랙트, 블록체인 및 지갑 포함), 실시간 공격 차단, 사건 분석, 불법 자금 추적, AML/CFT 의무 준수를 프로토콜과 플랫폼의 전체 생애주기에 걸쳐 고객이 수행할 수 있도록 돕는 제품과 서비스를 구축합니다.
BlockSec은 저명한 컨퍼런스에서 다수의 블록체인 보안 논문을 발표하고, DeFi 애플리케이션의 여러 제로데이 공격을 보고했으며, 2,000만 달러 이상을 구제하기 위해 여러 해킹을 차단하고 수십억 달러 상당의 암호화폐를 보호했습니다.
-
공식 웹사이트: https://blocksec.com/
-
공식 트위터 계정: https://twitter.com/BlockSecTeam
-
🔗 BlockSec 감사 서비스 : 요청 제출



