[SK shieldus Rookies 19기][시스템 / 네트워크 보안] - UDP, TCP, 3-Way Handshking, Scapy, Wireshark
1. UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜)
UDP 설명
- 인터넷 프로토콜 스위트의 주요 프로토콜 가운데 하나로, 데이터그램으로 알려진 단문 메시지를 교환하기 위해서 사용
- 연결을 설정하지 않고 수신자가 데이터를 받을 준비를 확인하는 단계를 거치지 않고 단방향으로 정보 전송
- 신뢰성 - 수신자가 메시지를 수신했는지 확인할 수 없음
- 순서 정렬 - 메시지 도착 순서를 예측할 수 없음
- 부하 - TCP보다 속도가 일반적으로 빠르고 오버헤더가 적음
- 8바이트로 고정
- 출발지 포트, 목적지 포트 : 16비트로 65,536개
- 길이 : UDP 페이로드와 UDP 헤더를 더한 데이터 그램의 크기
- 오류검사 : 기본적으로 비활성화
중간에 데이터가 유실되거나 손상되어도 크게 중요하지 않은 프로토콜에 많이 사용한다. 게임, 스트리밍 방송에서 사용하는데 이는 데이터가 빠짐없이 전송되는 것보다는 빠른 응답속도가 중요하기 때문이다.
2. TCP(Transmission Control Protocol, 전송 제어 프로토콜)
TCP 설명
- 일련의 옥텟을 안정적으로, 순서대로, 에러 없이 교환
- 전송 계층에 위치
- 네트워크의 정보 전달을 통제하는 프로토콜
- 연결 지향형 프로토콜
- 흐름 제어
- 에러 제어
- 데이터의 확실한 전송 보장
- TCP 헤더의 크기는 가변적
- TCP Options에 따라 달라지고 Offset에서 지정
- 출발지와 목적지 포트
- 16비트로 65.536
- 일련번호(Sequence Number)
- 전송하는 데이터의 순서를 의미
- 수신 측에서 쪼개진 세그먼트의 순서를 파악해서 재조립할 수 있도록 제공
- 최초로 데이터를 전송할 때는 랜덤한 수로 초기화
- 자신이 보낼 데이터의 1바이트당 일련번호를 1식 증가시켜서 데이터의 순서를 표현
- 확인 또는 승인 번호(Knowledgement Number)
- 수신자가 예상하는 다음 일련번호를 의미
- 데이터를 주고받을 때는 상대방이 보낸 일련번호 + 자신이 받은 데이터의 바이트 수를 확인 번호로 설정
- 연결 설정과 연결 해제 과정에서는 상대방이 보낸 일련번호 + 1을 확인 번호로 설정
- 핸드쉐이킹 과정에서는 데이터를 주고받지 않음
- 오프셋
- 전체 세그먼트에서 헤더가 아닌 데이터가 시작되는 위치를 표시
- 세그먼트 - TCP 헤더 + TCP 페이로드
- 데이터가 시작되는 위치 - TCP 헤더의 끝 - TCP 헤더의 크기가 가변적이므로 필요
- TCP 플래그 : 현재 세그먼트 속성
- CWR(Congestion Window Reduced) - 혼잡 윈도우 크기 감소 신호
- ECN(Explicit Congestion Notification) - 혼잡 신호 발생
- URG(Urgent) - 긴급 데이터
- ACK(Acknowledgement) - 확인 응답 신호
- PSH(Push) - 수신 측에 데이터를 최대한 빠르게 응용 프로그램에 전달
- RST(Reset) - 연결을 강제로 초기화해달라는 요청
- SYN(Synchronize) - 연결 생성
- FIN(Finish) - 연결 종료
- 윈도우 크기
- 슬라이딩 윈도우(Sliding Window) 크기는 한 번에 전송할 수 있는 데이터의 양(크기)
- 오류 검사
- 데이터 송수신 중에 발생하는 오류를 검출하기 위해 사용
- 긴급 포인트
- URG 플래그가 설정된 경우, 해당 데이터를 우선 처리
1) 정상적인 트래픽 전송 과정
최초의 시퀀스 넘버는 랜덤한 수로 초기화한다. 최초의 시퀀스 넘버를 5000, 데이터 크기를 500으로 가정하면 두 번째 데이터의 시퀀스 넘버는 5500이되고, 두 번째 데이터의 크기는 600이면 세 번째 시퀀스 넘버는 6100이 된다.
SN과 Data Size를 전송하면 AN을 돌려주는데 AN은 SN + Data Size이다.
2) 비정상적인 트래픽이 전송되는 과정
SN과 Data Size를 합쳐서 5500을 보냈는데 AN으로 5200이 오면 불일치가 되어 처음 보냈던 트래픽을 다시 보내는데 SN과 Data Size의 합과 AN이 같을 때까지 다시 전송한다.
3. 포트 번호
- 16비트(65,536개를 표현하는 것이 가능)로 구성된 가상적 주소
- 운영체제에서 응용 계층에 속하는 프로토콜을 고유한 식별자 번호로 인식할 때 사용하는 번호
- IANA기구에서 관리
- 잘 알려진 포트 번호(well known port)
- 0 ~ 1023
- 잘 알려진 특정 애플리케이션의 사용을 위해 ICANN에서 할당한 포트
- 일반적으로 서버에서 사용
- C:\Windows\system32\drivers\etc\services에서 확인
- ftp - 21/tcp,FTP control
- ssh - 22/tcp, SSH Remote Login Protocol
- telnet - 23/tcp
- smtp - 25/tcp, Simple Mail Transfer Protocol
- domain - 53/tcp, Domain Name Server
- domain - 53/udp, Domain Name Server
- http - 80/tcp, World Wide Web
- pop3 - 110/tcp, Post Office Protocol - Version 3
- https - 443/tcp, Http over TLS/SSL
- 등록된 포트 번호(Registered port)
- 1024 ~ 49151
- 특정 용도로 사용되기 위해 등록된 포트 번호
- 1433 - MSQSQL
- 3306 - MySQL
- 8080 - HTTP (개발용도)
- 사설 또는 동적 포트 번호(Dynamic port)
- 49152 ~ 65535
- 어느 프로그램에서나 사용할 수 있는 포트
4. 포트 스캔
원격지 호스트를 대상으로 어떤 포트를 사용하고 있는지 확인하는 기법
- nmap
- 오픈소스 기반의 포트 스캐닝 도구
1) Kali에서 namp
80/tcp 포트로 서비스 중인 것을 볼 수 있다.
9999/tcp 포트로 서비스 하지 않는 것을 볼 수 있다.
사용 방법은 "nmap -p 스캔할_포트_번호 호스트"로 사용한다.
5. TCP 연결 설정(3-way handshaking)
모든 통신의 시작은 클라이언트부터 시작한다. 클라이언트가 TCP 플래그 SYN를 1로 설정해서 보낸다. 클라이언트가 SYN를 보낼 때 seq를 100으로 설정해서 보내면, 서버는 그 포트가 대기 중인 것을 확인하면 SYN-ACK를 보내는데 이때 ack는 클라이언트에서 받은 seq에 + 1 해서 ack를 보낸다. 서버가 보내는 seq는 서버가 클라이언트에게 주는 데이터의 순서를 나타낸다.
수신자 AN는 송신자 SN + Data Size or 1이 되고, 송신자 AN는 수신자 SN + Data Size or 1이 되므로 상호 교차 검증이 된다. 클라이언트가 보낸 것은 서버에서 넘어오는 ack를 이용해서 정상적으로 전송되었다는 것을 판단할 수 있다. 클라이언트가 다음에 보낼 seq와 차이가 나면 중간에 소실 되었다는 것을 알 수 있고, 소실 되었다면 재전송을 한다.
마지막으로 클라이언트는 서버에서 보낸 SYN-ACK를 확인했다는 ACK를 보낸다. 이때 seq는 서버에서 보낸 ack와 같다.
ack는 서버에서 준 seq에 +1 한 값이다. 이 값이 확인되면 서버는 자신이 보낸 SYN-ACK 패킷을 잘 받았다는 것을 확인하는 것이다.
6. 트래픽 흐름제어
이러한 데이터가 있을 때 위에서처럼 한 번씩 주고받으면 비효율적이다. 수신만 하다가 설정해 놓은 버퍼 크기만큼 데이터를 받으면 거기에 대해서 응답을 줄 수 있다. 윈도우 사이즈는 조절할 수 있다.
윈도우 사이즈를 설정해서 처리할 수 있는 만큼 받아들이고, 자원의 상황에 따라 크기가 크다면 줄여서 받을 수 있다.
데이터를 받는 중에 오류가 발생하면 데이터가 전부 오지 않는다. 이것을 어떻게 알 수 있는가 하면 다음에 나오는 SN가 앞에서 나오는 SN에 내가 받는 데이터의 크기를 더한 값이다. 그런데 오류가 발생한 SN과 수신한 데이터를 더해서 다음 SN을 비교하면 SN이 일치해야 하는데 일치하지 않기 때문에 오류가 발생해서 데이터가 전부 오지 않는 것을 알 수 있다.
그러면 수신에서 다시 SN 5800을 다시 요청하고 수신이 완료되면 AN 7200을 리턴해 다음 데이터를 보내라는 응답을 보낸다.
7. 연결 종료(4-way handshake)
클라이언트가 종료할 때 FIN을 보내고 서버에서 FIN-ACK를 보내는데, ACK를 보낼 때 약간의 텀이 생긴다. 이는 연결 시점에 만들어 놓은 채널을 close 해야 하기 때문이다. 서버 입장에서는 자원이므로 자원을 해제해야 한다. close가 완료되면 서버는 종료 준비 완료가 되었다는 FIN을 보낸다. FIN을 받은 클라이언트는 알았다는 응답을 전달하고 연결을 종료한다.
8. TCP 동작
- 응용 계층에서 페이로드를 생성하고, 전송 전에 3단계 연결 설정을 수행
- 응용 계층에서 생성한 페이로드를 응용 계층 버퍼에 임시 보관하고 전송 계층에서 SYN 신호를 담은 세그먼트 한 개를 생성
- SYN 세그먼트는 네트워크 계층, 데이터 링크 계층, 물리 계층을 통과 해서 수신지로 전달
- 수신 측에서는 해당 SYN 신호를 전송 계층까지 끌어올린 후 전송 계층에서 SYN/ACK 신호를 담은 세그먼트를 생성해서 송신지로 전달
- 송신 측에서는 해당 SYN/ACK 신호를 전송 계층까지 끌어올린 후 전송 계층에서 ACK 신호를 담은 세그먼트를 생성해서 수신지로 전달해서 3단계 설정을 완료
- 3단계 연결 설정이 완료되면 운영체제는 응용 계층 버퍼에 저장했던 TCP 페이로드를 전송 계층으로 전달
- 전송 계층은 응용 계층에서 전달된 TCP 페이로드를 대상으로 단편화 수행
- 단편화(Fragmentation)
- 전송 효율성과 데이터 기밀성을 위해 TCP 페이로드를 여러 개로 분할하는 기법
- 단편화(Fragmentation)
- 단편화가 끝나면 조각난 페이로드 앞에 출발지 포트와 목적지 포트 등을 담은 헤더가 붙으면서 여러 개의 세그먼트를 생성하고, 각 세그먼트는 네트워크 계층으로 넘어 가면서 각각의 패킷을 생성
9. IP 헤더
네트워크 계층(L3)에서 각 세그먼트/데이터그램 앞에 IP 주소를 추가한 것
- 일반적으로 IP 헤더는 20바이트 크기를 사용
- IP Option 항목을 이용해 21바이트 이상으로 사용이 가능
- 서비스 종류(Type of Service)
- 해당 패킷의 전송 우선순위를 지정
- 회선이 혼잡할 경우 패킷의 전송 우선순위를 부여할 때 사용
- 전체 길이
- IP 헤더를 포함한 패킷 전체의 길이
- Identification, IP Flags, Fragment Offset
- MTU(Ma,imum Transmission Unit : 최대 전송 단위)에 따른 패킷 분할 정보
- IP Flags
- 패킷 분할 여부 표시
- D : Do not fragment
- M : More fragment
- MTU 1500 바이트인 이더넷 구간을 1400 바이크 크기의 패킷이 통과하는 경우
- 패킷 분할 없음
- MTU 1500 바이트인 이더넷 구간을 5900 바이트 크이의 패킷이 통과하는 경우
- 패킷 분할이 발생
- 생존 시간
- 해당 패킷이 통과할 수 있는 라우터의 개수
- 프로토콜
- 상위 계층에 속한 프로토콜 번호
- 수신 측에서 해당 패킷의 속성을 파악하는데 사용
- 헤더 오류 검사
- 비활성화 상태
- 출발지 IP 주소, 목적지 IP 주소
- 32비트의 IP 주소
10. Scapy 이용한 3-Way Handshking
- Scapy
- 파이썬으로 제작된 패킷 조작 프로그램
- 패킷 챕처, 전송, 수정, 디코딩 등의 다양한 기능 제공
1) ls()
- 지원하는 프로토콜 전체 목록
(1) ls(TCP)
- 현재 설정된 TCP 헤더 정보 출력
- ls(프로토콜 이름)
- 해당하는 이름의 프로토콜 헤더 정보 출력
2) TCP 정보
(1) TCP().display()
(2) TCP().show()
- 위에 했던 내용과 같지만 보여지는 형식이 다름
3) IP 헤더 정보 출력
(1) IP().show()
IP().show() 하는 것과 IP에 인스턴스 변수를 만들고 show 하는것은 같다.
4) 현재 IP 헤더의 목적지 주소 변경
- scr = kali.linux 가상머신 IP 주소
- dst = bee.box 가상머신 IP 주소
- 생성자에 매매변수의 값을 설정하는 방법으로 초기화
5) 스니핑(sniffing)
- 시간이 경과한 후 Ctrl + C
- 스니핑 결과를 요약 출력
- S : 연결 유청
- SA : 연결 수락
- A : 연결 수락 확인
- 인덱스 24번의 패킷 상세 조회
- 캡처할 패킷의 개수 지정
11. VSCode 설치
$ sudo apt-get install wget gpg
$ wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
$ sudo install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg
$ sudo sh -c 'echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list'
$ rm -f packages.microsoft.gpg
$ sudo apt install apt-transport-https
$ sudo apt update
$ sudo apt install code
위 명령어를 순서대로 입력해 VSCode 설치한다.
12. Scapy로 구현
kali 가상머신에서 Bee.box 가상머신으로 연결(3-way handshaking) 하는 것을 구현
- sport
- 1024 이후의 임의 포트로 설정
- flags
- SYN(연결요청)
- sprot를 1024부터 65535 중 랜덤으로 설정
- bee.box 가상머신 IP 주소로 변경
- TCP 세그먼트에 IP 헤더를 덧붙여 SYN 패킷 생성
- 전송하고 첫 번째 응답이 올 때까지 대기
13 handshaking.py
kali 가상머신에서 handshaking.py 파일 만들고 실행
from scapy.all import *
# tcp = TCP()
# tcp.sport = RnadNum(1024, 65535)
# tcp.dport = "80"
# tcp.flags = "S"
tcp = TCP(sport=RandNum(1024, 65535), dport=80, flags='S')
# ip = IP()
# ip.src = "192.168.40.129"
# ip.dst = "192.168.40.130"
ip = IP(src="192.168.40.129", dst="192.168.40.130")
syn = ip / tcp
syn_ack = sr1(syn)
ack = ip / TCP(sport=syn_ack[TCP].dport, dport=syn_ack[TCP].sport, flags="A", seq=syn_ack[TCP].ack, ack=syn_ack[TCP].seq+1)
send(ack)
1) wireshark 실행한 상태에서 handshaking.py 실행
2) RST 패킷이 자동으로 전달되는 이유
일반적인 프로그램들은 운영체제를 통해서 패킷이 나가는데 스카피는 자기가 그냥 전송하는 것이다. 갑자기 bee.box서버에서 SYN-ACK가 들어오는 것이다. 갑자기 알 수 없'는 SYN-ACK가 들어오니까 kali에서 리셋 시키는 것이다.
그것 때문에 운영체제가 보내는 리셋이 섞여서 나온다. 이런식으로 리셋이 나오면 bee.box 서버에서는 재연결을 시도한다. 그러면 패킷 분석이 어려워지기 때문에 iptables과 갈은 방화벽에 리셋 패킷이 나가는 것을 막는다.
3) RST 패킷이 나가지 않도록 방화벽에 룰 추가
iptables 리스트를 보면 아무 룰도 적용되어 있지 않다.
위 IP는 kali IP다.
wireshark를 실행하고 다시 handshaking.py를 실행하면 RST가 나오지 않는 것을 확인할 수 있다.