From: rtrimana Date: Wed, 12 Feb 2020 22:22:38 +0000 (-0800) Subject: Completing files. X-Git-Url: http://plrg.eecs.uci.edu/git/?p=pingpong.git;a=commitdiff_plain;h=b26baeaf2c21750ef360103d0c484ce4c6770cea Completing files. --- diff --git a/Code/Projects/PacketLevelSignatureExtractor/.idea/PacketLevelSignatureExtractor.iml b/Code/Projects/PacketLevelSignatureExtractor/.idea/PacketLevelSignatureExtractor.iml new file mode 100644 index 0000000..0db0396 --- /dev/null +++ b/Code/Projects/PacketLevelSignatureExtractor/.idea/PacketLevelSignatureExtractor.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Code/Projects/PacketLevelSignatureExtractor/.idea/modules.xml b/Code/Projects/PacketLevelSignatureExtractor/.idea/modules.xml new file mode 100644 index 0000000..e20fa83 --- /dev/null +++ b/Code/Projects/PacketLevelSignatureExtractor/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Code/Projects/PacketLevelSignatureExtractor/.idea/modules/PacketLevelSignatureExtractor_main.iml b/Code/Projects/PacketLevelSignatureExtractor/.idea/modules/PacketLevelSignatureExtractor_main.iml index f03acab..e1b3b2d 100644 --- a/Code/Projects/PacketLevelSignatureExtractor/.idea/modules/PacketLevelSignatureExtractor_main.iml +++ b/Code/Projects/PacketLevelSignatureExtractor/.idea/modules/PacketLevelSignatureExtractor_main.iml @@ -15,14 +15,5 @@ - - - - - - - - - \ No newline at end of file diff --git a/Code/Projects/PacketLevelSignatureExtractor/execute_layer2_smarthome_all_detection.sh b/Code/Projects/PacketLevelSignatureExtractor/execute_layer2_smarthome_all_detection.sh index 3d444f6..44346db 100755 --- a/Code/Projects/PacketLevelSignatureExtractor/execute_layer2_smarthome_all_detection.sh +++ b/Code/Projects/PacketLevelSignatureExtractor/execute_layer2_smarthome_all_detection.sh @@ -731,8 +731,6 @@ OFF_SIGNATURE="$SIGNATURES_BASE_DIR/wemo-insight-plug/signatures/wemo-insight-pl RESULTS_FILE="$OUTPUT_DIR/wemo-insight-plug/wemo-insight-plug.wlan1.detection.pcap___device-side.detectionresults" SIGNATURE_DURATION="521" EPSILON="10.0" -#ON_SKIPPED_PACKETS="5" -#OFF_SKIPPED_PACKETS="5" PROGRAM_ARGS="'$PCAP_FILE' '$ON_ANALYSIS' '$OFF_ANALYSIS' '$ON_SIGNATURE' '$OFF_SIGNATURE' '$RESULTS_FILE' '$SIGNATURE_DURATION' '$EPSILON'" #./gradlew run -DmainClass=edu.uci.iotproject.detection.layer2.Layer2SignatureDetector --args="$PROGRAM_ARGS" diff --git a/Code/Projects/PacketLevelSignatureExtractor/execute_layer2_smarthome_all_detection_results_analysis.sh b/Code/Projects/PacketLevelSignatureExtractor/execute_layer2_smarthome_all_detection_results_analysis.sh index e5a36d3..1ee4d43 100755 --- a/Code/Projects/PacketLevelSignatureExtractor/execute_layer2_smarthome_all_detection_results_analysis.sh +++ b/Code/Projects/PacketLevelSignatureExtractor/execute_layer2_smarthome_all_detection_results_analysis.sh @@ -397,7 +397,7 @@ RESULTS_FILE="$RESULTS_BASE_DIR/tplink-plug/tplink-plug.wlan1.detection.pcap___d ANALYSIS_RESULTS_FILE="$RESULTS_FILE.analysis" EXACT_MATCH="false" PROGRAM_ARGS="'$TIMESTAMPS_FILE' '$RESULTS_FILE' '$ANALYSIS_RESULTS_FILE' '$EXACT_MATCH'" -./gradlew run -DmainClass=edu.uci.iotproject.evaluation.DetectionResultsAnalyzer --args="$PROGRAM_ARGS" +#./gradlew run -DmainClass=edu.uci.iotproject.evaluation.DetectionResultsAnalyzer --args="$PROGRAM_ARGS" # DEVICE SIDE OUTBOUND RESULTS_FILE="$RESULTS_BASE_DIR/tplink-plug/tplink-plug.wlan1.detection.pcap___device-side-outbound.detectionresults" @@ -406,6 +406,16 @@ EXACT_MATCH="true" PROGRAM_ARGS="'$TIMESTAMPS_FILE' '$RESULTS_FILE' '$ANALYSIS_RESULTS_FILE' '$EXACT_MATCH'" #./gradlew run -DmainClass=edu.uci.iotproject.evaluation.DetectionResultsAnalyzer --args="$PROGRAM_ARGS" # ====================================================================================================================== +# IFTTT +TIMESTAMPS_FILE="$TIMESTAMPS_BASE_DIR/tplink-plug/timestamps/tplink-plug-ifttt-smarthome-dec-11-2019.timestamps" + +# DEVICE SIDE +RESULTS_FILE="$RESULTS_BASE_DIR/tplink-plug/tplink-plug.wlan1.detection.pcap___device-side.detectionresults" +ANALYSIS_RESULTS_FILE="$RESULTS_FILE.analysis" +EXACT_MATCH="false" +PROGRAM_ARGS="'$TIMESTAMPS_FILE' '$RESULTS_FILE' '$ANALYSIS_RESULTS_FILE' '$EXACT_MATCH'" +#./gradlew run -DmainClass=edu.uci.iotproject.evaluation.DetectionResultsAnalyzer --args="$PROGRAM_ARGS" +# ====================================================================================================================== # ================================================== WEMO INSIGHT PLUG ================================================= # LOCAL @@ -423,7 +433,7 @@ PROGRAM_ARGS="'$TIMESTAMPS_FILE' '$RESULTS_FILE' '$ANALYSIS_RESULTS_FILE' '$EXAC #./gradlew run -DmainClass=edu.uci.iotproject.evaluation.DetectionResultsAnalyzer --args="$PROGRAM_ARGS" # ====================================================================================================================== # IFTTT -TIMESTAMPS_FILE="$TIMESTAMPS_BASE_DIR/wemo-insight-plug/timestamps/wemo-insight-plug-ifttt-smarthome-dec-17-2019.timestamps" +TIMESTAMPS_FILE="$TIMESTAMPS_BASE_DIR/wemo-insight-plug/timestamps/wemo-insight-plug-ifttt-smarthome-dec-19-2019.timestamps" # DEVICE SIDE RESULTS_FILE="$RESULTS_BASE_DIR/wemo-insight-plug/wemo-insight-plug.wlan1.detection.pcap___device-side.detectionresults" diff --git a/Code/Projects/PacketLevelSignatureExtractor/execute_layer3_unb_all_detection.sh b/Code/Projects/PacketLevelSignatureExtractor/execute_layer3_unb_all_detection.sh index 1aed5de..2bde575 100755 --- a/Code/Projects/PacketLevelSignatureExtractor/execute_layer3_unb_all_detection.sh +++ b/Code/Projects/PacketLevelSignatureExtractor/execute_layer3_unb_all_detection.sh @@ -396,8 +396,6 @@ DELTA="21" PACKETLIST="592,1234,593,1235" PROGRAM_ARGS="'$PCAP_FILE' '$ON_ANALYSIS' '$OFF_ANALYSIS' '$ON_SIGNATURE' '$OFF_SIGNATURE' '$RESULTS_FILE' '$SIGNATURE_DURATION' '$EPSILON' '$MINUS_R' '$DELTA' '$PACKETLIST'" - -PROGRAM_ARGS="'$PCAP_FILE' '$ON_ANALYSIS' '$OFF_ANALYSIS' '$ON_SIGNATURE' '$OFF_SIGNATURE' '$RESULTS_FILE' '$SIGNATURE_DURATION' '$EPSILON'" #./gradlew run -DmainClass=edu.uci.iotproject.detection.layer3.Layer3SignatureDetector --args="$PROGRAM_ARGS" # ====================================================================================================================== diff --git a/Code/Projects/PacketLevelSignatureExtractor/execute_signature_generation.sh b/Code/Projects/PacketLevelSignatureExtractor/execute_signature_generation.sh index 8afb3f1..99d60f9 100755 --- a/Code/Projects/PacketLevelSignatureExtractor/execute_signature_generation.sh +++ b/Code/Projects/PacketLevelSignatureExtractor/execute_signature_generation.sh @@ -1032,24 +1032,6 @@ PROGRAM_ARGS="'$INPUT_PCAP' '$OUTPUT_PCAP' '$TIMESTAMP_FILE' '$DEVICE_IP' '$ON_S #./gradlew run -DmainClass=edu.uci.iotproject.SignatureGenerator --args="$PROGRAM_ARGS" # ====================================================================================================================== -# ==================================================== TP-LINK BULB ==================================================== -INPUT_PCAP="$SIGNATURES_BASE_DIR/tplink-bulb/tplink-bulb-onoff/wlan1/tplink-bulb-onoff.wlan1.local.pcap" - -OUTPUT_PCAP="$OUTPUT_DIR/tplink-bulb/tplink-bulb-onoff/wlan1/tplink-bulb-onoff-processed.pcap" -TIMESTAMP_FILE="$SIGNATURES_BASE_DIR/tplink-bulb/tplink-bulb-onoff/timestamps/tplink-bulb-onoff-retraining-dec-23-2019.timestamps" -DEVICE_IP="192.168.1.140" -ON_SIGNATURE="$OUTPUT_DIR/tplink-bulb/tplink-bulb-onoff/signatures/tplink-bulb-onoff-onSignature-device-side.sig" -OFF_SIGNATURE="$OUTPUT_DIR/tplink-bulb/tplink-bulb-onoff/signatures/tplink-bulb-onoff-offSignature-device-side.sig" -ON_ANALYSIS="$OUTPUT_DIR/tplink-bulb/tplink-bulb-onoff/analyses/tplink-bulb-onoff-onClusters-device-side.cls" -OFF_ANALYSIS="$OUTPUT_DIR/tplink-bulb/tplink-bulb-onoff/analyses/tplink-bulb-onoff-offClusters-device-side.cls" -EPSILON="10.0" -DELETED_SEQUENCES_ON="-1" -DELETED_SEQUENCES_OFF="-1" - -PROGRAM_ARGS="'$INPUT_PCAP' '$OUTPUT_PCAP' '$TIMESTAMP_FILE' '$DEVICE_IP' '$ON_SIGNATURE' '$OFF_SIGNATURE' '$ON_ANALYSIS' '$OFF_ANALYSIS' '$EPSILON' '$DELETED_SEQUENCES_ON' '$DELETED_SEQUENCES_OFF'" -#./gradlew run -DmainClass=edu.uci.iotproject.SignatureGenerator --args="$PROGRAM_ARGS" -# ====================================================================================================================== - # ================================================== WEMO INSIGHT PLUG ================================================= INPUT_PCAP="$SIGNATURES_BASE_DIR/wemo-insight-plug/wlan1/wemo-insight-plug.wlan1.local.pcap" diff --git a/Code/Projects/PacketLevelSignatureExtractor/execute_signature_validation_results_analysis.sh b/Code/Projects/PacketLevelSignatureExtractor/execute_signature_validation_results_analysis.sh index 1a83a72..1cd383e 100755 --- a/Code/Projects/PacketLevelSignatureExtractor/execute_signature_validation_results_analysis.sh +++ b/Code/Projects/PacketLevelSignatureExtractor/execute_signature_validation_results_analysis.sh @@ -444,6 +444,8 @@ PROGRAM_ARGS="'$TIMESTAMPS_FILE' '$RESULTS_FILE' '$ANALYSIS_RESULTS_FILE' '$EXAC # ==================================================== TP-LINK PLUG ==================================================== # LOCAL TIMESTAMPS_FILE="$TIMESTAMPS_BASE_DIR/tplink-plug/timestamps/tplink-plug-nov-8-2018.timestamps" +# TODO: Timestamp file for retraining PCAP file +#TIMESTAMPS_FILE="$TIMESTAMPS_BASE_DIR/tplink-plug/timestamps/tplink-plug-retraining-dec-25-2019.timestamps" # DEVICE SIDE RESULTS_FILE="$RESULTS_BASE_DIR/tplink-plug/tplink-plug.wlan1.validation.pcap___device-side.detectionresults" diff --git a/Code/Projects/PacketLevelSignatureExtractor/gradle/wrapper/gradle-wrapper.properties b/Code/Projects/PacketLevelSignatureExtractor/gradle/wrapper/gradle-wrapper.properties index a17f184..d6ac6d2 100644 --- a/Code/Projects/PacketLevelSignatureExtractor/gradle/wrapper/gradle-wrapper.properties +++ b/Code/Projects/PacketLevelSignatureExtractor/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Aug 21 11:14:11 PDT 2018 +#Wed Aug 21 12:49:44 PDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip diff --git a/packet-padding/timing_detection_tls_padding.py b/packet-padding/timing_detection_tls_padding.py new file mode 100644 index 0000000..2f9952e --- /dev/null +++ b/packet-padding/timing_detection_tls_padding.py @@ -0,0 +1,226 @@ +import argparse +import ipaddress +import socket +import unicodecsv as csv + +from scapy.all import * + + +def full_duplex(p): + """ + For reassembling bidirectional sessions (streams). By default, Scapy only groups packets in one direction. That is, + bidirectional sessions are split into two sessions, one with client-to-server packets, and one with server-to-client + packets. + + Note that this is simplified session reassembly as it does not consider TCP FIN/RST packets --- packets are mapped + to their respective session based solely on the (src_ip, src_port, dst_ip, dst_port) four-tuple. If the client (or + server) closes a TCP stream and the client by chance selects the same ephemeral port number when contacting the same + server again, the two DIFFERENT TCP streams will be identified as a single stream. + + Code courtesy of: https://pen-testing.sans.org/blog/2017/10/13/scapy-full-duplex-stream-reassembly + + Also note that this assumes Ethernet as layer-2 wrapper for everything. This assumption holds for our TP-Link trace, + but will not hold in general. See discussion at: + https://gist.github.com/MarkBaggett/d8933453f431c111169158ce7f4e2222#file-scapy_helper-py + + :param p: A Scapy packet object. + :return: Session identifier for the packet. + """ + sess = "Other" + if 'Ether' in p: + if 'IP' in p: + if 'TCP' in p: + sess = str(sorted(["TCP", p[IP].src, p[TCP].sport, p[IP].dst, p[TCP].dport],key=str)) + elif 'UDP' in p: + sess = str(sorted(["UDP", p[IP].src, p[UDP].sport, p[IP].dst, p[UDP].dport] ,key=str)) + elif 'ICMP' in p: + sess = str(sorted(["ICMP", p[IP].src, p[IP].dst, p[ICMP].code, p[ICMP].type, p[ICMP].id] ,key=str)) + else: + sess = str(sorted(["IP", p[IP].src, p[IP].dst, p[IP].proto] ,key=str)) + elif 'ARP' in p: + sess = str(sorted(["ARP", p[ARP].psrc, p[ARP].pdst],key=str)) + else: + sess = p.sprintf("Ethernet type=%04xr,Ether.type%") + return sess + + +def get_tls_app_data_pkts(session): + """ + Extract the TLS Application Data packets from a (TCP) stream. + :param tcp_session: The (TCP) stream. + :return: The (ordered) list of TLS application data packets in session. + """ + return session.filter(lambda pkt: TLS in pkt and pkt[TLS].type == 23) + + +def find_matches(pcap_file, device_ip, sig_duration): + """ + Find all matches of [C-->S, S-->C] signatures in TLS conversations involving the device with IP=device_ip. Packet + lengths are not considered, only directions and timing (packet lengths are assumed unavaiable due to TLS padding). + :param pcap_file: The pcap file that is the target of the signature matching. + :param device_ip: IP of the device whose TLS sessions are to be examined for matches. + :param sig_duration: Maximum duration between request and response packets. + :return: A list of (request_packet, reply_packets) tuples, where reply_packets is a list of reply packets that + satisfy the signature match conditions (i.e., that they are within sig_duration after the request packet + and that no other request packet interleaves the request_packet and the reply packet). + """ + # Read all packets into memory (stored as a list). + # This is slow and consumes lots of memory. + # There are more efficient ways to read the pcap (which clear each packet from memory after it's been processed). + # However, to simplify the detection implementation we stick with the quick-and-dirty approach. + pkts = rdpcap(pcap_file) + matches = [] + # Group packets into sessions (streams) + sessions_dict = pkts.sessions(full_duplex) + for sess_key in sessions_dict: + session = sessions_dict[sess_key] + tls_app_data_pkts = get_tls_app_data_pkts(session) + if len(tls_app_data_pkts) == 0: + # Session w/o any TLS traffic, not relevant. + continue + first_pkt = tls_app_data_pkts[0] + if IP not in first_pkt: + # Only consider IPv4 traffic. + continue + if first_pkt[IP].src != device_ip and first_pkt[IP].dst != device_ip: + # Traffic from some other device; ignore -- not relevant to us. + continue + if ipaddress.ip_address(first_pkt[IP].src).is_multicast or ipaddress.ip_address(first_pkt[IP].dst).is_multicast: + # Don't include multicast traffic in the results. + # (Should never occur as TLS is not used for multicast?) + continue + # Now let's find all the potential matches for the current TLS session. + for i, request_pkt in enumerate(tls_app_data_pkts): + if request_pkt[IP].src != device_ip: + # We are trying to find matches for a simple [C->S, S->C] signature, so we want to first identify an + # outbound (device-to-cloud) packet and then subsequently find all potential reply packets + # (cloud-to-device). If this is a cloud-to-device packet, it is of no interest to us at this stage, so + # move on. + continue + # All subsequent cloud-to-device packets (replies) in this TLS session that lie within the signature + # duration after this packet AND that are not preceded by a device-to-cloud packet that is later than the + # current packet can be paired with the current packet to constitute a potential signature match. + idx = i+1 + replies = [] + while idx < len(tls_app_data_pkts) and tls_app_data_pkts[idx][IP].dst == device_ip: + reply_pkt = tls_app_data_pkts[idx] + if reply_pkt.time - request_pkt.time <= sig_duration: + # Could have this check in the loop condition as well. But some times packet order != timestamp + # order. + replies.append(reply_pkt) + idx += 1 + matches.append((request_pkt, replies)) + return matches + + +def get_pkt_key(pkt): + """ + Get a string representation of a packet that can be used as a key in a dictionary. + :param pkt: A Scapy packet. + :return: A string representation of a packet that can be used as a key in a dictionary. + """ + return f'src={pkt.src} dst={pkt.dst} timestamp={pkt.time}' + + +def build_pkt_number_dict(pcap_file): + """ + Create a dictionary mapping packets to their packet number in pcap_file. + The keys are generated by passing each packet to get_pkt_key(pkt). + :param pcap_file: The pcap file for which a packet number dictionary is desired. + :return: A dictionary mapping packet keys (obtainable from get_pkt_key(pkt)) to the packets packet number. + """ + pkts = rdpcap(pcap_file) + map = {} + for i, pkt in enumerate(pkts): + pkt_num = i + 1 + key = get_pkt_key(pkt) + assert(key not in map) + map[key] = pkt_num + assert(len(map) == len(pkts)) + # Double check that numbers come out right. Can be removed in final version. + pkts = rdpcap(pcap_file) + for i, pkt in enumerate(pkts): + pkt_key = get_pkt_key(pkt) + assert(pkt_key in map and map[pkt_key] == i+1) + return map + + +def add_pkt_numbers_to_matches(pcap_file, matches): + """ + Hacky way to augment the matches with packet numbers. Assumes the same device does not send or receive more than + one packet at a given timestamp. + :param pcap_file: The pcap file where the matches were found in. + :param matches: The matches. + :return: matches augmented with packet numbers; each packet is converted to a (pkt, pkt_number) tuple. + """ + pkt_nums_dict = build_pkt_number_dict(pcap_file) + result = [] + for req_pkt, replies in matches: + req_pkt_num = pkt_nums_dict[get_pkt_key(req_pkt)] #find_pkt_number(req_pkt, pcap_file) + numbered_req_pkt = (req_pkt, req_pkt_num) + numbered_reply_pkts = [] + for reply_pkt in replies: + reply_pkt_num = pkt_nums_dict[get_pkt_key(reply_pkt)] #find_pkt_number(reply_pkt, pcap_file) + numbered_reply_pkts.append((reply_pkt, reply_pkt_num)) + result.append((numbered_req_pkt, numbered_reply_pkts)) + return result + + +def write_matches_to_csv(matches, csv_filename): + """ + Output matches to a .csv file. + matches argument is expected to be in the format returned by add_pkt_numbers_to_matches(pcap_file, matches). + :param matches: A list of matches w/ packet numbers, as returned by add_pkt_numbers_to_matches(pcap_file, matches). + :param csv_filename: Path to the .csv file where the output is to be written. + :return: None. + """ + key_req_pkt = 'request_pkt' + key_reply_pkts = 'reply_pkts' + key_reply_pkts_count = 'number_of_reply_pkts' + key_conversation_info = 'tls_conversation_between' + columns = [key_req_pkt, key_reply_pkts, key_reply_pkts_count, key_conversation_info] + with open (csv_filename, 'wb') as csv_file: + writer = csv.DictWriter(csv_file, fieldnames=columns) + writer.writeheader() + for m in matches: + request_pkt = m[0][0] + request_pkt_num = m[0][1] + reply_pkts_numbers = [] + for (reply_pkt, reply_pkt_num) in m[1]: + reply_pkts_numbers.append(reply_pkt_num) + info = f'{request_pkt[IP].src+":"+str(request_pkt[TCP].sport)} and ' + \ + f'{request_pkt[IP].dst+":"+str(request_pkt[TCP].dport)}' + row = { key_req_pkt: request_pkt_num, + key_reply_pkts: '; '.join(str(pkt_num) for pkt_num in reply_pkts_numbers), + key_reply_pkts_count: len(reply_pkts_numbers), + key_conversation_info: info} + writer.writerow(row) + + +if __name__ == '__main__': + desc = 'Perform detection on padded TLS traffic; ' + \ + 'i.e., the detection is entirely based on timing information and packet directions. ' + \ + 'NOTE: THIS CODE IS SIMPLIFIED AND ONLY WORKS FOR SIMPLE [Client-to-Server, Server-to-Client] TWO ' + \ + 'PACKET SIGNATURES.' + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('pcap_file', help='Full path to the target pcap file (detection target trace).') + parser.add_argument('device_ip', help='Perform detection on TLS flows from this device (identified by IP) only.') + h = 'Duration of the signature ' + \ + '(max time between request and reply packet for the two packets to be considered a match). ' + \ + 'Unit: seconds (floating point number expected).' + parser.add_argument('signature_duration', + help=h, type=float) + parser.add_argument('output_csv', help='Filename of CSV file where results are to be written.') + args = parser.parse_args() + + pcap_file = args.pcap_file + device_ip = args.device_ip + signature_duration = args.signature_duration + output_csv = args.output_csv + + load_layer('tls') + + matches = find_matches(pcap_file, device_ip, signature_duration) + matches = add_pkt_numbers_to_matches(pcap_file, matches) + write_matches_to_csv(matches, output_csv) + diff --git a/packet-padding/timing_detection_vpn_padding.py b/packet-padding/timing_detection_vpn_padding.py new file mode 100644 index 0000000..dd63c23 --- /dev/null +++ b/packet-padding/timing_detection_vpn_padding.py @@ -0,0 +1,114 @@ +import argparse +import ipaddress +import socket +import unicodecsv as csv + +from scapy.all import * + + +def find_matches(pcap_file, router_wan_ip, sig_duration): + # Read all packets into memory (stored as a list). + # This is slow and consumes lots of memory. + # There are more efficient ways to read the pcap (which clear each packet from memory after it's been processed). + # However, to simplify the detection implementation we stick with the quick-and-dirty approach. + pkts = rdpcap(pcap_file) + # The potential signature matches, with the request packet as the first item of a tuple, and all possible reply + # packets as the second item. + matches = [] + for idx, p in enumerate(pkts): + # Only consider IP traffic (note: this does not account for IPv6). + if not IP in p: + continue + # Note: IP addresses are apparently stored in string form (odd) + src = p[IP].src + dst = p[IP].dst + if src != router_wan_ip: + # We are trying to find matches for a simple [C->S, S->C] signature, so we want to first identify an + # outbound (router-to-cloud) packet and then subsequently find all potential reply packets (cloud-to-router) + # If this is a cloud-to-router packet, it is of no interest to us at this stage, so move on. + continue + # TODO should we exclude all multicasts+broadcasts? They wouldn't occur and/or be tunneled? + if ipaddress.ip_address(dst).is_multicast: + # Don't include multicast traffic originating from the router in the results. + continue + # Find the set of potential reply packets for this request. + replies = find_reply_pkts(router_wan_ip, pkts, idx, sig_duration) + # Store packet index alongside packet so that we can provide packet numbers for post analysis. + matches.append(((p, idx), replies)) + return matches + + +def find_reply_pkts(router_wan_ip, pkts, request_pkt_idx, sig_duration): + request_pkt = pkts[request_pkt_idx] + idx = request_pkt_idx + 1 + reply_pkts = [] + while idx < len(pkts) and pkts[idx].time - request_pkt.time <= sig_duration: + pkt = pkts[idx] + if is_inbound_ip_pkt(pkt, router_wan_ip): + # Only count IP packets with router WAN IP as destination as potential replies to the request. + # Store packet index alongside packet so that we can provide packet numbers for post analysis. + reply_pkts.append((pkt, idx)) + idx += 1 + return reply_pkts + + +def is_inbound_ip_pkt(pkt, router_wan_ip): + return IP in pkt and pkt[IP].dst == router_wan_ip + + +def write_matches_to_csv(matches, csv_filename): + key_req_pkt = 'request_pkt' + key_reply_pkts = 'reply_pkts' + key_reply_pkts_count = 'number_of_reply_pkts' + columns = [key_req_pkt, key_reply_pkts, key_reply_pkts_count] + with open (csv_filename, 'wb') as csv_file: + writer = csv.DictWriter(csv_file, fieldnames=columns) + writer.writeheader() + for m in matches: + request_pkt = m[0][0] + request_pkt_idx = m[0][1] + # Wireshark packet numbers start from 1 (are not 0-based) + request_pkt_num = request_pkt_idx + 1 + reply_pkts_numbers = [] + for (reply_pkt, reply_pkt_idx) in m[1]: + reply_pkt_num = reply_pkt_idx + 1 + reply_pkts_numbers.append(reply_pkt_num) + row = { key_req_pkt: request_pkt_num, + key_reply_pkts: '; '.join(str(pkt_num) for pkt_num in reply_pkts_numbers), + key_reply_pkts_count: len(reply_pkts_numbers) } + writer.writerow(row) + + +if __name__ == '__main__': + desc = 'Perform detection on traffic in a VPN tunnel where traffic is padded; ' + \ + 'i.e., the detection is entirely based on timing information and packet directions. ' + \ + 'NOTE: THIS CODE IS SIMPLIFIED AND ONLY WORKS FOR SIMPLE [Client-to-Server, Server-to-Client] TWO ' + \ + 'PACKET SIGNATURES.' + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('pcap_file', help='Full path to the target pcap file (detection target trace).') + parser.add_argument('router_wan_ip', help='IP of WAN interface of the home router (in decimal format).') + h = 'Duration of the signature ' + \ + '(max time between request and reply packet for the two packets to be considered a match). ' + \ + 'Unit: seconds (floating point number expected).' + parser.add_argument('signature_duration', + help=h, type=float) + parser.add_argument('output_csv', help='Filename of CSV file where results are to be written.') + args = parser.parse_args() + + pcap_file = args.pcap_file + router_wan_ip = args.router_wan_ip + signature_duration = args.signature_duration + output_csv = args.output_csv + print('Parsed arguments:') + print(f'pcap_file={pcap_file}') + print(f'router_wan_ip={router_wan_ip}') + print(f'signature_duration={signature_duration}') + print(f'output_csv={output_csv}') + + events = find_matches(pcap_file, router_wan_ip, signature_duration) + # for e in events: + # request_pkt = e[0][0] + # request_pkt_num = e[0][1] + 1 + # for (reply_pkt, reply_pkt_num) in e[1]: + # print(f"MATCH: Packet number {request_pkt_num} (request) with packet number {reply_pkt_num + 1} (reply).") + write_matches_to_csv(events, output_csv) diff --git a/vpn/run-all.sh b/vpn/run-all.sh new file mode 100755 index 0000000..18060ef --- /dev/null +++ b/vpn/run-all.sh @@ -0,0 +1,112 @@ +#!/bin/sh + +TRACES_DIR="../smarthome" + +DIR="amazon-plug" +FEATURE="amazon-plug" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="arlo-camera" +FEATURE="arlo-camera" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="blossom-sprinkler/blossom-sprinkler-quickrun" +FEATURE="blossom-sprinkler-quickrun" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="blossom-sprinkler/blossom-sprinkler-mode" +FEATURE="blossom-sprinkler-mode" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="dlink-plug" +FEATURE="dlink-plug" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="dlink-siren" +FEATURE="dlink-siren" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="ecobee-thermostat/ecobee-thermostat-hvac" +FEATURE="ecobee-thermostat-hvac" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="ecobee-thermostat/ecobee-thermostat-fan" +FEATURE="ecobee-thermostat-fan" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="kwikset-doorlock" +FEATURE="kwikset-doorlock" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="nest-thermostat" +FEATURE="nest-thermostat" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="rachio-sprinkler/rachio-sprinkler-quickrun" +FEATURE="rachio-sprinkler-quickrun" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="rachio-sprinkler/rachio-sprinkler-mode" +FEATURE="rachio-sprinkler-mode" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="ring-alarm" +FEATURE="ring-alarm" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="roomba-vacuum-robot" +FEATURE="roomba-vacuum-robot" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="sengled-bulb/sengled-bulb-onoff" +FEATURE="sengled-bulb-onoff" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +#./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="sengled-bulb/sengled-bulb-intensity" +FEATURE="sengled-bulb-intensity" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="st-plug" +FEATURE="st-plug" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + +DIR="tplink-plug" +FEATURE="tplink-plug" +echo "==> START TIME for $DIR: " +date +"%m/%d/%Y %r" +./run.sh $TRACES_DIR/$DIR/eth0/$FEATURE.eth0.detection.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.from-router.pcap $TRACES_DIR/$DIR/event/$FEATURE.eth0.event.to-router.pcap + diff --git a/vpn/run-in-loop.sh b/vpn/run-in-loop.sh new file mode 100755 index 0000000..e1fe6dd --- /dev/null +++ b/vpn/run-in-loop.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +FILE_TO_INJECT=$1 +#RAND_START=0 +#RAND_END=60 +TIMES=101 + +COUNT=1 +while [ $COUNT -lt $TIMES ] +do + #RAND=$[`shuf -i $RAND_START-$RAND_END -n 1`] + #RAND="$(awk 'BEGIN{srand();print int(rand()*60)}')" + # This one works well to generate a random number between 0-999 on OpenWrt + RAND=$(head -30 /dev/urandom | tr -dc "0123456789" | head -c3) + sleep $(($RAND%240)) + tcpreplay -i eth1 -q "$FILE_TO_INJECT" + echo "Delay for $FILE_TO_INJECT: $(($RAND%240)) seconds" + date +"%m/%d/%Y %r" + COUNT=`expr $COUNT + 1` +done diff --git a/vpn/run.sh b/vpn/run.sh new file mode 100755 index 0000000..096a715 --- /dev/null +++ b/vpn/run.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# File that is to be injected by traffic +FILE_MAIN_PCAP=$1 + +# File that contains event packets that are sent from router +FILE_PACKETS_FROM_ROUTER=$2 + +# File that contains event packets that are sent to router +FILE_PACKETS_TO_ROUTER=$3 + +./run-in-loop.sh "$FILE_PACKETS_FROM_ROUTER" & +./run-in-loop.sh "$FILE_PACKETS_TO_ROUTER" & +tcpreplay -i eth1 -q "$FILE_MAIN_PCAP" +echo "==> Finished with the file $FILE_MAIN_PCAP: " +date +"%m/%d/%Y %r"