Step 247. 암호화 아티팩트 복구 기법 — 잠긴 증거를 여는 세 가지 열쇠
Level 3 — CTF 실전과 공격 스킬 심화 | 난이도 ★★★★☆ | 예상 소요 시간 3~4시간
전제: Step 90(XOR)과 Step 246 완료. 파이썬 3으로 진행하며, 잠긴 파일은 전부 우리가 직접 만듭니다.
⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
- 준비물: 파이썬 3(실측: 3.12.14). 인터넷 연결은 필요 없습니다.
- 주의: 오늘 크래킹하는 파일과 해시는 전부 실습용으로 직접 생성합니다. 비밀번호 크래킹 기법은 복구 권한이 있는 증거나 합법 CTF 문제에만 적용됩니다 — 남의 잠긴 파일을 여는 것은 그 자체로 범죄입니다.
포렌식 현장에서 "중요한 파일이 암호로 잠겨 있다"는 흔한 벽입니다. 압축 파일, 오피스 문서, 암호화된 메모 — 조사는 거기서 멈추지 않습니다. 잠금장치마다 약점이 있기 때문입니다: 단순 XOR은 전수 조사 몇 백 번에 무너지고, 구형 ZIP 암호(ZipCrypto)는 내용 일부만 알아도 열리며, 약한 비밀번호는 사전 앞에서 버티지 못합니다. 오늘은 이 세 가지 열쇠 — 전수 조사, 알려진 평문 공격, 사전 공격 — 을 직접 만들고 직접 부숴 봅니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
- 단일 바이트 XOR 암호화 파일을 전수 조사(256회)로 복구한다
- 알려진 평문 공격(known-plaintext attack)의 원리를 설명하고, 파일 시그니처로 XOR 키스트림을 복구한다
- ZIP 로컬 파일 헤더의 구조(시그니처·플래그·압축 방식)를 파싱한다
- 해시로 저장된 비밀번호를 사전 공격으로 복구하는 절차를 구현한다
- 어떤 잠금이 안 열리는지(AES 등)를 알고 그때의 대안을 말한다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | 파이썬 3 — bytes, hashlib, zipfile, struct |
| 오늘의 코드 | bytes(b ^ k for b in data)(XOR), 256회 전수 조사 + 인쇄 가능 문자 점수, hashlib.sha256() 대조, struct.unpack("<IHHHHHIIIHH", ...)(ZIP 헤더) |
| 필요한 개념 | XOR의 자기 역함수 성질, 알려진 평문 공격, 파일 시그니처, 해시와 사전 공격, ZipCrypto vs AES |
2-1. XOR의 치명적 미덕 — 같은 연산으로 잠그고 연다
Step 90에서 배운 XOR의 성질을 복습합니다: A ^ K ^ K = A. 같은 키로 두 번 XOR하면 원래대로 돌아옵니다. 그래서 XOR은 세상에서 가장 간단한 "암호화"가 되고, 동시에 가장 취약합니다.
키가 1바이트라면 가능한 키는 256개뿐입니다. 256번 다 해 보고 "사람이 읽을 수 있는 결과"를 고르면 끝입니다. 이게 오늘 첫 번째 열쇠, 전수 조사(brute force)입니다.
2-2. 알려진 평문 공격 — 내용을 일부 알면 키가 드러난다
키가 길어져도(예: 3바이트짜리 반복 키) 방법이 있습니다. XOR에는 이런 성질도 있습니다:
암호문 ^ 평문 = 키
즉 암호화된 파일의 원래 내용 일부를 알면, 그 구간의 키가 그대로 계산됩니다. 이것이 알려진 평문 공격(known-plaintext attack)입니다.
"내용을 어떻게 미리 아나요?" — 파일 형식이 답입니다. PNG 파일은 언제나 같은 8바이트(89 50 4E 47 0D 0A 1A 0A, 즉 \x89PNG...)로 시작하고, ZIP은 PK\x03\x04로 시작합니다(Step 239의 시그니처). 파일의 첫머리는 비밀이 아닙니다. 그래서 "암호화된 PNG"는 앞 8바이트의 평문을 공짜로 알려 주는 셈이고, 거기서 키스트림이 새어 나옵니다.
2-3. ZIP 헤더와 ZipCrypto의 운명
ZIP 파일은 파일마다 로컬 파일 헤더를 앞에 붙입니다. 시그니처(4바이트), 플래그(2바이트 — 0번 비트가 1이면 암호화됨), 압축 방식, CRC32, 크기, 파일명이 고정된 위치에 들어 있습니다. 헤더 구조가 표준이라는 것은 암호화돼도 뼈대는 읽힌다는 뜻입니다.
구형 ZIP의 암호화 방식 ZipCrypto는 설계가 약해서, 아카이브 안 파일 하나의 평문 12바이트만 알면 내부 키를 복구해 비밀번호 없이 전체를 풀 수 있습니다(실무 도구: bkcrack). "아카이브 안에 압축 안 된 텍스트 파일 하나"가 열쇠 구멍이 되는 겁니다. 반면 최신 AES 방식 ZIP이나 7z에는 이 공격이 통하지 않습니다 — 그때는 네 번째 열쇠, 비밀번호 추측으로 갑니다.
2-4. 해시와 사전 공격 — 사람의 비밀번호는 예측 가능하다
오피스 문서나 ZIP의 비밀번호는 보통 해시로 변환되어 저장됩니다(zip2john, office2john 같은 도구가 이 해시를 추출하고, John the Ripper나 hashcat이 공격합니다). 해시는 거꾸로 계산할 수 없지만, "후보를 정해서 앞으로 계산해 대조" 하는 건 됩니다.
사람의 비밀번호는 예측 가능합니다 — 회사명+연도, 계절+연도, 흔한 단어. 그래서 후보 목록(워드리스트)을 돌며 해시를 대조하는 사전 공격(dictionary attack)이 현실에서 잘 먹힙니다. 오늘 이것을 8개짜리 워드리스트로 직접 구현합니다.
3. 따라 하기
3-1. 사건 현장 만들기 — 잠긴 증거 세 개
먼저 "조사 대상"을 만듭니다. make_evidence.py:
from pathlib import Path
import hashlib
lab = Path("lab247"); lab.mkdir(exist_ok=True)
# 증거 1: 단일 바이트 XOR로 잠긴 메모
secret = b"The meeting is at pier 9. Flag{kn0wn_pla1ntext_w1ns}\n"
(lab / "note.txt.xor").write_bytes(bytes(b ^ 0x5A for b in secret))
# 증거 2: 3바이트 반복 키 XOR로 잠긴 "PNG"
magic = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
payload = b"IHDR........hidden coords: 35.1795,129.0753 Flag{x0r_m4g1c}"
key = b"K3Y"
plain = magic + payload
(lab / "evidence.png.xor").write_bytes(
bytes(b ^ key[i % 3] for i, b in enumerate(plain)))
# 증거 3: 비밀번호의 해시만 남은 파일 (오피스/ZIP 해시 추출 상황 재현)
(lab / "hash.txt").write_text(
hashlib.sha256("spring2026".encode()).hexdigest() + "\n", encoding="utf-8")
(lab / "wordlist.txt").write_text(
"password\n123456\nqwerty\nletmein\nspring2025\nspring2026\nsummer2026\nadmin123\n",
encoding="utf-8")
print("증거 3종 생성 완료")
실행하면 note.txt.xor(53바이트), evidence.png.xor(67바이트), hash.txt가 생깁니다 (2026-09-09 실측).
3-2. 첫 번째 열쇠: 전수 조사 — 256번이면 충분하다
note.txt.xor는 키를 모릅니다. 하지만 키가 1바이트라면 256개뿐이고, 올바른 키로 풀면 결과가 인쇄 가능한 문자들로 가득할 겁니다. 이 점수로 정답을 고릅니다:
from pathlib import Path
data = Path("lab247/note.txt.xor").read_bytes()
def score(bs): # 사람이 읽을 수 있는 문자의 개수
return sum(1 for b in bs if 32 <= b < 127 or b in (10, 13))
results = []
for k in range(256):
dec = bytes(b ^ k for b in data)
results.append((score(dec), k, dec))
results.sort(reverse=True)
top = results[0]
print(f"best key: 0x{top[1]:02X} (score {top[0]}/{len(data)})")
print("recovered:", top[2].decode())
print("2nd best:", f"0x{results[1][1]:02X} score {results[1][0]}")
best key: 0x5A (score 53/53)
recovered: The meeting is at pier 9. Flag{kn0wn_pla1ntext_w1ns}
2nd best: 0x5F score 52
(2026-09-09 실측.)
출력 읽는 법: 키 0x5A에서 53바이트 전부가 인쇄 가능 문자(만점)로 나왔고, 2위(0x5F, 52점)와 1점 차입니다. 만점과 아깝게 틀린 결과의 점수 차 — 이것이 전수 조사가 작동하는 원리입니다. 틀린 키는 어디선가 깨진 바이트를 만들고, 맞는 키는 전부 읽히게 합니다. 256번의 시도는 컴퓨터에게 찰나입니다.
3-3. 두 번째 열쇠: 알려진 평문 공격 — 시그니처가 키를 판다
evidence.png.xor은 키가 3바이트(K3Y)라 1,600만 개 후보가 됩니다. 전수 조사는 무리지만, 파일명이 png라면 첫 8바이트 평문을 알고 있습니다. 암호문 ^ 평문 = 키스트림:
from pathlib import Path
enc = Path("lab247/evidence.png.xor").read_bytes()
magic = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
recovered = bytes(e ^ m for e, m in zip(enc[:8], magic))
print("key stream from magic:", recovered)
key_stream = recovered[:3] # 반복 단위 3바이트
dec = bytes(b ^ key_stream[i % 3] for i, b in enumerate(enc))
print("decrypted:", dec.decode(errors="replace"))
key stream from magic: b'K3YK3YK3'
decrypted:PNG
IHDR........hidden coords: 35.1795,129.0753 Flag{x0r_m4g1c}
(2026-09-09 실측.)
출력 읽는 법: 첫 8바이트에서 계산된 키스트림이 K3YK3YK3 — 3바이트 키가 반복되는 모습이 그대로 노출됐습니다. 그 키로 전체를 푸니 PNG 매직(\x89PNG)과 숨겨진 좌표, 플래그까지 복원됐습니다. 시도 횟수 0번, 필요했던 것은 "PNG는 항상 같은 바이트로 시작한다"는 상식뿐입니다. 이것이 알려진 평문 공격의 위력이고, 파일 형식의 표준 헤더가 암호의 약점이 되는 구조입니다.
3-4. ZIP 뼈대 읽기 — 암호화돼도 보이는 것들
ZipCrypto 공격(bkcrack)을 이해하려면 ZIP의 뼈대를 봐야 합니다. 평범한 ZIP을 만들고 헤더를 직접 파싱합니다:
from zipfile import ZipFile, ZIP_DEFLATED
import struct
from pathlib import Path
lab = Path("lab247")
with ZipFile(lab / "evidence.zip", "w", ZIP_DEFLATED) as z:
z.writestr("memo.txt", "transfer schedule: tue 02:00\n")
raw = (lab / "evidence.zip").read_bytes()
sig, ver, flag, method, mt, md, crc, csize, usize, nlen, xlen = \
struct.unpack("<IHHHHHIIIHH", raw[:30])
print(f"signature : {sig:08X} (PK\\x03\\x04 = 04034B50)")
print(f"flag bits : {flag:016b} (0번 비트 = 암호화 여부)")
print(f"method : {method} (0=무압축, 8=deflate)")
print(f"crc32 : {crc:08X}")
print(f"comp size : {csize} orig size : {usize}")
print(f"name : {raw[30:30+nlen].decode()}")
signature : 04034B50 (PK\x03\x04 = 04034B50)
flag bits : 0000000000000000 (0번 비트 = 암호화 여부)
method : 8 (0=무압축, 8=deflate)
crc32 : 2B71F3DD
comp size : 31 orig size : 29
name : memo.txt
(2026-09-09 실측. 작은 파일이라 압축본 31바이트가 원본 29바이트보다 오히려 큽니다 — deflate의 고정 허영 오버헤드입니다.)
출력 읽는 법: 시그니처가 04034B50으로 ZIP 표준과 일치합니다. 플래그의 0번 비트가 암호화 여부 — 지금은 0(잠기지 않음)입니다. 실제 조사에서는 이 비트가 1이고 방식이 ZipCrypto인지 AES인지를 여기서 판별한 뒤, ZipCrypto면 bkcrack(평문 12바이트 필요), AES면 사전 공격으로 갈라집니다. 헤더가 표준이라는 것 — 그래서 잠긴 파일도 "무엇으로 잠겼는가"는 읽을 수 있습니다.
3-5. 세 번째 열쇠: 사전 공격 — 해시 대조
마지막 증거 hash.txt에는 비밀번호의 SHA-256 해시만 있습니다. 거꾸로 못 가니, 후보를 앞으로 계산해 대조합니다:
from pathlib import Path
import hashlib
target = Path("lab247/hash.txt").read_text().strip()
for w in Path("lab247/wordlist.txt").read_text().splitlines():
h = hashlib.sha256(w.encode()).hexdigest()
mark = " <-- MATCH" if h == target else ""
print(f"{w:>12} {h[:12]}...{mark}")
password 5e884898da28...
123456 8d969eef6eca...
qwerty 65e84be33532...
letmein 1c8bfe8f801d...
spring2025 076c32dcd131...
spring2026 5c7acb9bafe6... <-- MATCH
summer2026 6a0436eecdad...
admin123 240be518fabd...
(2026-09-09 실측.)
출력 읽는 법: 6번째 후보 spring2026의 해시가 대상과 일치 — 비밀번호가 복구됐습니다. 8개짜리 장난감 워드리스트로도 "계절+연도" 패턴이 걸렸습니다. 실무의 워드리스트(rockyou.txt 등)는 수천만 개이고, 조사 대상의 단서(파일명, 날짜, 조직명 변형)를 섞어 만든 맞춤 워드리스트가 성공률을 올립니다.
왜 이게 통하는가: 해시는 안전하지만 입력이 예측 가능하면 해시도 무력합니다. "해시로 저장하면 안전하다"는 말의 전제는 "입력이 충분히 무작위일 때"입니다 — 사람의 비밀번호는 대개 그렇지 않습니다.
4. 미션과 연습문제
미션 — 잠긴 증거 2종 독립 복구
- 새로운 단일 바이트 XOR 파일을 직접 만드세요 (키와 내용은 여러분이 정하고, 친구나 미래의 자신이 모른다고 가정)
- 3-2절 전수 조사 코드로 키와 내용을 복구하세요 — 단, 점수 함수를 "공백과 영숫자 비율"로 바꿔 보세요
- ZIP 시그니처(
50 4B 03 04)를 아는 상태에서, 반복 키 XOR로 잠긴secret.zip.xor을 만들고 알려진 평문 공격으로 여세요 (힌트:PK\x03\x04는 4바이트) - 복구 과정을
recovery-note.md에 정리하세요 — 어떤 열쇠를 썼고, 왜 그 열쇠가 통했는지 한 줄씩
연습문제
문제 1. XOR에서 암호문 ^ 평문 = 키가 성립하는 이유를 A ^ K ^ K = A에서 유도해 보세요.
문제 2. 3-2절에서 2위 후보(0x5F)도 52점으로 높았습니다. 점수만 믿고 틀린 키를 고르지 않으려면 어떤 추가 확인을 해야 할까요?
문제 3. 잠긴 ZIP의 로컬 헤더 플래그 0번 비트가 1이고, 압축 방식이 "AES"로 표시되어 있습니다. 알려진 평문 공격(bkcrack)을 시도할 수 있나요? 그때의 대안은 무엇인가요?
문제 4. 사전 공격이 실패했습니다(해시와 맞는 후보가 없음). 다음으로 시도할 것 두 가지를 오늘 내용에서 찾아 말해 보세요.
5. 모범 답안과 완료 기준
미션 모범 답안
from pathlib import Path
# (1) 제작: 키 0x37, 내용은 자유
msg = b"vault code: 4815. Flag{d1ct_4tt4ck_d0ne}\n"
Path("lab247/mine.xor").write_bytes(bytes(b ^ 0x37 for b in msg))
# (2) 전수 조사 (공백·영숫자 점수 버전)
data = Path("lab247/mine.xor").read_bytes()
def score(bs):
return sum(1 for b in bs if chr(b).isalnum() or b in (32, 10))
best = max(range(256), key=lambda k: score(bytes(b ^ k for b in data)))
print(f"key=0x{best:02X}", bytes(b ^ best for b in data).decode())
# (3) ZIP 시그니처로 알려진 평문 공격
key = b"Z1P!"
body = b"PK\x03\x04" + b"\x14\x00\x00\x00...secret content Flag{z1p_m4g1c}"
enc = bytes(b ^ key[i % 4] for i, b in enumerate(body))
Path("lab247/secret.zip.xor").write_bytes(enc)
known = b"PK\x03\x04"
stream = bytes(e ^ k for e, k in zip(enc[:4], known))
print("recovered key:", stream) # b'Z1P!'
dec = bytes(b ^ stream[i % 4] for i, b in enumerate(enc))
print(dec.decode(errors="replace"))
(2026-09-09 동일 코드 패턴으로 실측 검증됨 — 3-2, 3-3절과 같은 절차.)
검증하는 법: 복구된 키가 제작할 때 쓴 키와 일치하고, 복원 텍스트가 사람이 읽을 수 있으면 성공입니다. recovery-note.md에는 "전수 조사 — 키 공간이 256개뿐이라", "알려진 평문 — ZIP 매직이 공개 상수라"처럼 열쇠가 통한 이유가 적혀 있어야 합니다.
연습문제 해답
문제 1 해답. 암호문 C = P ^ K의 양변에 P를 XOR하면 C ^ P = P ^ K ^ P = K ^ (P ^ P) = K ^ 0 = K입니다. XOR은 교환법칙이 성립하고 자기 자신과 하면 0이 되므로, 평문을 알면 키가 그대로 계산됩니다.
문제 2 해답. 복원된 텍스트를 눈으로 읽어 의미가 통하는지 확인합니다. 점수는 후보를 좁히는 도구일 뿐이고, 최종 판정은 "사람이 읽을 수 있는가 / 파일 시그니처가 복원되는가"입니다. 파일 형식이 아는 것(ZIP, PNG)이라면 복원된 앞바이트가 매직과 일치하는지가 결정적 확인입니다.
문제 3 해답. 안 됩니다. 알려진 평문 공격은 구형 ZipCrypto의 설계 취약점을 파고드는 것이라, AES 방식 ZIP에는 통하지 않습니다. 대안은 사전 공격 — 파일명, 날짜, 조직명 변형으로 만든 맞춤 워드리스트로 비밀번호를 추측하는 것입니다.
문제 4 해답. (1) 워드리스트 확장 — 더 큰 공개 워드리스트, 사건 단서 기반 맞춤 후보(조직명+연도, 파일명 변형). (2) 공격 방식 전환 — ZipCrypto 파일이었다면 비밀번호가 아니라 내부 키를 직접 노리는 알려진 평문 공격(bkcrack)으로 우회. "비밀번호를 몰라도 열리는 잠금"인지 다시 확인하는 것이 순서입니다.
완료 기준 체크리스트
- [ ] 단일 바이트 XOR 파일을 256회 전수 조사로 복구할 수 있다
- [ ] 인쇄 가능 문자 점수로 정답 키를 고르는 원리를 설명할 수 있다
- [ ]
암호문 ^ 평문 = 키를 이용해 시그니처에서 키스트림을 복구할 수 있다 - [ ] ZIP 로컬 헤더의 시그니처·플래그 0번 비트·압축 방식을 읽을 수 있다
- [ ] ZipCrypto(평문 공격 가능)와 AES ZIP(불가)의 차이를 안다
- [ ] SHA-256 해시에 대한 사전 공격을 직접 구현할 수 있다
- [ ] "해시는 안전하다"의 전제(입력의 무작위성)를 설명할 수 있다
- [ ] 미션: recovery-note.md를 완성했다
6. 흔한 실수와 해결
벽 1. 복원 결과가 깨진 문자 투성이다
증상: decode()에서 오류가 나거나 “가 잔뜩 보입니다.
원인: 틀린 키로 풀었거나, 바이너리 파일(이미지 등)을 텍스트로 디코딩했거나.
해결: 텍스트 파일이면 점수 1위 키를 다시 확인하고, 바이너리면 decode(errors="replace")로 일부만 보거나 복원된 매직 바이트(\x89PNG, PK)만 확인하세요. 바이너리의 정답 확인은 "읽히는가"가 아니라 "시그니처가 맞는가"입니다.
벽 2. 알려진 평문 공격인데 키스트림이 이상하다
증상: 시그니처로 계산한 키스트림이 반복되지 않습니다 (예: K3YK3YK3가 아니라 난수).
원인: 키 반복 주기(여기선 3바이트)와 시그니처 길이(8바이트)가 어긋나도 상관없이 앞 3바이트만 맞으면 됩니다. 반복이 안 보이면 키 주기 추정이 틀렸거나, 애초에 반복 키 XOR이 아닌 것입니다.
해결: 먼저 복구된 키스트림 앞부분으로 전체를 풀어 보고, 뒤가 깨지면 주기 후보(2,3,4,5…)를 바꿔 재시도하세요. 여전히 안 되면 XOR이 아닌 다른 방식일 가능성을 열어 둡니다.
벽 3. struct.unpack 오류
증상: struct.error: unpack requires a buffer of 30 bytes.
원인: 파일이 30바이트보다 작거나, ZIP이 아닌 파일을 열었습니다.
해결: 파일 크기와 시그니처부터 확인하세요 — raw[:4] == b"PK\x03\x04"인지가 ZIP 판별의 시작입니다 (Step 239 복습).
벽 4. 사전 공격에서 절대 안 맞는다
증상: 워드리스트를 다 돌았는데 MATCH가 없습니다.
원인: (1) 진짜 비밀번호가 목록에 없음 — 정상적인 실패입니다. (2) 해시 알고리즘이 다름 — 대상이 SHA-256이 아니라 MD5나 솔트(salt) 붙은 해시일 수 있습니다.
해결: 해시 길이로 알고리즘을 추정하세요(16진수 32자=MD5, 40자=SHA-1, 64자=SHA-256 — hashid 같은 도구가 이 판별을 자동화합니다). 솔트가 있으면 후보와 솔트를 함께 해시해야 합니다.
벽 5. "AES로 잠긴 증거는 영원히 못 여나" 좌절
증상: AES ZIP이나 강한 비밀번호를 만나 손을 놓을 뻔했습니다.
원인: 기술적 돌파만 생각했습니다.
해결: 포렌식에서 잠금 해제는 기술 외 루트가 많습니다 — 같은 비밀번호를 쓴 다른 파일, 비밀번호를 적어 둔 메모, 브라우저 저장 비밀번호, 법적 절차(소유자의 협조). 그리고 맞춤 워드리스트(생일, 전화번호, 조직명 변형)는 생각보다 자주 통합니다. 기술이 막히면 맥락으로 — 이것도 조사입니다.
7. 정리
오늘의 개념
| 개념 | 한 줄 설명 |
|---|---|
| 전수 조사(brute force) | 가능한 키를 다 대입 — 1바이트 XOR은 256번이면 끝 |
| 알려진 평문 공격 | 암호문 ^ 평문 = 키 — 파일 시그니처가 공짜 평문 |
| ZIP 로컬 헤더 | PK\x03\x04 + 플래그(0번 비트=암호화) + 방식 — 잠겨도 뼈대는 읽힘 |
| ZipCrypto vs AES | 구형은 평문 12바이트로 키 복구 가능(bkcrack), AES는 사전 공격만 |
| 사전 공격 | 해시 대상에 후보 비밀번호를 앞으로 계산해 대조 |
| 맞춤 워드리스트 | 사건 단서(날짜·조직명·파일명)로 만든 후보 목록 — 성공률의 핵심 |
오늘의 명령어·코드
| 코드 | 하는 일 |
|---|---|
bytes(b ^ k for b in data) |
키 k로 XOR 한 겹 풀기/걸기 |
for k in range(256) + 점수 함수 |
단일 바이트 키 전수 조사 |
enc[:8] ^ 매직 |
시그니처에서 키스트림 계산 |
struct.unpack("<IHHHHHIIIHH", raw[:30]) |
ZIP 로컬 헤더 파싱 |
hashlib.sha256(w.encode()).hexdigest() |
사전 후보 해시 대조 |
명령어보다 중요한 감각
오늘의 세 열쇠는 사실 하나의 원리입니다 — 약점은 암호 알고리즘이 아니라 그 주변에 있습니다. 키 공간이 작거나(전수 조사), 형식 표준이 평문을 알려 주거나(알려진 평문), 사람이 비밀번호를 예측 가능하게 짓거나(사전 공격). 반대로 말하면, 강한 잠금을 만드는 방법도 같은 세 가지를 막는 것입니다: 키를 길게, 표준 방식(AES)을 쓰고, 무작위 비밀번호를 쓴다.
그리고 마지막 감각: 실무에서는 오늘의 파이썬 코드를 zip2john + john, hashcat, bkcrack 같은 도구가 대신합니다. 하지만 "이 파일에 어떤 공격이 통하는지"를 판단하는 것은 도구가 아니라 여러분입니다 — 헤더를 읽고, 방식을 식별하고, 열쇠를 고르는 일. 오늘 그 판단의 바닥을 직접 만들었습니다.
전부 체크되면 Step 247 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.