preliminary work on signature detection
authorJanus Varmarken <varmarken@gmail.com>
Thu, 20 Sep 2018 23:45:39 +0000 (16:45 -0700)
committerJanus Varmarken <varmarken@gmail.com>
Thu, 20 Sep 2018 23:53:15 +0000 (16:53 -0700)
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Conversation.java
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/analysis/TcpConversationUtils.java
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java [new file with mode: 0644]
Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/util/PcapPacketUtils.java

index 73d165d00d3c036d67a43aebe2b5ec1b774ecfd0..5722303c242f092fefe7d87ef988dcf3e31a18f8 100644 (file)
@@ -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);
     }
 
     /**
index bd7f9ac515718cef5585f911ab2132d755000722..55dfa42aa3b9340ceb3b241c9ba4de751eaf0ad4 100644 (file)
@@ -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<Integer> 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} <em>iff</em> {@code sb} already contains some content.
      * @param sb A {@link StringBuilder} that should have a space appended <em>iff</em> 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 (file)
index 0000000..5c04c23
--- /dev/null
@@ -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 <jvarmark@uci.edu>}
+ * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
+ */
+public class SignatureDetector implements PacketListener {
+
+    /**
+     * The signature that this {@link SignatureDetector} is trying to detect in the observed traffic.
+     */
+    private final List<List<PcapPacket>> mSignature;
+
+    /**
+     * For reassembling the observed traffic into TCP connections.
+     */
+    private final TcpReassembler mTcpReassembler = new TcpReassembler();
+
+    public SignatureDetector(List<List<PcapPacket>> 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 <a href="https://stackoverflow.com/a/20545604/1214974">this</a>.
+     * @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<PcapPacket> 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<PcapPacket> 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<PcapPacket> 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);
+    }
+
+}
index bd1f1c83cc161ddff9fa6bdb03dbe7a3cf4bbce1..c5d944f093d27b961b75feb63fc6523ee166ea7a 100644 (file)
@@ -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.