메타마스크 지갑을 복구하는 방법: 비밀번호 분실, 볼트 저장소, 암호화 및 데이터 포렌식

메타마스크 지갑을 복구하는 방법: 비밀번호 분실, 볼트 저장소, 암호화 및 데이터 포렌식

이 글을 읽고 계신다면, 아마도 MetaMask 지갑에 접속할 수 없게 되어 더 이상 할 수 있는 방법이 없는 상황일 것입니다. 저는 수년 동안 사람들의 지갑을 복구해 왔습니다. 노트북을 포맷해 버린 DeFi 트레이더부터 브라우저 확장 프로그램을 “정리”해 버린 할아버지, 할머니에 이르기까지 다양한 분들을 도와드렸죠. 이 가이드에는 MetaMask가 키를 저장하는 방식, 해당 데이터가 디스크의 어디에 실제로 저장되는지, 그리고 문제가 발생했을 때 이를 복구하는 방법에 대해 제가 알고 있는 모든 내용을 담았습니다. 이 내용은 모두 이론이 아닙니다. 여기에 소개된 모든 기법은 실제 복구 사례에서 얻은 것입니다.

MetaMask가 실제로 사용자의 키를 어떻게 저장하는지

무언가를 복구하기 전에, 무엇을 복구하는지 먼저 파악해야 합니다. MetaMask는 사용자의 개인 키를 private_keys.txt. 이를 ‘’라고 불리는 암호화된 블롭 안에 저장합니다. 금고, 이는 브라우저의 확장 프로그램 저장소에 저장됩니다.

MetaMask 지갑을 생성하고 비밀번호를 설정하면, 내부적으로는 다음과 같은 과정이 진행됩니다. MetaMask는 BIP-39 니모닉(12단어)을 생성합니다. 비밀 복구 문구)를 가져와, 가져온 모든 개인 키와 함께 “keyring” 객체로 구성된 JSON 배열에 포함시킵니다. 이 배열은 문자열로 직렬화된 후, 다음을 사용하여 암호화됩니다. AES-256-GCM 다음 방법을 통해 귀하의 비밀번호에서 파생된 키를 사용하여 PBKDF2-HMAC-SHA256. 그 결과, 세 개 또는 네 개의 필드를 가진 JSON 블롭이 chrome.storage.local (크로미움 기반 브라우저의 경우) 또는 IndexedDB (파이어폭스의 경우).

이전 버전에서 암호화된 볼트는 다음과 같이 표시됩니다:

{"data":"SGFuZGxlIHRoaXMgZW5jcnlwdGVkIGRhdGE=","iv":"MTIzNDU2Nzg5MDEyMzQ1Ng==","salt":"cmFuZG9tMzJieXRlc2FsdHZhbHVlaGVyZQ=="}

또한 최신 버전(v11.16.x 이후)에는 네 번째 필드가 추가되었습니다:

{"data":"...","iv":"...","keyMetadata":{"algorithm":"PBKDF2","params":{"iterations":600000}},"salt":"..."}

keyMetadata 이 부분이 바로 회복에 몇 분이 걸리는지와 몇 주가 걸리는지의 차이를 결정합니다. 이에 대해서는 잠시 후에 더 자세히 설명하겠습니다.

암호화 파이프라인은 간단하지만 정확히 이해하는 것이 중요합니다. UTF-8 형식의 비밀번호는 Web Crypto API를 통해 원시 PBKDF2 키로 가져옵니다. A 32바이트 무작위 솔트 (다음에서 생성됨: crypto.getRandomValues())는 비밀번호와 함께 사용되어 256비트 AES-GCM 키를 도출합니다. 그런 다음 16바이트의 임의의 IV 직렬화된 키링 데이터를 암호화합니다. 이 data 이 필드에는 암호문과 GCM 인증 태그가 모두 base64로 인코딩된 형태로 포함되어 있습니다. 주목할 만한 한 가지 특징은, MetaMask가 AES-GCM용 16바이트 IV, 이는 표준에 부합하지 않습니다. NIST SP 800-38D에서는 12바이트를 권장합니다. 이로 인해 일부 암호화 라이브러리와 상호 운용성에 문제가 발생합니다(예를 들어, Ruby의 OpenSSL은 이를 지원하지 않습니다).

