Browse Source

- Improves using statistics in the PortscanAttack
- Removes (RST,ACK) answer from target host if port is not open
- Fixes bug: Temporary attack pcap is not deleted from /tmp directory
- Adds methods for retrieving statistics by Statistics class

Patrick Jattke 7 years ago
parent
commit
2e9a925eb8

+ 7 - 3
code/Attack/BaseAttack.py

@@ -124,8 +124,8 @@ class BaseAttack(metaclass=ABCMeta):
                 ports_output.append(port_entry)
             elif '-' in port_entry or '..' in port_entry:
                 # port_entry describes a port range
-                # allowed format: '12-123', '12..123', '12...123'
-                match = re.match('^([0-9]{1,4})(?:-|\.{2,3})([0-9]{1,4})$', port_entry)
+                # allowed format: '1-49151', '1..49151', '1...49151'
+                match = re.match('^([0-9]{1,5})(?:-|\.{2,3})([0-9]{1,5})$', port_entry)
                 # check validity of port range
                 # and create list of ports derived from given start and end port
                 (port_start, port_end) = int(match.group(1)), int(match.group(2))
@@ -135,7 +135,11 @@ class BaseAttack(metaclass=ABCMeta):
                     ports_list = [i for i in range(port_start, port_end + 1)]
                 # append ports at ports_output list
                 ports_output += ports_list
-        return True, ports_output
+
+        if len(ports_output) == 1:
+            return True, ports_output[0]
+        else:
+            return True, ports_output
 
     @staticmethod
     def _is_timestamp(timestamp: str):

+ 34 - 26
code/Attack/PortscanAttack.py

@@ -42,25 +42,30 @@ class PortscanAttack(BaseAttack.BaseAttack):
 
         # PARAMETERS: initialize with default values
         # (values are overwritten if user specifies them)
-        most_used_ipAddress = self.statistics.process_db_query("most_used(ipAddress)")
-        if isinstance(most_used_ipAddress, list):
-            most_used_ipAddress = most_used_ipAddress[0]
-        self.add_param_value(Param.IP_SOURCE, most_used_ipAddress)
+        most_used_ip_address = self.statistics.get_most_used_ip_address()
+        if isinstance(most_used_ip_address, list):
+            most_used_ip_address = most_used_ip_address[0]
+
+        self.add_param_value(Param.IP_SOURCE, most_used_ip_address)
         self.add_param_value(Param.IP_SOURCE_RANDOMIZE, 'False')
-        self.add_param_value(Param.IP_DESTINATION, '192.168.178.13')
+        self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
+
+        random_ip_address = self.statistics.get_random_ip_address()
+        self.add_param_value(Param.IP_DESTINATION, random_ip_address)
+        self.add_param_value(Param.MAC_DESTINATION, self.statistics.get_mac_address(random_ip_address))
+
         self.add_param_value(Param.PORT_DESTINATION, '0-1023,1720,1900,8080')
-        self.add_param_value(Param.PORT_SOURCE, '8542')
         self.add_param_value(Param.PORT_OPEN, '8080,9232,9233')
-        self.add_param_value(Param.PORT_SOURCE_RANDOM, 'False')
         self.add_param_value(Param.PORT_DEST_SHUFFLE, 'False')
         self.add_param_value(Param.PORT_ORDER_DESC, 'False')
-        macAddress = self.statistics.process_db_query('macAddress(ipAddress=' + most_used_ipAddress + ")")
-        self.add_param_value(Param.MAC_SOURCE, macAddress)
-        self.add_param_value(Param.MAC_DESTINATION, 'A0:1A:28:0B:62:F4')
+
+        self.add_param_value(Param.PORT_SOURCE, '8542')
+        self.add_param_value(Param.PORT_SOURCE_RANDOM, 'False')
+
         self.add_param_value(Param.PACKETS_PER_SECOND,
-                             (self.statistics.get_pps_sent(most_used_ipAddress) +
-                              self.statistics.get_pps_received(most_used_ipAddress)) / 2)
-        self.add_param_value(Param.INJECT_AT_TIMESTAMP, '1410733342')  # Sun, 14 Sep 2014 22:22:22 GMT
+                             (self.statistics.get_pps_sent(most_used_ip_address) +
+                              self.statistics.get_pps_received(most_used_ip_address)) / 2)
+        self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
 
     def get_packets(self):
         def update_timestamp(timestamp, pps, maxdelay):
