랜섬웨어
키가 없는 eCh0raix 피해에서 복호기를 먼저 검증한 방법
eCh0raix 암호화 파일 22,888개를 보존한 채, 키 탐색을 멈춘 근거와 별도 출력 복호기·SHA256 검증으로 즉시 복호 준비를 마친 방법입니다.
2021-09-07 eCh0raix(QNAPCrypt) 후기 변종에 감염된 뒤, 원래 파일명 뒤에 .encrypt가 붙은 피해 파일이 23,015개 남았습니다. 무료 키 복구는 불가능한 상태였지만, 키가 없는 것과 복호기를 준비할 수 없는 것은 다른 문제였습니다. 저는 나중에 올바른 키를 확보했을 때 복호 구현부터 다시 시작하지 않도록, 먼저 복호 로직과 검증 절차를 완성해 두었습니다.
중복본을 대조해 사진 127장을 복구한 뒤에는 그 사진에 대응하는 암호화 파일 127개만 정리했습니다. 그 결과 NAS에는 22,888개의 암호화 원본이 남았습니다. 복구본이 있는 파일만 제한적으로 정리하고, 그 밖의 파일은 키 확보 가능성에 대비해 유지하는 방식이었습니다.
.encrypt 확장자만 되돌리지 않고 암호화 구조부터 확인했습니다
피해 파일은 사진.jpg.encrypt처럼 원래 이름을 유지했습니다. 파일명이 남아 있으면 어떤 파일이었는지는 알 수 있지만, 확장자를 제거하는 작업만으로 내용이 되돌아오지는 않습니다. 복호 도구는 파일명 규칙이 아니라 실제 암호화 구조를 처리해야 했습니다.
확인한 방식은 AES-256 CFB 모드였습니다. 키는 32바이트 문자열이고, 파일 첫 16바이트가 IV로 쓰입니다. 파일 앞부분만 보고 일반적인 AES 파일처럼 처리하면 큰 파일에서 잘못된 결과가 생길 수 있었습니다.
큰 파일은 10MB 또는 파일 크기의 절반을 기준으로 한 period 단위 청크로 나뉩니다. 각 청크에는 16바이트 IV가 다시 들어가고, 암호화된 내용은 2배로 기록됩니다. 원본 끝에는 0x00 7바이트와 eCh0raix 문자열 마커도 붙습니다. 복호기는 첫 IV, 청크별 IV, 끝 마커를 이 형식대로 다루도록 맞췄습니다.
이 구조를 확인한 뒤에는 파일을 열어 보는 정도를 성공 기준으로 삼지 않았습니다. 복호 결과가 원본과 바이트 단위로 같은지를 확인할 수 있어야, 나중에 실제 피해 파일에 적용할 수 있다고 판단했습니다.
C2 키 수신 변종이라는 결론 뒤에는 시드 탐색 결과가 있었습니다
2019년 7월 이후 변종은 AES 키를 C2 서버에서 받는 방식으로 분석되어 있습니다. 이 경우 키 공간은 2^256이므로 AES 키 자체를 브루트포스로 찾는 방법은 현실적인 선택지가 아닙니다. 다만 로컬에서 재현 가능한 제한된 난수 모델이 있었는지는 별도로 확인했습니다.
초 단위 시드와 나노초 시드를 시도했고, Go math/rand 시드는 21억 개 전체를 탐색했습니다. 키 생성 방식 4가지와 문자 집합 6가지를 조합해 24개 경우를 실행했으며, 이 작업은 86분이 걸렸습니다. 결과적으로 맞는 키는 나오지 않았습니다.
이 결과는 복호기의 오류를 뜻하지 않았습니다. 제한된 시드 모델에서 키가 재현되지 않았고, C2에서 키를 전달받는 후기 변종이라는 판단과 맞아떨어졌습니다. 이후에는 후보 생성 범위를 계속 넓히는 대신, 올바른 키가 주어졌을 때 안전하게 실행할 복호 환경을 유지하는 쪽으로 정리했습니다.
| 항목 | 확인값 |
|---|---|
| 초기 피해 파일 | 23,015개 |
| 중복본으로 복구한 사진 | 127장 |
| 삭제한 대응 암호화 파일 | 127개 |
| 보존한 암호화 원본 | 22,888개 |
| Go math/rand 시드 탐색 | 21억 개 전체 |
| 키 생성 방식·문자 집합 | 4가지 × 6가지, 총 24조합 |
| 탐색 시간 | 86분 |
복호 결과와 .encrypt 원본을 같은 위치에 두지 않았습니다
복호 도구는 PowerShell 스크립트로 만들었고, 실행 시 받는 값은 32자 키 하나입니다. 내부 복호기는 Python 기반의 recover.py이며, 필요한 경우 pycryptodome 설치를 시도합니다. Go가 설치된 환경에서는 검증한 recover.go도 사용할 수 있지만, Go가 없어도 Python 경로로 실행할 수 있게 두었습니다.
실행 시 사용하는 키 인자 형식은 다음과 같습니다.
-Key <32자 키>
복호 결과는 9999.decrypted라는 별도 폴더에 기록하도록 했습니다. 스크립트는 .encrypt 원본을 삭제하지 않습니다. 키가 틀렸거나 결과 확인이 더 필요해도 암호화 파일을 다시 입력으로 사용할 수 있도록, 원본과 결과를 처음부터 분리했습니다.
도구 폴더에는 키 탐색용 Go 코드, 분석 스크립트, 로그도 함께 남겼습니다. 원본 복호기 저장소 클론과 포터블 Go는 정리했지만, Go 없이 실행할 수 있는 recover.py와 검증에 필요한 코드는 보존했습니다. 피해 파일의 복호 여부를 결정하는 것은 설치 파일의 보관이 아니라 올바른 키와 검증된 복호 로직이었습니다.
파일이 열리는지보다 SHA256이 원본과 같은지를 기준으로 삼았습니다
복호 로직은 공개 저장소의 알려진 키와 테스트 파일로 검증했습니다. 테스트 파일을 복호한 뒤 결과의 SHA256이 원본과 완전히 일치하는지 비교했습니다. 단순히 이미지나 문서가 열리는 현상은 일부 데이터 손상을 놓칠 수 있으므로, 일치하는 해시값을 통과 조건으로 정했습니다.
recover.py는 검증된 Go 버전의 로직을 옮긴 구현입니다. 알려진 키로 복호한 결과가 SHA256까지 일치했기 때문에, 현재 키가 없다는 문제와 복호 구현의 신뢰성은 분리해 관리할 수 있었습니다.
키 탐색 코드에도 교차 검증 경로를 남겼습니다. 교정한 브루트포서 소스 brute3_corrected.go와 직접 호출 결과를 비교하는 brute3_verify.go를 보존해, 후보 생성 결과와 실제 복호 호출 결과가 어긋나는 상황을 확인할 수 있게 했습니다.
실제 파일 5개 샘플은 코드에 하드코딩되어 있습니다. 이 부분은 현재 피해 파일을 기준으로 되어 있으므로, 다른 피해 사례에 적용하려면 해당 샘플을 바꿔야 합니다. 키 탐색 코드의 결과를 다른 감염 사례에 그대로 대입하지 않은 이유입니다.
복구본이 없는 22,888개는 키 확보 전까지 보존하는 쪽을 택했습니다
중복본으로 이미 복구한 127장에 대응하는 암호화 파일만 삭제한 이유는 분명했습니다. 그 파일들은 복구본이 확인된 범위였지만, 나머지 파일은 아직 유일한 입력 파일이었습니다. 키가 없다는 이유로 이 원본을 지우면, 나중에 키가 공개되거나 확보되었을 때 복호할 대상 자체가 사라집니다.
우선순위는 감염 전 외부 백업 매체를 확인하는 일이었습니다. 옛 PC, 외장하드, 다른 클라우드에 남은 복사본을 찾는 경로가 가장 먼저였습니다. 공개 복호 도구의 신버전도 다시 확인했지만, 당시에는 이미 효과가 없는 것으로 확인했습니다.
신고·등록과 공개 도구 확인에는 No More Ransom도 포함했습니다. 이 사이트는 감염 파일 샘플과 랜섬노트로 종류를 확인하는 Crypto Sheriff, 공개 복호 도구 목록을 제공하며 몸값을 내지 말 것을 권고합니다.
몸값 지불은 복호를 보장하지 않으며 C2 서버의 생존 여부도 확실하지 않아 선택하지 않았습니다. 외부 백업 확인, 공개 복호 도구 재확인, 신고·등록, 암호화 원본 보존을 병행하고, 키가 확보될 경우에는 검증을 마친 복호기로 별도 폴더에 결과를 생성하는 상태로 유지했습니다.
확인한 공식 자료
구현 과정에서 판단 기준을 교차 확인한 공식 문서입니다. 글의 사례와 결론은 운영자가 직접 겪은 작업을 바탕으로 작성했습니다.