“콜드월렛에서 ZIL이 도난당했다”는 첫 공지만 보면 장비가 탈취됐거나 복구 문구가 유출된 사건을 떠올리기 쉽습니다. 이번 일은 달랐습니다. 개인키를 장치 밖으로 직접 꺼내지 않고도, 장치가 과거에 만들어 공개한 서명만 모아 개인키를 계산할 수 있었습니다.

문제는 Ledger 하드웨어 전체나 Zilliqa 네트워크 자체가 아니라, Zilliqa Ledger 앱이 네이티브 ZIL 거래에 서명하는 과정에 있었습니다. 콜드월렛의 안전성을 이야기할 때 보관 방식뿐 아니라 서명 구현까지 봐야 하는 이유가 여기에 있습니다.

진행 중 | 2026년 7월 23일 15:05 KST 기준. 조사·복구 계획과 서비스 상태는 달라질 수 있습니다. 실제 조치 전 Zilliqa와 이용 중인 거래소의 공식 공지를 확인해야 합니다.

먼저, 영향 범위부터 분리해서 봐야 합니다

구분 2026년 7월 23일 현재 확인된 내용
영향 대상 Ledger 기기에서 Zilliqa Ledger 앱으로 서명해 전파한 네이티브(non-EVM) ZIL 거래
위험 판단 기준 Zilliqa는 이런 거래가 약 5건 이상 기록된 계정을 개인키가 노출된 것으로 간주해야 한다고 설명
영향이 없다고 발표된 범위 EVM 거래, zilliqa-js, gozilliqa-sdk, pyzil에서 생성한 서명
아직 공개되지 않은 내용 최초 피해 거래소의 이름, 도난 수량, 최종 복구 절차와 전체 사후 보고서

여기서 “약 5건”은 안전과 위험을 가르는 보증선이 아닙니다. 다섯 건보다 적으면 괜찮다는 뜻이 아니라, Zilliqa가 그 정도의 공개 서명만으로도 개인키 복원이 가능하다고 확인했다는 뜻에 가깝습니다.

문제는 키 보관이 아니라 서명용 일회성 난수 k였습니다

Zilliqa의 네이티브 거래는 secp256k1 곡선 위의 EC-Schnorr 서명을 사용합니다. 서명 과정에는 개인키 d 외에 거래마다 새로 뽑아 한 번만 쓰는 비밀 숫자 k가 들어갑니다. 암호학 문헌에서는 이를 ‘서명 nonce’라고도 부르지만, 이 글에서는 계정이 보낸 거래의 순번인 ‘트랜잭션 nonce’와 혼동하지 않도록 **서명용 k**라고 부르겠습니다.

Zilliqa 구현의 핵심 관계를 단순화하면 다음과 같습니다.

s = k - r·d  (mod n)

r과 s는 서명의 일부로 공개되고, d는 숨겨야 할 개인키입니다. k가 256비트 범위에서 충분히 무작위로 뽑힌다면 서명을 여러 개 봐도 d를 알아내기 어렵습니다. 반대로 k가 재사용되거나 일정한 방향으로 치우치면, 공개된 서명마다 개인키에 대한 단서가 남습니다.

이번 오류는 k를 만드는 장치 자체보다 만들어진 값을 옮겨 담는 구간에서 발생했습니다. Zilliqa의 설명에 따르면 앱은 40바이트의 난수를 생성해 곡선의 차수로 나눈 뒤 32바이트 k 버퍼에 복사했습니다. 이때 40바이트 결과의 잘못된 쪽 32바이트를 복사하면서, 앞쪽의 0 여덟 바이트는 남고 뒤쪽의 무작위 여덟 바이트는 버려졌습니다.

공개된 초기 구현에서도 다음 흐름을 확인할 수 있습니다.

unsigned char nonce[size+8];
cx_rng(nonce, size+8);
cx_math_modm(nonce, size+8, ..., size);
os_memcpy(T->K, nonce, size);

결과적으로 모든 서명용 k의 상위 64비트가 0으로 고정됐습니다.

정상적으로 기대한 규모: 약 2²⁵⁶
실제 사용된 범위:       0 ≤ k < 2¹⁹²

같은 k를 반복 사용한 것은 아닙니다. 값은 매번 달랐지만 항상 192비트 범위 안에서만 만들어졌고, 가능한 범위의 큰 부분이 처음부터 잘려 있었습니다.

서명 하나만 놓고 보면 k도 모르고 d도 모르기 때문에 식을 바로 풀 수 없습니다. 그러나 여러 서명에서 사용한 k가 모두 2¹⁹²보다 작다는 사실은 알고 있습니다. 같은 개인키로 만든 서명이 쌓일수록, 각 식은 d가 있을 수 있는 범위를 조금씩 좁힙니다.