@@ -88,9 +93,6 @@ class PortscanAttack(BaseAttack.BaseAttack):
         # TTL_samples = numpy.random.choice(keys, size=len(dest_ports), replace=True, dport=values)
         ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
 
-        # MSS (Maximum Segment Size) for Ethernet. Allowed values [536,1500]
-        mss = ('MSS', int(self.statistics.process_db_query('avg(mss)')))
-
         # Timestamp
         timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
         self.attack_start_utime = timestamp_next_pkt  # store start time of attack
@@ -105,6 +107,9 @@ class PortscanAttack(BaseAttack.BaseAttack):
         mac_source = self.get_param_value(Param.MAC_SOURCE)
         mac_destination = self.get_param_value(Param.MAC_DESTINATION)
 
+        # MSS (Maximum Segment Size) for Ethernet. Allowed values [536,1500]
+        mss = self.statistics.get_mss(ip_destination)
+
         for dport in dest_ports:
             # Parameters changing each iteration
             if self.get_param_value(Param.IP_SOURCE_RANDOMIZE) and isinstance(ip_source, list):
@@ -125,10 +130,13 @@ class PortscanAttack(BaseAttack.BaseAttack):
             reply_ether = Ether(src=mac_destination, dst=mac_source)
             reply_ip = IP(src=ip_destination, dst=ip_source, flags='DF')
 
-            if str(dport) in self.get_param_value(Param.PORT_OPEN):  # destination port is OPEN
+            if dport in self.get_param_value(Param.PORT_OPEN):  # destination port is OPEN
                 # target answers
-                reply_tcp = TCP(sport=dport, dport=sport, seq=0, ack=1, flags='SA', window=29200,
-                                options=[mss])
+                if mss is None:
+                    reply_tcp = TCP(sport=dport, dport=sport, seq=0, ack=1, flags='SA', window=29200)
+                else:
+                    reply_tcp = TCP(sport=dport, dport=sport, seq=0, ack=1, flags='SA', window=29200,
+                                    options=[('MSS', mss)])
                 # reply_tcp.time = time_sec_start + random.uniform(0.00005, 0.00013)
                 reply = (reply_ether / reply_ip / reply_tcp)
                 timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
@@ -144,13 +152,13 @@ class PortscanAttack(BaseAttack.BaseAttack):
                 reply.time = timestamp_next_pkt
                 packets.append(reply)
 
-            else:  # destination port is NOT OPEN
-                reply_tcp = TCP(sport=dport, dport=sport, flags='RA', seq=1, ack=1, window=0)
-                # reply_tcp.time = time_sec_start + random.uniform(0.00005, 0.00013)
-                reply = (reply_ether / reply_ip / reply_tcp)
-                timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
-                reply.time = timestamp_next_pkt
-                packets.append(reply)
+                # else:  # destination port is NOT OPEN -> no reply is sent by target
+                #     reply_tcp = TCP(sport=dport, dport=sport, flags='RA', seq=1, ack=1, window=0)
+                #     # reply_tcp.time = time_sec_start + random.uniform(0.00005, 0.00013)
+                #     reply = (reply_ether / reply_ip / reply_tcp)
+                #     timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
+                #     reply.time = timestamp_next_pkt
+                #     packets.append(reply)
 
         # store end time of attack
         self.attack_end_utime = reply.time

+ 4 - 0
code/ID2TLib/AttackController.py

