지난 한 주(2026/09/07 - 2026/09/13) 동안 총 예상 손실액 약 3억 2,000만 달러에 달하는 2건의 보안 사고가 발생했습니다.
| 날짜 | 사고 | 유형 | 예상 손실액 |
|---|---|---|---|
| 2026/09/06 * | Liquid Network | 결함이 있는 캐시 키 구성 | ~$320M |
| 2026/09/11 | Symbiosis | 결함이 있는 오프체인 예치 검증 | ~$770K |
*Liquid Network 사고는 9월 6일에 발생했으며 지난주 리포트에서는 다루지 않았습니다. 완전성을 위해 여기에 포함했습니다.
Web3를 위한 최고의 보안 감사업체
출시 전 설계, 코드, 비즈니스 로직을 검증하세요
주간 하이라이트: Liquid Network
Liquid Network 사고는 미묘한 캐시 키 충돌 메커니즘과 그로 인해 발생한 막대한 손실 때문에 선정되었습니다. 공격자는 검증 데이터가 이전 트랜잭션의 바이트를 다시 분할하도록 트랜잭션을 조작하여, 한 번도 검사되지 않은 증명에 대해 캐시된 검증 결과가 반환되도록 했습니다.
2026/09/06, 비트코인 사이드체인인 Liquid Network가 약 3억 2,000만 달러 규모의 공격을 당했습니다 [1]. Liquid가 실행하는 노드 소프트웨어인 Elements의 rangeproof 검증 캐시에서 발생한 충돌로 인해, 한 출력에 대해 기록된 valid 판정이 전혀 다른 두 번째 출력에 대해 반환될 수 있었고, 그 결과 두 번째 출력의 증명은 한 번도 검사되지 않은 채 승인되었습니다. 공격자는 이를 이용해 어떤 비트코인으로도 뒷받침되지 않는 4,000 L-BTC를 생성한 뒤, 네트워크의 일반적인 페그아웃을 통해 이를 인출했습니다. 다음 날 3,400 BTC가 연합(federation)에 반환되었고, 약 598.5 BTC는 공격자에게 남았습니다 [2].
배경
Liquid Network는 비트코인 코드베이스에서 파생된 오픈소스 블록체인 플랫폼인 Elements 위에 구축된 비트코인 사이드체인입니다. 메인체인보다 더 빠르고, 더 저렴하고, 더 사적으로 비트코인을 이동시키기 위해 존재합니다. 비트코인은 양방향 페그(two-way peg)를 통해 두 네트워크 사이를 오갑니다. 페그인(peg-in)에서는 사용자가 페그 자금을 공동으로 관리하는 검증된 조직들로 구성된 고정 그룹인 네트워크의 연합(federation)에 BTC를 예치하고 동일한 양의 L-BTC를 받습니다. 페그아웃(peg-out)에서는 사용자가 L-BTC를 소각하면 연합이 그에 상응하는 BTC를 방출합니다. Liquid 블록은 채굴되지 않습니다. 연합의 기능 노드(functionary node) 중 순환하는 집합이 블록을 제안하고, 연합 서명의 임계값이 이를 최종 확정합니다.
비트코인과 마찬가지로 Liquid는 자금을 계정 잔액이 아니라 미사용 트랜잭션 출력(UTXO)으로 추적합니다. 트랜잭션은 기존의 미사용 출력을 지정하고, 이를 잠금 해제한 뒤 새로운 출력을 생성하며, 생성되는 가치는 소비되는 가치와 같아야 합니다. 모든 출력은 잠금 스크립트인 scriptPubKey를 가집니다. 스크립트가 OP_RETURN으로 시작하는 출력은 결코 사용될 수 없습니다. 이는 데이터를 담기 위해 존재하며, 페그아웃은 정확히 그런 종류의 출력으로 표현됩니다.
Liquid는 또한 기본적으로 금액을 숨깁니다. 출력에 수치를 직접 기록하는 대신, 그 수치에 대한 Pedersen 커밋먼트를 기록합니다. 커밋먼트는 가산적(additive)이기 때문에, 노드는 관련된 어떤 값도 알지 못한 채로 트랜잭션의 입력과 출력이 균형을 이루는지 확인할 수 있습니다. 이 속성은 양날의 검이기도 합니다. 커밋먼트는 마치 음수처럼 동작하는 값도 얼마든지 숨길 수 있어서, 트랜잭션의 출력이 입력을 초과하면서도 여전히 균형을 이루도록 만들 수 있습니다. 따라서 금액이 숨겨진 모든 출력은 커밋된 금액이 [0, 2^64) 범위 내에 있음을 증명하는 rangeproof를 가집니다. rangeproof를 검증하는 것은 비용이 많이 들고, 같은 출력이 두 번 이상 검증됩니다 — 트랜잭션이 멤풀에 들어올 때, 그리고 블록에 도착할 때 다시 한번. 그래서 Elements는 증명과 그것이 검사된 데이터를 키로 하여, 이미 승인한 증명들의 캐시를 유지합니다.
취약점 분석
결함은 모든 Liquid 참가자가 실행하는 노드 소프트웨어인 Elements가 이미 검증된 rangeproof를 캐싱하는 방식에 있습니다.
CachingRangeProofChecker::VerifyRangeProof()는 앞에 있는 출력에 대한 캐시 항목을 도출하고, 캐시가 적중(hit)하면 증명을 건드리지 않고 즉시 성공을 반환합니다:

이 항목은 ComputeEntryRangeProof()에서 생성되는데, 이 함수는 네 개의 필드를 하나씩 순서대로 단일 SHA-256 스트림에 기록하고 다이제스트를 확정합니다:

네 개의 필드는 rangeproof 자체, 금액에 대한 Pedersen 커밋먼트, 출력이 보유한 자산을 식별하는 자산 생성자(asset generator), 그리고 승인된 증명이 특정 잠금 스크립트에 결속되도록 포함된 scriptPubKey입니다.
네 필드 중 어느 것도 길이 접두사(length prefix)나 구분자(separator)를 가지고 있지 않습니다. rangeproof와 scriptPubKey는 길이가 가변적이며, Pedersen 커밋먼트와 자산 생성자는 항상 33바이트 포인트입니다. 따라서 다이제스트는 하나의 필드가 끝나고 다음 필드가 시작되는 지점을 표시하는 것 없이, 그저 연결된 바이트만을 기록합니다. 우연히 동일한 바이트 스트림으로 연결되는 서로 다른 두 세트의 네 필드는 동일한 항목을 생성하며, 먼저 검증된 쪽이 다른 쪽이 가져갈 판정을 남기게 됩니다. 해시 함수는 노드별 무작위 솔트(salt)로 시딩되지만, 이 솔트는 두 스트림 모두에 동일하게 접두어로 붙기 때문에 다이제스트를 변화시킬 뿐 두 입력을 구별해내지는 못합니다. 이 결함은 커밋 94000967에서 수정되었습니다 [3].
공격 분석
다음 분석은 취약한 코드를 실행 중인 노드들이 승인한 두 트랜잭션 271147...187ec5 및 f24a4b...0a183f를 기반으로 합니다.
- 1단계: 공격자는 첫 번째 출력이 67바이트의 데이터를 담은
OP_RETURN인 트랜잭션을 게시했습니다.
이 출력의 scriptPubKey는 6a 43 — OP_RETURN 옵코드 뒤에 67바이트를 푸시하는 명령 — 그다음 두 개의 33바이트 값, 마지막에 6a로 이루어져 있습니다. 이 푸시된 67바이트는 이 트랜잭션에 필요한 데이터가 아닙니다. 이는 아직 존재하지 않는 어떤 출력의 값 커밋먼트와 자산 생성자입니다. 이 출력을 검증함으로써 그 자체의 네 필드에 대한 항목 아래에 valid 판정이 저장되었습니다.
- 2단계: 그런 다음 공격자는 인플레이션 트랜잭션을 제출했습니다. 이 트랜잭션의
OP_RETURN출력은 단일 바이트6a로 이루어진scriptPubKey를 가지며, 그 값 커밋먼트는 이전 트랜잭션의 푸시에 내장된 33바이트 값과 정확히 일치합니다. 이 출력의 rangeproof 필드는 이전 트랜잭션의 rangeproof, 커밋먼트, 자산 생성자에 이어 두 바이트6a 43을 붙여서 구성되었습니다.

