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.flag = 0x0100
dns.questions = 1
dns.answer = 0
dns.authority = 0
dns.additional = 0
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_class = struct.pack('!H', 0x0001)
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으로 끝을 맺는다 )
naver 05 6e 61 76 65 72
com 03 63 6f 6d 00
'프로그래밍 > NETWORK HACKING' 카테고리의 다른 글
[네트워크 보안] DNS Spoofing 실습 / FTP 개념 및 서버 구축 (0) | 2017.07.28 |
---|---|
[네트워크 보안] DNS Spoofing 개념 및 DNS Spoofer를.py 이용한 실습 (0) | 2017.07.27 |
[네트워크 보안] DNS 역질의 구조 설계 (0) | 2017.07.24 |
[네트워크 보안] DHCP 동작 및 역이용 스니핑 / DNS 개념 (0) | 2017.07.20 |
[네트워크 보안] DHCP / wireshark 사용법 (0) | 2017.07.20 |
댓글