@@ -1,4 +1,5 @@
 import importlib
+import os
 import tempfile
 
 from scapy.utils import PcapWriter
@@ -105,6 +106,9 @@ class AttackController:
         # Merge attack with existing pcap
         pcap_dest_path = self.pcap_file.merge_attack(temp_attack_pcap_path)
 
+        # Delete temporary attack pcap
+        os.remove(temp_attack_pcap_path)
+
         # Store label into LabelManager
         l = Label(attack, self.get_attack_start_utime(),
                   self.get_attack_end_utime(), attack_note)

+ 39 - 0
code/ID2TLib/Statistics.py

@@ -207,6 +207,45 @@ class Statistics:
         """
         return self.file_info['packetCount']
 
+    def get_most_used_ip_address(self):
+        """
+        :return: The IP address/addresses with the highest sum of packets sent and received
+        """
+        return self.process_db_query("most_used(ipAddress)")
+
+    def get_random_ip_address(self, count: int = 1):
+        """
+        :param count: The number of IP addreses to return
+        :return: A randomly chosen IP address from the dataset or iff param count is greater than one, a list of randomly
+         chosen IP addresses
+        """
+        if count == 1:
+            return self.process_db_query("random(all(ipAddress))")
+        else:
+            ip_address_list = []
+            for i in range(0, count):
+                ip_address_list.append(self.process_db_query("random(all(ipAddress))"))
+            return ip_address_list
+
+    def get_mac_address(self, ipAddress: str):
+        """
+        :return: The MAC address used in the dataset for the given IP address.
+        """
+        return self.process_db_query('macAddress(ipAddress=' + ipAddress + ")")
+
+    def get_mss(self, ipAddress: str):
+        """
+
+        :param ipAddress: The IP address whose used MSS should be determined
+        :return: The TCP MSS value used by the IP address, or if the IP addresses never specified a MSS,
+        then None is returned
+        """
+        mss_value = self.process_db_query('SELECT mss from tcp_mss WHERE ipAddress="' + ipAddress + '"')
+        if isinstance(mss_value, int):
+            return mss_value
+        else:
+            return None
+
     def get_statistics_database(self):
         """
         :return: A reference to the statistics database object

+ 5 - 4
code/ID2TLib/StatsDatabase.py

@@ -116,7 +116,7 @@ class StatsDatabase:
 
     def get_field_types(self, *table_names):
         """
-        Creates a dictionary whose keys are the fields of the given table(s) and whose values are the appropriates field
+        Creates a dictionary whose keys are the fields of the given table(s) and whose values are the appropriate field
         types, like TEXT for strings and REAL for float numbers.
 
         :param table_names: The name of table(s)
@@ -213,7 +213,7 @@ class StatsDatabase:
                         isinstance(last_result, list) or isinstance(last_result, tuple)):
                 extractor = q[0]
                 if extractor == 'random':
-                    index = randint(a=0, b=len(last_result))
+                    index = randint(a=0, b=len(last_result) - 1)
                     last_result = last_result[index]
                 elif extractor == 'first':
                     last_result = last_result[0]
@@ -277,7 +277,9 @@ class StatsDatabase:
             return
 
         # If result is tuple/list with single element, extract value from list
-        requires_extraction = (isinstance(result, list) or isinstance(result, tuple)) and len(result) == 1 and len(result[0]) == 1
+        requires_extraction = (isinstance(result, list) or isinstance(result, tuple)) and len(result) == 1 and \
+                              (not isinstance(result[0], tuple) or len(result[0]) == 1)
+
         while requires_extraction:
             if isinstance(result, list) or isinstance(result, tuple):
                 result = result[0]
@@ -312,7 +314,6 @@ class StatsDatabase:
         # Print number of results according to type of result
         if isinstance(result, list):
             print("Query returned " + str(len(result)) + " records:\n")
-
         else:
             print("Query returned 1 record:\n")