Network Security - Packet Sniffing & Spoofing (2)

조성열·2025년 7월 14일

Network Security

목록 보기
4/10
post-thumbnail
지난 포스트에서 Packet을 어떻게 생성하고 받는지 확인했습니다. 이번 포스트에는 본격적으로 Sniffing에 대해 알아보도록 하겠습니다.

3. How Packets are Recieved?

packet을 받는 과정을 다시 요약해보면 NIC으로 Packet이 들어오면 NIC에 물리적인 Buffer인 Ring Buffer를 거치고, 각 Layer를 지나 User space (Application)에 도달하게 됩니다.
이제 Attacker 입장에서 생각 해봅시다. Network를 통해 들어오는 Packet을 Sniffing 하기 위해선, Raw Socket을 이용 해야합니다. 모든 port에서 들어오는 packet의 내용을 1byte 단위로 모두 보고 싶을 때 packet의 완전 복제본을 만들고 원래 목적지로 보냅니다.

Raw Socket

우리가 새로운 프로토콜을 사용하거나, 헤더의 정보를 이용해 보안 프로그램을 만들 때, 또는 불순한 목적으로 가짜 패킷을 만드는 경우 등에는 헤더를 직접 제어 해야합니다. 간단히 말해서, 어떤 특정 프로토콜의 전송 계층 formatting 없이 IP packet을 직접 송수신 가능하게 하는 Socket입니다.

int main() {
    int PACKET_LEN = 512;
    char buffer[PACKET_LEN];
    struct sockaddr saddr;
    struct packet_mreq mr;

    // Create the raw socket
    int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

    // Turn on the promiscuous mode.
    mr.mr_type = PACKET_MR_PROMISC;
    setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr));

    // Getting captured packets
    while (1) {
        int data_size = recvfrom(
            sock,
            buffer,
            PACKET_LEN,
            0,
            &saddr,
            (socklen_t*)sizeof(saddr)
        );
        if (data_size)
            printf("Got one packet\n");
    }

    close(sock);
    return 0;
}

raw socket을 이용한 packet capture code를 보면, 기존 socket 설정과 달리 모든 interface 대상 packet을 sniffing 해야 하기 때문에, bind 과정이 없습니다.
하지만 원치 않는 정보들도 모두 읽기 때문에, BPF라는 Filter를 사용하여 필요한 정보만 걸러 냅니다.


4. Packet Sniffing Tools

Packet Sniffing 할 때 Scapy(python) Wireshark(or Tshark) Tcpdump를 사용했습니다. Scapy는 구현이 쉽기 때문에 Packet Sniffing 실험을 진행할 때 유용합니다.

#!/usr/bin/python3

from scapy.all import *

pkt = sniff(iface='enp0s3', filter='icmp or udp', count=10)
pkt.summary()

위 코드는 scapy를 활용한 sniffing 예시 코드입니다. sniff함수가 자체적으로 있고, 매개변수로 network interface, filter, count를 입력 받습니다.
pkt.summary()는 sniffing한 결과를 반환하는 함수입니다.

  • iface: Network Interface
  • filter: 어떤 packet을 sniff할건지 지정
  • count: sniff할 packet의 최대 개수를 지정

pkt.summary()는 sniffing한 결과를 반환하는 함수입니다.

#!/usr/bin/python3

from scapy.all import *

def process_packet(pkt):
	pkt.show()
    print("----------------------------")
    
f = 'udp and dst portrange 50-55 or icmp')
sniff(iface='enp0s3', filter = f, prn=process_packet)

다른 예시 코드입니다. 함수를 정의하고, sniff 함수의 prn 매개변수를 통해 call back 함수를 지정할 수도 있습니다.

Network Layer별 Packet

예를 들어 pkt = Ether()/IP()/UDP()/"hello" packet을 이런 식으로 구성했다고 합시다.

Packet 구성 방식

위를 보면 packet을 '/'를 이용하여 구성하고 있는데 이 방식을 stacking 기법이라고 합니다. 마치 packet을 쌓아 올리면서 구성한다고 해서 붙여졌다고 합니다.

이때 구성한 packet을 출력 해보면 <Ether type=IPv4 |<IP frag=0 proto=udp |<UDP |<Raw load='hello' |>>>>
우리가 일반적으로 알고 있듯이 payload가 hello인 것을 확인할 수 있습니다. 하지만 payload는 상대적인 것입니다. 우리 입장(client) 즉, Application 입장에선 hello가 맞지만 Network layer, Transport layer 등 각각의 layer에선 아닙니다.

>>> pkt = Ether()/IP()/UDP()/"hello"
>>> pkt.payload
<IP  frag=0 proto=udp |<UDP  |<Raw  load='hello' |>>>

>>> pkt.payload.payload
<UDP  |<Raw  load='hello' |>>

>>> pkt.payload.payload.payload
<Raw  load='hello' |>

>>> pkt.payload.payload.payload.load
b'hello'

위 코드 실행 결과를 보면 .payload를 몇 번 붙이냐에 따라 출력 값이 달라지고 있습니다. payload를 붙일때마다 각 Layer의 Header가 하나씩 벗겨지면서 나머지 값이 출력되고 있습니다.
정리 해보면 payload라는건 Ether Header를 제외한 나머지 부분 전체라는 것입니다.


이번 포스팅에선 Packet Sniffing에 대한 개념과 간단한 예시에 대해 학습 해봤습니다.
다음 포스팅에선 Packet Spoofing에 대해 작성 해보도록 하겠습니다.

0개의 댓글