이런 문제는 암호학에서 Hidden Number Problem으로 바꿔 다룰 수 있습니다. 여러 개의 불완전한 단서를 격자 축소(lattice reduction)라는 방법으로 함께 풀어, 공통으로 들어 있는 개인키를 찾아내는 방식입니다. Zilliqa는 영향을 받은 서명이 약 다섯 개 이상이면 일반적인 컴퓨터에서도 수초 안에 개인키를 복원할 수 있다고 밝혔습니다.

공격자가 Ledger 기기를 손에 넣거나 네트워크로 침입할 필요도 없었습니다. 거래 서명은 검증을 위해 블록체인에 공개됩니다. 취약한 앱으로 이미 서명한 기록이 충분하다면, 공격 재료도 이미 온체인에 올라가 있었습니다.

콜드월렛은 개인키를 인터넷에 연결된 PC나 휴대전화로 내보내지 않고, 장치 안에서 거래를 서명합니다. 이번 사건에서도 개인키 파일을 직접 읽어 낸 것이 아니라는 점은 그대로입니다.

하지만 거래를 하려면 장치 안의 앱이 개인키를 사용해 계산한 결과를 밖으로 내보내야 합니다. 그 계산이 잘못되면 키를 보관한 장소가 안전해도 키를 사용하는 방식에서 정보가 샐 수 있습니다. 하드웨어, 장치용 앱, 난수 생성과 처리, 서명 알고리즘은 하나의 보안 경계로 봐야 합니다.

따라서 이번 일을 “콜드월렛은 소용없다”로 일반화하는 것도, 반대로 “개인키가 장치 밖으로 안 나오니 안전하다”고 보는 것도 정확하지 않습니다. 취약점은 Zilliqa Ledger 앱의 특정 서명 경로에 있었고, Zilliqa는 EVM 거래와 다른 Zilliqa SDK의 서명용 k 생성은 영향을 받지 않았다고 밝혔습니다.

앱을 업데이트해도 과거의 단서는 사라지지 않습니다

수정 앱은 앞으로 약한 k가 만들어지는 일을 막을 수 있습니다. 이미 블록체인에 기록된 서명은 지울 수 없습니다. 노출 가능성이 생긴 개인키는 앱 업데이트로 다시 안전해지지 않으며, 결국 폐기하고 새 키로 전환해야 합니다.

그렇다고 지금 평범한 전송으로 자산을 옮기면 된다는 뜻도 아닙니다. 같은 온체인 자료로 공격자도 개인키를 계산할 수 있기 때문에, 네이티브 거래가 재개되는 순간 정상 사용자의 전송보다 앞서 거래를 넣을 수 있습니다. Zilliqa가 독자적인 이체를 시도하지 말고 조정된 복구 안내를 기다리라고 한 이유입니다.

체인을 멈춘 게 아니라, 긴급 하드포크로 네이티브 거래만 막았습니다

Zilliqa 재단이 관리자 권한으로 이미 올라온 거래를 지운 것은 아닙니다. 개발팀은 7월 20일 긴급 노드 프로그램 v0.21.7을 배포했습니다. 이 버전은 먼저 기존 CreateTransaction API가 네이티브 Zilliqa 거래를 받지 않도록 막고, 별도로 메인넷 31,759,109번 블록부터 네이티브 거래 실행을 오류로 처리하는 포크 규칙을 넣었습니다. 해당 블록의 시각은 메인넷 RPC 기준 7월 21일 01:37 KST입니다.

핵심 실행 코드는 거래 유형이 Transaction::Zilliqa인지 확인한 뒤, 포크 설정이 켜져 있으면 Zilliqa transaction execution is disabled 오류를 반환합니다. EVM 거래는 이 분기 밖에 있으므로 블록 생성과 EVM 실행은 계속될 수 있습니다. 엄밀히 말하면 ‘블록체인 전체 정지’가 아니라 네이티브 전송과 Scilla 호출을 포함한 레거시 Zilliqa 거래 경로만 합의 규칙에서 잠근 것입니다.

이 변경은 거래 실행 결과가 달라지는 규칙 변경이므로 하드포크에 해당합니다. 개발팀이 새 바이너리와 활성화 높이를 정해 제안·배포할 수는 있지만, 그 발표만으로 모든 검증자가 자동 복종하는 것은 아닙니다. 충분한 합의 지분을 가진 검증자들이 새 버전으로 올라와야 새 규칙이 정식 체인으로 확정됩니다. 구버전 검증자가 네이티브 거래를 담은 블록을 제안하면 새 버전 검증자들은 실행 오류로 처리해 합의 투표를 하지 않습니다. 구버전 노드가 목록에서 강제로 삭제되는 것은 아니지만, 구버전 쪽이 별도의 합의 정족수를 확보하지 못하면 그 블록을 확정할 수 없어 사실상 정식 체인에서 이탈합니다. 실제로 Zilliqa의 검증자 문서도 거래 실행 의미가 달라지는 변경을 하드포크로 설명하며, 활성화 높이 전에 업그레이드하지 않으면 노드가 동기화에서 이탈하고 보상을 잃을 수 있다고 안내합니다.

