랜섬웨어
랜섬웨어 암호화 파일을 열지 않고 복구 가능성을 판별한 세 가지 검사
엔트로피, JPEG 바이트 스터핑(FF00) 비율, IV 중복 검사로 eCh0raix 암호화 파일에 부분 암호화나 IV 재사용 같은 빈틈이 있는지 판별한 방법과 검사 스크립트입니다.
NAS가 eCh0raix 랜섬웨어에 걸려 사진 22,888개가 암호화된 채 남아 있습니다. 키는 공격자만 갖고 있다는 결론이 났는데(변종 확인 과정, 키 탐색 기록), 그래도 하나가 더 남아 있었습니다. 키 없이도 원본을 일부라도 건질 수 있는 구현상의 빈틈이 있는지입니다.
랜섬웨어 중에는 속도 때문에 파일 앞부분만 암호화하거나, 같은 IV를 재사용하는 것들이 있습니다. 그런 빈틈이 있으면 키 없이도 일부 복구가 됩니다. 이걸 파일을 열어 보지 않고 바이트 통계만으로 판별한 방법을 정리합니다.
검사 1. 엔트로피 — 생각보다 쓸모가 없었습니다
엔트로피는 바이트가 얼마나 무작위인지를 0~8비트로 나타냅니다. 암호문은 8에 거의 붙습니다. 그래서 "파일 뒷부분 엔트로피가 낮으면 평문이 남아 있다"고 판단하려 했습니다.
문제는 JPEG 자체가 이미 압축된 데이터라 엔트로피가 원래 높다는 겁니다. 직접 재 보니 멀쩡한 JPEG가 7.979, 순수 난수가 8.000이었습니다. 0.02 차이로는 판단할 수 없습니다. 텍스트나 BMP처럼 압축 안 된 파일이면 쓸 만하지만, 사진에는 다른 기준이 필요합니다.
검사 2. JPEG 바이트 스터핑 비율 — 이게 결정적이었습니다
JPEG 본문에는 규칙이 하나 있습니다. 압축 데이터 안에서 0xFF가 나오면 마커와 헷갈리지 않게 바로 뒤에 0x00을 붙입니다(바이트 스터핑). 그래서 평문 JPEG 본문에서 FF 다음 바이트가 00인 비율은 아주 높습니다.
반대로 무작위 데이터에서 FF 다음이 00일 확률은 1/256, 약 0.39%입니다. 이 비율을 파일 앞·가운데·끝에서 따로 재면, 어느 구간에 평문이 남았는지 바로 보입니다.
검사 스크립트를 만들어 멀쩡한 JPEG(Windows 기본 배경화면)와 3MB 난수로 먼저 시험했습니다.
import math, os, sys
from collections import Counter
def entropy(buf):
c = Counter(buf); n = len(buf)
return -sum(v / n * math.log2(v / n) for v in c.values())
def ff00_ratio(buf):
# JPEG 본문에서 0xFF 뒤에는 거의 항상 0x00(바이트 스터핑)이 온다
ff = [i for i in range(len(buf) - 1) if buf[i] == 0xFF]
return sum(1 for i in ff if buf[i + 1] == 0x00) / len(ff) if ff else 0.0
def check(path, win=1 << 20):
size = os.path.getsize(path)
with open(path, "rb") as f:
parts = {}
for name, pos in (("head", 0), ("mid", max(0, size // 2 - win // 2)), ("tail", max(0, size - win))):
f.seek(pos); parts[name] = f.read(win)
f.seek(0); first = f.read(4096)
f.seek(max(0, size - 2)); last2 = f.read(2)
print(path, f"{size:,} bytes")
for name, buf in parts.items():
print(f" {name:4} entropy={entropy(buf):.3f} bits/byte FF00={ff00_ratio(buf):.1%}")
print(f" Exif={first.count(b'Exif')} JFIF={first.count(b'JFIF')} ends_FFD9={last2 == bytes([0xFF, 0xD9])}")
def iv_report(paths):
ivs = []
for p in paths:
with open(p, "rb") as f:
ivs.append(f.read(16))
print(f"IV {len(ivs)}개, 고유 {len(set(ivs))}개")
print(" 위치별 서로 다른 값 수:", [len({iv[i] for iv in ivs}) for i in range(16)])
if __name__ == "__main__":
if sys.argv[1] == "--iv":
iv_report(sys.argv[2:])
else:
for p in sys.argv[1:]:
check(p)
sample.jpg 542,091 bytes
head entropy=7.979 bits/byte FF00=92.3%
mid entropy=7.979 bits/byte FF00=92.3%
tail entropy=7.979 bits/byte FF00=92.3%
Exif=0 JFIF=0 ends_FFD9=True
random.bin 3,145,728 bytes
head entropy=8.000 bits/byte FF00=0.4%
mid entropy=8.000 bits/byte FF00=0.3%
tail entropy=8.000 bits/byte FF00=0.3%
Exif=0 JFIF=0 ends_FFD9=False
평문 JPEG는 세 구간 모두 92.3%, 난수는 0.3~0.4%입니다. 엔트로피와 달리 차이가 확실합니다.
이걸 암호화된 파일 중 가장 큰 86MB짜리(2020년 카메라 원본)에 돌렸더니 앞·가운데·끝 모두 0.3~0.4%였습니다. 난수와 똑같습니다. 끝부분만 평문으로 남았다면 tail에서 수십 %가 나와야 합니다.
보조 확인: 메타데이터와 끝 마커
평문 JPEG라면 맨 앞에 Exif나 JFIF 문자열이 있고, 맨 끝은 FF D9로 끝납니다. 86MB 파일은 Exif 0, JFIF 0, 끝 두 바이트도 FFD9가 아니었습니다.
다만 이건 보조 지표입니다. 제가 시험한 배경화면 JPEG도 Exif와 JFIF가 0이었습니다. 메타데이터를 지운 JPEG는 흔해서, 이것만으로 암호화 여부를 판단하면 안 됩니다. 반면 카메라 원본은 거의 항상 Exif가 있으니, 카메라 사진에서 0이면 의미가 있습니다.
검사 3. IV 재사용 — CFB 모드의 약점을 확인
eCh0raix는 AES-CFB를 쓰고 파일 맨 앞 16바이트를 IV로 둡니다. CFB는 같은 키에 같은 IV를 두 번 쓰면 두 파일의 첫 블록 암호문을 XOR해서 평문끼리의 관계를 얻을 수 있습니다. 한 파일의 원본이 있으면 다른 파일의 첫 부분이 풀립니다. 중복으로 되찾은 원본이 127장 있어서, IV가 겹치기만 하면 써먹을 수 있는 상황이었습니다.
그래서 22,888개 파일의 앞 16바이트를 전부 뽑아 비교했습니다. 스크립트의 --iv 옵션이 그 일을 합니다. 아래는 난수로 만든 가짜 파일 2,000개에 먼저 돌려 본 결과입니다.
$ python check_encrypted.py --iv iv_*.encrypt
IV 2000개, 고유 2000개
위치별 서로 다른 값 수: [256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256]
당시 실제 22,888개에 같은 검사를 돌린 결과도 같은 모양이었습니다. 고유 IV 22,888개, 중복 0건, 16바이트 모든 자리에서 256가지 값이 다 나왔습니다. 시간이나 카운터로 만든 IV라면 특정 자리 값이 몰려서 256보다 훨씬 적게 나옵니다. 파일마다 제대로 된 난수로 IV를 만들었다는 뜻입니다.
결론
| 검사 | 빈틈이 있다면 | 실제 결과 |
|---|---|---|
| 구간별 FF00 비율 | 일부 구간이 수십 % | 전 구간 0.3~0.4% → 끝까지 전부 암호화 |
| Exif·JFIF·끝 마커 | 평문 흔적 존재 | 없음 |
| IV 중복 | 같은 IV 재사용 | 22,888개 전부 고유 |
세 검사 모두 빈틈이 없다고 나왔습니다. 이 변종은 키 없이는 복구할 방법이 없다는 결론을 여기서 확정했습니다. 그래도 파일은 지우지 않고 보관 중입니다. 나중에 키가 공개되면 바로 돌릴 수 있게 복호기는 준비해 뒀습니다.
다른 랜섬웨어 피해 파일에도 이 순서는 그대로 쓸 수 있습니다. 사진이면 FF00 비율, 문서나 압축 안 된 파일이면 엔트로피를 구간별로 재고, IV 자리가 알려진 방식이면 중복을 봅니다. 복구 도구를 찾아다니기 전에 먼저 해 볼 만합니다.
확인한 공식 자료
구현 과정에서 판단 기준을 교차 확인한 공식 문서입니다. 글의 사례와 결론은 운영자가 직접 겪은 작업을 바탕으로 작성했습니다.