DNS Lab.

IPTables, DNS 패킷 차단 설정 방법 모색 (2) 본문

보안

IPTables, DNS 패킷 차단 설정 방법 모색 (2)

승홍 2017. 1. 13. 21:09


DNS ANY 타입 질의와 DDoS 증폭공격


2013년 초 DNS증폭 DDoS공격은 300Gbps의 트래픽 공격능력을 보여주어 모두를 놀라게 했었습니다.


공격자들은 'isc.org' ANY 질의 또는 'ripe.net' ANY 질의를 이용하였습니다.

이후 'isc.org'와 'ripe.net'에 대한 ANY 질의는 캐시DNS서버를 구동하는 사이트에서 속속 차단조치 되었습니다.

하지만 DNS증폭 DDoS공격에 이들 도메인만 꼭 사용해야 하는 것은 아닙니다. 적당한 다른 도메인을 찾아내어 ANY 질의를 사용할 수 있습니다.


DNS 증폭 DDoS 공격 개념도




DDoS 증폭공격에 특정 도메인 사용 이유


그런데, 왜 수많은 도메인 중에서 하필 isc.org, ripe.net을 이용했을까요?


첫째, 이들은 잘 알려진 도메인입니다. 

isc.org는 DNS BIND 네임서버를 제작 배포하는 기관입니다. 네임서버 S/W 외에도 DNS와 관련된 각종 활동을 활발히 전개하고 있습니다. 

ripe.net은 유럽지역의 RIR입니다. 유럽대륙에 IP주소와 AS번호 자원의 할당업무를 담당하고 있으며, 이외에 DNS 관련 모니터링과 네트워크 모니터링 등 인터넷 운영관리에 관련된 각종 선도적 프로젝트를 수행하고 있는 기관입니다.


둘째, 이들 도메인은 모두 일찌기 DNSSEC을 선도적으로 도입 적용한 도메인입니다. 

DNSSEC을 도입한 도메인은 DNS응답 메시지 사이즈가 늘어납니다. DNS 레코드의 위-변조 방지를 위한 전자서명 레코드(RRSIG)가 응답에 추가 포함되기 때문입니다.



DDoS 증폭공격에 ANY 질의사용 이유


왜 ANY 타입 질의를 사용할까요?

ANY 타입 질의는 메타 질의타입입니다. '메타질의'라는 의미는 그 질의하고자 하는 대상이 실재하지 않다는 의미입니다.

ANY 타입질의가 있지만 "ANY 레코드"는 없습니다. 

ANY 질의는 모든 유형의 레코드를 질의하는 용도로 정의되었습니다. 1980년대 DNS의 초기 표준시절부터  정의되어 사용되어 온 오래된 질의타입입니다.


ripe.net에 대해 ANY 질의를 하면, repe.net 도메인이름에 설정된 '응답 가능한 모든(ANY)' 유형의 DNS 레코드들이 한꺼번에 응답됩니다.

ripe.net 도메인은 존 이름으로써 여기에는 SOA 레코드, NS 레코드, MX 레코드, 메일발송 호스트를 지정하는 TXT(SPF) 레코드 등이 기본적으로 설정됩니다.

요즈음은 웹 브라우저에 www.ripe.net이 아니라 ripe.net 만 입력해도 웹 사이트 홈 페이지에 접속되도록 A 레코드와 AAAA 레코드가 설정되어 있기도 합니다.

DNSSEC이 적용된 경우, ripe.net에 DNSSEC 서명 공개키인 DNSKEY가 설정되어 있고, 서명레코드인 RRSIG 레코드가 각각의 레코드 종류에 따라 다수 설정됩니다.

이 뿐만 아니라, Authority 섹션과 Additional 섹션에 딸려 응답되는 레코드들도 있습니다.

이러한 레코드들이 한꺼번에 응답됩니다. 응답 메시지 전체 사이즈가 커질 수 밖에 없습니다.


