Quellcode durchsuchen

add platform specifics to SMB scan

add global host os variable
add security blobs for linux, macOS and windows
add generateSourcePortFromPlatform function
add getSmbPlatformData function
add get_rnd_boottime function
add get_filetime_format helper function
add current time generation
fix port selection overflow
fix generate_attack_pcap is not supposed to be a property
fix errorcodes
fix typos
Jens Keim vor 6 Jahren
Ursprung
Commit
caaddb2896
3 geänderte Dateien mit 140 neuen und 36 gelöschten Zeilen
  1. 138 35
      code/Attack/SmbScanAttack.py
  2. 1 1
      code/ID2TLib/AttackController.py
  3. 1 0
      code/ID2TLib/smb2.py

+ 138 - 35
code/Attack/SmbScanAttack.py

@@ -1,7 +1,8 @@
 import logging
 
 from random import shuffle, randint, choice, uniform
-
+from datetime import datetime, timedelta, tzinfo
+from calendar import timegm
 from lea import Lea
 
 from Attack import BaseAttack
@@ -28,6 +29,41 @@ class SmbScanAttack(BaseAttack.BaseAttack):
     # SMB dialects
     smb_dialects = ["PC NETWORK PROGRAM 1.0", "LANMAN1.0", "Windows for Workgroups 3.1a", "LM1.2X002", "LANMAN2.1",
                     "NT LM 0.12", "SMB 2.002", "SMB 2.???"]
+    # SMB security blobs
+    security_blob_windows = "\x60\x82\x01\x3c\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x82\x01\x30" \
+                            "\x30\x82\x01\x2c\xa0\x1a\x30\x18\x06\x0a\x2b\x06\x01\x04\x01\x82" \
+                            "\x37\x02\x02\x1e\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" \
+                            "\xa2\x82\x01\x0c\x04\x82\x01\x08\x4e\x45\x47\x4f\x45\x58\x54\x53" \
+                            "\x01\x00\x00\x00\x00\x00\x00\x00\x60\x00\x00\x00\x70\x00\x00\x00" \
+                            "\xbc\x84\x03\x97\x6f\x80\x3b\x81\xa6\x45\x1b\x05\x92\x39\xde\x3d" \
+                            "\xd6\x91\x85\x49\x8a\xd0\x3b\x58\x87\x99\xb4\x98\xdf\xa6\x1d\x73" \
+                            "\x3b\x57\xbf\x05\x63\x5e\x30\xea\xa8\xd8\xd8\x45\xba\x80\x52\xa5" \
+                            "\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\x00\x00\x01\x00\x00\x00" \
+                            "\x00\x00\x00\x00\x00\x00\x00\x00\x5c\x33\x53\x0d\xea\xf9\x0d\x4d" \
+                            "\xb2\xec\x4a\xe3\x78\x6e\xc3\x08\x4e\x45\x47\x4f\x45\x58\x54\x53" \
+                            "\x03\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\x98\x00\x00\x00" \
+                            "\xbc\x84\x03\x97\x6f\x80\x3b\x81\xa6\x45\x1b\x05\x92\x39\xde\x3d" \
+                            "\x5c\x33\x53\x0d\xea\xf9\x0d\x4d\xb2\xec\x4a\xe3\x78\x6e\xc3\x08" \
+                            "\x40\x00\x00\x00\x58\x00\x00\x00\x30\x56\xa0\x54\x30\x52\x30\x27" \
+                            "\x80\x25\x30\x23\x31\x21\x30\x1f\x06\x03\x55\x04\x03\x13\x18\x54" \
+                            "\x6f\x6b\x65\x6e\x20\x53\x69\x67\x6e\x69\x6e\x67\x20\x50\x75\x62" \
+                            "\x6c\x69\x63\x20\x4b\x65\x79\x30\x27\x80\x25\x30\x23\x31\x21\x30" \
+                            "\x1f\x06\x03\x55\x04\x03\x13\x18\x54\x6f\x6b\x65\x6e\x20\x53\x69" \
+                            "\x67\x6e\x69\x6e\x67\x20\x50\x75\x62\x6c\x69\x63\x20\x4b\x65\x79"
+    security_blob_ubuntu = "\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e" \
+                           "\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa3\x2a" \
+                           "\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f\x64\x65\x66\x69\x6e\x65" \
+                           "\x64\x5f\x69\x6e\x5f\x52\x46\x43\x34\x31\x37\x38\x40\x70\x6c\x65" \
+                           "\x61\x73\x65\x5f\x69\x67\x6e\x6f\x72\x65"
+    security_blob_macos =   "\x60\x7e\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x74\x30\x72\xa0\x44" \
+                            "\x30\x42\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02\x06\x09\x2a" \
+                            "\x86\x48\x86\xf7\x12\x01\x02\x02\x06\x06\x2a\x85\x70\x2b\x0e\x03" \
+                            "\x06\x06\x2b\x06\x01\x05\x05\x0e\x06\x0a\x2b\x06\x01\x04\x01\x82" \
+                            "\x37\x02\x02\x0a\x06\x06\x2b\x05\x01\x05\x02\x07\x06\x06\x2b\x06" \
+                            "\x01\x05\x02\x05\xa3\x2a\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f" \
+                            "\x64\x65\x66\x69\x6e\x65\x64\x5f\x69\x6e\x5f\x52\x46\x43\x34\x31" \
+                            "\x37\x38\x40\x70\x6c\x65\x61\x73\x65\x5f\x69\x67\x6e\x6f\x72\x65"
+
 
     def __init__(self):
         """
@@ -98,7 +134,8 @@ class SmbScanAttack(BaseAttack.BaseAttack):
 
         rnd_ip_count = self.statistics.get_ip_address_count()/2
         self.add_param_value(Param.HOSTING_IP, self.statistics.get_random_ip_address(rnd_ip_count))
-        self.add_param_value(Param.HOSTING_VERSION, self.get_rnd_smb_version())
+        self.host_os = self.get_rnd_os()
+        self.add_param_value(Param.HOSTING_VERSION, self.get_smb_version(self.host_os))
         self.add_param_value(Param.SOURCE_PLATFORM, self.get_rnd_os())
         self.add_param_value(Param.PROTOCOL_VERSION, "1")
         self.add_param_value(Param.IP_DESTINATION_END, "0.0.0.0")
@@ -108,8 +145,7 @@ class SmbScanAttack(BaseAttack.BaseAttack):
                                        "linux": 3.38, "win8": 1.35, "winvista": 0.46, "winnt": 0.31})
         return os_dist.random()
 
-    def get_rnd_smb_version(self):
-        os = self.get_rnd_os()
+    def get_smb_version(self, os: str):
         if os is "linux":
             return random.choice(list(self.smb_versions_per_samba.values()))
         elif os is "macos":
@@ -118,7 +154,10 @@ class SmbScanAttack(BaseAttack.BaseAttack):
         else:
             return self.smb_versions_per_win[os]
 
-    @property
+    def get_rnd_smb_version(self):
+        os = self.get_rnd_os()
+        return self.get_smb_version(os)
+
     def generate_attack_pcap(self):
         def update_timestamp(timestamp, pps, delay=0):
             """
