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

[네트워크 보안] DNS header 구조 / 파이썬을 이용해 DNS 요청 보내서 응답 받아오기

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

DNS header 구조 / DNS Request 보내서 Response 받아오기에 대해서 정리한다.

 

 

* dns도 dhcp와 마찬가지로 응용헤더에서 트랜젝션 아이디로 구분 시켜준다.


 

DNS Header

  1). transaction id(2byte): 통신에 대한 식별번호
  2). Flag(2byte): DNS 메세지 타입
  3). questions(2byte): 질의 갯수
     - 요청 필드의 갯수
     - 질의 하려는 도메인의 갯수와 일치
     * 데이터의 크기가 커지면 자동으로 TCP 통신으로 전환된다.
  4). answer(2byte): 응답 필드의 갯수
  5). authority(2byte): 권한 필드의 갯수
  6). additional(2byte): 추가적인 정보를 표현를 표현하는 필드의 갯수

 

------------고정 사이즈 ( 전체 사이즈는 필드 정보에 따라서 가변적이다 )

 

* questions 
  1). 질의하려는 도메인의 이름(가변적)
  2). 질의하려는 레코드 타입(2byte): A(0x0001), ...
  3). 질의하려는 클래스 타입(2byte): IN(0x0001)

 

* answer
  1). 질의하려는 도메인의 이름(가변적)
  2). 질의하려는 도메인의 레코드 타입(2byte)
  3). 질의하려는 도메인의 클래스 타입(2byte)
  4). ttl(4byte): 캐시하는 시간
  5). 데이터의 길이: A 레코드(4byte)
  6). 데이터 필드: 레코드에 따라 달라짐


 

 

( dns 요청을 할때 헤더에 questions 필드의 질의 갯수에 따라서 헤더 크기가 결정되고 그 내용은 Queries에 적히게 된다.

보통 요청을 할때는 questions는 1인 경우가 많다 )

 

( DNS 응답 패킷을 보면 Answer와 Authority 필드에 1이 설정이 되있고 아래 자세하게 내용이 나와있는걸 볼 수있고

Answer, Authority, Additional 필드의 값은 해당 도메인별 응답마다 다 다른값을 가진다)

 

 


 

 

- 파이썬을 이용해 DNS 요청 보내서 응답 받아오기

 

   * 파이썬을 이용해서 dns 요청 header를 구성한다음 UDP socket을 이용해서 요청을 보내고

    정상적으로 응답이 온다면 wireshark를 통해서 확인한다.

 

dns_request.py 

 

import socket

import struct
from header.dns import *

sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
sock.bind( ('', 30303) )
dns = Dns()
dns.transaction_id = 0x1234
dns.flag = 0x0100
dns.questions = 1
dns.answer = 0
dns.authority = 0
dns.additional = 0
name = input().split('.')
domain = b''
for i in range( 0, len(name) ):
  domain += struct.pack('!B', len(name[i]) )
  domain += name[i].encode()
  if len(name)-1 == i:
    domain += b'\x00'
dns_type = struct.pack('!H', 0x0001)
dns_class = struct.pack('!H', 0x0001)
sock.sendto( dns.header + domain + dns_type + dns_class, ('168.126.63.1', 53) )

 

dns.py

 

import struct

 

class Dns:

 

  def __init__( self, raw=None ):
    if raw != None:
      self._transaction_id = raw[:2]
      self._flag = raw[2:4]
      self._questions = raw[4:6]
      self._answer = raw[6:8]
      self._authority = raw[8:10]
      self._additional = raw[10:12]

 

  @property
  def header( self ):
    return self._transaction_id + self._flag + self._questions + self._answer + \
           self._authority + self._additional

 

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

 

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

 

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

 

  @flag.setter
  def flag( self, flag ):
    self._flag = struct.pack('!H', flag)

 

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

 

  @questions.setter
  def questions( self, questions ):
    self._questions = struct.pack('!H', questions)

 

  @property
  def answer( self ):
    (answer,) = struct.pack('!H', self._answer)
    return answer

 

  @answer.setter
  def answer( self, answer ):
    self._answer = struct.pack('!H', answer)

 

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

 

  @authority.setter
  def authority( self, authority ):
    self._authority = struct.pack('!H', authority)

 

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

 

  @additional.setter
  def additional( self, add ):
    self._additional = struct.pack('!H', add)

 

 

 

 

 

 

 - wireshark에서 확인해보면 dns 응답 패킷에서 c0가 의미하는 부분은 이름 정보를 직접 표시하지않고 해당 이름이 있는 위치를 가르키는 포인터를 의미하고 그 뒤에 적힌 숫자는 dns header부터 시작해서 해당 이름의 위치를 나타낸다.

    ( 예를 들어 c0 0c 이면 0c는 10진수로 12를 나타내고 dns header부터 12바이트 후에

      해당 이름이 있는걸 확인 할 수 있다 )


  - 킷에서 도메인 이름을 나타날때 .으로 구분하고 구분된 길이를 앞에 숫자로 표현해준다. 
    ( 예를 들어
www.naver.com을 나타내보면 03 77 77 77 05 6e 61 76 65 72 03 63 6f 6d 00 으로
      나타내주고 마지막 마무리는 null 문자인 00으로 끝을 맺는다 )

    www     03 77 77 77
    naver    05 6e 61 76 65 72
    com      03 63 6f 6d 00

 

반응형

댓글