DNSSEC이 적용된 ripe.net이나 isc.org에 대해 직접 ANY 질의를 해 보면 응답 메시지 사이즈가 상당히 크다는 것을 확인할 수 있습니다.


isc.org의 ANY 질의를 해보면, 이에 대한 응답메시지에는 ANSWER 섹션에 30개 레코드, AUTHORITY 섹션에 4개, ADDITIONAL 섹션에 15개 레코드가 설정되어 있습니다. 

응답메시지 총 사이즈는 무려 3,731 바이트에 이릅니다.

$
$ dig @192.168.123.104 isc.org ANY +noall +comment +stats

; <<>> DiG 9.11.0b1 <<>> @192.168.123.104 isc.org ANY +noall +comment +stats
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42105
;; flags: qr rd ra; QUERY: 1, ANSWER: 30, AUTHORITY: 4, ADDITIONAL: 15

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 24b32c5f3ab3595b63a9523f5843c93ad2f0d0763cf884ea (good)
;; Query time: 1 msec
;; SERVER: 192.168.123.104#53(192.168.123.104)
;; WHEN: Sun Dec 25 16:43:54 KST 2016
;; MSG SIZE  rcvd: 3731
$


ripe.net의 ANY 질의 경우, 응답메시지에 ANSWER 섹션에 25개 레코드, AUTHORITY 섹션에 7개, ADDITIONAL 섹션에 8개 레코드가 설정되어 있습니다. 

응답메시지 총 사이즈는 2,739 바이트 입니다.

$
$ dig @192.168.123.104 ripe.net ANY +noall +comment +stats

; <<>> DiG 9.11.0b1 <<>> @192.168.123.104 ripe.net ANY +noall +comment +stats
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13022
;; flags: qr rd ra; QUERY: 1, ANSWER: 25, AUTHORITY: 7, ADDITIONAL: 8

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 0d02ab9f6291747570659bfa5843c946961681e3eb551daa (good)
;; Query time: 980 msec
;; SERVER: 192.168.123.104#53(192.168.123.104)
;; WHEN: Sun Dec 25 16:44:06 KST 2016
;; MSG SIZE  rcvd: 2739

$ 


DNS 질의응답의 "증폭"효과를 악용하는 DDoS 증폭공격


DNS증폭 DDoS 공격의 파괴력은 'DNS질의패킷 사이즈' 대비 'DNS응답패킷 사이즈'의 배율이 얼마나 되는가에 달려 있습니다.


ripe.net 경우, ripe.net ANY 질의 패킷길이는 91 바이트, 이에 대한 응답 패킷 길이는 2,325 바이트입니다.  

91바이트 패킷 발생 --> 2,325 바이트 패킷 발생 유발 :=> 25.5 배의 증폭효과 가 발생합니다.

즉, 소량의 패킷으로 대량의 패킷의 발생을 유발시킬 수 있습니다.


isc.org 경우, isc.org ANY 질의 패킷길이는 90 바이트, 이에 대한 응답 패킷 길이는 3,370 바이트입니다. 

90바이트 패킷 발생 --> 3,370 바이트 패킷 발생 유발 :=> 37.4 배의 증폭효과 가 발생합니다.


DDoS 증폭공격은 캐시DNS 네임서버가 작은 사이즈의 DNS질의에 대해 큰 사이즈의 DNS응답 메시지를 발생시키는 특성을 이용합니다.

캐시DNS 네임서버를 "트래픽 증폭기" 용도로 악용하는 공격방식입니다.


DNS증폭 DDoS 공격은 이렇게 발생하는 큰 사이즈의 DNS응답 패킷을 공격목표 사이트로 돌려놓는 방법을 사용합니다.

DNS 질의 패킷의 소스 IP주소를 공격목표 사이트의 서버 IP주소로 설정하는 방식을 사용합니다. 

DNS 질의패킷의 IP 소스 주소를 가짜로 설정하는 것입니다. (IP spoofing)


