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

[네트워크 보안] 2계층, 3계층 개념 및 프로토콜 종류 / ip헤더 체크섬 계산 / DNS서버에 에코 리퀘스트 전송 실습

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

2계층, 3계층 개념 및 프로토콜 종류 / ip헤더 체크섬 계산 / DNS서버에 에코 리퀘스트 전송 실습에 대해서 정리한다.

 

 

2계층의 대표적인 통신 프로토콜

 

* Ethernet
* ARP

 

1. 2계층에서는 이더넷 헤더를 통해 통신

  -> 2계층의 스위치(L2 S/W)가 이더넷 헤더를 참조해서 패킷을 교환해준다.

 

2. 호스트는 상대방의 맥 어드레스를 알아야만 통신이 가능

  -> ARP 캐시 테이블을 참조하여 전송하려는 호스트를 확인

 

3. ARP 캐시 테이블을 오염시키는 방법으로 목적지를 속일 수 있었다.
   ( ARP Cache Poisoning )

 

  - ARP Spoofing: sniffing, MITM attack
  - S/W jamming: 맥 테이블을 공격하는 방법( 맥 테이블을 꽉 채우면 스위치가 dummy 환경으로 작동 )
  - icmp redirect


* 2계층은 신뢰도가 아주 높은 구간으로 반드시 통신이 되어야만 한다


 

인터넷이 안될 때 확인할 수 있는 방법

 

1. 하드웨어 점검
  - 케이블 불량, 이더넷 카드, ... 불량

 

2. 2계층
  - 게이트웨이까지의 통신이 가능한가를 확인
  - 주변 호스트와 통신이 가능한가 확인


 

ARP Spoofing을 막을 수 있는 방법은 없다( 아직까진 )

  - 백신
  - 네트워크 장비
  - 과도한 ARP 패킷(request, reply)을 탐지
  - 맥 캐시 테이블을 정적으로 사용 ( 가장 확실한 방법 )

 

  * 어떠한 방법도 100% 완벽하게 차단하지 못한다.

 

취약점 발생 원인

  - 인증을 하지 않는다. ( 인증을 하게 되면 너무 느려진다 )
  - 동적인 설정


 

3계층

  - 서로 다른 네트워크를 연결
  - 서로 다른 스위치를 쓴다( 물리적으로 분리된 공간 )

 

* 하나의 스위치에 연결 -> 같은 네트워크

 

  - 라우터(L3 S/W), 방화벽, ...
  - PDU: packet
  - protocol: IP, IPSEC, dynamic routing protocol

 

* IP( Internet Protocol )

  - 목적지까지의 경로를 설정
  - IP가 없으면 통신을 할 수 없다.


* ICMP( Internet Control Message Protocol )

  - 네트워크의 에러를 전송
  - 네트워크가 점점 커져서 이제는 icmp만으로는 관리가 어려워졌다.
  - 취약점은 많아졌고, ...

 

공통 헤더
  1). type: 1바이트 ( 0: Echo Reply / 8: Echo Request )
  2). code: 1바이트
    - icmp의 종류를 나타낸다.
  3). chksum: 2바이트

 

* echo request / reply

 

  1). identifier: 2바이트
  2). seq number: 2바이트
  3). payload
    - payload는 데이터를 전달하는 용도가 아니다.
    - icmp의 크기를 조절할때 많이 사용한다.

 

 

체크섬( check sum )

  - 다 더해서 숫자 값이 일치하면 이상이 없고 다르면 이상이 있음을 확인하는 것
  - 모든 경우의 에러를 다 체크하진 못한다.

 

ex).  100

  - 어떤수를 더해서 100을 만들 수 있는 경우는?
  - 99 + 1, 98 + 2, 97 + 3, ...  ( 이런 여러가지 경우의 수가 존재 하기 때문에 모든 에러를 다 체크하진 못한다 )

  - CRC 에러 테크, 패리티 부호, ... 를 같이 사용해서 체크해준다.


 

ip헤더 체크섬 계산 방법

 

1. 2바이트씩 잘라서 모두 더해준다
   ( 헤더에서 체크섬 부분은 0으로 바꾼후 진행한다 )

 

2. 올림수가 발생하는 경우

  - 올림수를 분리해서 더해준다.

  - 올림수가 발생하지 않을 때까지 해준다.

 

3. 1의 보수를 취해준다.


 


 

[실습]
  - 192.168.6.200에 에코 리퀘스트 전송

 

프로토콜 스택

  이더넷 헤더 + 아이피 헤더 + ICMP 헤더

 

* 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) 

 

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 ):
    type = struct.pack('!H', type)
    self._type = 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.unpack('!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.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:
      self.analyze_icmp( 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

 

 

반응형

댓글