Phát hiện và phòng chống DNS Tunneling trên Linux: Ngăn chặn rò rỉ dữ liệu và C2 độc hại

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Vấn đề: Điểm mù bên trong cổng 53

Hầu hết firewall biên đều siết chặt lưu lượng ingress nhưng lại âm thầm cho phép lưu lượng UDP và TCP chiều outbound trên các cổng 80, 443 và 53 đi thẳng qua. Các sysadmin thường coi DNS outbound chỉ là dịch vụ nền vô hại.

Suy cho cùng, các máy chủ chỉ cần phân giải tên miền để kết nối tới apt mirror, NTP pool và API. Kẻ tấn công lợi dụng chính điểm mù này. Khi firewall egress chặn các kết nối SSH, HTTPS hoặc raw TCP trực tiếp, tunneling qua DNS trở thành con đường dễ dàng để tuồn bí mật doanh nghiệp ra ngoài biên và vận hành shell Command and Control (C2) tương tác.

Do các recursive resolver chuyển tiếp query thay cho máy nội bộ, các gói tin độc hại dễ dàng vượt qua ranh giới VLAN nội bộ mà không gặp trở ngại nào. Từ đó, các request đi thẳng tới authoritative nameserver do kẻ tấn công kiểm soát. Các firewall deep packet inspection tiêu chuẩn thường bỏ qua subdomain, nhầm lẫn hàng triệu query ngẫu nhiên với lưu lượng tra cứu thông thường.

Cách đây vài năm, một đợt tấn công brute-force đã nhắm vào một trong các database server production của tôi lúc 2:00 sáng. Sự cố đó đã thôi thúc tôi harden SSH, khóa quyền sudo và cài đặt Fail2ban trên toàn bộ máy chủ. Những biện pháp đó đã chặn đứng các scanner tự động. Nhưng khi rà soát lại các kết nối mạng outbound vài tháng sau đó, tôi phát hiện ra một điều tồi tệ hơn: cấu hình resolver mặc định đã để ngỏ một tunnel egress trực tiếp ra internet công cộng.

Khái niệm cốt lõi: Cách DNS Tunneling hoạt động

DNS tunneling không khai thác lỗ hổng phần mềm trong BIND, Unbound hay systemd-resolved. Thay vào đó, nó lạm dụng chính thiết kế nền tảng của giao thức DNS. Để bảo vệ máy chủ của mình, trước tiên bạn phải hiểu cách các công cụ tunneling đóng gói dữ liệu và duy trì phiên C2.

1. Cơ chế trích xuất dữ liệu (Data Exfiltration)

DNS có tốc độ chậm. Throughput thường chỉ dao động từ 1 KB/s đến 100 KB/s. Dù vậy, tốc độ này là quá đủ để đánh cắp API token, SSH key hoặc SQL dump. Kẻ tấn công chia nhỏ các tệp nhạy cảm thành từng đoạn byte, mã hóa chúng bằng Base32 hoặc hexadecimal, rồi gắn chúng vào domain tùy chỉnh dưới dạng các subdomain:

# Chuỗi nhạy cảm đã mã hóa được nhúng vào truy vấn lookup
data-chunk-4d616c6963696f7573.c2-listener.example.com

Host cục bộ yêu cầu resolver được cấu hình tìm địa chỉ IP. Resolver đó đi theo chuỗi ủy quyền (delegation chain) tiêu chuẩn qua root hints và các server TLD.

Cuối cùng, nó chuyển tiếp query đến authoritative nameserver của c2-listener.example.com. Daemon tùy chỉnh của kẻ tấn công sẽ bắt lấy truy vấn lookup từ query log, loại bỏ hậu tố domain và ghép các byte đã giải mã lại thành tệp tin gốc. Hãy chú ý điều vừa xảy ra: máy chủ bị xâm phạm chưa từng mở bất kỳ một network socket trực tiếp nào tới kẻ tấn công.

2. Kênh Command and Control (C2)

Các framework C2 hoàn chỉnh biến DNS thành một tunnel hai chiều. Client Linux sẽ poll domain của kẻ tấn công sau mỗi vài giây. Để phản hồi, nameserver tùy chỉnh sẽ trả về các bản ghi chứa payload:

  • Bản ghi TXT: Phương thức truyền tải phổ biến nhất. Một chuỗi TXT đơn lẻ có thể chứa tối đa 255 ký tự, và nhiều chuỗi ký tự trong một bản ghi có thể mang vài kilobyte output của shell được mã hóa base64.
  • Bản ghi CNAME và A: Được sử dụng cho các tín hiệu heartbeat nhanh, đánh dấu định tuyến hoặc xác nhận thực thi lệnh (acknowledgment).
  • Bản ghi NULL: Được RFC 1035 hỗ trợ để lưu trữ tới 65.535 byte dữ liệu nhị phân thô. Các công cụ tấn công như iodine rất chuộng bản ghi NULL để tối ưu throughput thô.