공격자는 리커시브 네임서버(캐시DNS서버)를 인터넷에서 찾아내어 이들을 동원하여 공격에 악용합니다. 

캐시DNS서버를 사용하는 사용자 호스트를 제한하는 설정없이 인터넷 누구에게나 열려 있는 리커시브 네임서버(캐시DNS서버)가 공격에 악용됩니다. 


"우리 사이트 네임서버가 공격받는게 아니고 다른 사이트를 공격하는 데에 이용되는 것일 뿐이니 괜찮다"고 생각할 수도 있습니다.

그러나 이런 저런 DDoS공격에 캐시DNS 네임서버가 이용되면 어떻게 될까요? 

이 네임서버를 '차단'하는 사이트가 인터넷에서 점차 늘어나게 됩니다. 

공격을 받는 사이트 입장에서는 공격에 '가담한' 네임서버에 대해 아무런 조치 없이 그대로 둘 수는 없습니다. 

공격상황 가운데, 그리고 공격종료 이후에도 차단 조치합니다. DDoS 공격에 이용당하는 네임서버는 보안관리가 되지 않는 서버/사이트로 간주될 수 밖에 없습니다.

공격에 '가담한' 네임서버는 인터넷 이곳 저곳에서 차단당하여 DNS 질의응답을 정상적으로 수행하지 못하는 상태가 될 가능성이 높습니다.

어느 시점이 되면, 이런 저런 도메인 접속이 안되고 있다는 불만을 사용자들이 제기하기 시작할 것입니다. 



IPTables로 모든 ANY 질의 차단하기 위한 설정 방법


ANY 질의를 캐시DNS 네임서버가 스스로 질의하는 경우는 거의 없습니다.

곧, 인터넷 서비스를 진행하는 과정에서 ANY 질의를 사용하는 경우는 거의 없습니다.

관리자가 DNS 상태 점검을 위해 ANY 질의를 하거나, 연구기관에서 DNS 관련 데이터 수집을 위해 사용하기도 합니다. 


ANY 질의를 거의 사용하지 않지만, 사용하는 경우가 드물게 있기도 합니다.

지금은 그렇지 않겠지만, 메일서버 개발자들이 메일서버를 개발하면서 도메인의 ANY 질의를 사용하도록 코딩한 사례가 있습니다.

MX 질의를 따로 하지 않고 도메인의 ANY 질의로 한번에 필요한 레코드를 모두 조회하여 한번에 처리하고자 하는 편이성 때문이었습니다.

전자메일 규격에는 착신 도메인의 MX 레코드가 없는 경우, 그 도메인의 A 레코드에 설정된 IP주소로 메일 착신을 시도하도록 하고 규정하고 있기 때문으로 보입니다. 도메인의 ANY 질의에 대한 응답은 MX 레코드와 도메인의 A, AAAA 2가지 레코드 모두를 하나의 응답 메시지로 한꺼번에 응답합니다.


지금처럼 DNS를 악용하는 공격이 빈발한 시점에서는 DNS ANY 질의는 되도록 차단해 두는 것이 바람직해 보이기도 합니다.



그러면, 모든 ANY 질의를 차단하기 위해서 IPTables 룰셋은 어떻게 작성할 수 있을까요?


DNS 질의메시지의 '질의타입' 필드는 DNS 메시지 첫 바이트를 기준할 때 그 떨어진 위치(offset)가 고정되어 있지 않고 질의하는 도메인이름 길이에 따라 위치가 변동됩니다. 


이러한 특성에 의해 DNS 메시지 어느 지점을 검사할 것인지를 확정하여 정하는 것이 쉽지 않습니다.


그렇다면, '고정된 위치의 데이터 값을 검사하는 방법' 대신에 '어떤 범위 내에 특정 값이 있는지 검사하는 방법'을 고려할 수 있습니다.


IPTables의 STRING 모듈을 여기에 다시 이용할 수 있습니다.


