Step 156. MITM 입문 — ARP 스푸핑 1: 신분증 없는 프로토콜

Step 156. MITM 입문 — ARP 스푸핑 1: 신분증 없는 프로토콜

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

전제: Step 80~85의 패킷 기초(계층, 캡처, 필터)를 마쳤다. WSL 리눅스 터미널을 쓸 수 있다.

  • 준비물: WSL 리눅스 + 파이썬 venv(scapy). MS2·Kali 랩이 있으면 마지막 절의 랩 절차까지 진행할 수 있고, 없어도 패킷 수준의 원리는 전부 실측됩니다.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 안전 한계: 오늘 scapy로 조립한 패킷은 전송하지 않습니다 — 조립과 해부(.show())까지만 합니다. 실제 스푸핑 장면(MS2 랩)은 "출력 예시"로 표기합니다. 내 랩 외의 네트워크에 ARP 응답을 뿌리는 것은 절대 금지입니다.

인터넷 통신의 신분 확인은 암호화가 담당하는 줄 알기 쉽지만, 그 한 단계 아래 — 같은 네트워크 안에서 "이 IP를 가진 장비가 물리적으로 어디 있지?"를 묻는 프로토콜에는 신분 확인이 아예 없습니다. 그 프로토콜이 ARP입니다. 누구나 "내가 공유기야"라고 대답할 수 있고, 들은 쪽은 확인 없이 믿습니다.

오늘은 공격 도구를 켜기 전에, 이 믿음의 구조를 패킷 한 장 수준에서 해부합니다. scapy로 정상 ARP와 위조 ARP를 손으로 만들어 나란히 놓고 비교하면 — 둘이 한 치도 다르지 않다는 것, 그래서 이 공격이 왜 막기 어려운지가 보입니다. 원리가 보이고 나서야 다음 챕터의 트래픽 가로채기가 "마법"이 아니라 "필드 두 개 바꾼 패킷"이 됩니다.


1. 학습 목표

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

  • ARP 요청(who-has)과 응답(is-at)의 구조와 필드를 설명한다
  • ARP 테이블(캐시)이 무엇이고 왜 필요한지 ip neigh로 확인한다
  • ARP에 인증이 없다는 것이 어떤 공격을 가능하게 하는지 설명한다
  • scapy로 정상 ARP 응답과 위조 ARP 응답을 조립하고 차이를 비교한다
  • MITM(중간자 공격)의 구조를 피해자·공격자·게이트웨이의 삼각 관계로 그린다

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

오늘의 도구 한눈에 보기

구분 내용
언어·환경 WSL 리눅스(Ubuntu 24.04), 파이썬 3 + scapy 2.7
오늘의 명령 ip neigh show, arp -a(윈도우), scapy Ether()/ARP(), .show()
필요한 개념 IP 주소 vs MAC 주소, 브로드캐스트, ARP 캐시, MITM
오늘의 산출물 정상/위조 ARP 패킷 대조 노트 — "왜 믿을 수밖에 없는가"의 정리

2-1. IP는 논리 주소, MAC은 물리 주소

같은 네트워크 안에서 데이터는 실제로 MAC 주소를 보고 배달됩니다. IP 주소는 "어느 네트워크의 어느 장비인가"라는 논리적 주소이고, 스위치와 케이블 위의 실제 배달 주소는 네트워크 카드에 새겨진 MAC입니다. 택배로 치면 IP가 주소지, MAC이 그 건물의 현관 번호인 셈입니다.

그런데 내 장비는 상대의 IP만 알 뿐 MAC은 모릅니다. 그래서 물어봐야 합니다 — 그 질문 프로토콜이 ARP입니다.

2-2. ARP의 두 메시지 — who-has와 is-at

ARP 대화는 단 두 종류입니다.

who-has (op=1): "192.168.56.1을 가진 장비, 192.168.56.101에게 MAC을 알려 주세요"
                → 네트워크 전체에 방송(브로드캐스트)