3. Dấu hiệu thống kê của lưu lượng Tunneling

Lưu lượng resolver thông thường tuân theo thói quen của con người và các dependency web có thể dự đoán được. Trong khi đó, lưu lượng tunneling lại hoàn toàn khác biệt khi phân tích thống kê:

  • Shannon Entropy cao: Các từ tiếng Anh trong hostname hợp lệ thường có điểm entropy từ 2,2 đến 3,0. Subdomain được mã hóa hoặc encode Base64 đạt điểm từ 3,8 đến 4,6, cho thấy phân bố ký tự gần như ngẫu nhiên.
  • Độ dài Label bất thường: Các truy vấn hostname bình thường hiếm khi vượt quá 25 ký tự. Các agent tunneling nhồi nhét càng nhiều dữ liệu càng tốt vào mỗi query, chạm ngưỡng giới hạn của RFC 1035: 63 ký tự cho mỗi label và 253 ký tự cho toàn bộ domain name.
  • Xóa bộ nhớ đệm (Cache Busters): Resolver lưu cache các bản ghi để giảm độ trễ. Vì mỗi payload tunneling sử dụng một chuỗi subdomain duy nhất, các recursive resolver ghi nhận tỷ lệ cache miss gần 100%, tạo ra hàng chục nghìn query độc nhất mỗi giờ.

Thực hành: Phát hiện, Phân tích và Phòng thủ

Bước 1: Bắt và kiểm tra lưu lượng DNS trên Linux

Bắt đầu bằng cách bắt lưu lượng DNS outbound trên interface chính của bạn (chẳng hạn như eth0 hoặc ens192) bằng tshark. Lọc các query đi ra để xem chính xác các workload đang yêu cầu những hostname nào.

# Bắt các query DNS outbound và hiển thị các hostname được yêu cầu
sudo tshark -i eth0 -f "src port 53 or dst port 53" -Y "dns.flags.response == 0" -T fields -e ip.src -e dns.qry.name

Nếu kẻ xâm nhập đang chạy một agent tunneling như dnscat2 hoặc iodine, terminal của bạn sẽ tràn ngập hàng trăm request dài, có entropy cao trỏ về một root domain duy nhất:

10.0.0.15   01a3f89c2b0e7741.tunnel.badactor.io
10.0.0.15   02e4d91a8c33902f.tunnel.badactor.io
10.0.0.15   03c801ba92d4f16a.tunnel.badactor.io

Bước 2: Phát hiện các truy vấn có Entropy cao bằng Python

Bạn có thể phát hiện hành vi trích xuất dữ liệu theo thời gian thực bằng cách tính Shannon entropy của các label trong query đến và đi. Sự tăng đột biến của cả entropy và độ dài label là dấu hiệu tin cậy của hoạt động tunneling.

import math
from collections import Counter

def calculate_entropy(text: str) -> float:
    """Tính Shannon entropy của chuỗi đầu vào."""
    if not text:
        return 0.0
    length = len(text)
    frequencies = Counter(text)
    entropy = -sum((count / length) * math.log2(count / length) for count in frequencies.values())
    return round(entropy, 2)

def analyze_query(domain: str):
    labels = domain.strip(".").split(".")
    if len(labels) < 2:
        return
    
    subdomain_payload = labels[0]
    entropy = calculate_entropy(subdomain_payload)
    length = len(subdomain_payload)
    
    # Các label hợp lệ hiếm khi vượt quá 30 ký tự với entropy trên 3.8
    if length > 30 and entropy > 3.8:
        print("[CẢNH BÁO] Phát hiện truy vấn DNS đáng ngờ!")
        print(f"  FQDN mục tiêu: {domain}")
        print(f"  Label đầu tiên: {subdomain_payload[:24]}... ({length} ký tự)")
        print(f"  Entropy:        {entropy} (Ngưỡng: >3.8)\n")

# Thử nghiệm với các ví dụ thực tế
analyze_query("4d616c6963696f75735061796c6f616444617461.tunnel.badactor.io")
analyze_query("sentry-cdn-origin-east.internal-infra.net")