@@ -198,6 +237,73 @@ class SmbScanAttack(BaseAttack.BaseAttack):
 
             return ips
 
+        def checkPlatform(platform: str):
+            if platform not in self.platforms:
+                print("\nERROR: Invalid platform: " + platform + "." +
+                      "\n Please select one of the following platforms: ", self.platforms)
+                exit(1)
+
+        def generateSourcePortFromPlatform(platform: str, previousPort=0):
+            checkPlatform(platform)
+            if platform in {"winnt", "winxp", "win2000"}:
+                if (previousPort == 0) or (previousPort+1 > 5000):
+                    return randint(1024, 5000)
+                else:
+                    return previousPort+1
+            elif platform == "linux":
+                return randint(32768, 61000)
+            else:
+                if (previousPort == 0) or (previousPort+1 > 65535):
+                    return randint(49152, 65535)
+                else:
+                    return previousPort+1
+
+        # FIXME: rework copy-pasted code
+        # source: http://reliablybroken.com/b/2009/09/working-with-active-directory-filetime-values-in-python/
+        # WORK IN PROGRESS
+        def get_filetime_format(timestamp):
+            EPOCH_AS_FILETIME = 116444736000000000  # January 1, 1970 as MS file time
+            HUNDREDS_OF_NANOSECONDS = 10000000
+            boot_datetime = datetime.fromtimestamp(timestamp)
+            if (boot_datetime.tzinfo is None) or (boot_datetime.tzinfo.utcoffset(boot_datetime) is None):
+                boot_datetime = boot_datetime.replace(tzinfo=boot_datetime.tzname())
+            boot_filetime = EPOCH_AS_FILETIME + (timegm(boot_datetime.timetuple()) * HUNDREDS_OF_NANOSECONDS)
+            return boot_filetime + (boot_datetime.microsecond * 10)
+
+        def get_rnd_boot_time(timestamp, platform="winxp"):
+            checkPlatform(platform)
+            # FIXME: create probability distribution for each OS
+            if platform is "linux":
+                # four years
+                timestamp -= randint(0, 126144000)
+            if platform is "macOS":
+                # three months
+                timestamp -= randint(0, 7884000)
+            else:
+                # one month
+                timestamp -= randint(0, 2678400)
+            return get_filetime_format(timestamp)
+
+        def getSmbPlatformData(platform: str, timestamp=time.time()):
+            checkPlatform(platform)
+            if platform == "linux":
+                blob = self.security_blob_ubuntu
+                capabilities = 0x5
+                dataSize = 0x800000
+                serverStartTime = 0
+            elif platform == "macos":
+                blob = self.security_blob_macos
+                capabilities = 0x6
+                dataSize = 0x400000
+                serverStartTime = 0
+            else:
+                blob = self.security_blob_windows
+                capabilities = 0x7
+                dataSize = 0x100000
+                serverStartTime = get_rnd_boot_time(timestamp)
+            return blob, capabilities, dataSize, serverStartTime
+
+
         pps = self.get_param_value(Param.PACKETS_PER_SECOND)
 
         # Calculate complement packet rates of the background traffic for each interval