is-at   (op=2): "192.168.56.1은 08:00:27:aa:bb:cc 입니다"
                → 물어본 장비에게 응답

여기에 치명적인 설계가 있습니다. 첫째, 응답에는 "내가 진짜 그 장비다"를 증명하는 수단이 없습니다. 둘째, 심지어 묻지도 않았는데 is-at을 내도 받는 쪽은 테이블에 적습니다(gratuitous ARP). 셋째, 나중에 온 응답이 이전 것을 덮어씁니다. 이 셋이 합쳐져 "가장 늦게, 가장 자주 거짓말하는 자가 이기는" 구조가 됩니다.

2-3. ARP 캐시 — 한번 들으면 믿고 저장하는 표

매번 묻는 것은 낭비라서, 운영체제는 들은 대답을 ARP 캐시(테이블)에 저장해 둡니다. IP → MAC 대응표입니다. 이 표가 오염되면(poisoning) 그 뒤의 모든 패킷이 잘못된 문으로 나갑니다 — 캐시가 신뢰의 저장소이자 공격의 표적인 이유입니다.

2-4. MITM — 중간에 끼어드는 공격

MITM(Man-in-the-Middle, 중간자 공격)의 구조는 단순합니다.

정상:   피해자 ←——————→ 게이트웨이
스푸핑: 피해자 ←——→ 공격자 ←——→ 게이트웨이

피해자의 ARP 테이블에서 "게이트웨이 IP = 공격자 MAC"이 되면, 피해자가 인터넷으로 보내는 모든 패킷은 일단 공격자의 장비에 배달됩니다. 공격자가 그것을 게이트웨이로 대신 전달(포워딩)해 주면 피해자는 인터넷이 잘 되는 채로, 모든 내용을 엿보입니다. 오늘은 이 그림의 재료 — ARP 패킷 그 자체를 손으로 만들어 봅니다.


3. 따라 하기

3-1. 내 장비의 ARP 테이블 읽기

WSL 터미널에서:

ip neigh show

출력 (2026-09-09 실측, MAC 일부 마스킹):

192.168.32.1 dev eth0 lladdr 00:15:5d:xx:xx:xx DELAY

윈도우 쪽에서는 arp -a가 같은 표를 보여 줍니다.

읽는 법: "IP 192.168.32.1은 MAC 00:15:5d:… 에 있다"는 한 줄의 기록입니다 — WSL이 바라보는 게이트웨이의 대응표입니다. lladdr(link-layer address)이 MAC 주소이고, 뒤의 상태(DELAY, STALE, REACHABLE 등)는 이 기록의 신선도입니다. 이 표의 한 줄을 공격자가 바꿔치기하는 것이 오늘 주제입니다.

3-2. 정상 ARP 요청 조립 — who-has

scapy로 "192.168.56.1의 MAC을 알려 달라"는 요청 패킷을 만듭니다. 조립만 하고 전송하지 않습니다.

from scapy.layers.l2 import Ether, ARP

req = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(op=1, pdst="192.168.56.1")
req.show()

출력 (2026-09-09 실측, MAC 일부 마스킹):

###[ Ethernet ]###
  dst       = ff:ff:ff:ff:ff:ff
  src       = 00:15:5d:xx:xx:xx
  type      = ARP
###[ ARP ]###
     hwtype    = Ethernet (10Mb)
     ptype     = IPv4
     op        = who-has
     hwsrc     = 00:15:5d:xx:xx:xx
     psrc      = 192.168.39.82
     hwdst     = 00:00:00:00:00:00
     pdst      = 192.168.56.1

읽는 법: 세 부분으로 봅니다. ① 이더넷 겉봉 — dst=ff:ff:ff:ff:ff:ff는 "네트워크 전원에게"라는 브로드캐스트입니다. ② op=who-has — 이것이 "물어보는 패킷"이라는 표시입니다. ③ 네 개의 주소 필드 — hwsrc/psrc(나의 MAC/IP)와 hwdst/pdst(상대의 MAC/IP). 상대의 MAC은 모르니 00:00:00:00:00:00으로 비어 있습니다. 비어 있는 칸을 채우러 방송하는 것, 그것이 ARP 요청입니다. (psrc의 주소는 scapy가 내 장비의 주소를 자동으로 채운 것 — 환경마다 다릅니다.)