STRING 모듈은 검사 영역을 한정하여 지시할 수 있습니다. 검사대상 영역 내에 --hex-string 옵션으로 지정된 매칭 조건의 데이터 패턴이 존재하는지 검사할 수 있습니다.


먼저, ANY 질의 패킷 샘플 내용을 살펴 봅니다. ripe.net ANY 질의 패킷을 하나 수집하고 DNS 부분을 제외한 나머지 상세부분은 생략처리한 내용입니다.

$
$ tshark -nnn -i enp0s3 -f "udp port 53 and udp[12] & 0x80 = 0" -c 1 -Vx
Capturing on 'enp0s3'
Frame 1: 91 bytes on wire (728 bits), 91 bytes captured (728 bits) on interface 0
...
Ethernet II, Src: 52:54:00:12:35:02, Dst: 08:00:27:79:7a:18
...
Internet Protocol Version 4, Src: 10.0.2.2, Dst: 10.0.2.15
...
User Datagram Protocol, Src Port: 46543 (46543), Dst Port: 53 (53)
...
Domain Name System (query)
    Transaction ID: 0x3857
    Flags: 0x0120 Standard query
        0... .... .... .... = Response: Message is a query
        .000 0... .... .... = Opcode: Standard query (0)
        .... ..0. .... .... = Truncated: Message is not truncated
        .... ...1 .... .... = Recursion desired: Do query recursively
        .... .... .0.. .... = Z: reserved (0)
        .... .... ..1. .... = AD bit: Set
        .... .... ...0 .... = Non-authenticated data: Unacceptable
    Questions: 1
    Answer RRs: 0
    Authority RRs: 0
    Additional RRs: 1
    Queries
        ripe.net: type ANY, class IN
            Name: ripe.net
            [Name Length: 8]
            [Label Count: 2]
            Type: * (A request for all records the server/cache has available) (255)
            Class: IN (0x0001)
    Additional records
        <root>: type OPT
            Name: <root>
            Type: OPT (41)
            UDP payload size: 4096
            Higher bits in extended RCODE: 0x00
            EDNS0 version: 0
            Z: 0x0000
                0... .... .... .... = DO bit: Cannot handle DNSSEC security RRs
                .000 0000 0000 0000 = Reserved: 0x0000
            Data length: 12
            Option: COOKIE
                Option Code: COOKIE (10)
                Option Length: 8
                Option Data: 1e319ccbfe856d3f
                Client Cookie: 1e319ccbfe856d3f
                Server Cookie: <missing>

0000  08 00 27 79 7a 18 52 54 00 12 35 02 08 00 45 00   ..'yz.RT..5...E.
0010  00 4d 11 05 00 00 40 11 51 8b 0a 00 02 02 0a 00   .M....@.Q.......
0020  02 0f b5 cf 00 35 00 39 38 6b 38 57 01 20 00 01   .....5.98k8W. ..
0030  00 00 00 00 00 01 04 72 69 70 65 03 6e 65 74 00   .......ripe.net.
0040  00 ff 00 01 00 00 29 10 00 00 00 00 00 00 0c 00   ......).........
0050  0a 00 08 1e 31 9c cb fe 85 6d 3f                  ....1....m?

1 packet captured
$


이 내용 중에서 바이너리 데이터로 표현된 영역을 살펴 봅니다.


아래 해당 부분을 정리했습니다. DNS 메시지 부분은 보라색으로 표시했습니다. Question 섹션의 질의도메인, 질의타입, 질의클래스 영역은 굵은 글자로 표시했습니다.


0000  08 00 27 79 7a 18 52 54 00 12 35 02 08 00 45 00   ..'yz.RT..5...E.
0010  00 4d 11 05 00 00 40 11 51 8b 0a 00 02 02 0a 00   .M....@.Q.......
0020  02 0f b5 cf 00 35 00 39 38 6b 38 57 01 20 00 01   .....5.98k8W. ..
0030  00 00 00 00 00 01 04 72 69 70 65 03 6e 65 74 00   .......ripe.net.
0040  00 ff 00 01 00 00 29 10 00 00 00 00 00 00 0c 00   ......).........
0050  0a 00 08 1e 31 9c cb fe 85 6d 3f                  ....1....m?