@@ -218,33 +324,23 @@ class SmbScanAttack(BaseAttack.BaseAttack):
         mac_source = self.get_param_value(Param.MAC_SOURCE)
         mac_dest = self.get_param_value(Param.MAC_DESTINATION)
         # Check smb version
-        def invalid_verison(version: str):
+        def invalid_version(version: str):
             print("\nInvalid smb version: " + version +
-                  "\nPlease select one of the following versions: 1, 2.0, 2.1, 3.0, 3.0.2, 3.1.1")
-            # FIXME: useful error code
-            exit(-1)
+                  "\nPlease select one of the following versions: ", self.smb_versions)
+            exit(1)
         smb_version = self.get_param_value(Param.PROTOCOL_VERSION)
         if smb_version not in self.smb_versions:
-            invalid_verison(smb_version)
+            invalid_version(smb_version)
         hosting_version = self.get_param_value(Param.HOSTING_VERSION)
         if hosting_version not in self.smb_versions:
-            invalid_verison(hosting_version)
+            invalid_version(hosting_version)
         # Check source platform
         src_platform = self.get_param_value(Param.SOURCE_PLATFORM).lower()
-        if src_platform not in self.platforms:
-            print("\nInvalid source platform: " + src_platform + ". Selecting random platform as source platform.")
-            src_platform = self.get_rnd_os()
         packets = []
 
         # randomize source ports according to platform, if specified
         if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
-            if src_platform in {"winnt", "winxp", "win2000"}:
-                sport = randint(1024, 5000)
-            elif src_platform == "linux":
-                sport = randint(32768, 61000)
-            else:
-                sport = randint(49152, 65535)
-
+            sport = generateSourcePortFromPlatform(src_platform)
         else:
             sport = self.get_param_value(Param.PORT_SOURCE)
 
@@ -307,10 +403,7 @@ class SmbScanAttack(BaseAttack.BaseAttack):
 
                 # Randomize source port for each connection if specified
                 if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
-                    if src_platform == "linux":
-                        sport = randint(32768, 61000)
-                    else:
-                        sport = sport+1
+                    sport = generateSourcePortFromPlatform(src_platform, sport)
 
                 # 1) Build request package
                 request_ether = Ether(src=mac_source, dst=mac_destination)
@@ -338,8 +431,7 @@ class SmbScanAttack(BaseAttack.BaseAttack):
                     reply_ether = Ether(src=mac_destination, dst=mac_source)
                     reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
                     reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='SA',
-                                    window=destination_win_value,
-                                    options=[('MSS', destination_mss_value)])
+                                    window=destination_win_value, options=[('MSS', destination_mss_value)])
                     victim_seq += 1
                     reply = (reply_ether / reply_ip / reply_tcp)
                     reply.time = timestamp_reply
@@ -355,8 +447,6 @@ class SmbScanAttack(BaseAttack.BaseAttack):
                     confirm.time = timestamp_confirm
                     packets.append(confirm)
 
-                    # INSERT SMB-REQUEST PACKAGE HERE
-                    # FIXME: CHECK FOR PROTOCOL VERSION?
                     smb_MID = randint(1, 65535)
                     smb_PID = randint(1, 65535)
                     smb_req_tail_arr = []
@@ -375,8 +465,8 @@ class SmbScanAttack(BaseAttack.BaseAttack):
                             smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail(BufferData = dia))
                             smb_req_tail_size += len(SMBNegociate_Protocol_Request_Tail(BufferData = dia))
 