31,759,109번 블록의 포크 지점에서 네이티브 Zilliqa 거래는 실행되지 않고 EVM 거래는 이후 블록으로 계속 처리되는 흐름을 나타낸 도식

긴급 하드포크 구조를 단순화한 개념도. v0.21.7은 체인 전체를 정지한 것이 아니라 31,759,109번 블록부터 레거시 Zilliqa(Scilla) 거래를 실행하지 않는 합의 규칙을 넣었습니다. EVM 거래와 블록 생성은 이 경로 밖에 있습니다.

현재 공개된 진행 상황은 다음과 같습니다.

  • 2026년 7월 19일, 실제 악용과 일치하는 온체인 활동이 관찰됐습니다.
  • 7월 20일, Zilliqa는 거래소 파트너의 콜드월렛에서 ZIL이 도난당했다고 알리고 각 거래소에 ZIL 입출금의 일시 중단을 요청했습니다. 같은 날 긴급 노드 버전 v0.21.7을 배포했습니다.
  • 7월 21일 01:37 KST, 31,759,109번 블록에서 네이티브 거래 실행 중단 규칙이 활성화됐습니다. 이후 온체인 서명으로 문제를 재현하고 서명용 k 처리 코드를 원인으로 특정했습니다. 결함은 2019년부터 2026년까지 출시된 Zilliqa Ledger 앱의 모든 버전에 존재했습니다.
  • 7월 22일 기술 공개에서 Zilliqa는 네이티브(non-EVM) 거래를 보호 조치로 중단했으며, 수정 빌드와 잔액 보호 절차를 Ledger 등 관계자와 조율 중이라고 밝혔습니다.

지금 확인할 것은 ‘ZIL을 보유했는가’보다 ‘어떻게 서명했는가’입니다

Ledger 기기와 Zilliqa Ledger 앱으로 네이티브 ZIL 거래를 서명해 전파한 적이 있다면, 거래 횟수를 확인하되 횟수만으로 안전을 단정해서는 안 됩니다. Zilliqa의 공식 복구 안내가 나오기 전 임의로 전송하거나, 검색 광고·SNS 메시지에서 안내하는 복구 도구를 사용하지 않는 편이 안전합니다. 복구·보상·키 교체를 이유로 시드 문구나 개인키, 원격 접속, 선입금, 임의의 지갑 연결이나 서명을 요구하면 응하지 마세요.

ZIL을 EVM 호환 도구로만 사용했다면 현재 Zilliqa가 발표한 영향 대상에는 포함되지 않습니다. 이는 EVM 서명 경로가 이번 결함을 만들지 않았다는 뜻입니다. 네이티브 거래 서명으로 이미 노출된 같은 개인키를 다른 도구에서 사용한다고 다시 안전해지는 것은 아닙니다.

거래소 계정에서만 ZIL을 보유한 이용자 역시 본인이 이 앱으로 서명한 경우와는 다릅니다. 다만 거래소별 입출금 지원 상태는 보호 조치와 네트워크 상황에 따라 달라질 수 있으므로, 이용 중인 거래소의 최신 공지를 따로 확인해야 합니다.

2018년 공개된 Kudelski Security의 Zilliqa Schnorr 감사 보고서도 “예측 가능하거나 편향된 k는 개인키 복원으로 이어질 수 있다”고 짚었습니다. 다만 그 감사 범위는 Zilliqa의 코어 libCrypto와 JavaScript 라이브러리였고, 이번 Ledger 앱 구현은 아니었습니다. 알려진 암호학 원칙이 있어도, 별도의 코드 경로가 검토 범위 밖에 있으면 같은 종류의 문제가 다시 들어갈 수 있다는 뜻입니다.

이번 사건을 콜드월렛 전체의 실패로 볼 필요는 없습니다. ‘콜드’는 개인키의 보관 상태를 설명할 뿐, 장치 안의 모든 서명 코드를 인증하는 품질표시는 아닙니다. 다음에는 개인키를 어디에 두었는지만이 아니라, 어떤 앱이 어떤 방식으로 서명했고 그 구현이 독립적으로 검증됐는지도 함께 봐야 합니다.


참고 자료

안내

이 글은 진행 중인 보안 사고와 이용자 주의사항을 설명하기 위한 일반 정보이며, ZIL 또는 다른 가상자산의 매수·매도·보유를 권유하거나 개별 계정의 안전을 진단하지 않습니다. Zilliqa의 후속 발표가 이 글보다 우선합니다. BTX의 ZIL 거래지원과 입출금 상태는 서로 다를 수 있으므로 BTX 공식 공지와 실제 입출금 화면에서 확인해 주세요.