< 04 72 69 70 65 03 6e 65 74 00 00 ff 00 01 > 은 질의 정보인 ripe.net ANY IN 이 지정된 부분입니다. 이 중에서 ANY IN 요소 부분은 < 00 ff 00 01 > 입니다. ANY 질의타입은 0x00ff 이고, 질의클래스 IN 은 0x0001 입니다.


< 04 72 69 70 65 03 6e 65 74 00 00 ff 00 01 > 이후에 나오는 데이터들은 DNS OPT 레코드로써, 이 데이터 부부은 달려 있을 수도 있고 없을 수도 있는 '옵션' 데이터입니다. 따라서 이 부분을 포함하는 매칭 조건을 설정하는 것은 의미가 없습니다.


DNS[12] 이후 영역에 대해 <  00 ff 00 01 > 에 일치하는 데이터가 있는지 검사함으로써 ANY IN 질의 여부를 식별해 낼 수 있을 것 같습니다.

하지만, 보다 정확한 식별을 위해 < 00  00 ff 00 01 >을 사용하는 것이 좋습니다. 여기서 < 00 >은 질의된 도메인 이름의 맨 마지막 레이블인 루트 도메인의 1바이트 값입니다. 모든 질의도메인은  < 00 >으로 끝나므로,  < 00 >를 검사요건 앞에 포함함으로써 "질의도메인 바로 다음의 4바이트 데이터가 < 00 ff 00 01 > 에 일치하는 경우"를 검사한다는 의미가 됩니다.


STRING 모듈의 --hex-string 옵션부분은 --hex-string "|00  00 ff 00 01|" 으로 지정할 수 있습니다.


검사할 대상 영역 범위를 정해야 합니다. 질의타입 필드와 질의클래스 필드는 Question 섹션에 있습니다. 

Question 섹션의 시작점은 DNS[12]입니다. 곧 DNS 메시지 첫바이트로부터 12번째 바이트부터 Question 섹션입니다.


하지만 STRING 모듈의 검사대상 영역은 IP헤더 첫바이트를 기준점으로 삼습니다.


DNS 메시지 시작지점 DNS[0]는 IP 헤더를 기준할 때 IP[28]이라 할 수 있습니다. IP헤더 20바이트 기본 길이와 UDP헤더 8바이트 고정길이를 합하여 28바이트를 건너뛴 위치입니다.

DNS Question 섹션은 IP헤더를 기준할 때 IP[40]입니다. DNS헤더의 12번째 바이트 지점이므로 IP[28+12]인 IP[40]이 됩니다.


그래서 검사영역 범위의 시작점은 STRING 모듈 --from 옵션을 사용하여 --from 40 으로 설정할 수 있습니다.


문제는 어디까지 검사할 것인가 하는 점입니다. Question 섹션의 길이는 고정되어 있지 않고 질의도메인 길이에 따라 달라지기 때문입니다.


질의가 가능한 도메인이름의 길이는 어떤 범위의 값을 가질 것으로 예상될까요?


레이블 하나의 최대길이는 63바이트입니다. 루트 도메인 바로 아래에 있는 TLD 이름은 2바이트(ccTLD), 3바이트(gTLD)에서 최대 24바이트(다국어도메인)에 이르고 있습니다. 


ANY 질의를 하게 될 대상 도메인은 대략 최대 63바이트의 레이블 하나와 최대 24바이트의 TLD 레이블 하나를 가진다고 가정하면 합리적일 수 있습니다.

최대의 경우를 기준한다면, 최대 길이의 질의도메인 이름은 1 (len) + 63 (label) + 1 (len) + 24 (label) + 1 (root) = 90바이트의 길이를 가지게 될 것입니다. 

