본문 바로가기
프로그래밍/NETWORK HACKING

[네트워크 보안] IP Fragment( 단편화 )

by B T Y 2017. 7. 2.
반응형

IP Fragment( 단편화 )에 대해서 정리한다.

 

 

IP Fragmentation( 단편화 )

 

3계층

  - 최대전송사이즈(MTU)
  - Maximum Transfer Unit: 1500byte ( Default 값 )
  - ip packet의 최소 사이즈: 8byte


identification
  - 단편화된 아이피 패킷을 식별

 

flag
  - 000: 단편화(분할)되지 않은 경우
  - 010: 단편화를 하지 않는 경우
  - 001: 단편화가 된 경우( 이 뒤에 단편화된 패킷이 존재 )

 

fragment offset
  - 단편화 된 패킷의 원래 위치( 시작 위치 )


  ! 도착지에서 재조립 하기 위해 필요한 정보

 

MTU: 1500 -> 실제 데이터의 크기: 1480 -> 전체 프레임의 크기: 1514

실제 데이터의 크기 = 1500 - 아이피 헤더(20) + icmp 헤더(8) = 1472

 


4계층

  - 최대전송사이즈(MSU)
  - Maximum Segment Unit: 1500byte

 

* fragrouter는 fragment 기능과 router기능을 가지고 있는 툴


 

- IP Fragment : 20 + 20 + 20

 

  original packet
  icmp(8) + payload(52)

  fragment #1
  ip header
    - total: 40
    - id: 0x1234
    - flag: 1
    - offset: 0

  icmp(8) + payload(12)

 

  fragment #2
    - total: 40
    - id: 0x1234
    - flag: 1
    - offset: 20

  payload(20)

 

  fragment #3
    - total: 40
    - id: 0x1234
    - flag: 0
    - offset: 40

  payload(20)

 

 

파이썬에서 offset 해결책
  - bit shift시에 값의 유실이 일어난다.

  - 해결책1: rotate shift를 사용 ( 파이썬에서는 rotate shift하게 해주는 함수가 있다 )
  - 해결책2: 8의 배수로 사이즈
    - offset 8, 16, 24, ...

 

- 24바이트 icmp echo 전송: 8바이트 분할

1. icmp(8)
  id: 0x1234
  flag: 1
  offset: 0

 

2. payload(8)
  id:0x1234
  flag: 1
  offset: 8

 

3. payload(8)
  id: 0x1234
  flag: 0
  offset: 8

 

 

- 3계층을 이용한 공격 종류


tiny attack
  - offset을 일부러 크거나 작게 만드는 방식
  - offset 값을 작게 만들어서 덮어써서 방화벽을 우회하거나 크게 만들어 운영체제를 마비시킨다.

 

smurf attack  ICMP Echo를 이용해서 증폭기( 같은 네트워크 안에 있는 다른 PC들 )를 이용해서 시작 IP 주소를 

                      피해자(Victim) PC로 속여 브로드캐스트로 다른 PC들에게 ICMP Request를 요청해 피해자 PC로 

                      모든 응답이 가게 하는 공격 방식

LAND attack  출발지/도착지 아이피가 동일한 공격방식

 

* 3계층 해당 공격들은 대부분 현재는 패치가 되어서 현재는 통하지 않는다.

 


 

 

IP Fragment( 단편화 ) 실습

( sniffer.py와 packet.py에 변경된 소스는 분홍색으로 표시 )

 

  * 단편화를 통해서 패킷(ICMP Request)를 보내서 응답(ICMP Reply)가 오는지 확인 해본다.

   

 

fragment.py

 

sock.send( ip2.header + payload3 )
import socket
from header.eth import *
from header.ip import *
from header.icmp import *

 

def make_chksum( header ):

  size = len( header )
  if size % 2:
    header = header + b'\x00'
    size = len( header )

  size = size // 2
  header = struct.unpack('!' + str(size) + 'H', header)
  chksum = sum( header )

  carry = chksum & 0xFF0000
  carry = carry >> 16
  while carry != 0:
    chksum = chksum & 0xFFFF
    chksum = chksum + carry
    carry = chksum & 0xFF0000
    carry = carry >> 16

  chksum = chksum ^ 0xFFFF
  return chksum

 