3-3. 정상 ARP 응답 조립 — is-at

"192.168.56.1은 08:00:27:aa:bb:cc입니다"라는 정상 응답을 만듭니다 (주소는 전부 가상의 예시 값).

rep = ARP(op=2, psrc="192.168.56.1", hwsrc="08:00:27:aa:bb:cc",
          pdst="192.168.56.101", hwdst="08:00:27:11:22:33")
rep.show()

출력 (2026-09-09 실측):

###[ ARP ]###
  hwtype    = Ethernet (10Mb)
  ptype     = IPv4
  op        = is-at
  hwsrc     = 08:00:27:aa:bb:cc
  psrc      = 192.168.56.1
  hwdst     = 08:00:27:11:22:33
  pdst      = 192.168.56.101

읽는 법: op=is-at이 응답의 표시입니다. 핵심은 psrc(IP)와 hwsrc(MAC)의 쌍 — "이 IP는 이 MAC에 있다"는 주장 한 문장입니다. 받는 장비는 이 주장을 확인 없이 캐시에 적습니다.

3-4. 위조 응답 조립 — 한 필드만 바꾸면 공격 패킷

이제 공격자(MAC aa:bb:cc:dd:ee:ff)가 "게이트웨이는 나야"라고 거짓 응답을 만듭니다.

fake = ARP(op=2, psrc="192.168.56.1", hwsrc="aa:bb:cc:dd:ee:ff",
           pdst="192.168.56.101", hwdst="08:00:27:11:22:33")
fake.show()
print("정상:", rep.psrc, "->", rep.hwsrc)
print("위조:", fake.psrc, "->", fake.hwsrc)
print("크기:", len(rep), "바이트 /", len(fake), "바이트")

출력 (2026-09-09 실측):

정상: 192.168.56.1 -> 08:00:27:aa:bb:cc
위조: 192.168.56.1 -> aa:bb:cc:dd:ee:ff (IP는 게이트웨이, MAC은 공격자)
크기: 28 바이트 / 28 바이트

읽는 법: 위조 응답은 hwsrc 하나만 다릅니다 — IP는 게이트웨이를 가리키는데 MAC은 공격자의 것. 구조·크기·모양 전부 정상 패킷과 동일합니다. ARP 스푸핑의 전부가 이 한 필드입니다. 받는 장비가 구별할 수단이 없는 이유가, 둘을 나란히 놓으면 바로 보입니다.

3-5. 왜 "확인"이 불가능한가 — 프로토콜의 관점에서

의문이 들어야 합니다: "받는 쪽이 검증하면 되지 않나?" 그런데 ARP에는 검증할 재료가 없습니다. 응답에 서명도, 암호도, 세션도 없습니다. 심지어 요청한 적 없는 응답도 받아들입니다. 이것은 버그가 아니라 1982년의 설계 — 같은 LAN 안은 전원이 신뢰할 수 있는 동료라는 전제 위에서 만들어진 프로토콜입니다. 그 전제가 깨지는 순간(같은 네트워크에 공격자가 들어온 순간) 프로토콜은 무방비가 됩니다.

방어가 어려운 게 아니라, 방어가 프로토콜 바깥에 있어야 하는 이유입니다 — 동적 ARP 검사(DAI), 정적 ARP 항목, 그리고 "같은 IP를 두 MAC이 다투는" 이상 탐지(Step 84의 ‘짝 없는 대답’의 ARP판)가 그 바깥의 장치들입니다.

3-6. (랩이 있는 경우) 스푸핑 한 방향 시연 — 출력 예시

MS2와 Kali가 같은 Host-only 네트워크에 있다면, 실제 오염 장면은 이렇게 진행됩니다. 아래는 출력 예시입니다 — 여러분의 랩에서 재현할 때의 모습이며, 이 책의 실측 환경에서는 패킷 전송을 하지 않았습니다.