그렇다면 약간 넉넉하게 잡아서 검사범위를 100바이트로 한정해도 무방할 것입니다. '질의타입' 및 '질의클래스'의 4바이트를 포함해서 입니다.


이제 검사범위가 대략 정해졌습니다. DNS 첫바이트로부터 12번째 바이트에서 시작하여 100바이트 범위 내 영역을 검사영역으로 삼는 것입니다.


STRING 모듈의 옵션 --from, --to 를 사용하여 --from 40 --to 140로 매칭조건 검사대상 영역을 설정할 수 있습니다.

 

-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00  00 ff 00 01|" -j DROP


외부에서 유입되어 서버의 네임서버 53번 포트로 착신하는 트래픽이 DNS 질의트래픽 밖에 없다고 가정한다면, 위 IPTables 룰셋은 모든 ANY 질의를 차단할 수 있을 것입니다.




서버 시스템 적용 & 확인


IPTables 룰셋을 작성하였습니다.

$ 
$ cat ipt-drop-all-ANY-LOG.txt 
*filter
:INPUT ACCEPT 
:FORWARD ACCEPT 
:OUTPUT ACCEPT
-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00 01|" -j LOG --log-prefix "DROP ALL ANY :" --log-level info
-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00 01|" -j DROP
COMMIT
$ 


서버시스템에 반영햇습니다.

$ 
$ cat ipt-drop-all-ANY-LOG.txt | sudo iptables-restore 
$


차단 패킷 현황를 확인했습니다.

$ 
$ sudo iptables -nvL
Chain INPUT (policy ACCEPT 44 packets, 2480 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 LOG        udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:53 STRING match  "|0000ff0001|" ALGO name bm FROM 40 TO 140 LOG flags 0 level 6 prefix "DROP ALL ANY :"
    0     0 DROP       udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:53 STRING match  "|0000ff0001|" ALGO name bm FROM 40 TO 140

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 24 packets, 2064 bytes)
 pkts bytes target     prot opt in     out     source               destination         
$ 


외부에서 ripe.net ANY 질의, ripe2.net ANY 질의, 123456789012345678901234567890.net ANY 질의를 했습니다.

모두 timeout 무응답 결과를 얻었습니다.

$ dig @10.0.2.15  ripe.net any +multi

; <<>> DiG 9.11.0b1 <<>> @10.0.2.15 ripe.net any +multi
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached
$ 
$ 
$ dig @10.0.2.15  ripe2.net any +multi

; <<>> DiG 9.11.0b1 <<>> @10.0.2.15 ripe2.net any +multi
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached
$ dig @10.0.2.15  123456789012345678901234567890.net any +multi

; <<>> DiG 9.11.0b1 <<>> @10.0.2.15 123456789012345678901234567890.net any +multi
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached
$ 


서버 시스템에서 IPTables 차단현황을 확인했습니다.

총 9개의 패킷이 차단처리되었습니다. dig을 사용한 3번의 질의마다 3번까지 재시도한 패킷을 모두 차단한 결과입니다.

$ sudo iptables -nvL
Chain INPUT (policy ACCEPT 118 packets, 5896 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    9   774 LOG        udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:53 STRING match  "|0000ff0001|" ALGO name bm FROM 40 TO 140 LOG flags 0 level 6 prefix "DROP ALL ANY :"
    9   774 DROP       udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:53 STRING match  "|0000ff0001|" ALGO name bm FROM 40 TO 140

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 89 packets, 9868 bytes)
 pkts bytes target     prot opt in     out     source               destination         
$ 


kernel 로그의 IPTables 로그를 확인했습니다. 9개의 패킷이 조건에 매칭되어 기록되어 있음을 확인할 수 있습니다.