eth = Eth()
echo = Echo()

 

ip = Ip()
ip1 = Ip()
ip2 = Ip()

 

echo.type = 8
echo.code = 0
echo.chksum = 0
echo.id = 0xabcd
echo.seq = 1
echo.payload = '12345667890123456'
echo.chksum = make_chksum( echo.header )
echo.payload = ''

 

ip.ver = 4
ip.length = 20
ip.service = 0
ip.total = 20 + len( echo.header )
ip.id = 0x1234
ip.offset = 0
ip.ttl = 64
ip.type = 1
ip.chksum = 0
ip.src = '192.168.6.112'
ip.dst = '168.126.63.1'
ip.chksum = make_chksum( ip.header )

 

payload2 = '12345678'.encode()

ip1.ver = 4
ip1.length = 20
ip1.service = 0
ip1.total = 20 + len( payload2 )
ip1.id = 0x1234
ip1.flag = 1
ip1.offset = 8
ip1.ttl = 64
ip1.type = 1
ip1.chksum = 0
ip1.src = '192.168.6.112'
ip1.dst = '168.126.63.1'
ip1.chksum = make_chksum( ip1.header )

 

payload3 = '91234567'.encode()
ip2.ver = 4
ip2.length = 20
ip2.service = 0
ip2.total = 20 + len( payload3 )
ip2.id = 0x1234
ip2.flag = 0
ip2.offset = 16
ip2.ttl = 64
ip2.type = 1
ip2.chksum = 0
ip2.src = '192.168.6.112'
ip2.dst = '168.126.63.1
ip2.chksum = make_chksum( ip2.header )

 

eth.dst = '00:05:66:23:30:19'
eth.src = '00:50:56:3B:25:F8'
eth.type = 0x0800

 

sock = socket.socket( socket.AF_PACKET, socket.SOCK_RAW )
sock.bind( ('eth0', socket.SOCK_RAW) )

 

sock.send( eth.header + ip.header + echo.header )
sock.send( ip1.header + payload2 )
sock.send( ip2.header + payload3 )

 

eth.py

 

import struct

 

class Eth:

 

  def __init__( self, raw=None ):
    if raw != None:
      self._dst = raw[:6]
      self._src = raw[6:12]
      self._type = raw[12:14]

 

  @property
  def header( self ):
    return self._dst + self._src + self._type

 

  @property
  def dst( self ):
    dst = struct.unpack('!6B', self._dst)
    dst = '%02x:%02x:%02x:%02x:%02x:%02x' % dst
    return dst

 

  @dst.setter
  def dst( self, mac ):
    mac = mac.split(':')
    for i in range( len(mac) ):
      mac[i] = int( mac[i] )

 

    self._dst = b''
    for i in range( len(mac) ):
      self._dst += struct.pack('!B', dst[i])

 

  @property
  def src( self ):
    src = struct.unpack('!6B', self._src)
    src = '%02x:%02x:%02x:%02x:%02x:%02x' % src
    return src

 

  @src.setter
  def src( self, ip ):
    ip = ip.split('.')
    for i in range( len(ip) ):
      ip[i] = int( ip[i] )

 

    self._src = b''
    for i in range( len(ip) ):
      self._src += struct.pack('!B', ip[i])

 

  @property
  def type( self ):
    (type,) = struct.unpack('!H', self._type)
    return type

 

  @type.setter
  def type( self, type ):
    self._type = struct.pack('!H', type)

 

ip.py

 

import struct

 

