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
偽の応答を返していることが確認できた。