Step 124. 패스워드 공격 3 — hashcat과 공격 모드

Step 124. 패스워드 공격 3 — hashcat과 공격 모드

Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간

전제: Step 122~123의 john과 해시 형식 식별($6$ 등), Step 49의 조합 수학을 마쳤다.

  • 준비물: 리눅스 환경(WSL 가능), 파이썬 3. 이 챕터의 hashcat 화면은 GPU 랩에서의 출력 예시이고, 해시 생성·조합 계산은 여러분의 컴퓨터에서 직접 실측합니다.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.

Step 123에서 john으로 shadow 해시를 깨 봤습니다. 그런데 현장에는 더 큰 짐승이 있습니다 — hashcat. GPU의 수천 개 연산 코어를 동원해 초당 수백억 회씩 후보를 대입하는 크래킹 도구의 끝판왕입니다. 오늘의 핵심 질문은 하나입니다 — "모르는 비밀번호를 어떻게 찾는가?" 답은 "전부 다 대입하는 것"이고, hashcat은 그 대입을 얼마나 빨리, 얼마나 똑똑하게 하느냐의 기술입니다. 오늘 배우는 공격 모드 선택이 크래킹 시간을 초에서 년으로, 년에서 초로 바꿉니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • GPU 크래킹이 CPU보다 빠른 이유를 코어 구조로 설명한다
  • 해시 종류별 모드 번호(-m)와 공격 모드(-a 0/1/3)의 뜻을 안다
  • 사전 공격과 마스크 공격을 상황에 맞게 골라 실행한다
  • 마스크의 조합 수를 직접 계산해 "깨지는 시간"을 추정한다
  • echo -n 같은 준비 단계의 함정을 피해 올바른 해시를 만든다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 리눅스 셸 + 파이썬 3 (조합 계산 실측용)
오늘의 도구 hashcat (출력 예시), md5sum, hashlib (실측)
오늘의 명령 hashcat -m 0 -a 0 해시 사전, hashcat -m 0 -a 3 해시 마스크, --show
필요한 개념 GPU 병렬 연산, 해시 모드 번호, 마스크 문자셋(?l ?u ?d ?a), 조합 폭발
오늘의 산출물 모드 선택 기준표 + 조합 계산 연습 노트

2-1. 왜 GPU인가 — 천 명의 단순 작업자

CPU는 똑똑한 작업자 몇 명, GPU는 단순한 작업자 수천 명의 공장입니다. 해시 크래킹은 "후보를 해시해서 비교"라는 단순 작업의 무한 반복 — 머리 나쁜 작업자가 많을수록 유리합니다. 중급 그래픽카드 하나가 MD5를 초당 수백억 회 시도하는 동안, CPU는 천만 회 언저리에 머뭅니다. 같은 해시를 두고 수천 배의 시간 차이가 나는 이유입니다.

단, 이 속도는 해시 종류에 따라 천차만별입니다. MD5·SHA-1처럼 "빠르게 계산되도록 설계된" 해시는 GPU에게 천국이고, bcrypt·yescrypt처럼 "일부러 느리게 설계된" 해시는 GPU도 속수무책입니다. 그래서 현대 시스템이 느린 해시를 쓰는 것이고, Step 123에서 $y$(yescrypt)가 $1$(MD5)보다 안전하다고 배운 이유가 여기 있습니다.

2-2. 해시 모드 번호 — hashcat에게 "이건 무슨 해시야" 알려 주기

hashcat은 해시 문자열만 보고 종류를 추측하지 않습니다. 사용자가 모드 번호로 알려 줘야 합니다.

번호 해시 예시
-m 0 MD5 f30aa7a662c728b7407c54ae6bfd27d1
-m 100 SHA-1 40자리 16진수
-m 1800 sha512crypt ($6$) 리눅스 shadow
-m 7400 sha256crypt ($5$) 리눅스 shadow

실제 환경에서는 hashcat --help | grep -i md5처럼 도움말에서 검색해 번호를 찾습니다. 번호가 틀리면 해시 형식 오류가 나거나, 조용히 영원히 못 깹니다.

2-3. 공격 모드 — 어떤 순서로 후보를 만들 것인가