따라서 이 출력의 네 필드는 첫 번째 트랜잭션과 동일한 바이트 스트림으로 연결되며, 단지 다른 경계선을 따라 다시 나뉘게 됩니다:
Primer: P0 │ C0 │ X │ S0, where S0 = 6a 43 <33-byte C1> <33-byte X> 6a
Inflation: P1 │ C1 │ X │ S1, where P1 = P0 ‖ C0 ‖ X ‖ 6a 43 and S1 = 6a
Both concatenate to: P0 ‖ C0 ‖ X ‖ 6a 43 ‖ C1 ‖ X ‖ 6a
캐시는 이전 판정을 반환했고, P1은 전혀 검증되지 않았습니다 — 이는 애초에 rangeproof가 아닙니다. C1은 작은 음수 금액에 대한 커밋먼트입니다. 이 트랜잭션의 다른 출력, 즉 공격자 주소 ex1q7kg...qa2w로 향하는 일반적인 pay-to-witness-public-key-hash 출력은 진짜 rangeproof를 가진 큰 양수 커밋먼트를 담고 있었으며, 두 값이 서로 상쇄되어 트랜잭션의 균형이 맞춰졌습니다. 그 결과 어떤 비트코인으로도 뒷받침되지 않는 4,000 L-BTC의 미사용 출력이 남게 되었습니다.
- 3단계: 공격자는 페그아웃을 수행했습니다. 두 개의 트랜잭션이 일반적인
OP_RETURN페그아웃 출력을 통해 각각3,996.01834922와2.65138358L-BTC, 합계3,998.66973280L-BTC를 소각했습니다. 페그아웃을 승인하는 기능 노드들은 동일한 취약한 코드로 이를 검증하고 비트코인을 방출했습니다:3,995.99999857BTC와2.49749857BTC가 비트코인 상에서 공격자에게 도달했으며, 합계는3,998.49749714BTC였습니다.
사고가 해결되는 동안 네트워크는 일시 중단되었다가 두 개의 체인으로 분기되었으며 [4], 유효하지 않은 페그아웃이 거부된 상태로 재개되었습니다 [2]. 오늘 블록 탐색기를 보면 primer 트랜잭션은 확정되어 있지만 인플레이션 트랜잭션은 확정되지 않은 것으로 나타나는데, 이는 이 트랜잭션이 방출한 비트코인이 이미 연합의 지갑을 떠난 뒤였음에도 그렇습니다. 다음 날 UTC 16:09:25에 공격자는 연합의 페그 지갑으로 3,400.00000000 BTC를 반환했고, 남은 약 598.5 BTC는 미반환 상태로 유지되었습니다 [2]. 양측은 비트코인 트랜잭션에 내장된 메시지를 통해 협상을 진행했습니다 [4].
결론
공격자는 어떤 암호화 체계도 깨뜨리지 않았고 어떤 키도 탈취하지 않았습니다. 결함은 rangeproof 캐시가 이미 검사한 것을 식별하는 방식에 있었습니다. 그 키는 네 개의 필드 — 그중 두 개는 길이가 가변적인 — 를 각 필드가 어디서 끝나는지 표시하는 것 없이 하나의 해시 스트림으로 연결했고, 그 결과 두 번째 필드 세트가 동일한 바이트를 다시 분할하여 같은 항목에 도달할 수 있었습니다. 저장된 valid 판정은 어떤 노드도 실제로 검증한 적 없는 증명에 대해 반환되었으며, rangeproof가 그 뒤에 있는 금액을 더 이상 제약하지 못하는 순간, 기밀 원장은 숨겨진 값들을 음수가 되지 않도록 유지해주는 유일한 장치를 잃게 됩니다. 조작된 하나의 출력이 4,000 L-BTC가 되었고, 양방향 페그는 이를 메인체인 상의 비트코인으로 바꾸었습니다.
둘 이상의 가변 길이 입력에서 파생된 식별자는 그 사이의 경계를 반드시 확정해야 합니다. 각 필드에 길이를 접두어로 붙이든, 필드들을 별도의 고정 크기 슬롯으로 해시하든 상관없습니다. 그렇지 않으면 그 도출 과정은 단사(injective)적이지 않으며, 서로 다른 두 입력이 하나로 오인될 수 있습니다. 이 요구사항은 캐시가 검사를 대신하는 모든 곳에서 가장 엄격하게 적용되어야 합니다. 왜냐하면 그곳에서 캐시 적중은 곧 검사를 실행하지 않기로 하는 결정이기 때문입니다.
이번 주의 다른 사고들
Symbiosis
2026/09/11, Symbiosis 크로스체인 브리지의 비트코인 경로가 BNB Smart Chain, Ethereum, Rootstock 배포 전반에 걸쳐 공격을 받아, 유동성 공급자와 사용자들에게 약 9.97 BTC(9월 11일 기준 약 7만 7,000달러 가격으로 환산 시 약 77만 달러)의 손실을 입혔습니다 [5]. 비트코인 예치에서 얼마나 많은 합성 비트코인을 발행할지 결정하는 오프체인 코드가 예치자의 신원을 예치자 자신이 통제할 수 있는 트랜잭션 부분에서 가져왔고, 이로 인해 공격자가 관리자로 위장하여 최소 수수료를 0 미만으로 낮출 수 있었습니다. 그런 다음 이 코드는 부호를 확인하지 않은 채 그 수수료를 예치액에서 빼버렸고, 그 결과 뺄셈이 오히려 더하기가 되었습니다.
배경
Symbiosis는 자산 자체를 이전하는 대신 잠금(lock)과 발행(mint)을 통해 체인 간 가치를 이동시키는 크로스체인 유동성 프로토콜입니다. 스마트 컨트랙트가 있는 체인에서는 사용자의 토큰이 소스 체인의 Portal 컨트랙트에 의해 잠기고, Synthesis 컨트랙트가 호스트 체인에서 동일한 양의 합성 토큰(sToken)을 발행합니다. sToken을 소각하면 원본 자산이 방출됩니다 [6]. 비트코인은 이 경로를 통해 syBTC로 들어오는데, 이는 비트코인 자체의 최소 단위와 일치하는 8자리 소수점을 가진 토큰으로, BNB Smart Chain, Ethereum, Rootstock에서 발행되며 BTCB, cbBTC, WBTC, RBTC와 짝을 이루어 풀에서 거래됩니다 [5].