$ grep 'DROP ALL ANY' /var/log/kern.log 
Dec 25 20:42:06 SLab1 kernel: [16446.292805] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=77 TOS=0x00 PREC=0x00 TTL=64 ID=5545 PROTO=UDP SPT=50373 DPT=53 LEN=57 
Dec 25 20:42:09 SLab1 kernel: [16448.981337] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=77 TOS=0x00 PREC=0x00 TTL=64 ID=5546 PROTO=UDP SPT=50373 DPT=53 LEN=57 
Dec 25 20:42:11 SLab1 kernel: [16451.149851] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=77 TOS=0x00 PREC=0x00 TTL=64 ID=5547 PROTO=UDP SPT=50373 DPT=53 LEN=57 
Dec 25 20:43:06 SLab1 kernel: [16476.165277] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=78 TOS=0x00 PREC=0x00 TTL=64 ID=5566 PROTO=UDP SPT=56316 DPT=53 LEN=58 
Dec 25 20:43:09 SLab1 kernel: [16478.859418] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=78 TOS=0x00 PREC=0x00 TTL=64 ID=5567 PROTO=UDP SPT=56316 DPT=53 LEN=58 
Dec 25 20:43:11 SLab1 kernel: [16481.437451] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=78 TOS=0x00 PREC=0x00 TTL=64 ID=5568 PROTO=UDP SPT=56316 DPT=53 LEN=58 
Dec 25 20:43:30 SLab1 kernel: [16500.014931] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=103 TOS=0x00 PREC=0x00 TTL=64 ID=5592 PROTO=UDP SPT=41649 DPT=53 LEN=83 
Dec 25 20:43:33 SLab1 kernel: [16502.931866] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=103 TOS=0x00 PREC=0x00 TTL=64 ID=5593 PROTO=UDP SPT=41649 DPT=53 LEN=83 
Dec 25 20:44:06 SLab1 kernel: [16505.440390] DROP ALL ANY :IN=enp0s3 OUT= MAC=08:00:27:79:7a:18:52:54:00:12:35:02:08:00 SRC=10.0.2.2 DST=10.0.2.15 LEN=103 TOS=0x00 PREC=0x00 TTL=64 ID=5595 PROTO=UDP SPT=41649 DPT=53 LEN=83 
$ 


이로써 질의도메인이 무엇이든, ANY 타입의 질의를 모두 차단하는 룰셋을 작성해 보았습니다.

룰셋 하나로 다양한 형태의 ANY 질의를 차단할 수 있으므로, 보다 효과적인 방식이라 할 수 있습니다.



한계점 체크


ANY 타입 질의는 통상 IN 클래스를 사용합니다. 

하지만, 공격에서 사용하는 질의메시지는 '만들어진 메시지'입니다. 즉, DNS 메시지 내의 모든 데이터를 마음대로 변경 조작하는 것이 가능합니다.


ANY 질의의 또 다른 형태가 가능합니다.


질의도메인 ANY ANY 형태입니다. 질의클래스로 IN이 아니라 대신에 ANY 클래스를 사용하는 것입니다.


ANY 클래스라니? 그런 것도 있어? 라고 의문을 가질 수 있습니다. ANY 클래스가 있습니다.

IANA의 "DNS CLASSes" 페이지를 보면, ANY 클래스가 정의되어 있는 것을 확인할 수 있습니다.

ANY 클래스의 코드값은 255 (0xff) 입니다.


ANY 클래스로 ANY 타입질의를 하면 도메인이름의 IN (Internet), CH (Chaos) 등의 여러 클래스에 설정된 모든 레코드를 "한번에" 조회할 수 있습니다.

현재 DNS에서는 IN 클래스 외에 다른 클래스는 쓰이지 않고 있어 실제 그 유용성은 없다고 볼 수 있습니다.


그러나, 표준으로 처리하도록 규정되어 있는 ANY 클래스에 대한 처리는 공격의 우회통로로 악용될 수 있습니다.



ripe.net ANY IN 질의는 차단되고 있는 상태에서, ripe.net ANY ANY 질의를 해 보았습니다.