-                    smb_req_head = SMBNegociate_Protocol_Request_Header(Flags2=0x2801, PID=smb_PID, MID=smb_MID,
-                                                                        ByteCount=smb_req_tail_size)
+                    smb_req_head = SMBNegociate_Protocol_Request_Header\
+                        (Flags2=0x2801, PID=smb_PID, MID=smb_MID, ByteCount=smb_req_tail_size)
                     smb_req_length = len(smb_req_head) + smb_req_tail_size
                     smb_req_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_req_length)
                     smb_req_tcp = TCP(sport=sport, dport=self.smb_port, flags='PA', seq=attacker_seq, ack=victim_seq)
@@ -402,13 +492,27 @@ class SmbScanAttack(BaseAttack.BaseAttack):
                     packets.append(confirm_smb_req)
 
                     # smb response package
+                    timestamp_smb_rsp = update_timestamp(timestamp_reply, pps, minDelay)
+
+                    security_blob, capabilities, dataSize, serverStartTime = getSmbPlatformData\
+                        (self.host_os, time.mktime(time.strptime(self.statistics.get_pcap_timestamp_start()[:19],
+                                                                 "%Y-%m-%d %H:%M:%S")))
+                    diff = timestamp_smb_rsp - timestamp_smb_req
+                    begin = get_filetime_format(timestamp_smb_req+diff*0.1)
+                    end = get_filetime_format(timestamp_smb_rsp-diff*0.1)
+                    systemtime = randint(begin, end)
+
                     if smb_version is not "1" and hosting_version is not "1":
                         smb_rsp_paket = SMB2_SYNC_Header(Flags = 1)
-                        smb_rsp_negotiate_body = SMB2_Negotiate_Protocol_Response(DialectRevision=0x02ff)
+                        smb_rsp_negotiate_body = SMB2_Negotiate_Protocol_Response\
+                            (DialectRevision=0x02ff, SecurityBufferOffset=124, SecurityBufferLength=len(security_blob),
+                             SecurityBlob=security_blob, Capabilities=capabilities, MaxTransactSize=dataSize,
+                             MaxReadSize=dataSize, MaxWriteSize=dataSize, SystemTime=systemtime,
+                             ServerStartTime=serverStartTime)
                         smb_rsp_length = len(smb_rsp_paket) + len(smb_rsp_negotiate_body)
                     else:
-                        smb_rsp_paket = SMBNegociate_Protocol_Response_No_Security_No_Key(Start="\xffSMB" , PID=smb_PID,
-                                                                                          MID=smb_MID, DialectIndex=5)
+                        smb_rsp_paket = SMBNegociate_Protocol_Response_Advanced_Security\
+                            (Start="\xffSMB", PID=smb_PID, MID=smb_MID, DialectIndex=5, SecurityBlob=security_blob)
                         smb_rsp_length = len(smb_rsp_paket)
                     smb_rsp_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_rsp_length)
                     smb_rsp_tcp = TCP(sport=self.smb_port, dport=sport, flags='PA', seq=victim_seq, ack=attacker_seq)
@@ -422,7 +526,6 @@ class SmbScanAttack(BaseAttack.BaseAttack):
                     if smb_version is not "1"and hosting_version is not "1":
                         smb_rsp_combined = (smb_rsp_combined / smb_rsp_negotiate_body)
 
-                    timestamp_smb_rsp = update_timestamp(timestamp_reply, pps, minDelay)
                     smb_rsp_combined.time = timestamp_smb_rsp
                     packets.append(smb_rsp_combined)
 

+ 1 - 1
code/ID2TLib/AttackController.py

@@ -81,7 +81,7 @@ class AttackController:
         # Write attack into pcap file
         print("Generating attack packets...", end=" ")
         sys.stdout.flush()  # force python to print text immediately
-        total_packets, temp_attack_pcap_path = self.current_attack.generate_attack_pcap
+        total_packets, temp_attack_pcap_path = self.current_attack.generate_attack_pcap()
         print("done. (total: " + str(total_packets) + " pkts.)")
 
         # Store label into LabelManager

+ 1 - 0
code/ID2TLib/smb2.py

@@ -37,6 +37,7 @@ class SMB2_Negotiate_Protocol_Response(Packet):
                    LELongField("ServerStartTime",0),
                    LEShortField("SecurityBufferOffset",0),
                    LEShortField("SecurityBufferLength",0),
+                   StrLenField("SecurityBlob", "", length_from=lambda x: x.ByteCount + 16),
                    LEIntField("NegotiateContextOffset/Reserved2",0)]