mrtc0.log

tail -f mrtc0.log

nfqueue+scapyで偽の応答を返す

PythonライブラリであるNetfilterQueueを使うことでiptablesのnfqueueルールにマッチしたパケットを扱うことができる。

NetfilterQueue 0.3 : Python Package Index

ここではNetfilterQueueとScapyを使用して偽のDNS応答パケットを返してみる。


ICMP Echo Requestをフックしてみる

まずはICMP Echo RequestパケットをフックしてReplyを返すfake_icmp.pyを作ってみる。

import os
import sys
from scapy.all import *
from netfilterqueue import NetfilterQueue

def fake_echo_reply(pkt):
    """ 偽のICMP Echo Replyパケットを作成する """
    ip = IP()
    icmp = ICMP()
    ip.src = pkt[IP].dst
    ip.dst = pkt[IP].src
    icmp.type = 0
    icmp.code = 0
    icmp.id = pkt[ICMP].id
    icmp.seq = pkt[ICMP].seq
    print("\t%s => %s" % (ip.src, ip.dst))
    data = pkt[ICMP].payload
    send(ip/icmp/data)


def process(pkt):
    """ NFQUEUEから受け取ったパケットを処理する関数 """
    packet = IP(pkt.get_payload())
    proto = packet.proto
    # 0x01 = ICMP
    if proto is 0x01:
        print("[*] ICMP Packet Detected")
        if packet[ICMP].type is 8:
            print("[*] ICMP Echo Request Packet Detected")
            fake_echo_reply(packet)


def main():
    nfqueue = NetfilterQueue()
    # キューID = 1
    nfqueue.bind(1, process)

    try:
        nfqueue.run()
    except:
        print("Exiting...")
        sys.exit(1)

main()

iptablesにてecho-requestなICMPパケットをNFQUEUEターゲットでキューに入れる。

iptables -A OUTPUT -p icmp --icmp-type echo-request -j NFQUEUE --queue-num 1

この状態でfake_icmp.pyを起動して、pingを打ってみる。

# ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=64 time=4.91 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=64 time=5.17 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=64 time=4.93 ms

--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 4.916/5.008/5.174/0.142 ms

実際には8.8.8.8にパケットは送信されず、iptablesのNFQUEUEターゲットによってフックされ、fake_icmp.pyへ渡される。
fake_icmp.pyでは受け取ったパケットに基づいて生成されたICMP echo replyが送信される。

# python fake_icmp.py 
WARNING: No route found for IPv6 destination :: (no default route?)
[*] ICMP Packet Detected
[*] ICMP Echo Request Packet Detected
        8.8.8.8 => 192.168.1.210
[*] ICMP Packet Detected
[*] ICMP Echo Request Packet Detected
        8.8.8.8 => 192.168.1.210
[*] ICMP Packet Detected
[*] ICMP Echo Request Packet Detected
        8.8.8.8 => 192.168.1.210

偽のDNS応答を返す

同様にDNSの問い合わせパケットをフックし、実際とは異なるIPアドレスを返すようにしてみる。

iptablesでポート53番へ送信されるパケットをNFQUEUEに渡す。
ここではキューIDを2にしている。

iptables -A OUTPUT -p udp --dport 53 -j NFQUEUE --queue-num 2

fake_dns.pyは以下のようになる。
先ほどのICMPと同様、受け取ったパケットに基づいてDNS応答パケットを作成する。

import os
import sys
from scapy.all import *
from netfilterqueue import NetfilterQueue


def fake_dns_reply(pkt, qname):
    """ 偽のDNS応答パケットを作成する """
    ip = IP()
    udp = UDP()
    ip.src = pkt[IP].dst
    ip.dst = pkt[IP].src
    udp.sport = pkt[UDP].dport
    udp.dport = pkt[UDP].sport

    solved_ip = sys.argv[1] # 偽のIPアドレス
    qd = pkt[UDP].payload
    dns = DNS(id = qd.id, qr = 1, qdcount = 1, ancount = 1, arcount = 1, nscount = 1, rcode = 0)
    dns.qd = qd[DNSQR]
    dns.an = DNSRR(rrname = qname, ttl = 3600, rdlen = 4, rdata = solved_ip)
    dns.ns = DNSRR(rrname = qname, ttl = 3600, rdlen = 4, rdata = solved_ip)
    dns.ar = DNSRR(rrname = qname, ttl = 3600, rdlen = 4, rdata = solved_ip)
    print("\t%s:%s" % (ip.dst, udp.dport))
    send(ip/udp/dns)

def process(pkt):
    """ NFQUEUEから受け取ったパケットを処理する関数 """
    packet = IP(pkt.get_payload())
    proto = packet.proto
    # 0x11 = UDP
    if proto is 0x11:
        if packet[UDP].dport is 53:
            print("[*] DNS request")
            dns = packet[UDP].payload
            qname = dns[DNSQR].qname
            print("[*] Requesting for %s" % qname)
            fake_dns_reply(packet, qname)
    


def main():
    nfqueue = NetfilterQueue()
    # キューID = 2
    nfqueue.bind(2, process)

    try:
        nfqueue.run()
    except:
        print("Exiting...")
        # iptables reset
        
        sys.exit(1)

main()

引数に返したいIPアドレスを渡して起動する。
ここでは153.120.0.175(mrtc0.com)を渡す。

# python fake_dns.py 153.120.0.175

digの結果は以下のようになる。

# dig www.google.com

; <<>> DiG 9.9.5-9+deb8u6-Debian <<>> www.google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21188
;; flags: qr; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; QUESTION SECTION:
;www.google.com.                        IN      A

;; ANSWER SECTION:
www.google.com.         3600    IN      A       153.120.0.175

;; AUTHORITY SECTION:
www.google.com.         3600    IN      A       153.120.0.175

;; ADDITIONAL SECTION:
www.google.com.         3600    IN      A       153.120.0.175

;; Query time: 15 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Mon Mar 28 01:30:43 JST 2016
;; MSG SIZE  rcvd: 122

偽の応答を返していることが確認できた。


参考