class Ip:

  def __init__( self, raw=None ):
    if raw != None:
      self._verlen = raw[:1]
      self._service = raw[1:2]
      self._total = raw[2:4]
      self._id = raw[4:6]
      self._flag_and_offset = raw[6:8]
      self._ttl = raw[8:9]
      self._type = raw[9:10]
      self._check_sum = raw[10:12]
      self._src = raw[12:16]
      self._dst = raw[16:20]
    else:
      self._verlen = b'\x00'
      self._flag_and_offset = b'\x00\x00'


 

  @property
  def header( self ):
    return self._verlan + self._service + self._total + self._id + self._flag_and_offset + self._ttl + self._type + self._check_sum + self._src + self._dst

 

  @property
  def ver( self ):
    (ver,) = struct.unpack('!B', self._verlen)
    ver = verlen >> 4
    return ver

 

  @ver.setter
  def ver( self, ver ):
    (len,) = struct.pack('!B', self._verlen)
    len = len & 0x0F
    ver = ver << 4
    tmp = ver + len
    self._verlen = struct.pack('!B', tmp)

 

  @property
  def length( self ):
    (len,) = struct.unpack('!B', self._verlen)

 

  @length.setter
  def length( self, len ):
    (ver,) = struct.unpack('!B', self._verlen)
    ver = ver & 0xF0
    tmp = ver + len
    self._verlen = struct.pack('!B', tmp)

 

  @property
  def service( self ):
    (service,) = struct.unpack('!B', self._service)
    return service

 

  @service.setter
  def service( self, service ):
    self._service = struct.pack('!B', service)

 

  @property
  def total( self ):
    (total,) = sturct.unpack('!H', self._total)
    return total

 

  @total.setter
  def total( self, total ):
    self._total = struct.pack('!H', total)

 

  @property
  def id( self ):
    (id,) = struct.unpack('!H', self._id)
    return id

 

  @id.setter
  def id( self, id ):
    self._id = struct.pack('!H', id)

 

  @property
  def flag( self ):
    (flag,) = struct.unpack('!H', self._flag_and_offset)
    flag = flag >> 13
    return flag

 

  @flag.setter
  def flag( self, flag ):
    (offset,) = struct.unpack('!H', self._flag_and_offset)
    offset = offset & 0x1FFF
    tmp = flag + offset
    self._flag_and_offset = struct.pack('!H', tmp)

 

  @property
  def offset( self ):
    (offset,) = struct.unpack('!H', self._flag_and_offset)
    offset = (offset & 0x1FFF) << 2
    return offset

 

  @offset.setter
  def offset( self, offset ):
    (flag,) = struct.unpack('!H', self._flag_and_offset)
    flag = flag & 0xE000
    tmp = flag + offset
    self._flag_and_offset = struct.pack('!H', tmp)

 

  @property
  def ttl( self ):
    (ttl,) = struct.unpack('!B', self._ttl)
    return ttl

 

  @ttl.setter
  def ttl( self, ttl ):
    self._ttl = struct.pack('!B', ttl)

 

  @property
  def type( self ):
    (type,) = struct.unpack('!B', self_type)

 

  @type.setter
  def type( self, type ):
    self._type = struct.pack('!B', type)

 

  @property
  def check_sum( self ):
    (check_sum,) = struct.unpack('!H', self._check_sum)

 

  @check_sum.setter
  def check_sum( self, chksum ):
    self._check_sum = struct.pack('!H', chksum)

 

  @property
  def src( self ):
    src = struct.unpack('!4B', self._src)
    src = '%d.%d.%d.%d' % src
    return src

 

  @src.setter

  def src( self, ip ):
    ip = ip.split('.')
    for i in range( len(ip) ):
      ip[i] = int( ip[i] )

 

    self._src = b''
    for i in range( len(ip) ):
      self._src += struct.pack('!B', ip[i])

 

  @property
  def dst( self ):
    dst = struct.unpack('!4B', self._dst)
    dst = '%d.%d.%d.%d' % dst
    return dst

 

  @dst.setter
  def dst( self, ip ):
    ip = ip.split('.')
    for i in range( len(ip) ):
      ip[i] = int( ip[i] )

 

    self._dst = b''
    for i in range( len(ip) ):
      self._dst += struct.pack('!B', ip[i])

 

icmp.py

 

import struct

 

class Icmp:

 

  def __init__( self, raw=None ):
    if raw != None:
      self._type = raw[:1]
      self._code = raw[1:2]
      self._chksum = raw[2:4]

 

  @property
  def header( self ):
    return self._type + self._code + self._chksum

 

  @property
  def type( self ):
    (type,) = struct.unpack('!B', self._type)
    return type

 

  @type.setter
  def type( self, type ):
    self._type = struct.pack('!B', type)

 

  @property
  def code( self ):
    (code,) = struct.unpack('!B', self._code)
    return code

 

  @code.setter
  def code( self, code ):
    self._code = struct.pack('!B', code)

 

  @property
  def chksum( self ):
    (chksum,) = struct.unpack('!H', chksum)
    return chksum

 

  @chksum.setter
  def chksum( self, chksum ):
    self._chksum = struct.pack('!H', chksum)

 


class Echo( Icmp ):

 

  def __init__( self, raw=None ):
    if raw != None:
      self._id = raw[:2]
      self._seq = raw[2:4]
      self._payload = raw[4:]

 

  @property
  def header( self ):
    return self._type + self._code + self._chksum + self._id + self._seq + self._payload

 

  @property
  def id( self ):
    (id,) = struct.unpack('!H', self._id)
    return id

 

  @id.setter
  def id ( self, id ):
    self._id = struct.pack('!H', id)

 

  @property
  def seq( self ):
    seq = struct.unpack('!H', self._seq)
    return seq

 

  @seq.setter
  def seq( self ):
    self._seq = struct.pack('!H', seq)

 

  @property
  def payload( self ):
    return payload.decode( errors='ignore' )

 

  @payload.setter
  def payload( self, payload ):
    self._payload = payload.encode()

 

packet.py

 

from header.eth import *
from header.ip import *
from header.udp import *
from header.arp import *
from header.icmp import *

 

class Packet:

 

  def __init__( self, raw ):

    self._eth = Eth( raw[:14] )

    if self._eth.type == 0x0800:
      self.analyze_ip( raw[14:] )
 
    if self._eth.type == 0x0806:
      self.analyze_arp( raw[14:] )
  

  def analyze_ip( self, raw ):
    self._ip = Ip( raw )

 

    if self._ip.type == 17:
      self.analyze_udp( raw[20:] )
    elif self._ip.type == 1:
      if self._ip.offset == 0:
        self.analyze_icmp( raw[20:] )
      else:
        self._raw = raw[20:]

 

  def analyze_udp( self, raw ):
    self._udp = Udp( raw )

 

  def analyze_arp( self, raw ):
    self._arp = Arp( raw ) 

 

  def analyze_icmp( self, raw ):
    self._icmp = Icmp( raw )

 

  @property
  def raw( self ):
    return self._raw
 
  @property
  def eth( self ):
    return self._eth

 

  @property
  def ip( self ):
    return self._ip

 

  @property
  def arp( self ):
    return self._arp

 

  @property
  def icmp( self ):
    return self._icmp

 

sniffer.py

 

import socket
import struct
import time
from header.packet import *

 

raw = socket.socket( socket.PF_PACKET, socket.SOCK_RAW )
raw.bind( ('eth0', socket.SOCK_RAW) )

 

while True:
  data, addr = raw.recvfrom( 65535 )
  packet = Packet( data )

 

  if packet.eth.type == 0x0800 and \
    (packet.ip.src == '168.126.63.1' or packet.ip.dst == '168.126.63.1') \
     and packet.ip.type == 1:

 

    print("data: ", data)
    print(packet.ip.src + ' -> ' + packet.ip.dst)
    print("id: ", packet.ip.id, "flag: ", packet.ip.flag, "offset: ", \
           packet.ip.offset)

  

    if packet.ip.offset == 0:
      print("type: ", pakcet.icmp.type, "code: ", packet.icmp.code)
      print()
    else:
      print( packet.raw )
 

 

 

 

 

반응형

댓글