비밀번호로 MetaMask의 잠금을 해제하면 이 과정이 역순으로 진행됩니다. 이 KeyringController 영구 저장소에서 암호화된 볼트 문자열을 가져와 이를 browser-passworder‘s decrypt 이 함수는 JSON을 파싱하고, 솔트를 추출한 뒤, 동일한 매개변수를 사용하여 PBKDF2로 키를 도출하며, AES-GCM으로 복호화한 후, 그 결과를 다시 키링 객체로 역직렬화합니다. 복호화된 키링은 오직 기억 속에서만 – 이들은 다음 위치에 저장되어 있습니다. memStore (an ObservableStore (예: )이며, 절대 평문 형태로 디스크에 기록되지 않습니다.

복호화된 볼트에는 일반적으로 다음과 같은 형태의 배열이 포함되어 있습니다:

[
  {
    "type": "HD Key Tree",
    "data": {
      "mnemonic": "abandon ability able about above absent ...",
      "numberOfAccounts": 3,
      "hdPath": "m/44'/60'/0'/0"
    }
  },
  {
    "type": "Simple Key Pair",
    "data": ["0xabc123..."]
  }
]

HD Key Tree 키링에는 니모닉과 파생된 계정 수가 저장됩니다. Simple Key Pair 항목들은 개별적으로 가져온 개인 키입니다. 하드웨어 지갑을 연결한 경우, 다음 내용도 표시됩니다. Trezor Hardware 그리고 Ledger Hardware 키링 유형 – 다만 여기에는 파생 경로와 공개 주소만 저장되며, 개인 키는 저장되지 않습니다(개인 키는 하드웨어 장치에 남아 있습니다).

Manifest V3(2023년부터 Chrome이 도입한 서비스 워커 아키텍처)를 통해 MetaMask는 암호화 키를 내보낸 JWK 형태로 캐시하는 기능을 추가했습니다. 이를 통해 서비스 워커가 재시작될 때 비밀번호를 다시 입력할 필요 없이 볼트를 다시 해독할 수 있습니다. 이 encryptionKey 그리고 encryptionSalt 필드는 다음 위치에 나타납니다. memStore ~할 때 cacheEncryptionKey 이 기능이 활성화되어 있습니다.

각 플랫폼에서 MetaMask가 사용자의 데이터를 어디에 저장하는지

볼트를 찾는 것이 성공의 절반입니다. MetaMask는 브라우저와 운영 체제에 따라 데이터를 서로 다른 위치에 저장하며, 이러한 차이는 복구 과정에서 중요한 역할을 합니다.

크롬ium 계열 브라우저(Chrome, Brave, Edge)

크로미움 기반 브라우저에서는, chrome.storage.local ~에 지속된다 LevelDB 데이터베이스 디스크에. 확장자 ID에 따라 폴더 이름이 결정됩니다.

Chrome 확장 프로그램 IDnkbihfbeogaeaoehlefnkodbefgpgknn – 모든 회복 전문가의 기억에 깊이 각인되어 있다. 용감한 Chrome 웹 스토어에서 설치되므로 동일한 ID를 사용합니다. Edge Edge 애드온 스토어에서 설치할 경우 고유한 ID가 할당됩니다: ejbalbakoplchlghecdalmeeeajnimhm. 하지만 사용자가 Chrome 웹 스토어(Edge에서 지원됨)를 통해 Edge에 MetaMask를 설치한 경우, Chrome ID가 그대로 유지됩니다. 이 차이점 때문에 사람들이 끊임없이 혼란을 겪곤 합니다.

다음은 전체 경로입니다:

Windows용 Chrome:
C:\Users\<USER>\AppData\Local\Google\Chrome\User Data\Default\Local Extension Settings\nkbihfbeogaeaoehlefnkodbefgpgknn\

macOS의 Chrome:
~/Library/Application Support/Google/Chrome/Default/Local Extension Settings/nkbihfbeogaeaoehlefnkodbefgpgknn/

리눅스용 크롬:
~/.config/google-chrome/Default/Local Extension Settings/nkbihfbeogaeaoehlefnkodbefgpgknn/

Windows용 Brave:
C:\Users\<USER>\AppData\Local\BraveSoftware\Brave-Browser\User Data\Default\Local Extension Settings\nkbihfbeogaeaoehlefnkodbefgpgknn\

macOS용 Brave:
~/Library/Application Support/BraveSoftware/Brave-Browser/Default/Local Extension Settings/nkbihfbeogaeaoehlefnkodbefgpgknn/

Windows용 Edge (Edge 스토어 설치):
C:\Users\<USER>\AppData\Local\Microsoft\Edge\User Data\Default\Local Extension Settings\ejbalbakoplchlghecdalmeeeajnimhm\

한 가지 중요한 사항: 사용자가 여러 개의 Chrome 프로필을 가지고 있는 경우, 다음을 Default ~와 함께 Profile 1, Profile 2, 등등. 사람들이 몇 시간 동안 잘못된 프로필을 검색하는 걸 본 적이 있습니다.

파이어폭스 – 완전히 다른 존재

Firefox는 LevelDB를 사용하지 않습니다. 확장 기능 데이터는 IndexedDB, 이는 Firefox가 SQLite를 기반으로 구현한 것입니다. 확장 프로그램 ID는 webextension@metamask.io, 하지만 파이어폭스는 설치마다 고유한 내부 UUID를 할당하는데, 대략 다음과 같은 형식입니다. 196319ec-3a5e-4efe-9413-c327a770d874. 다음에서 확인하실 수 있습니다. about:debugging “이 Firefox” 항목 아래에서

데이터는 다음 위치에 있습니다:

Windows: %APPDATA%\Mozilla\Firefox\Profiles\<PROFILE>\storage\default\moz-extension+++<UUID>^userContextId=4294967295\idb\

macOS: ~/Library/Application Support/Firefox/Profiles/<PROFILE>/storage/default/moz-extension+++<UUID>^userContextId=4294967295/idb/

리눅스: ~/.mozilla/firefox/<PROFILE>/storage/default/moz-extension+++<UUID>^userContextId=4294967295/idb/

~ 안쪽에서 idb/ 폴더 안에는 숫자로 이름이 지정된 바이너리 파일들이 있습니다. 이들은 Snappy 압축 – LevelDB 파일처럼 그냥 grep으로 검색할 수는 없습니다. 먼저 다음과 같은 도구를 사용하여 압축을 해제해야 합니다. snappy-fox 볼트 데이터를 추출할 수 있게 되기 전입니다. 제가 목격하는 파이어폭스 복구 과정에서 가장 흔한 실수는 바로 이것입니다. 사용자들이 바이너리 파일을 열어보았을 때, 알아볼 수 있는 텍스트 조각들이 뒤섞인 뒤죽박죽인 데이터를 보고 볼트가 손상되었다고 착각하는 경우가 많습니다. 하지만 그렇지 않습니다. 단지 압축되어 있을 뿐입니다.

파이어폭스 63 이전에는 확장 기능 저장소가 다음과 같은 간단한 JSON 파일을 사용했습니다. storage.js 프로필 디렉터리에서. 아주 오래된 설치 환경을 복원하는 경우, 다음을 찾아보세요. storage.js 또는 storage.js.migrated.

LevelDB와 모두가 궁금해하는 0001.ldb 파일

LevelDB는 구글의 키-값 저장 엔진이며, 크로미움 기반 브라우저에서 MetaMask를 복구하려면 이 저장 엔진의 파일 구조를 이해하는 것이 필수적입니다.

MetaMask LevelDB 디렉토리에는 여러 종류의 파일이 포함되어 있습니다. 이 .ldb 파일들 (정렬된 문자열 테이블, 즉 SSTable)은 영구적이고 정렬된 키-값 저장소입니다. .log 파일들 Write-Ahead Log는 최근에 기록된 데이터가 플러시되기 전까지 임시로 저장되는 버퍼로, .ldb 파일들. 거기에는 MANIFEST-###### 파일 추적 데이터베이스 메타데이터, a CURRENT 활성 매니페스트를 가리키는 파일과, LOCK 동시 액세스를 방지하는 파일.

볼트 데이터는 대개 번호가 낮은 .ldb 파일000003.ldb, 000005.ldb, 또는 이와 유사한 값입니다. MetaMask의 공식 문서에 따르면 “낮은 수치여야 합니다. 숫자가 크다면 금고가 아닙니다.”라고 되어 있습니다. 0001.ldb 파일 (또는 000001.ldb)는 MetaMask가 처음 초기화될 때 생성된 가장 초기의 SSTable 중 하나입니다. 여기에는 대개 지갑 생성 시 작성된 원래의 볼트가 포함되어 있습니다.

다음에서 볼트 데이터 추출 .ldb 파일들 크로미움에서는 대개 간단합니다. 텍스트 편집기(Sublime Text, VS Code, 심지어 Notepad++도 가능)에서 파일을 열고 다음 문자열을 검색하세요. vault. LevelDB 레코드 구조 내에 암호화된 JSON 블롭이 포함되어 있습니다. 다음의 모든 내용을 복사하세요. {"data":" 마감 시까지 "} – 그게 바로 당신의 금고입니다.

Mac이나 Linux에서는 다음 한 줄의 명령어로 해결할 수 있습니다:

LC_ALL="C" egrep -roa 'vault":"(.*?\\"})' ~/Library/Application\ Support/Google/Chrome/Default/Local\ Extension\ Settings/nkbihfbeogaeaoehlefnkodbefgpgknn/ | sed -E 's/.*({.*}).*/\1/g' | head -1

주목할 점 하나: .ldb 파일에서는 다음을 사용할 수 있습니다 빠른 압축 데이터 블록에서. 문자열 검색이 실패하고 이진 쓰레기 데이터 블록이 표시된다면, 필요한 데이터 블록이 Snappy로 압축되어 있을 수 있습니다. 해당 .log 반면, 파일은 항상 압축되지 않은 상태로, 32KB 단위의 원시 키-값 데이터로 구성됩니다. 만약 .ldb 접근 방식이 실패하면, 항상 다음을 확인하십시오. .log 파일. MetaMask의 공식 문서에는 다음과 같이 언급되어 있습니다. “ .ldb 파일을 사용하여 볼트를 복구할 수 없는 경우, .log 파일이 있는지 확인해 보세요.”

복구 작업에 매우 유용한 LevelDB의 특징은 바로 다음과 같습니다. 추가 전용. 업데이트 및 삭제 작업은 기존 레코드를 수정하지 않고, 더 큰 시퀀스 번호를 가진 새로운 항목을 추가합니다. 기존 레코드는 압축 과정에서 병합되어 제거될 때까지 그대로 유지됩니다. 즉, 누군가가 기존 SRP 위에 새로운 SRP를 가져온 경우, 이전 볼트 데이터는 여전히 이전 버전의 .ldb 아직 압축되지 않은 파일입니다. 저는 이런 방식으로 “덮어쓴” 지갑을 한 번 이상 복구한 적이 있습니다.

프로그래매틱 추출의 경우, btcrecover 이 프로젝트에는 다음이 포함됩니다. extract-metamask-vaults.py, 이는 LevelDB 데이터베이스를 올바르게 읽어들이고, 압축되지 않은 파일에 숨어 있을 수 있는 오래된 항목을 포함해 모든 볼트 항목을 추출합니다. 이 cyclone-github/metamask_extractor 이 도구는 동일한 기능을 수행하며, hashcat 호환 형식으로 직접 출력할 수 있습니다.

볼트 파일이 손상되었을 때

볼트 손상은 대개 다음과 같은 몇 가지 원인 중 하나에서 비롯됩니다. 쓰기 작업 중 브라우저가 충돌하는 경우(정전, 커널 패닉, 강제 종료), 저장소를 재기록하는 도중에 중단된 MetaMask 업데이트, 실제 디스크 오류 (불량 섹터, SSD 마모), 또는 점점 더 흔해지고 있는 안티바이러스 소프트웨어가 MetaMask의 자바스크립트 파일을 격리하는 경우입니다. 이 경우 볼트 데이터 자체는 손상되지 않았더라도 LevelDB 데이터베이스가 일관성 없는 상태로 남을 수 있습니다.

JSON이 파싱되지 않으면 볼트가 손상된 것임을 알 수 있습니다. 중괄호가 누락되었거나, base64 문자열이 잘렸거나, 데이터 필드 중간에 null 바이트가 삽입된 경우 등이 있습니다. MetaMask Vault Decryptor는 “볼트 디코딩 문제”라는 오류 메시지를 표시하거나, 아무런 오류 메시지 없이 단순히 실패할 수 있습니다.

복구 가능 여부는 손상의 유형과 정도에 따라 달라집니다. JSON 구조가 손상되었더라도 암호화된 데이터가 대체로 온전한 경우, 대개는 볼트를 수동으로 재구성합니다. 형식이 엄격합니다 – 정확히 data, iv, 그리고 salt 필드 (및 keyMetadata (최신 볼트의 경우). null 바이트와 제어 문자를 제거합니다. 다음을 확인하십시오. data, iv, 그리고 salt 값은 유효한 base64입니다. 만약 data 그러나 필드가 잘려 있다면, 문제가 생깁니다 – AES-GCM은 전체 암호문을 필요로 합니다. 여기에 인증 태그(의 마지막 16바이트)도 포함되며, data (필드)를 해독해야 합니다. 단 한 바이트라도 누락되면 전체 과정이 실패합니다.

다음과 같이 부분적으로 손상된 LevelDB 데이터베이스의 경우, .ldb 파일 자체가 손상된 경우, 다음을 읽어보십시오. .log 대신 파일을 사용해 보세요. 이 파일은 더 간단한 형식(7바이트 헤더가 포함된 순차적 32KB 블록)이며, 데이터 손상 사고를 견뎌낸 볼트의 최신 사본을 포함하고 있을 수 있습니다. 두 방법 모두 효과가 없다면, 때로는 Python LevelDB 파싱 라이브러리(CCL Solutions Group에서 LevelDB 파싱, Snappy 압축 해제, V8 역직렬화를 위한 순수 Python 구현체를 공개했습니다)를 사용하여 손상된 블록을 건너뛰고 손상된 데이터베이스 파일에서 레코드를 정밀하게 추출할 수 있습니다.

단일 LevelDB 디렉토리 내에 여러 개의 SRP가 공존할 수 있습니다. 사용자가 새로운 시드 문구를 가져온 경우, 기존 볼트 항목은 별도의 .ldb 파일 내 또는 심지어 동일한 파일 내의 다른 오프셋 위치에서도 검색합니다. 항상 다음을 검색하십시오. 모든 인스턴스 볼트 패턴 중 첫 번째 것뿐만 아니라 다른 것들도 포함됩니다.

파일 덮어쓰기 문제와 해결 방법

이것이 가장 가슴 아픈 상황입니다. 누군가가 메타마스크를 제거해 버리는 경우입니다. 문제 해결을 하려던 것일 수도 있고, 브라우저를 ‘정리’하려던 것일 수도 있으며, 자신이 무슨 일을 하는지 깨닫지 못한 채 그랬을 수도 있습니다. 크로미움 기반 브라우저에서 확장 프로그램을 제거하면 확장 데이터 폴더 전체를 삭제합니다., 다음을 포함하여 모두 .ldb, .log, 그리고 매니페스트 파일. 파이어폭스에서는 moz-extension+++<UUID> 폴더가 삭제되고, 재설치할 때마다 새로운 UUID가 생성되기 때문에 새 설치본은 기존 위치에 영향을 주지 않습니다(단, 기존 위치는 사라진 상태입니다).

MetaMask의 공식 문서에는 이에 대해 다음과 같이 명확히 명시되어 있습니다. “브라우저 확장 프로그램을 제거하면 확장 프로그램 데이터가 삭제됩니다. 일반적으로 이는 금고 데이터가 사라진다는 것을 의미합니다.”

“일반적으로”라는 단어가 상당히 중요한 의미를 담고 있습니다. 데이터는 논리적으로 삭제됩니다 (파일 시스템이 해당 섹터를 사용 가능한 공간으로 표시함). 하지만 실제 바이트는 즉시 0으로 초기화되지는 않습니다. 기존 HDD의 경우, 해당 섹터가 새로운 파일에 의해 재사용될 때까지는 데이터가 그대로 남아 있습니다. 반면 SSD에서는 TRIM 명령을 통해 삭제된 데이터를 몇 분, 때로는 몇 초 만에 복구 불가능하게 만들 수 있습니다.

덮어쓴 볼트 복구의 첫 번째 규칙은 컴퓨터 사용을 즉시 중단하는 것입니다. 드라이브에 새로 기록되는 모든 파일은 이전에 볼트가 저장되어 있던 섹터를 덮어쓸 수 있습니다. 가능하다면 드라이브를 분리하여 다른 컴퓨터에 읽기 전용 모드로 마운트하십시오.

이러한 상황에 대비한 복구 도구들을 제가 가장 먼저 사용하는 순서대로 나열하면 다음과 같습니다:

  • 논리적으로 삭제된 파일을 복구하기 위한 당사의 맞춤형 소프트웨어 (크로스 플랫폼)
  • Linux ext3/ext4 파일 시스템용 extundelete 또는 ext4magic
  • 원시 디스크 검색 최후의 수단으로: grep -rboa "vault" /dev/sdX 원시 디스크 장치를 스캔하여 볼트 문자열을 검색하기 위해
  • 당사의 맞춤형 공구 파일 시스템 메타데이터가 손실된 경우 파일 추출을 위해 – 다음을 검색하도록 구성하십시오. {"data":" 패턴

저는 몇 주 전에 제거된 드라이브(HDD)에서 볼트를 복구한 적이 있습니다. SSD의 경우, 단 몇 시간만 계속 사용해도 성공률이 급격히 떨어집니다. MetaMask를 다시 설치하는 것은 절대 해서는 안 될 일입니다. 다시 설치하면 정확히 같은 디렉터리에 새로운 LevelDB 데이터베이스가 생성되어, 기존 섹터를 덮어쓸 가능성이 있기 때문입니다.

이 점에서 파이어폭스에는 한 가지 장점이 있습니다. 설치할 때마다 새로운 UUID가 할당되기 때문에, 재설치 시 파일이 새로운 디렉터리 경로에 생성됩니다. 기존 moz-extension+++<OLD-UUID> 폴더가 삭제되었더라도 해당 데이터는 새로 설치된 파일로 덮어쓰이지 않습니다. 또한 두 가지 숨겨진 Firefox 환경 설정도 있는데 – extensions.webextensions.keepUuidOnUninstall 그리고 extensions.webextensions.keepStorageOnUninstall – 만약 이를 true 에서 about:config 제거하기 전에 브라우저가 확장 프로그램 저장소를 지우지 못하도록 방지해야 합니다. 하지만 아무도 이를 사전에 설정해 두지는 않습니다.

모바일용 메타마스크는 완전히 다른 차원의 서비스입니다

MetaMask 모바일 버전은 React Native 기반으로 구축되었으며, 데이터 저장 방식이 브라우저 확장 프로그램과는 완전히 다릅니다. 암호화된 볼트는 다음 단계를 거칩니다. @react-native-async-storage/async-storage, 이는 플랫폼별 백엔드를 사용합니다.

안드로이드에서, AsyncStorage는 일반적으로 SQLite 데이터베이스 에서 /data/data/io.metamask/databases/RKStorage. 이 경로를 통해 직접 읽으려면 루트 권한이 필요합니다. 볼트 암호화는 PBKDF2 파생 키 방식을 동일하게 사용하지만, 데스크톱 확장 프로그램과는 중요한 차이점이 있습니다. 모바일 앱은 과거부터 오직 5,000회의 PBKDF2 반복 그리고 다음을 사용하여 암호화합니다. AES-CBC AES-GCM 대신에. 즉, 비밀번호가 동일하더라도 모바일 볼트와 데스크톱 볼트는 서로 호환되지 않습니다.

iOS에서, 데이터는 앱 샌드박스 내의 <AppSandbox>/Documents/. iCloud 백업 복구의 경우, 관련 경로는 다음과 같습니다. Apps → MetaMask → Documents → persistStore → persist-root – Mac에서 iOS 백업을 확인하려면 iMazing과 같은 도구가 필요합니다. 이 방법이 작동하는지 여부는 사용자가 MetaMask를 사용 중일 때 iCloud 백업을 활성화해 두었는지 여부에 달려 있으며, 애플 측의 변경 사항으로 인해 이 방법이 간헐적으로 작동하지 않는 것으로 알려졌습니다.

MetaMask 모바일 버전 또한 다음 기능을 구현하고 있습니다. SecureKeychain 모듈 (다음 기반으로 구축됨 react-native-keychain)는 생체 인식 잠금 해제를 위해 사용자의 지갑 비밀번호를 iOS 키체인이나 Android 키스토어에 저장합니다. 보안 감사 결과, SecureKeychain이 “foxCode” 솔트를 사용하여 추가 암호화 계층을 적용하는 것으로 드러났는데, 이 솔트는 하드코딩된 문자열로 밝혀졌습니다. "encrypt". 메타마스크 팀은 이것이 보안상 중대한 기밀이라기보다는 다층 방어 전략의 일환이라고 인정했다.

모바일 복구에서 직면하는 근본적인 과제는 바로 수동으로 볼트를 추출할 수 없습니다. ~를 찾는 것과 같다 .ldb 바탕화면의 파일들. 아이폰에 SSH로 접속해서 볼트 문자열을 grep으로 검색할 수는 없습니다. 다음부터 MetaMask 모바일 v6.3.0, 이 앱에는 데이터 손상이 감지되면 자동으로 실행되는 볼트 복구 기능이 포함되어 있습니다. 하지만 앱을 삭제하면 기기 백업이 없는 한 로컬 볼트 데이터는 사라집니다. 안드로이드의 백업 방식은 앱 내부의 모든 데이터를 완벽하게 백업하는 데 특히 신뢰성이 떨어집니다. 메타마스크의 공식 입장 안드로이드 볼트 복구 기능에 의존하기보다는 자산을 새로운 SRP로 이전할 것을 권장합니다.

모든 복구 도구를 무용지물로 만든 PBKDF2 반복 횟수 변경

수년 동안 MetaMask는 10,000회의 PBKDF2 반복 – 의 하드코딩된 기본값 browser-passworder 라이브러리. 모든 복구 도구, 모든 해시캣 모듈, 모든 무차별 대입 스크립트는 10,000회의 반복 횟수를 기준으로 조정되어 있었습니다. 그러다 2023년 말/2024년 초, 모든 것이 바뀌었습니다.

@metamask/browser-passworder 라이브러리 v4.2.0(2023년 11월 13일 출시)에서는 구성 가능한 키 도출 옵션에 대한 지원이 도입되었습니다. 이 라이브러리의 새로운 기본값은 90만 번의 반복, 하지만 MetaMask 확장 프로그램이 이를 사용하도록 설정했기 때문에 60만 번의 반복 – OWASP의 2023년 PBKDF2-HMAC-SHA256 권고 사항을 따릅니다. MetaMask 확장 프로그램 기준 v11.16.11 (2024년 6월 해시캣(hashcat) 이슈에서 확인된 바와 같이), 60만 번의 반복 횟수를 가진 새로운 볼트가 생성되고 있었으며, 새로운 keyMetadata 필드.

이는 비밀번호 한 번 시도당 계산 비용이 60배 증가한 것입니다. 동일한 하드웨어에서 1만 번의 반복으로 하루가 걸리던 무차별 대입 공격이, 60만 번의 반복을 거치면 이제 두 달이 소요됩니다. 해시캣(hashcat) 커뮤니티는 새로운 형식을 처리하기 위해 cyclone이 기여한 ‘모드 26620’과 같은 맞춤형 커널을 개발해야 했습니다. 표준 모드인 26600에는 1만 번의 반복 횟수가 하드코딩되어 있었습니다.

중요한 점은, 기존 볼트는 자동으로 재암호화되지 않는다는 것입니다. 2021년에 지갑을 생성한 경우, MetaMask가 명시적으로 재암호화를 실행하지 않는 한 금고는 여전히 10,000회의 반복을 사용합니다(해당 updateVault 이 목적을 위한 함수가 존재하지만, MetaMask가 잠금 해제 시 이 함수를 얼마나 적극적으로 호출하는지는 불분명합니다). 다음의 유무에 따라 현재 사용 중인 버전을 확인할 수 있습니다. keyMetadata 필드. 아니요 keyMetadata? 10,000번의 반복입니다. keyMetadata ~와 함께 "iterations": 600000? 새로운 형식이에요.

복구에 중요한 암호화 라이브러리 버전의 전체 타임라인은 다음과 같습니다:

  • browser-passworder v1.x–v2.x (2022년 이전): 10,000회 반복, 하드코딩됨. 다음을 제외하고 게시됨: @metamask 범위.
  • v3.0.0 (2022년 8월): 다음으로 명칭이 변경되었습니다. @metamask/browser-passworder. 반복 횟수 변경 없음.
  • v4.2.0 (2023년 11월): 추가됨 keyMetadata 지원. 기본 암호화 반복 횟수가 900,000회로 변경되었습니다. keyFromPassword 10,000에서 하위 호환성을 유지합니다.
  • v4.3.0 (2023년 11월): 추가됨 isVaultUpdated 볼트가 목표 매개변수를 충족하는지 확인하기 위해.
  • v5.0.0 (2024년 4월): 최소 Node.js v16이 필요합니다. 암호화 관련 변경 사항은 없습니다.
  • v6.0.0 (2024년 12월): 최소 Node.js v18.18이 필요합니다. 암호화 관련 변경 사항은 없습니다.

복구 전문가 여러분께: 항상 먼저 볼트 형식을 확인하십시오. 이는 전체적인 접근 방식을 결정짓습니다.

MetaMask의 비밀번호 정책과 이것이 무차별 대입 공격에 미치는 의미

MetaMask는 비밀번호 길이를 최소 8자 이상으로 제한합니다. 그게 전부입니다. 대문자, 숫자, 특수 문자의 사용은 필수 사항이 아닙니다. 확장 프로그램은 비밀번호의 보안 수준을 시각적으로 표시해 주지만(“약함” / “양호”), 최소 길이를 충족하는 약한 비밀번호의 사용을 차단하지는 않습니다. 최대 길이 제한도 없으며, 유니코드 문자를 지원합니다. 이 정책은 적어도 2018년 초부터 시행되어 왔습니다(GitHub 이슈 #3515 참조).

일부 제3자 사이트에서는 메타마스크(MetaMask) 비밀번호에 대문자, 소문자, 숫자, 특수 문자가 모두 포함되어야 한다고 잘못 주장하고 있습니다. 이는 사실이 아닙니다. 유일한 필수 조건은 8자 이상이어야 한다는 점뿐입니다.

무차별 대입 공격으로 비밀번호를 복구할 때, 이는 매우 중요한 요소입니다. 소문자만 포함된 8자 비밀번호의 키스페이스는 약 2,080억 가지 조합 (26^8)입니다. 반복 횟수가 1만 번인 구형 금고의 경우, 최신 GPU에서 실행되는 hashcat은 초당 수천 개의 해시를 테스트할 수 있어 며칠 만에 해독이 가능합니다. 반복 횟수가 60만 번인 금고의 경우, 소요 시간이 60배로 늘어납니다. 대소문자와 기호가 혼합된 12자 이상의 강력한 비밀번호라면? 현재 하드웨어로는 두 경우 모두 사실상 해독이 불가능합니다.

실무에서 가장 많이 선택되는 복구 도구는 btcrecover – MetaMask 측에서는 비밀번호를 대략적으로 기억하고 있는 사용자에게 이 방법을 권장합니다. 이 기능은 토큰 기반 및 패턴 기반 추측을 지원하며, 다음과 같은 템플릿을 정의할 수 있게 해줍니다. "my" + [dog|cat|bird] + [2019|2020|2021] + ["!"|"@"|"#"] 그리고 모든 순열을 효율적으로 테스트합니다. GPU 가속 공격의 경우, hashcat 모드 26600은 구형 볼트를, 모드 26610은 모바일 볼트를, 모드 26620(또는 반복 횟수가 업데이트된 재컴파일된 26600)은 새로운 600,000회 반복 형식을 처리합니다.

유출 경로와 ‘자금 유실’의 만연

자금 유출 경로는 “내 자금이 사라졌다”는 문제가 발생하는 주된 원인입니다. 메타마스크 이더리움의 경우 BIP-44 표준 경로를 사용합니다: m/44'/60'/0'/0. 계수는 최종 인덱스에 값을 더하여 구합니다: m/44'/60'/0'/0/0 (첫 번째 계정), m/44'/60'/0'/0/1 (둘째), m/44'/60'/0'/0/2 (세 번째), 그 외의 경우도 마찬가지입니다. 이 경로는 모든 EVM 호환 체인에 적용되며, 이더리움, 폴리곤, 아비트럼, BSC에서 모두 동일한 주소를 사용합니다.

문제는 모든 사람이 같은 경로를 사용하는 것은 아니라는 점입니다. Ledger Live 용도 m/44'/60'/x'/0/0, 다음을 증가시키면서 account ~ 대신 ~ 필드 address_index. 레저 레거시 (이전 MEW/MyCrypto 경로)는 다음을 사용합니다. m/44'/60'/0'/x – 5단계가 아닌 4단계뿐입니다. 첫 번째 주소(m/44'/60'/0'/0/0)는 MetaMask, Ledger Live, Trezor에서 모두 동일합니다. 하지만 두 번째 계정부터는 완전히 달라진다, 각 지갑은 경로의 서로 다른 구성 요소를 증가시키기 때문입니다.

이로 인해 매우 특정한 오류 패턴이 발생합니다. 사용자가 MetaMask에서 Ledger 시드를 복원하고, 첫 번째 계정과 잔액을 확인한 뒤, 두 번째 계정을 추가하면… 잔액이 0으로 표시됩니다. 자금이 사라진 것은 아닙니다. 자금은 다음에서 파생된 주소에 그대로 남아 있습니다. m/44'/60'/1'/0/0 (Ledger Live 방식), 하지만 MetaMask는 m/44'/60'/0'/0/1. 키도 완전히 다르고, 주소도 완전히 다릅니다.

Trezor의 기본 이더리움 파생 경로 MetaMask의 것과 일치합니다: m/44'/60'/0'/0/x. 따라서 Trezor에서 MetaMask로의 복구 기능은 일반적으로 모든 계정에서 작동합니다. 지갑 간 호환성 문제는 주로 Ledger ↔ MetaMask와 Ledger ↔ Trezor 사이에서 발생합니다.

“잘못된” 도출 경로에서 자금을 찾으려면 다음과 같은 방법이 있습니다:

  • contact@cryptorecovery.io으로 이메일을 보내주시면 최선을 다해 도와드리겠습니다.
  • MyEtherWallet (MEW): “경로 추가”를 통해 사용자 지정 파생 경로를 지원합니다. 수동으로 입력하십시오. m/44'/60'/1'/0/0, m/44'/60'/2'/0/0, 등 Ledger Live 형식의 주소를 확인하기 위해.
  • MyCrypto: 유사한 사용자 정의 도출 경로 지원.
  • Ian Coleman의 BIP-39 도구 (오프라인 실행): 니모닉을 입력하고, ETH를 선택한 뒤, BIP-44 탭 간을 전환하며, 경로 구성 요소를 수동으로 조정합니다.
  • btcrecover: 다양한 도출 경로 체계에 걸쳐 테스트를 자동화할 수 있습니다.

한 가지 아쉬운 점은, MetaMask의 Trezor 연동 기능이 사용자 지정 파생 경로를 지원하지 않는다는 것입니다. Ledger용 사용자 지정 파생 경로 옵션은 병합되었지만(PR #9367), 최신 정보에 따르면 Trezor용 해당 기능(GitHub 이슈 #11197)은 여전히 미해결 상태입니다. MetaMask와 Trezor를 통해 Ledger에서 파생된 계정에 접근하려는 경우, 대신 MEW나 MyCrypto를 사용해야 합니다.

메타마스크와 트레저: 삐걱거리는 관계

MetaMask와 Trezor의 연동은 다음을 통해 이루어집니다. Trezor Bridge – 로컬에 설치된 백그라운드 프로세스 (trezord)에서 수신 대기하는 http://127.0.0.1:21325/ 또한 브라우저 샌드박스와 USB 하드웨어 액세스 간의 간극을 메워줍니다. 다음을 입력하여 실행 중인지 확인할 수 있습니다. http://127.0.0.1:21325/status/.

가장 흔한 오류 원인은 겉보기에는 아주 단순합니다. 바로 Trezor Suite가 실행 중이라는 점입니다. Trezor Suite는 장치와의 USB 연결을 잠그기 때문에 MetaMask가 통신할 수 없게 됩니다. Suite를 단순히 최소화하는 것이 아니라, 시스템 트레이에서 완전히 종료해야 합니다. 제가 확인한 “MetaMask가 Trezor를 찾을 수 없습니다”라는 문의 건 중 약 40%가 이 문제 때문인 것으로 추정됩니다.

그 밖의 일반적인 문제와 해결 방법:

“Trezor를 찾고 있습니다…”라는 메시지가 끝없이 맴돌고 있습니다 – Bridge가 설치되어 있는지 확인하고, trezord 실행 중입니다. 다른 USB 케이블과 포트를 사용해 보세요. VPN, 방화벽, 브라우저 확장 프로그램(광고 차단기 및 개인정보 보호 확장 프로그램이 자주 간섭을 일으킵니다)을 비활성화하세요. 시크릿 모드를 사용하면 확장 프로그램 간의 충돌을 방지할 수 있습니다.

“장치 연결 중” – 다른 애플리케이션이나 브라우저 탭이 이미 Trezor와 통신 중입니다. 해당 장치에 접근할 가능성이 있는 다른 모든 탭과 애플리케이션을 닫으십시오.

WebUSB의 제한 사항 – Firefox는 WebUSB를 전혀 지원하지 않으므로, Firefox에서 Trezor를 연결하려면 전적으로 Bridge에 의존해야 합니다. Chrome, Brave 및 Edge는 Chromium의 WebUSB/WebHID 구현을 통해 작동합니다.

Trezor Safe 5 파생 경로 경고 – 사용자들은 거래 검증 과정에서 Trezor 화면에 “선택한 계정의 파생 경로가 잘못되었습니다. m/44’/60’/0’/0”이라는 메시지가 표시된다고 보고하고 있습니다. 이 경고는 일반적으로 안전하게 무시해도 됩니다. 메시지가 표시되더라도 지갑은 정상적으로 작동합니다. 이는 Trezor 펌웨어가 비표준으로 보이는 경로를 검증하는 방식과 관련된 표면적인 문제입니다.

2025년, Trezor는 Trezor Suite 자체에 Bridge 기능을 통합하기 시작했습니다. 이러한 전환으로 인해 일부 사용자는 MetaMask 연결을 위해 Suite를 백그라운드에서 실행해야 하는 반면, 다른 사용자는 Suite를 완전히 종료해야 하는 등 일련의 연결 문제가 발생했습니다. 관련 설정은 Trezor Suite → 설정 → 애플리케이션 → Trezor Connect에서 확인하시기 바랍니다.

MetaMask가 Trezor에 연결되면 생성되는 계정은 MetaMask의 소프트웨어 지갑 계정과 근본적으로 다릅니다. Trezor에서 파생된 계정은 금고(vault)에 공개 키와 파생 경로만 저장하며, 개인 키는 하드웨어 기기에 남아 있습니다. 메타마스크는 트레저가 연결되지 않은 상태에서도 잔액(공개 키를 통한 읽기 전용)을 표시할 수 있지만, 실제 기기가 없으면 거래에 서명할 수 없습니다. 트레저 계정 생성 시 패스프레이즈를 사용한 경우, 동일한 주소를 다시 생성하려면 정확히 동일한 패스프레이즈를 입력해야 합니다.

실제로 현장에서 효과가 입증된 회복 기법

수백 건의 복구 작업을 거치면서, 제가 가장 자주 마주치는 상황들과 그에 대처하는 방법은 다음과 같습니다.

시나리오 1: 비밀번호는 알고 있지만 시드 문구를 분실했고, 확장 프로그램은 여전히 설치된 상태

이건 아주 간단해요. 다음 링크를 통해 MetaMask의 백그라운드 페이지를 열어주세요. chrome://extensions → 개발자 모드 → “서비스 워커”(MV3) 또는 “백그라운드 페이지”(MV2)를 클릭합니다. 콘솔에서:

chrome.storage.local.get('data', result => {
    console.log(result.data.KeyringController.vault);
});

볼트 JSON을 복사한 다음, 이를 MetaMask 볼트 복호화 도구 (metamask.github.io/vault-decryptor), 비밀번호를 입력하면 니모닉과 가져온 모든 개인 키가 표시됩니다. Vault Decryptor는 MetaMask의 공동 창립자인 Dan Finlay가 개발했으며, 다음을 지원합니다. .ldb 파일 업로드를 통해 파일을 직접 업로드합니다.

시나리오 2: 비밀번호를 알고 있으며, 확장 프로그램이 제거되었지만 데이터가 디스크에 남아 있을 가능성이 있는 경우

확장 프로그램 데이터 디렉터리로 이동하세요. 해당 폴더가 여전히 존재하는 경우(때로는 제거 시 완전히 정리되지 않거나, 사용자가 확장 프로그램을 삭제하지 않고 비활성화만 한 경우), 다음 파일을 가져오세요. .ldb 그리고 .log 파일들. grep이나 텍스트 편집기, 또는 btcrecover‘s extract-metamask-vaults.py. Vault Decryptor를 사용하여 암호를 해독하십시오.

폴더가 사라진 경우, contact@cryptorecovery.io으로 문의해 주시면 디스크 포렌식 조사를 도와드리겠습니다.

드라이브를 읽기 전용으로 마운트한 다음, 볼트 문자열 패턴에 대한 원시 디스크 검색을 수행할 수도 있습니다. 리눅스에서는 다음과 같이 합니다: grep -rboa '{"data":"' /dev/sdX. 성공 여부는 삭제 후 디스크 활동이 얼마나 발생했는지, 그리고 드라이브가 SSD인지 HDD인지에 따라 크게 달라집니다.

시나리오 3: 비밀번호를 잊어버렸지만 볼트 파일은 가지고 있는 경우

이것은 무차별 대입 공격 시나리오입니다. 볼트를 추출한 후, 다음 명령어를 사용하여 hashcat 형식으로 변환하십시오. metamask2hashcat.py 또는 cyclone-github/metamask_extractor. 그런 다음, 비밀번호에 대해 기억나는 내용을 담은 토큰 파일을 사용하여 hashcat(구형 볼트의 경우 모드 26600, 신형 볼트의 경우 모드 26620) 또는 btcrecover를 실행하세요. 비밀번호가 “myDog”에 연도와 기호를 더한 형태였다는 것을 알고 있다면, btcrecover를 통해 이러한 조합을 효율적으로 테스트할 수 있습니다.

시나리오 4: 파이어폭스 복구

다음 위치에서 MetaMask UUID를 찾으세요. about:debugging. 저장소 디렉터리로 이동합니다. 다음 명령어를 사용하여 IndexedDB 바이너리 파일의 압축을 해제합니다. snappy-fox:

./snappy-fox input_file.snappy output.txt

압축이 해제된 출력 파일에서 Vault JSON을 찾으십시오. 그런 다음 Vault Decryptor를 실행하십시오. 해당 JesseBusman/FirefoxMetamaskWalletSeedRecovery 이 파이썬 스크립트는 이 전체 과정을 자동화합니다. 파이어폭스 프로필을 스캔하여 볼트 데이터를 찾아내고, 복호화가 가능한 형식으로 정리된 JSON 데이터를 출력합니다.

시나리오 5: 시드 수입 후 자금이 사라진 것처럼 보이는 경우

파생 경로를 확인하세요. Ledger 시드를 MetaMask로 가져올 경우, 두 번째 계정부터는 다른 주소가 표시되고 잔액이 0으로 나타납니다. Ledger Live에서 자금을 찾으려면 사용자 지정 파생 경로를 설정하여 MEW 또는 MyCrypto를 사용하세요 (m/44'/60'/x'/0/0) 또는 Ledger Legacy (m/44'/60'/0'/x) 경로. Trezor 비밀번호가 필요한 경우, 반드시 정확히 동일한 비밀번호를 입력해야 합니다. 비밀번호가 다르면 동일한 시드에서 완전히 다른 주소 집합이 생성됩니다.

키트에 꼭 챙겨두면 좋은 포렌식 도구들

  • MetaMask 볼트 복호화 도구 – 공식 버전, 오프라인에서 실행 가능, 직접 처리가 가능 .ldb 파일 업로드
  • btcrecover – 패턴 매칭을 통한 비밀번호 복구, MetaMask 추천
  • hashcat – GPU 가속 무차별 대입 공격 (모드 26600, 26610, 26620)
  • snappy-fox – Firefox Snappy 압축 해제
  • CCL Solutions Group의 Python 도구 – 순수 Python을 활용한 LevelDB 파싱, Snappy 압축 해제, V8 역직렬화

결론: 성공적인 회복과 실패를 가르는 요인

지갑을 되찾을 수 있는지, 아니면 영원히 잃게 될지는 거의 항상 문제를 발견한 직후의 첫 몇 분 동안 어떤 조치가 취해졌는지에 달려 있습니다. 가장 치명적인 행동은 동일한 브라우저 프로필에 MetaMask를 다시 설치하는 것인데, 이로 인해 LevelDB 디렉터리가 새로운 파일로 덮어쓰게 됩니다. 두 번째로 치명적인 행동은 볼트 삭제 후 SSD에서 평소처럼 컴퓨터를 계속 사용하는 것으로, 이로 인해 TRIM이 실행되어 섹터 단위의 복구가 불가능해집니다.

이 가이드에서 꼭 기억해야 할 세 가지는 다음과 같습니다. 시드 문구를 종이에 백업해 두세요. 금고(Vault)가 복사하여 보관할 수 있는 특정 폴더에 저장되어 있다는 점을 숙지하세요. 그리고 문제가 발생하면, 복구 시도를 하기 전에 즉시 해당 컴퓨터 사용을 중단하세요. 암호화 방식은 매우 견고합니다. 600,000회의 PBKDF2 반복을 거친 AES-256-GCM은 비밀번호가 취약하지 않은 한 무차별 대입 공격으로 해독될 우려가 없습니다. 하지만 데이터 자체는 의외로 취약합니다. LevelDB 데이터베이스에 저장된 데이터는 불과 몇 킬로바이트에 불과하며, 브라우저에서 ‘정리’ 버튼을 한 번만 클릭해도 사라질 수 있습니다.

제가 복구하지 못한 모든 지갑의 근본 원인은 하나였습니다. 바로 데이터가 손실된 후에도 사용자가 계속 기기를 사용했기 때문입니다. 반면, 제가 성공적으로 복구한 모든 지갑은 데이터가 디스크에 여전히 남아 있던 경우였습니다. 때로는 의외의 장소에 있기도 했고, LevelDB의 추가 전용(append-only) 아키텍처 덕분에 여러 개의 복사본이 존재하기도 했지만, 항상 누군가가 행동에 나서기 전에 잠시 멈춰 생각했기 때문이었습니다.

메타마스크 지갑 복구에 도움이 필요하시면 이메일로 문의해 주세요: contact@cryptorecovery.io 전문적이고 무료인 상담을 받으시거나, 저희 연락처 페이지를 방문해 주세요

[contact-form-7 id=”f245613″ title=”Newsletter”]