비트코인에는 그런 것이 전혀 없습니다. 스마트 컨트랙트가 없으니 예치금을 잠글 Portal 컨트랙트도 없습니다. 그 자리를 대신하는 것이 포털(portal)입니다 — 동일한 역할을 수행하는 지정된 비트코인 주소입니다. 예치는 목적지 체인을 위한 지시사항이 데이터로 첨부된 일반적인 비트코인 트랜잭션으로, 이 주소로 지불됩니다. 브리지에 속한 오프체인 코드는 그 데이터를 읽어 누가, 얼마를, 어디로 예치하는지, 그리고 합성 토큰이 어디로 가야 하는지를 파악하고, 발행할 금액이 확정되기 전에 예치된 금액에서 차감되는 포털의 수수료를 적용하며, 이 수수료의 최소값은 관리자가 설정하는 매개변수입니다.
비트코인 쪽에서 일어나는 일은 목적지 체인에서 전혀 보이지 않기 때문에, Relayers Network가 그 결과로 생성된 요청을 전달합니다. 이 요청은 브리지의 온체인 진입점인 BridgeV2에 인코딩된 호출로 도착하며, 이는 Synthesis로 전달됩니다. 권한 부여는 MPC 키를 기반으로 합니다: 프로토콜의 서명자들은 단일 개인 키의 조각(share)을 보유하고 있으며, 함께 요청에 대한 하나의 서명을 생성합니다. 릴레이된 요청의 진입점은 단일 modifier만을 가지고 있으며, 호출을 전달하기 전에 다른 어떤 작업도 하지 않습니다:

이 modifier는 요청을 해시하고, 동반된 서명이 MPC 주소에 대해 유효할 것을 요구합니다:

이 컨트랙트가 확립하는 것은 MPC 키가 요청에 서명했다는 사실뿐입니다. 요청이 담고 있는 금액은 비트코인 쪽에서 계산된 것입니다.
취약점 분석
결함은 목적지 체인의 컨트랙트에 있지 않습니다. 이는 비트코인 예치를 읽어 크로스체인 요청으로 변환하는 오프체인 서비스에 있습니다. 그 소스 코드는 공개되어 있지 않으므로, 아래 설명은 팀의 사후 분석 보고서 [5]와 관련되어 보이는 코드 스니펫이 담긴 2024년의 제3자 감사 보고서 [7]를 바탕으로 작성되었습니다.
예치 경로에서 두 가지 결함이 함께 맞아떨어져야 했으며, 사후 분석 보고서는 어느 쪽도 단독으로는 충분하지 않았다고 명시합니다.
첫 번째는 예치자를 식별하는 방식에 있습니다. 예치에 첨부된 지시사항은 디코딩되어 누가 예치를 하는지를 알아내는데, 이 디코더는 그 신원을 트랜잭션 데이터 중 입력을 소비하는 자가 통제할 수 있는 부분에서 가져왔습니다. 따라서 예치자는 프로토콜이 인식하는 어떤 당사자로든, 심지어 포털이 받아들일 최소 수수료를 설정하는 관리자 역할로도 자신을 지칭할 수 있었습니다.
두 번째는 그 수수료가 적용되는 방식에 있습니다. 2024년 감사는 decodeWrap()에서 예치 출력의 값에서 수수료를 빼서 발행할 금액을 계산하는 다음 라인을 보고했습니다:
Value: types.Satoshi(tx.TxOut[idx].Value) - info.PortalFee,
그 발견 내용은 types.Satoshi가 부호 있는 정수 타입인 반면 PortalFee를 담고 있는 info 구조체는 신뢰할 수 없고 사용자가 제어할 수 있어서, 결과가 음수로 유도될 수 있고, 목적지 체인을 위해 부호 없는 값으로 직렬화되면서 임의 수량의 합성 비트코인을 발행할 수 있을 만큼 큰 값이 된다는 것이었습니다. 감사 당시에는 이 문제가 수정된 것으로 기록되었습니다 [7]. 2026년의 사후 분석 보고서는 동일한 종류의 실패를 설명합니다: 수수료가 부호 확인 없이 예치액에서 차감되었고 [5], 그 결과 0 미만의 수수료가 예치액을 줄이는 대신 오히려 늘렸습니다. 예치 경로의 어디에도 발행할 금액을 실제로 수령한 비트코인으로 제한하는 부분은 없는 것으로 보입니다.
공격 분석
BNB Smart Chain, Ethereum, Rootstock 전반에 걸쳐 약 4분 만에 열두 건의 위조된 발행(mint)이 이루어졌습니다 [5]. 다음 분석은 그중 하나인 BNB Smart Chain 상의 트랜잭션 0x9a2bc0...21b9b959를 기반으로 합니다.

-
1단계: 공격자는 포털 관리자로 위장하여 포털이 받아들이는 최소 수수료를 0 미만으로 낮췄고, 이로써 음수 수수료를 신고하는 예치가 거부되지 않고 처리되도록 만들었습니다 [5].
-
2단계: 공격자는
0x000000000000014a로 표시되는 330사토시를 예치했습니다. BNB Smart Chain에 도달한 요청은0x400000000000014a를 담고 있었는데, 이는2^62에 해당하는 비트가 설정된 동일한 값으로, 기본 단위로4,611,686,018,427,388,234에 해당합니다. 두 값의 차이는 정확히2^62이므로, 이 예치에서 차감된 수수료는-4,611,686,018,427,387,904사토시였습니다:330 - (-4,611,686,018,427,387,904) = 4,611,686,018,427,388,234이 형태로 요청에 서명이 이루어졌고 요청이 릴레이되었습니다.
-
3단계:
receiveRequestV2Signed()는 요청에 대한 MPC 서명을 검증하고 이를 전달했으며, 결국metaMintSyntheticTokenBTC()에 도달했습니다. 이 함수는 소스 체인 ID에 대한 실제 토큰의 합성 표현인syBTC를 결정하고, 서명된 전체 금액에 대해synthesize()를 호출했습니다. 전체 금액은 공격자의 주소0x025122...5d3Ba2로 이체되었습니다:46,116,860,184.27388234 syBTC. -
4단계: 공격자는 이 합성 토큰을 이를 보유한 풀에 매도했습니다. Uniswap v4의
syBTC/WBTC풀을 통한 단일 스왑이 위 트랜잭션에서 발행된 양의 약 네 배에 달하는18,446,744,072,845,450,682기본 단위의syBTC를 처리했고, 그 대가로4.38897292 WBTC를 얻었습니다.

그 이후 경로를 거쳐, 이 스왑의 수익은 약 137 ether에 달했습니다.
팀은 비트코인 경로를 일시 중단했고, 몇 시간 안에 포털 자금 중 약 15.2 BTC가 예비(reserve) 주소로 옮겨졌습니다 [5]. 반환되는 금액의 20%를 바운티로 제공하겠다는 제안이 나왔으며, 이는 9월 13일까지 유효했습니다 [8]. 프로토콜의 다른 경로들은 영향을 받지 않았습니다.
결론
키가 탈취되지도 않았고 어떤 컨트랙트도 작성 의도와 다른 코드를 실행하도록 속아 넘어가지도 않았습니다. 목적지 체인이 확인한 서명은 진짜였습니다. 그 서명이 승인한 것은 오프체인 코드가 이미 두 가지 서로 다른 방식으로 잘못 산출해낸 숫자였습니다. 하나는 예치자의 신원을 예치자 자신이 통제할 수 있는 필드에서 읽어와, 공격자가 포털 관리자로 위장하여 최소 수수료를 0 미만으로 낮출 수 있게 만든 것이고, 다른 하나는 그 수수료를 부호 확인 없이 예치액에서 차감하여, 음수 수수료가 예치액을 줄이는 대신 오히려 늘리게 만든 것입니다.
신뢰할 수 없는 입력에서 읽어낸 신원은 신원이 아닙니다: 권한 있는 역할은 예치자가 임의로 선택할 수 없는 무언가, 예를 들어 소비된 입력을 실제로 승인한 주소가 프로토콜이 유지하는 목록과 대조되는 방식으로만 확립되어야 하며, 예치자가 자유롭게 채워 넣을 수 있는 필드로 확립되어서는 안 됩니다. 차감되는 값에는 양쪽 모두에 대한 경계가 필요합니다: 수수료는 0과 예치액 사이의 범위 내에 있도록 강제되어야 하며, 발행할 금액은 소스 체인이 실제로 수령한 금액을 절대 초과해서는 안 됩니다. 이 두 경계 중 어느 하나만 있었어도 이번 사고는 단독으로 막을 수 있었을 것입니다.
참고 자료
[1] https://x.com/Liquid_BTC/status/2096696272447218108
[2] https://x.com/Liquid_BTC/status/2097404704028545175
[3] https://github.com/ElementsProject/elements/commit/94000967f6dc05b1afd435e79b1bbc597e29f816
[4] https://www.coindesk.com/markets/2026/09/08/white-hat-hackers-return-most-of-usd320m-bitcoin-taken-from-liquid-network
[5] https://x.com/symbiosis_fi/status/2099566361940795831
[6] https://docs.symbiosis.finance/crosschain-liquidity-engine/synthesizing-process
[7] https://github.com/symbiosis-finance/audits/blob/master/Symbiosis%20Relayers%20Network/Symbiosis%20Relayers%20Network%202024%20-%20Decurity.pdf
[8] https://x.com/symbiosis_fi/status/2098442463358718264
BlockSec 소개
BlockSec은 풀스택 블록체인 보안 및 암호화폐 컴플라이언스 제공업체입니다. 저희는 고객이 프로토콜 및 플랫폼의 전체 수명 주기에 걸쳐 코드 감사(스마트 컨트랙트, 블록체인, 지갑 포함)를 수행하고, 실시간으로 공격을 차단하며, 사고를 분석하고, 부정한 자금을 추적하고, AML/CFT 의무를 준수할 수 있도록 돕는 제품과 서비스를 구축합니다.
BlockSec은 권위 있는 학회에서 다수의 블록체인 보안 논문을 발표했으며, DeFi 애플리케이션의 여러 제로데이 공격을 신고했고, 여러 해킹을 차단하여 2,000만 달러 이상을 구제했으며, 수십억 달러 규모의 암호화폐를 보호해왔습니다.
-
공식 웹사이트: https://blocksec.com/
-
공식 트위터 계정: https://twitter.com/BlockSecTeam