Query đầu tiên kích hoạt cảnh báo ngay lập tức. Query thứ hai dài 22 ký tự nhưng từ ngữ dễ đọc giúp entropy của nó chỉ ở mức khoảng 2,9, tránh được cảnh báo giả (false positive).

Bước 3: Giới hạn truy cập DNS Outbound bằng Netfilter

Cho phép các node production kết nối trực tiếp đến các public resolver như 8.8.8.8 hoặc 1.1.1.1 là rước họa vào thân. Các máy bị xâm phạm có thể dễ dàng vượt qua bộ lọc DNS cục bộ bằng cách gửi thẳng query tới các authoritative server độc hại của riêng chúng.

Hãy khóa chặt Linux gateway hoặc firewall iptables cục bộ. Bắt buộc mọi host phải phân giải query thông qua các DNS server nội bộ được chỉ định (chẳng hạn như 10.0.0.2 và 10.0.0.3):

# 1. Chỉ cho phép DNS outbound nghiêm ngặt tới các internal resolver đã duyệt
sudo iptables -A OUTPUT -p udp -d 10.0.0.2 --dport 53 -j ACCEPT
sudo iptables -A OUTPUT -p tcp -d 10.0.0.2 --dport 53 -j ACCEPT
sudo iptables -A OUTPUT -p udp -d 10.0.0.3 --dport 53 -j ACCEPT
sudo iptables -A OUTPUT -p tcp -d 10.0.0.3 --dport 53 -j ACCEPT

# 2. Chặn ngay lập tức mọi lưu lượng outbound cổng 53 khác
sudo iptables -A OUTPUT -p udp --dport 53 -j REJECT --reject-with icmp-port-unreachable
sudo iptables -A OUTPUT -p tcp --dport 53 -j REJECT --reject-with tcp-reset

Các rule này bịt kín các lỗ rò rỉ egress trực tiếp. Mọi query giờ đây bắt buộc phải đi qua các resolver trung tâm, nơi các công cụ giám sát có thể phân tích cú pháp và ghi log lưu lượng.

Bước 4: Hardening Internal Resolver (RPZ và Giới hạn tốc độ)

Các resolver tập trung cũng cần được bảo vệ. Nếu bạn vận hành BIND9, hãy bật Response Rate Limiting (RRL) để điều tiết các đợt bùng nổ query tự động. Hãy kết hợp với Response Policy Zones (RPZ) để sinkhole các C2 domain độc hại đã biết.

Mở /etc/bind/named.conf.options và cập nhật cấu hình của bạn:

options {
    directory "/var/cache/bind";

    # Chỉ cho phép recursion nghiêm ngặt cho các subnet nội bộ
    recursion yes;
    allow-recursion { 127.0.0.1; 10.0.0.0/16; };

    # Response Rate Limiting (RRL) điều tiết các đợt bùng nổ query C2 liên tiếp
    rate-limit {
        responses-per-second 15;
        window 5;
    };

    # Áp dụng blocklist của response policy zone
    response-policy { zone "rpz.blocklist"; };
};

Tiếp theo, định nghĩa file sinkhole bên trong /etc/bind/db.rpz.blocklist để loại bỏ các query trỏ tới các domain độc hại:

; Tệp: /etc/bind/db.rpz.blocklist
$TTL 1H
@       IN      SOA     localhost. root.localhost. ( 2026102801 3H 1H 1W 1H )
        IN      NS      localhost.

; Sinkhole các C2 domain về NXDOMAIN hoặc loopback
*.badactor.io                   CNAME   .
badactor.io                     CNAME   .
*.c2-listener.example.com       A       127.0.0.1

Tải lại cấu hình bằng lệnh sudo rndc reload. Nếu một node bị nhiễm mã độc cố gắng phân giải một địa chỉ exfiltration thuộc badactor.io, BIND sẽ lập tức trả về lỗi NXDOMAIN mà không gửi bất kỳ gói tin nào lên upstream.

Kết luận

Để ngỏ cổng 53 tới mọi đích đến sẽ vô tình biến DNS resolver của bạn thành một proxy cho kẻ tấn công. Một hệ thống phòng thủ Linux vững chắc dựa trên hai lớp: phát hiện các mẫu query bất thường, có entropy cao và khóa chặt egress để các endpoint chỉ có thể giao tiếp với các internal resolver đã được harden. Hãy dành 10 phút để rà soát lại các firewall rule và kiểm tra query log ngay hôm nay. Hãy đảm bảo cổng 53 đang phục vụ phân giải dịch vụ của bạn chứ không phải làm rò rỉ dữ liệu.

Share: