From 7c6d37fe3c3925279128dbe63b79e9e1b3b1d675 Mon Sep 17 00:00:00 2001 From: Janus Varmarken Date: Thu, 20 Sep 2018 16:45:39 -0700 Subject: [PATCH] preliminary work on signature detection --- .../java/edu/uci/iotproject/Conversation.java | 5 +- .../analysis/TcpConversationUtils.java | 16 +++ .../detection/SignatureDetector.java | 117 ++++++++++++++++++ .../uci/iotproject/util/PcapPacketUtils.java | 37 ++++++ 4 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Conversation.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Conversation.java index 73d165d..5722303 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Conversation.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Conversation.java @@ -1,5 +1,6 @@ package edu.uci.iotproject; +import edu.uci.iotproject.analysis.TcpConversationUtils; import edu.uci.iotproject.util.PcapPacketUtils; import org.pcap4j.core.PcapPacket; import org.pcap4j.packet.IpV4Packet; @@ -481,8 +482,10 @@ public class Conversation { * inspect the first 4 bytes of each potential TLS packet to see if they match the SSL record header. * * 08/31/18: Added unconvetional TLS ports used by WeMo plugs and LiFX bulb. + * 09/20/18: Moved hardcoded ports to other class to allow other classes to query the set of TLS ports. */ - return mServerPort == 443 || mServerPort == 8443 || mServerPort == 41143; +// return mServerPort == 443 || mServerPort == 8443 || mServerPort == 41143; + return TcpConversationUtils.isTlsPort(mServerPort); } /** diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TcpConversationUtils.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TcpConversationUtils.java index bd7f9ac..55dfa42 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TcpConversationUtils.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TcpConversationUtils.java @@ -325,6 +325,22 @@ public class TcpConversationUtils { return s.collect(Collectors.joining(" ")); } + /** + * Set of port numbers that we consider TLS traffic. + * Note: purposefully initialized as a {@link HashSet} to get O(1) {@code contains()} call. + */ + private static final Set TLS_PORTS = Stream.of(443, 8443, 41143). + collect(Collectors.toCollection(HashSet::new)); + + /** + * Check if a given port number is considered a TLS port. + * @param port The port number to check. + * @return {@code true} if the port number is considered a TLS port, {@code false} otherwise. + */ + public static boolean isTlsPort(int port) { + return TLS_PORTS.contains(port); + } + /** * Appends a space to {@code sb} iff {@code sb} already contains some content. * @param sb A {@link StringBuilder} that should have a space appended iff it is not empty. diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java new file mode 100644 index 0000000..5c04c23 --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java @@ -0,0 +1,117 @@ +package edu.uci.iotproject.detection; + +import edu.uci.iotproject.Conversation; +import edu.uci.iotproject.TcpReassembler; +import edu.uci.iotproject.analysis.TcpConversationUtils; +import edu.uci.iotproject.util.PcapPacketUtils; +import org.pcap4j.core.PacketListener; +import org.pcap4j.core.PcapPacket; + +import java.util.List; + +/** + * TODO add class documentation. + * + * @author Janus Varmarken {@literal } + * @author Rahmadi Trimananda {@literal } + */ +public class SignatureDetector implements PacketListener { + + /** + * The signature that this {@link SignatureDetector} is trying to detect in the observed traffic. + */ + private final List> mSignature; + + /** + * For reassembling the observed traffic into TCP connections. + */ + private final TcpReassembler mTcpReassembler = new TcpReassembler(); + + public SignatureDetector(List> signature) { + mSignature = signature; + } + + + @Override + public void gotPacket(PcapPacket packet) { + // Present packet to TCP reassembler so that it can be mapped to a connection (if it is a TCP packet). + mTcpReassembler.gotPacket(packet); + + } + +// private void performDetection() { +// // Let's start out simple by building a version that only works for signatures that do not span across multiple +// // TCP conversations... +// for (Conversation c : mTcpReassembler.getTcpConversations()) { +// boolean matchFound = isSequenceInConversation(c); +// } +// } + + /** + * Examine if a {@link Conversation} contains a given sequence of packets. Note: the current implementation actually + * searches for a substring as it does not allow for interleaved packets in {@code c} that are not in + * {@code sequence}; for example, if {@code sequence} consists of packet lengths [2, 3, 5] and {@code c} consists of + * packet lengths [2, 3, 4, 5], the result will be {@code false}. If we are to allow interleaved packets, we need + * a modified version of this. + * @param sequence The sequence to look for. + * @param c The {@link Conversation} to search for {@code sequence} in. + * @return {@code true} if {@code c} contains {@code sequence}, {@code false} otherwise. + */ + private boolean isSequenceInConversation(List sequence, Conversation c) { + // The packets we match against differ depending on whether the signature is a TLS or non-TLS signature. + boolean tlsSequence = isTlsSequence(sequence); + if (tlsSequence && !c.isTls()) { + // If we're looking for a TLS signature and this conversation does not appear to be a TLS conversation, we + // are done. Note: this assumes that they do NOT start performing TLS on new ports that are not captured in + // Conversation.isTls() + return false; + } + // Based on TLS or non-TLS signature, fetch the corresponding list of packets to match against. + List packets = tlsSequence ? c.getTlsApplicationDataPackets() : c.getPackets(); + // If sequence is longer than the conversation, it can obviously not be contained in the conversation. + if (packets.size() < sequence.size()) { + return false; + } + int seqIdx = 0; + int convIdx = 0; + while (convIdx < packets.size()) { + PcapPacket seqPkt = sequence.get(seqIdx); + PcapPacket convPkt = packets.get(convIdx); + if (convPkt.getOriginalLength() == seqPkt.getOriginalLength()) { + // TODO should also check direction of packets -- how to? + // A match, advance both indices to consider next packet in sequence vs. next packet in conversation + seqIdx++; + convIdx++; + if (seqIdx == sequence.size()) { + // we managed to match the full sequence in the conversation. + return true; + } + } else { + // Mismatch. + if (seqIdx > 0) { + /* + * If we managed to match parts of sequence, we restart the search for sequence in c at the index of + * c where the current mismatch occurred. I.e., we must reset seqIdx, but leave convIdx untouched. + */ + seqIdx = 0; + } else { + /* + * First packet of sequence didn't match packet at convIdx of conversation, so we move forward in + * conversation, i.e., we continue the search for sequence in c starting at index convIdx+1 of c. + */ + convIdx++; + } + } + } + return false; + } + + private boolean isTlsSequence(List sequence) { + // NOTE: Assumes ALL packets in sequence pertain to the same TCP connection! + PcapPacket firstPkt = sequence.get(0); + int srcPort = PcapPacketUtils.getSourcePort(firstPkt); + int dstPort = PcapPacketUtils.getDestinationPort(firstPkt); + return TcpConversationUtils.isTlsPort(srcPort) || TcpConversationUtils.isTlsPort(dstPort); + } + +} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/util/PcapPacketUtils.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/util/PcapPacketUtils.java index bd1f1c8..c5d944f 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/util/PcapPacketUtils.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/util/PcapPacketUtils.java @@ -25,6 +25,15 @@ public final class PcapPacketUtils { */ private static final int SIGNATURE_MERGE_THRESHOLD = 5; + /** + * Determines if a given {@link PcapPacket} wraps a {@link TcpPacket}. + * @param packet The {@link PcapPacket} to inspect. + * @return {@code true} if {@code packet} wraps a {@link TcpPacket}, {@code false} otherwise. + */ + public static boolean isTcp(PcapPacket packet) { + return packet.get(TcpPacket.class) != null; + } + /** * Gets the source IP (in decimal format) of an IPv4 packet. * @param packet The packet for which the IPv4 source address is to be extracted. @@ -36,6 +45,34 @@ public final class PcapPacketUtils { return ipPacket == null ? null : ipPacket.getHeader().getSrcAddr().getHostAddress(); } + /** + * Gets the source port of a TCP packet. + * @param packet The packet for which the source port is to be extracted. + * @return The source port of the {@link TcpPacket} encapsulated by {@code packet}. + * @throws IllegalArgumentException if {@code packet} does not encapsulate a {@link TcpPacket}. + */ + public static int getSourcePort(PcapPacket packet) { + TcpPacket tcpPacket = packet.get(TcpPacket.class); + if (tcpPacket == null) { + throw new IllegalArgumentException("not a TCP packet"); + } + return tcpPacket.getHeader().getSrcPort().valueAsInt(); + } + + /** + * Gets the destination port of a TCP packet. + * @param packet The packet for which the destination port is to be extracted. + * @return The destination port of the {@link TcpPacket} encapsulated by {@code packet}. + * @throws IllegalArgumentException if {@code packet} does not encapsulate a {@link TcpPacket}. + */ + public static int getDestinationPort(PcapPacket packet) { + TcpPacket tcpPacket = packet.get(TcpPacket.class); + if (tcpPacket == null) { + throw new IllegalArgumentException("not a TCP packet"); + } + return tcpPacket.getHeader().getDstPort().valueAsInt(); + } + /** * Helper method to determine if the given combination of IP and port matches the source of the given packet. * @param packet The packet to check. -- 2.34.1