Liquid 페그아웃 유출 — 뚫린 건 문이 아니라 발행이었다
블록스트림 Liquid 손실은 인가 우회가 아니었습니다 — 제 이전 추측은 틀렸습니다. Elements 발행 버그가 기술적으로 유효한 LBTC를 만들어냈고, 페그아웃 시스템은 그것을 정상적으로 인정했습니다. 문(인가)은 제대로 작동했고, 세던 대상이 발행 단계에서 오염돼 있었습니다.
구현이 아니라 정정과 체크리스트입니다. 먼저 추측을 정정 — 이건 접근제어 결함(제 이전 판단)이 아니라, 페그아웃 로직이 유효하다고 인정한 LBTC를 만들어낸 Elements 발행 버그였습니다. 그다음, 공급량을 근거로 소각/상환/정산하는 시스템을 감사하세요: 하류 어딘가가 '이게 유효한 단위인가?'만 믿고 '이 단위가 불변식에 따라 발행됐는가?'는 안 믿는 곳이 있는가? 두 질문을 서로 다른 코드가 답하는 지점을 전부 적으세요 — 그 틈이 이 부류가 사는 곳입니다. 구체 수치는 블록스트림 사고 공지와 온체인 OP_RETURN / PGP 협상 기록으로 확인하세요.
기술 노트
각 항목이 실제로 무엇을 보여주고 어떻게 동작하는지 — 위 카드보다 자세한 기술 설명입니다.
목적: 정정이 곧 핵심입니다. 저는 처음에 이걸 접근제어 결함으로 봤습니다 — 출금을 막았어야 할 검사가 안 막은 것. 그건 틀렸고, 틀린 방식이 바로 교훈입니다. 인가는 작동했습니다. 페그아웃 로직은 옳은 질문('이게 유효한 LBTC인가?')을 던졌고 정직한 '예'를 받았습니다. 문제는 상류에 있었습니다 — Elements 소프트웨어의 발행 버그가 존재해선 안 될 LBTC를 만들었고, 하류의 모든 검사는 그것을 충실히 인정했습니다. 그 검사들이 강제하는 규칙상 그건 진짜였으니까요. 그래서 실패의 축은 '누가 무엇을 하도록 허용됐는가'가 아닙니다. '세고 있는 대상이 불변식에 따라 발행됐는가'입니다. 이 둘은 서로 다른 질문이고 대개 서로 다른 코드가 답하며, 시스템은 첫 번째를 완벽히 통과하면서 두 번째가 이미 깨져 있을 수 있습니다. 페그는 이게 가장 아픈 자리입니다 — 상품 전체가 '온체인 공급량 = 오프체인 준비금'이라는 약속인데, 발행 버그는 그 등식을 조용히 깹니다. 토큰은 유효하기 때문에 유효해 보이지만, 다만 뒷받침이 없습니다. 이건 권한 감사가 무엇을 잡고 무엇을 못 잡는지를 다시 정의합니다. 권한 행렬(누가 발행/정지/업그레이드할 수 있는가)은 필요조건이지 충분조건이 아닙니다 — 올바른 주체가 올바른 권한을 쥐었음은 증명하지만, 그 주체가 돌리는 코드가 공급 불변식을 보존하는지는 증명하지 않습니다. 올바른 권한이 버그 있는 발행을 실행하면 유효해 보이지만 뒷받침 없는 단위가 나오고, 어떤 접근제어 검토도 이를 잡지 못합니다 — 접근제어는 위반된 적이 없기 때문입니다.
동작 방식: ### 두 개의 질문, 서로 다른 두 코드 | 질문 | 누가 답하나 | Liquid의 결과 | |---|---|---| | **인가** — 이 주체가 페그아웃해도 되는가? | 페그아웃 게이트 | 정상 작동 | | **공급 무결성** — 이 LBTC가 페그 불변식에 따라 1:1로 발행됐는가? | Elements 발행 경로 | **깨짐 — 유효해 보이는 무효 단위 생성** | 유출은 전적으로 두 번째 행을 타고 일어났습니다. 페그아웃은 속은 적이 없습니다 — 기술적으로 유효한 토큰을 건네받아 제 일을 했을 뿐입니다. ### '유효한 토큰'이 들리는 것보다 약한 주장인 이유 Liquid은 연합형 비트코인 사이드체인입니다: LBTC는 연합이 보유한 BTC와 1:1이어야 하고, 페그인 때 발행되고 페그아웃 때 소각됩니다. '유효한 LBTC'는 체인의 규칙을 만족한다는 뜻입니다. '올바르게 발행된 LBTC'는 실제 페그인에 대응해 발행됐다는 뜻입니다. 발행 버그는 이 둘을 갈라놓습니다 — 모든 유효성 검사를 통과하지만 뒷받침이 없는 단위. 하류에서는 이를 구분할 방법이 없어, 페그아웃은 다른 것과 똑같이 인정합니다. ### 협상이 특이한 부분이었다 공격자는 사라지는 대신 온체인에서 협상했습니다 — OP_RETURN과 PGP 서명 메시지로 조건을 걸었습니다: *버그부터 고치고 모든 노드를 패치하라.* 블록스트림도 PGP 서명으로 답했고 — *브릿지 노드 패치 완료, 반환해도 안전하다* — 공격자는 대부분을 반환하겠다고 신호했습니다. 이 글을 쓰는 시점에 반환은 완료되지 않았습니다. 모양이 주목할 만합니다: 체인 자체의 데이터 필드가 협상을 실어 날랐고, 브릿지에는 양측 사이에 다른 신뢰 채널이 없으므로 PGP 서명이 신원을 대신했습니다. ### 이 협상은 더 큰 두 번째 문제를 가리킨다 — 누가 이 메시지를 보냈는가 온체인 협상은 크립토 이야기지만 함의는 넓습니다: **어떤 메시지가 정말 그 조직에서 나왔음을 어떻게 증명하는가.** OP_RETURN은 말을 실었고 PGP 서명은 *신원*을 실었습니다 — 브릿지에는 다른 신뢰 채널이 없기 때문입니다. 이 목록이 2026-08-01에 기록한 '탈취된 임원 계정으로 RWA 토큰을 홍보한' 사건은 공격자 쪽에서 본 같은 축이었고, 여기서는 *방어자*의 필요로 돌아옵니다. 그때 적은 관찰이 지금 실물로 확인됩니다: **검증된 신원의 공식 발표 채널**(서명된 공지·온체인 attestation)에 대한 실수요가 있고, AI 생성 콘텐츠가 늘수록 그 수요는 커집니다. PGP 서명된 OP_RETURN은 바로 그 채널의 조악한 버전입니다. ### Jayverse에서의 자리 - **토큰 브릿지**(jayverse-token-bridge)는 1:1 lock/mint 금고입니다 — 안전 주장 전체가 이번에 깨진 그 공급 불변식입니다. 불변식 테스트('목적지 발행량 == 출발지 잠금량, 항상')가 직접적 방어이며, 인출 게이트가 아니라 발행 경로에 붙어야 합니다. - **권한 감사기**(#8)는 권한을 증명하지 발행의 정확성을 증명하지 않습니다 — 권한 행렬이 필요조건이지 충분조건이 아닌 이유의 구체 사례이며, 문서가 이를 말해야 합니다. - **보안 취약점 연구**(#10 Dark Horse) — 이 부류를 로컬 포크에서 재현: 유효하지만 뒷받침 없는 단위를 만드는 발행 버그와 그걸 인정하는 상환 경로. 이를 잡는 불변식은 접근제어가 아니라 보존(conservation)입니다. ### 한 줄 요약 주체를 검사하는 것과 대상을 검사하는 것은 다릅니다. 대상이 잘못 발행됐을 때, 올바른 검사는 오염된 단위를 충실히 승인합니다 — 문은 멀쩡했고, 세는 대상이 오염돼 있었습니다.
검토 후 보완: ### 사건에서 불변식으로 — 운영 가능한 형태로 위 섹션은 *실패*(인가가 아니라 공급 무결성)입니다. 여기는 *방어*를 돌아가는 계층으로 본 것입니다 — '불변식 테스트를 추가하라'는 말은 쉽고 잘못 만들기도 쉽기 때문입니다. **좋은 불변식은 세 성질을 갖고, 사람들이 빼먹는 건 세 번째입니다.** (1) *독립적으로 계산 가능* — 시스템 자체 장부를 믿지 않고 도출되어야 함, 아니면 페그아웃이 믿은 그 거짓말을 다시 읽을 뿐; (2) *위반이 곧 무의미* — `준비금 = 발행량`이 거짓이면 하위 숫자 전부가 무효라, 절뚝이며 굴러갈 '저하 모드'가 없음; (3) *자동 정지에 연결* — 경보는 사람이 알아채 달라는 요청인데, 실패의 요점은 사람이 못 알아챘다는 것. 준비금 = 발행량은 (1)(2)를 공짜로 갖고도 아무것도 확인하지 않았습니다 — (3)이 빠진 것. **감시자는 감시 대상이 닿을 수 없는 곳에 두세요.** 불변식 감시자가 발행/정산 경로와 인프라·신뢰 경계를 공유하면, 하나를 뚫는 침해가 둘 다 뚫습니다 — 감시자가 대상과 함께 죽습니다. 느슨한 임계값이 아니라 좁은 *정지 범위*로 조율하세요: 자주 울리는 감시자는 꺼지고, 꺼진 감시자는 믿어졌기에 없느니만 못합니다. **앞에서 시뮬레이션, 뒤에서 검사 — 하나의 규율.** 불변식은 *등식이 방금 깨졌나*를, 실행 전 시뮬레이션은 *이 트랜잭션이 그걸 깰까*를 묻습니다 — 현재 상태에 트랜잭션을 재생해 서명 전에 상태 diff를 읽는 것. Tenderly(또는 CI의 Foundry 포크) 같은 도구는 그걸 발행 트랜잭션 자체에 대한 검사로 만듭니다 — '총발행량이 예기치 않게 변함' 경보 하나가, 페그아웃이 그 토큰을 보기도 전에 잡습니다. ### 우리 시스템을 위한 범위 잡기 항상 성립해야 하고 독립적으로 계산 가능한 모든 등식을 나열하고, 각각의 자동 정지를 적으세요: | 불변식(항상 성립) | 독립 계산 근거 | 위반 시 멈추는 것 | |---|---|---| | 목적지 발행 = 출발지 잠금 | 출발지 금고 + 목적지 공급량 | 신규 발행 / 인출 | | 정산합계 = 마켓별합계 | 마켓 장부 합산 | 정산 쓰기 | | 에스크로 잔고 >= 미결제 청구액 | 에스크로 계정 + 청구 레지스트리 | 신규 청구 | 교차: `foundry-invariant-reachability`(테스트 시점의 같은 생각 — 불변식 테스트는 자기 액션이 닿는 것만 잡는다), `simulate-before-sign`(실행 전 절반), `who-holds-the-mint`(발행은 불변식이 묶어야 할 바로 그 특권 쓰기), `receipt-is-not-settlement`(성공 플래그는 상태가 건전하다는 증명이 아니다).