# Kali에서 — 피해자(MS2)에게 "게이트웨이는 나야"라고 계속 속이기
sudo arpspoof -i eth0 -t 192.168.56.101 192.168.56.1
# MS2에서 공격 전 arp -a        (출력 예시)
게이트웨이 192.168.56.1 → 08:00:27:aa:bb:cc
# 공격 중 arp -a
게이트웨이 192.168.56.1 → 08:00:27:cc:dd:ee   ← Kali의 MAC으로 바뀜

이 한 줄의 변화 — 게이트웨이 IP에 묶인 MAC이 공격자의 것으로 바뀌는 것 — 이 3-4에서 손으로 만든 위조 패킷이 피해자의 캐시에 적힌 결과입니다. 도구(arpspoof)는 우리가 조립한 패킷을 자동으로 반복 전송할 뿐입니다. 도구의 마법이 사라지는 순간입니다.


4. 미션과 연습문제

미션 — ARP 대조 노트 작성

  1. ip neigh show(또는 윈도우 arp -a)로 내 장비의 ARP 캐시를 읽고, 게이트웨이 행을 노트에 베껴 적습니다
  2. scapy로 who-has 요청을 조립하고, 네 개의 주소 필드(hwsrc/psrc/hwdst/pdst)가 각각 무엇인지 주석을 답니다
  3. 정상 is-at과 위조 is-at을 나란히 조립하고, 다른 필드가 무엇인지 한 줄로 답합니다
  4. "피해자의 캐시에 위조 응답이 적히면 이후 무슨 일이 일어나는가"를 패킷의 흐름으로 서술합니다 — 어디로 가야 할 패킷이 어디로 가는지
  5. 방어 관점 한 문단: "이 공격을 탐지하거나 막으려면 무엇을 보거나 설정해야 하는가"

연습문제

문제 1. IP 주소와 MAC 주소의 역할 차이를, 같은 네트워크 안의 배달 관점에서 설명해 보세요.

문제 2. ARP 응답(is-at)을 받은 장비가 그 내용을 곧바로 믿는 세 가지 설계적 이유(무인증, 묻지 않은 응답 수락, 덮어쓰기)를 각각 설명해 보세요.

문제 3. 3-4에서 정상 응답과 위조 응답이 28바이트로 똑같다는 사실이 "탐지가 어려운 이유"와 어떻게 연결되는지 써 보세요.

문제 4. 공격자가 위조 응답을 한 번만 보내지 않고 계속 반복해서 보내야 하는 이유는 무엇인가요? (힌트: 정상 장비도 응답을 보낸다는 점, 캐시의 신선도)


5. 모범 답안과 완료 기준

미션 모범 답안

3번의 대조 노트 예 (2026-09-09 실측 기반):

[ARP 패킷 대조]
정상 is-at: psrc=192.168.56.1 / hwsrc=08:00:27:aa:bb:cc  ← IP와 MAC이 일치
위조 is-at: psrc=192.168.56.1 / hwsrc=aa:bb:cc:dd:ee:ff  ← IP는 그대로, MAC만 공격자
다른 필드: hwsrc(응답자 MAC) 하나뿐. op, 주소 체계, 크기(28B) 모두 동일.
결론: 받는 장비 입장에서 둘을 구별할 프로토콜상의 수단이 없다.

4번의 서술 예: "캐시에 ‘게이트웨이 IP = 공격자 MAC’이 적힌 뒤, 피해자가 인터넷으로 보내는 패킷은 이더넷 겉봉의 목적지 MAC이 공격자를 가리키므로 물리적으로 공격자 장비에 배달된다. 공격자가 포워딩을 켜 두면 패킷은 다시 게이트웨이로 전달되어 통신은 정상처럼 보이지만, 내용은 공격자를 경유한다."

5번의 예: "같은 IP를 두 개의 MAC이 번갈아 주장하는지 감시(ARP 모니터링), 중요 장비(게이트웨이)는 정적 ARP 항목으로 고정, 스위치의 동적 ARP 검사(DAI) 기능 사용. 근본적으로는 평문 프로토콜을 없애 내용이 보여도 안전하게(HTTPS) 만드는 것이 상호 보완 방어."

검증하는 법: ① who-has의 네 주소 필드 주석이 정확한가(hwdst가 비어 있는 이유까지). ② 정상/위조 대조에서 "다른 필드는 hwsrc 하나"라고 답했는가. ③ 흐름 서술에 "목적지 MAC이 바뀌어 배달이 바뀐다"는 인과가 있는가.

연습문제 해답

문제 1 해답. IP 주소는 네트워크를 넘나드는 논리적 목적지이고, 실제 마지막 구간(같은 LAN 안)의 배달은 이더넷 프레임의 MAC 주소가 담당합니다. 라우터를 넘을 때 IP는 유지되지만 MAC은 구간마다 바뀝니다 — 그래서 같은 네트워크 안에서 "이 IP의 MAC이 뭐지?"를 묻는 절차(ARP)가 필요합니다.

문제 2 해답. 첫째 무인증 — 응답에 "나는 진짜다"를 증명하는 암호학적 장치가 없습니다. 둘째 묻지 않은 응답 수락 — gratuitous ARP로, 아무도 묻지 않았는데 "내 MAC이 바뀌었다"고 선전해도 캐시에 적습니다. 셋째 덮어쓰기 — 같은 IP에 새 응답이 오면 이전 기록을 검증 없이 교체합니다. 셋이 합쳐져 "늦게 온 거짓말이 이기는" 구조가 됩니다.

문제 3 해답. 패킷 수준에서 정상과 위조를 구분하는 표지(플래그, 크기 차이, 이상 필드)가 전혀 없기 때문입니다. 탐지는 패킷 하나를 보고 하는 것이 아니라 맥락을 보고 해야 합니다 — "이 IP는 원래 저 MAC이었는데"라는 이력 비교, 묻지 않은 응답의 빈도 같은 것들입니다. 즉 탐지는 프로토콜이 아니라 관찰(모니터링)의 몫입니다.

문제 4 해답. 진짜 게이트웨이도 정상 응답을 보내고, 그 응답이 캐시를 덮어쓰면 오염이 풀리기 때문입니다(정상으로의 자연 회복). 또 캐시 항목은 시간이 지나면 만료(STALE)되어 다시 묻게 됩니다. 그래서 스푸핑은 지속적으로 위조 응답을 반복해 "항상 내 거짓말이 마지막이도록" 유지합니다 — 2-2의 "가장 늦게, 가장 자주" 규칙의 실전 형태입니다.

완료 기준 체크리스트

  • [ ] ip neigh 또는 arp -a로 ARP 캐시를 읽을 수 있다
  • [ ] who-has와 is-at의 차이(op 필드)를 설명할 수 있다
  • [ ] ARP 패킷의 네 주소 필드(hwsrc/psrc/hwdst/pdst)를 안다
  • [ ] scapy로 정상·위조 응답을 조립하고 차이를 확인했다
  • [ ] 위조 패킷이 정상과 구별 불가능한 이유를 설명할 수 있다
  • [ ] MITM의 삼각 구조(피해자↔공격자↔게이트웨이)를 그릴 수 있다
  • [ ] 미션: ARP 대조 노트 완성

6. 흔한 실수와 해결

벽 1. scapy가 없다 — ModuleNotFoundError: No module named 'scapy'

원인: scapy가 설치되지 않은 파이썬을 실행했습니다.
해결: 이 책의 랩 venv(예: /opt/codex-security-lab/venv/bin/python)를 쓰거나, pip install scapy로 설치하세요. 어느 파이썬으로 실행 중인지 which python3부터 확인하는 습관이 이 벽의 예방책입니다.

벽 2. .show()의 psrc가 내가 적은 값과 다르다