hashcat의 진짜 실력은 공격 모드(attack mode)에 있습니다. -a 옵션으로 고릅니다.

  • -a 0 사전 공격(dictionary): 준비된 단어 목록을 위에서부터 차례로 대입. 사람의 비밀번호는 단어에서 파생되므로, 실전에서 가장 먼저, 가장 높은 확률로 깨지는 모드입니다. 대표 사전은 유출 비밀번호 1,400만 개짜리 rockyou.txt.
  • -a 1 조합 공격(combinator): 두 사전의 단어를 이어 붙입니다. apple + pieapplepie. "단어 두 개를 붙인" 비밀번호를 노립니다.
  • -a 3 마스크 공격(mask): 후보의 자리별 형태를 직접 지정해 전수 시도. ?d?d?d?d는 "숫자 네 자리 전부"(0000~9999)라는 뜻입니다.

마스크의 문자표(문자셋, charset)입니다.

기호 의미 문자 수
?l 소문자 a~z 26
?u 대문자 A~Z 26
?d 숫자 0~9 10
?s 특수문자 33
?a 위 전부 (출력 가능한 ASCII) 95

마스크 공격의 철학은 이것입니다 — "비밀번호의 패턴을 아는 만큼 시도 횟수가 줄어든다." "8자리 아무거나"를 다 대입하면 95⁸이지만, "소문자 다섯 자리 + 숫자 세 자리"라는 패턴을 알면 26⁵×10³으로 — 얼마나 줄어드는지는 곧 직접 계산해 봅니다.


3. 따라 하기

3-1. 크래킹 대상 만들기 — md5sum 실측

연습용 해시를 직접 만듭니다 (2026-09-09 WSL 실측):

echo -n "hello123" | md5sum
f30aa7a662c728b7407c54ae6bfd27d1  -

읽는 법: 이 32자리 16진수가 hello123의 MD5입니다. 오늘의 크래킹 대상으로 쓸 값이니 복사해 두세요.

여기서 오늘의 첫 함정 — -n을 빼면 어떻게 될까요? 실측해 봤습니다.

echo "hello123" | md5sum
0766c52d63e56019890004b598edd005  -

읽는 법: 완전히 다른 해시가 나왔습니다. echo는 기본적으로 끝에 개행 문자(엔터)를 붙이는데, 해시는 그 개행까지 포함해 계산하기 때문입니다. 파이썬으로도 확인됩니다 (2026-09-09 실측):

import hashlib
hashlib.md5(b"hello123").hexdigest()    # f30aa7a662c728b7407c54ae6bfd27d1
hashlib.md5(b"hello123\n").hexdigest()  # 0766c52d63e56019890004b598edd005

해시를 만들 때 -n을 빼먹으면, 정답이 hello123인 줄 뻔히 알면서도 영원히 못 깨는 해시를 까는 비극이 벌어집니다. 크래킹 대상을 만들 때는 항상 echo -n입니다.

3-2. 사전 공격 — 출력 예시로 읽기

GPU 랩에서의 실행 모습입니다 (이 환경에는 hashcat이 없으므로 출력 예시입니다):

hashcat -m 0 -a 0 hash.txt /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
...
f30aa7a662c728b7407c54ae6bfd27d1:hello123

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Speed.#1.........: 23411.2 MH/s (0.12ms)

읽는 법: 세 곳만 봅니다. ① 해시:평문 형태로 깨진 결과 — hello123이 복구됐습니다. ② Status: Cracked. ③ 속도 23411.2 MH/s — 초당 234억 회 시도라는 뜻입니다. rockyou.txt에 hello123이 들어 있으므로 사전 공격은 순식간에 끝납니다.

3-3. 마스크 공격 — 패턴으로 전수 시도

이번엔 사전 없이, "소문자 5자리 + 숫자 3자리"라는 패턴 정보만으로 깹니다 (출력 예시):

hashcat -m 0 -a 3 hash.txt "?l?l?l?l?l?d?d?d"
f30aa7a662c728b7407c54ae6bfd27d1:hello123

Progress.........: 11881376000/11881376000 (100.00%)

읽는 법: Progress의 분모 — 11,881,376,000. 마스크가 만들어 낸 후보의 총수입니다. 26⁵×10³을 정확히 시도한 것이죠. 이 숫자, 어디서 나왔을까요? 다음 절에서 직접 계산합니다.

3-4. 조합 폭발 계산 — 파이썬 실측

마스크의 시도 횟수를 손으로 계산해 보는 것이 오늘의 핵심 훈련입니다 (2026-09-09 파이썬 3.12 실측):

masks = {
    "?d?d?d?d (숫자4)": 10**4,
    "?l?l?l?l (소문자4)": 26**4,
    "?l?l?l?l?l?d?d?d (소문자5+숫자3)": 26**5 * 10**3,
    "?l?l?l?l?l?l?l?l (소문자8)": 26**8,
    "?a x8 (모든문자8)": 95**8,
}
for mask, n in masks.items():
    print(f"{mask:30s} {n:,} 회")
?d?d?d?d (숫자4)               10,000 회
?l?l?l?l (소문자4)             456,976 회
?l?l?l?l?l?d?d?d (소문자5+숫자3)  11,881,376,000 회
?l?l?l?l?l?l?l?l (소문자8)     208,827,064,576 회
?a x8 (모든문자8)              6,634,204,312,890,625 회

읽는 법: 자릿수가 하나 늘 때마다 시도 횟수가 문자셋 크기만큼 곱해집니다. 이것이 Step 49에서 배운 조합 폭발이고, 비밀번호 길이가 곧 보안인 이유입니다. "소문자 8자리"(2,088억)와 "아무 문자 8자리"(6.6경) 사이의 3만 배 차이도 눈여겨보세요 — 문자 종류를 섞는 것이 왜 강력한지 숫자가 말해 줍니다.

3-5. "얼마나 걸릴까" 추정 — 속도를 곱해 보기

조합 수에 속도를 나누면 예상 시간이 나옵니다. 속도를 CPU 초당 천만 회, GPU 초당 500억 회(MD5 기준의 거친 가정)로 두고 계산해 봤습니다 (2026-09-09 실측 — 속도는 가정값, 조합 수는 실계산):

마스크 시도 횟수 CPU (천만/초) GPU (500억/초)
?l?l?l?l 456,976 0.05초 0.00초
?l?l?l?l?l?d?d?d 11,881,376,000 약 20분 0.24초
?a x 8 6,634,204,312,890,625 약 21년 약 1.5일

읽는 법: 세 가지 교훈이 한 표에 들어 있습니다. 첫째, 짧은 비밀번호는 CPU로도 순삭입니다. 둘째, GPU는 시간 단위를 통째로 바꿉니다(분→초, 년→일). 셋째, 그래도 "8자리 모든 문자"는 GPU로도 하루 반이 걸립니다 — 그리고 이것은 빠른 해시(MD5) 이야기라는 것을 잊지 마세요. yescrypt였다면 이 표 전체가 수만 배씩 불어납니다.

3-6. 결과 확인 — –show

크래킹이 끝난 뒤 결과만 다시 보려면 (출력 예시):

hashcat -m 0 hash.txt --show
f30aa7a662c728b7407c54ae6bfd27d1:hello123

읽는 법: hashcat은 깬 해시를 potfile(결과 저장 파일)에 기록해 두므로, --show는 재크래킹 없이 저장된 결과만 꺼내 보여 줍니다. Step 123의 john --show와 같은 발상입니다 — 도구가 달라져도 "해시:평문" 보고 형식은 같습니다.

3-7. 모드 선택 기준 정리

상황별로 어떤 모드를 먼저 쓸지 정리하면 이렇습니다.

상황 첫 번째 선택
아무 정보가 없다 -a 0 사전(rockyou) — 사람은 단어를 쓴다
"생일+숫자" 같은 패턴 소문이 있다 -a 3 마스크로 그 패턴만 전수
단어 두 개를 붙인 흔적이 있다 -a 1 조합
사전도 마스크도 실패했다 사전+규칙(rule) 또는 마스크 범위 확대 — 그리고 "못 깨는 것도 결과"로 보고서에 기록

: 크래킹은 무작정 돌리는 게 아니라 확률 높은 후보부터 싼 순서대로 소진하는 자원 배분 게임입니다. 사전이 마스크보다 항상 먼저인 이유 — 사전 한 번 돌리는 비용은 초 단위, 큰 마스크는 일 단위이기 때문입니다.


4. 미션과 연습문제

미션 — 두 모드로 같은 해시 깨기

  1. echo -n "test99" | md5sum으로 연습 해시를 만들고 파일로 저장합니다
  2. GPU 랩(또는 --force CPU 환경)에서 사전 공격으로 깨 봅니다 — 사전에 test99를 직접 추가해도 좋습니다
  3. 같은 해시를 마스크 공격(?l?l?l?l?d?d)으로도 깨 봅니다
  4. --show로 결과를 확인하고, 두 모드의 시도 횟수 차이를 계산해 기록합니다
  5. 위키에 "상황별 모드 선택 기준" 한 표를 정리합니다

연습문제

문제 1. echo "pass" | md5sumecho -n "pass" | md5sum의 결과가 다른 이유를 설명해 보세요.

문제 2. 마스크 ?u?l?l?l?l?d?d의 총 시도 횟수를 계산해 보세요.

문제 3. 사전 공격이 마스크 공격보다 거의 항상 먼저 시도되는 이유를 "비용"의 관점에서 설명해 보세요.

문제 4. 같은 8자리 비밀번호인데 MD5 해시는 GPU로 1.5일이면 깨지고 yescrypt 해시는 사실상 못 깹니다. 이 차이를 만드는 해시 설계상의 요인은 무엇인가요?


5. 모범 답안과 완료 기준

미션 모범 답안

echo -n "test99" | md5sum
# 1d56a580bb00ff669f38e5c1f69b497c  -   (여러분의 출력과 비교 — 이 값은 실측)

echo -n "test99" | md5sum | awk '{print $1}' > hash.txt
# 사전 공격: hashcat -m 0 -a 0 hash.txt mydict.txt   (출력 예시 — 사전에 test99 포함)
# 마스크 공격: hashcat -m 0 -a 3 hash.txt "?l?l?l?l?d?d"   (출력 예시)

검증하는 법: ① 마스크 ?l?l?l?l?d?d의 시도 총수가 26⁴×10² = 45,697,600회임을 직접 계산해 봤는가. ② md5sum 결과가 1d56a5…로 시작하는가 — 다르면 -n 누락을 의심. ③ 두 모드 모두에서 같은 평문이 복구됐는가. ④ 모드 선택 기준표가 "아무 정보 없을 때 → 사전"으로 시작하는가.

연습문제 해답

문제 1 해답. echo는 기본적으로 출력 끝에 개행 문자(\n)를 붙입니다. 해시는 입력의 모든 바이트를 계산하므로 개행 유무로 완전히 다른 해시가 나옵니다 (3-1절 실측: hello123이 f30aa7…, hello123\n이 0766c5…).

문제 2 해답. ?u는 26, ?l 네 개는 26⁴, ?d 두 개는 10² — 26×26⁴×100 = 26⁵×100 = 1,188,137,600회. 대문자 시작 + 소문자 4 + 숫자 2는 흔한 비밀번호 패턴(예: Apple01)이라 실전에서 자주 보는 마스크입니다.

문제 3 해답. 사전은 사람이 실제로 쓴 비밀번호들의 모음이라 후보 하나당 성공 확률이 압도적으로 높고, 1,400만 개 정도면 GPU 기준 초 단위로 끝납니다. 반면 마스크는 후보가 기하급수로 불어나 큰 마스크는 시간·일 단위의 비용이 듭니다. "싸고 확률 높은 것부터"가 자원 배분의 정석입니다.

문제 4 해답. 계산 속도를 일부러 늦춘 설계입니다. MD5는 빠르게 계산하도록 만들어져 GPU가 초당 수백억 회 시도할 수 있지만, yescrypt·bcrypt 같은 비밀번호 전용 해시는 반복 연산과 메모리 요구량을 키워 한 번 계산에 비용이 크게 듭니다. 시도 속도가 초당 수천 회 수준으로 떨어지면 전수 시도의 총시간이 수만 배로 불어납니다.

완료 기준 체크리스트

  • [ ] echo -n으로 올바른 연습 해시를 만들 수 있다
  • [ ] 해시 종류별 모드 번호(-m)를 찾는 법을 안다
  • [ ] -a 0(사전)과 -a 3(마스크)의 차이를 설명할 수 있다
  • [ ] 마스크 문자셋(?l ?u ?d ?a)과 시도 횟수를 계산할 수 있다
  • [ ] 조합 수와 속도로 예상 크래킹 시간을 추정할 수 있다
  • [ ] --show로 저장된 결과를 확인할 수 있다
  • [ ] 미션: 두 모드 크래킹과 기준표 작성을 끝냈다

6. 흔한 실수와 해결

벽 1. 정답을 아는데도 안 깨진다

증상: hello123이 정답인데 사전에도 있는데 Status가 Exhausted(전부 시도했으나 실패)입니다.

원인: 열에 아홉은 해시 생성 단계의 -n 누락 — 개행까지 해시된 다른 값을 까고 있습니다 (3-1절 실측 대조).

해결: 대상 해시가 f30aa7a662c728b7407c54ae6bfd27d1인지 확인하세요. 0766c5...로 시작하면 개행이 들어간 것입니다. echo -n으로 다시 만드세요.

벽 2. "No devices found" 또는 너무 느리다

증상 (출력 예시): No devices found/left 오류가 나거나, VM 안이라 GPU가 안 잡힙니다.

원인: 가상머신은 대부분 GPU를 직접 쓰지 못합니다.

해결: hashcat --force로 CPU 실행은 가능합니다 — 느리지만 개념 학습에는 충분합니다. 이 챕터의 목적은 속도가 아니라 모드 선택의 사고방식입니다. 실제 속도 측정은 GPU가 있는 랩 장비의 몫입니다.

벽 3. "Hash-mode was not specified" 같은 형식 오류

증상 (출력 예시): 시작하자마자 해시를 인식하지 못합니다.

원인: -m 번호가 해시 종류와 안 맞습니다. sha512crypt($6$)를 -m 0(MD5)으로 돌리면 형식부터 안 맞습니다.

해결: 해시의 모양부터 식별하세요 — 16진수 32자리면 MD5 후보, $6$로 시작하면 1800. hashcat --help | grep -i <이름>으로 번호를 찾습니다.

벽 4. 마스크를 따옴표 없이 쳤더니 이상하게 동작한다

증상: hashcat -m 0 -a 3 hash.txt ?l?l?l이 쉘에 의해 뒤틀립니다.

원인: ?는 쉘의 와일드카드라 따옴표 없이 쓰면 쉘이 먼저 해석해 버립니다.

해결: 마스크는 항상 따옴표로 감싸세요 — "?l?l?l?l?d?d".

벽 5. rockyou.txt가 없다

증상: /usr/share/wordlists/rockyou.txt가 없다고 나옵니다.

원인: 배포판에 따라 압축(rockyou.txt.gz) 상태이거나 아예 미포함입니다.

해결: 있으면 gunzip으로 풀고, 없으면 연습용으로 직접 작은 사전을 만들어 쓰세요 — printf 'hello123\ntest99\npassword\n' > mydict.txt. 개념 실습에는 열 줄짜리 사전이면 충분합니다.


7. 정리

오늘의 개념

개념 한 줄 설명
GPU 크래킹 단순 연산 코어 수천 개로 후보 대입을 병렬화 — CPU 대비 수천 배
해시 모드 번호(-m) 해시 종류를 도구에게 알려 주는 번호 (MD5=0, sha512crypt=1800)
사전 공격(-a 0) 실제 유출 단어 목록을 차례로 대입 — 가장 싸고 확률 높은 첫 수
조합 공격(-a 1) 두 사전의 단어를 이어 붙여 후보 생성
마스크 공격(-a 3) 자리별 형태(?l?d 등)를 지정해 전수 시도
조합 폭발 자릿수 하나에 문자셋 크기만큼 곱 — 비밀번호 길이가 보안인 이유

오늘의 명령어·코드

명령 하는 일
echo -n "문구" | md5sum 개행 없이 연습용 해시 만들기
hashcat -m 0 -a 0 해시 사전 MD5 사전 공격 (출력 예시)
hashcat -m 0 -a 3 해시 "?l?l?l?l?d?d" MD5 마스크 공격 (출력 예시)
hashcat -m 0 해시 --show 깨진 결과 다시 보기 (출력 예시)
26**5 * 10**3 (파이썬) 마스크 시도 횟수 계산 (실측)

명령어보다 중요한 감각

크래킹의 본질은 도구가 아니라 후보의 순서 설계입니다. 모르는 비밀번호를 찾는 유일한 방법은 전부 대입하는 것이고, 승부는 "확률 높은 후보를 싼 순서로 먼저 대는가"에서 갈립니다. 사전 → 패턴 마스크 → 범위 확대의 순서가 그 설계입니다. 그리고 표 뒤집어 방어자의 눈으로 읽어 보세요 — 여러분이 오늘 계산한 그 숫자들(길이 하나에 곱하기, 느린 해시에 수만 배)이 곧 비밀번호 정책의 근거입니다. 공격자의 계산표가 수비자의 설계도가 되는 지점, 그것이 이 과의 리듬입니다.


전부 체크되면 Step 124 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.