Red Team: Packet Captures and BPF Filtering
One of the primary goals during a red team engagement is to enumerate the internal network once the initial foothold is established. Internal network enumeration is critical to expanding access, lateral movement, and identifying the key network resources in use. There are both active and passive methods to complete this task. This post will discuss an active enumeration method: packet captures. We will also touch on filtering packet captures using BPF and the differences when using Wireshark to filter packets.
PCAP Tools
In order to start capturing packets in a network, you will need software capable of doing so. On Linux/UNIX systems, tcpdump is a commonly used tool. In a Windows environment, you can use tshark or Wireshark to capture packets. Tcpdump is the focus here, but the same concepts can be applied to Windows and Wireshark/tshark. Note that admin/root/sudo permissions are required to start capturing on any network interface.
TCPDUMP
tcpdump [args] "filter"
-i interface
-c count
-n[n] disable name resolution [disable port resolution]
-w output path
-s snapshot length
-e include data link layer
-v[vv] verbosity level
-D list all available interfaces
-r read path
-XX include hexdump in output
-d no capture, display compiled BPF filter
There are many arguments available in tcpdump but you will find critical options listed above. A common combination of arguments is as follows:
1. tcpdump -d -i lo "ether host ff:ff:ff:ff:ff:ff"
2. tcpdump -s 0 -n -i eth0 -c 1000 -w output.pcap "ether host ff:ff:ff:ff:ff:ff" &
In this example, our applied BPF filter is "ether host ff:ff:ff:ff:ff:ff". This filter will only capture broadcast traffic. This can be useful when enumerating an internal network to find additional hosts and routers when their IP addresses are unknown. More about BPF filters later down below...
The first command does not start a capture; it only verifies that our BPF filter is valid. It will output the compiled filter if valid or produce an error if it's invalid. The second command will start the capture with our validated filter. We set the snapshot length (-s) to 0 to capture all bytes within each packet. We use (-n) to disable DNS name resolution and use (-i) to capture on the eth0 interface. This (-i) argument will accept specific interface names or "any" to capture on all interfaces. Next, we use (-c) to limit the capture to the first 1000 packets. And finally, we use (-w) to write our output to the "output.pcap" file. The entire command ends with "&" to background the process. This isn't required but is useful to maintain your shell session after running this command. This packet capture will run until it sees 1000 packets or the tcpdump process is stopped - whichever comes first. Running tcpdump without a packet count or output file size limit will cause the capture to run until stopped (this can produce a large amount of data depending on your filter). Be creative and mindful when crafting tcpdump commands; what information do you want to include, how stealthy do you need to be, how much data is too much?
Simplified TCP/IP Model Review
4. Application Layer (HTTP/DNS/FTP/SMB/SSH)
3. Transport Layer (TCP/UDP)
2. Internet Layer (IP Addressing)
1. Network Access Layer (MAC Addressing)
Berkeley Packet Filter (BPF)
The filters that we can apply with packet captures in tcpdump or tshark use BPF. The filters can be applied at any layer of the network stack using keywords (ether, host, net, proto, port, src, dst, etc). Review these and other common filters because quick knowledge of this is important for red teams to reference. Notably, Wireshark also uses BPF when using it to actively capture packets, however, Wireshark uses a different syntax when viewing and filtering a completed packet capture.
BPF works by specifying which packets you want to select for capture based on byte offsets and protocol headers. The aforementioned keywords are shortcuts for byte patterns. Complex filters can be used with brackets [] in your filter. For example:
ether[5] = 0x0 - Filter for the value 0 in the 5th byte of the packet
ether[12:2] = 0xAABB - Filter for the value 43707 in the 12th through 13th bytes of the packet
Notice that the BPF filter uses hex values to compare data. In the second example "[12:2]", you are starting selection from the 12th byte in the packet and reading two bytes from there (12th and 13th bytes).
You can also use bit masking in BPF filters:
ether[0] & 0x01 = 0
ether[20] & 0x60 = 0x40
These two methods of advancing filtering can be useful for specific protocol filtering or specific data found within the packet's application. For example, you could use range filtering to capture ARP packets but only ARP replies.
Compiled BPF
BPF filters use keywords (ether, host, net, proto, port, src, dst, etc) that are easy to use and understand however these are translated to a machine language for implementation. Packet capture tools (tcpdump) work directly with the network interface (NIC) without any additional network stack processing by the operating system kernel.
ld: Loads 4 bytes into A register starting from the specified offset
ldh: Loads 2 bytes into A register starting from the specified offset
ldb: Loads 1 byte into A register starting from the specified offset
jeq: Jump if equal; Comparisons between A register and value
jgt: Jump if greather than
jset: Jump if set
and: Logical and
or: Logical or
ret: Return
Recap: Why Do Packet Captures?
Packet captures are very important in enumerating an unknown network. Lots of network discovery information can be found in broadcast traffic and other chatty hosts. Captures can also provide system administrators with information for network troubleshooting or assist blue team defenders on identifying how malware is behaving over a network.
Imagine what you could find in ARP, SNMP, or DNS traffic 👍