증상: 요청 패킷의 psrc가 이상한 주소(내 네트워크 주소)로 채워져 있습니다.
원인: scapy가 비어 있는 필드를 내 장비의 실제 값으로 자동 완성합니다 — 3-2의 192.168.39.82가 그렇게 채워진 것입니다 (2026-09-09 실측).
해결: 버그가 아니라 친절입니다. 시연용으로 고정하고 싶으면 ARP(op=1, psrc="...", hwsrc="...", pdst="...")처럼 명시하세요.

벽 3. ip neigh가 텅 비어 있다

원인: 캐시는 통신이 있을 때만 생기고 시간이 지나면 지워집니다. 조용한 장비에서는 빈 표가 정상입니다.
해결: 게이트웨이로 ping을 한 번 보내고 다시 보세요 — 대화가 있어야 기록이 생깁니다. "캐시는 대화의 흔적"이라는 이해가 곧 개념 정리입니다.

벽 4. 패킷을 실제로 보내 보고 싶어진다

원인: 호기심은 좋은 것입니다. 하지만 경계가 문제입니다.
해결: 전송 실험은 전부 격리된 내 랩(VirtualBox Host-only 네트워크 속 MS2·Kali)에서만 하세요. 공유기·회사망·기숙사망에 ARP 응답을 뿌리는 것은 같은 방의 모든 장비의 통신을 붙잡는 행위이며, 허가 없으면 범죄입니다. 오늘의 조립·해부 실측만으로 원리는 전부 확인됩니다.

벽 5. ARP와 DNS를 섞는다

증상: "ARP가 주소를 변환하는 것"까지는 아는데 DNS와 헷갈립니다.
원인: 둘 다 "변환"이라는 단어를 써서입니다.
해결: 변환의 방향이 다릅니다 — DNS는 이름→IP(전화번호부), ARP는 IP→MAC(같은 건물 안의 호실 찾기). DNS는 인터넷 어디든 묻지만, ARP는 같은 네트워크 안에서만 작동합니다.


7. 정리

오늘의 개념

개념 한 줄 설명
ARP 같은 네트워크 안에서 IP→MAC을 묻는 프로토콜 — 신분증 없음
who-has / is-at ARP의 두 메시지 — 방송하는 질문과 무인증 대답
ARP 캐시 들은 대답을 저장하는 표 — 오염되면 모든 패킷이 엉뚱한 문으로
gratuitous ARP 묻지도 않았는데 보내는 자기 소개 — 검증 없이 수락됨
ARP 스푸핑 위조 is-at으로 캐시의 한 줄을 바꿔치기 — 바뀌는 필드는 hwsrc 하나
MITM 피해자와 게이트웨이 사이에 끼어들어 전달자 노릇을 하는 공격

오늘의 명령어·도구

명령·도구 하는 일
ip neigh show 리눅스 ARP 캐시 읽기
arp -a 윈도우 ARP 캐시 읽기
Ether()/ARP() + .show() ARP 패킷 조립·해부 (전송 금지)
op=1 / op=2 who-has / is-at 구분자
arpspoof -i eth0 -t 피해자 게이트웨이 랩 전용 스푸핑 도구 (출력 예시로만)

명령어보다 중요한 감각

오늘의 핵심 장면은 정상 패킷과 위조 패킷이 나란히 28바이트로 서 있는 그 대조입니다. "공격자의 패킷"이란 특별한 물건이 아니라, 정상 패킷과 완전히 같은 틀에 한 필드만 다른 물건입니다 — 이것이 보안의 많은 공격이 그러하듯이. 그래서 방어는 패킷을 보는 것이 아니라 맥락을 보는 것입니다: 이 IP는 원래 어느 MAC이었는가, 이 응답을 누가 물었는가, 빈도가 정상인가. 그리고 기억하세요 — 이 기술의 사용 면허는 "내 랩"이라는 경계선이고, 그 경계 안에서 원리를 완전히 이해하는 것이 오늘의 목표였습니다.


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