dig에서는 ANY 클래스는 class255 ( 또는 -c any )로 표기해 주어야 합니다.


질의 결과, IPTables 차단 룰셋에 의해 차단되지 않고 그대로 통과하여 정상처리 응답되고 있습니다.

 
$ dig @10.0.2.15 ripe.net any class255 +noall +comment +stats

; <<>> DiG 9.11.0b1 <<>> @10.0.2.15 ripe.net any class255 +noall +comment +stats
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63848
;; flags: qr rd ra; QUERY: 1, ANSWER: 25, AUTHORITY: 7, ADDITIONAL: 13

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 52138f02ff3b7898f761a81458440a48ad91570c11ef5714 (good)
;; Query time: 734 msec
;; SERVER: 10.0.2.15#53(10.0.2.15)
;; WHEN: Sun Dec 25 21:21:33 KST 2016
;; MSG SIZE  rcvd: 2855

$  


보완이 필요합니다.


ANY질의 ANY 클래스인 질의 경우를 차단하는 룰셋을 추가 하는 것입니다.


-A INPUT -p udp --dport 53 -m string --algo bm --from 12 --to 112 --hex-string "|00 00 ff 00 ff|" -j DROP



서버 시스템에 반영하기 위한 IPTables 룰셋을 고쳐 작성한 내용입니다.

$ 
$ cat ipt-drop-all-ANY-LOG.txt 
*filter
:INPUT ACCEPT 
:FORWARD ACCEPT 
:OUTPUT ACCEPT
-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00 ff|" -j LOG --log-prefix "DROP ALL ANY ANY :" --log-level info
-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00 ff|" -j DROP
-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00 01|" -j LOG --log-prefix "DROP ALL ANY IN  :" --log-level info
-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00 01|" -j DROP
COMMIT
$


다시 ripe.net ANY ANY 질의를 한 결과, 이번에는 차단되어 무응답 결과를 얻었습니다.


하다보니 룰셋항목 개수가 많아졌습니다. 복잡해 보입니다.


-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00 ff|" -j DROP

-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00 01|" -j DROP


이 2개 차단 룰셋의 매칭조건을 "|00 00 ff 00|" 하나로 변경하면 하나의 차단 조건으로 작성하는 것이 가능합니다.

2개의 차단용 매칭조건에 공통되는 조건을 사용하는 것입니다.


매칭조건이 짧아졌다는 것은 매칭 오류 경우가 발생할 확률이 높아질 수 있음을 의미합니다.

질의내용이 아닌 다른 부분(DNS OPT 레코드)에 우연히 "|00 00 ff 00|" 형태를 갖는 바이너리 데이터가 포함되는 경우가 이에 해당합니다.

이런 경우가 발생하면, ANY 질의가 아님에도 불구하고 차단처리되는 DNS질의가 발생할 수 있습니다.

매칭 오류 경우를 최대한 줄이기 위해서는 별도의 매칭 조건을 부가할 필요가 있을 수 있습니다. (이에 대해서는 다른 기회에 다루기로 합니다.)


IPTables 내용을 다시 작성했습니다.

$ 
$ cat ipt-drop-all-ANY-LOG.txt 
*filter
:INPUT ACCEPT 
:FORWARD ACCEPT 
:OUTPUT ACCEPT
-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00|" -j LOG --log-prefix "DROP ALL ANY IN  :" --log-level info
-A INPUT -p udp --dport 53 -m string --algo bm --from 40 --to 140 --hex-string "|00 00 ff 00|" -j DROP
COMMIT
$


이 조건으로 모든 종류의 UDP ANY 질의를 차단할 수 있습니다. 

다만, ANY 질의가 아님에도 불구하고 <00 00 ff 00> 패턴이 엉뚱한 곳에서 나타나 매칭되는 바람에 잘못 차단되는 경우를 방지하는 장치를 부가해 줄 필요가 있습니다.





Comments