Generator.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. from scapy.packet import Raw
  2. import numpy.random as random2
  3. import random
  4. import string
  5. from numpy.random import bytes
  6. from random import getrandbits
  7. from scapy.layers.inet import IP, Ether, UDP, TCP
  8. from scapy.packet import Raw
  9. from Attack.MembersMgmtCommAttack import MessageType
  10. from . import IPv4 as ip
  11. #################################################
  12. ######## Functions operating on payloads ########
  13. #################################################
  14. def add_padding(packet, bytes_padding:int = 0, user_padding:bool=True, rnd:bool = False):
  15. '''
  16. Adds padding to a packet with the given amount of bytes, but a maximum of 100 bytes, if called by the user.
  17. :param packet: the packet that will be extended with the additional payload
  18. :param bytes_padding: the amount of bytes that will be appended to the packet. Capped to 100,
  19. if called by the user.
  20. :param user_padding: true, if the function add_padding by the user and not within the code
  21. :param rnd: adds a random padding between 0 and bytes_padding, if true
  22. :return: the initial packet, extended with the wanted amount of bytes of padding
  23. '''
  24. if(user_padding == True and bytes_padding > 100):
  25. bytes_padding = 100
  26. if (rnd is True):
  27. r = int(round(bytes_padding / 4)) #sets bytes_padding to any number between 0 and bytes_padding
  28. bytes_padding = random2.random_integers(0, r) * 4 #, that's dividable by 4
  29. payload = generate_payload(bytes_padding)
  30. packet[Raw].load += Raw(load=payload).load
  31. return packet
  32. def equal_length(list_of_packets:list, length:int = 0, padding:int = 0, force_len:bool = False):
  33. '''
  34. Equals the length of all packets of a given list of packets to the given length. If the given length is smaller than the largest
  35. packet, all the other packets are extended to the largest packet's length. Add additional padding
  36. afterwards to create realism.
  37. :param list_of_packets: The given set of packet.
  38. :param length: The length each packet should have. Can be redundant, if the largest packet has more bytes
  39. :param force_len: if true, all packets are forced to take on the length of param length
  40. than length.
  41. :return: The list of extended packets.
  42. '''
  43. if not force_len:
  44. largest_packet = length
  45. for packet in list_of_packets:
  46. packet_length = len(packet)
  47. if(packet_length > largest_packet):
  48. largest_packet = packet_length
  49. else:
  50. largest_packet = length
  51. for packet in list_of_packets:
  52. bytes_padding = largest_packet - len(packet)
  53. if(bytes_padding > 0):
  54. add_padding(packet, bytes_padding, False, False) #Add padding to extend to param:length
  55. add_padding(packet, padding, False, True) #Add random additional padding to create realism
  56. return list_of_packets
  57. def generate_payload(size:int=0):
  58. """
  59. Generates a payload of random bytes of the given amount
  60. :param size: number of generated bytes
  61. :return: the generated payload
  62. """
  63. payload = bytes(size)
  64. return payload
  65. #################################################
  66. ######## Generation of random port ########
  67. #################################################
  68. def gen_random_server_port(offset: int=2199):
  69. """
  70. Generates a valid random first and last character for a bots hostname
  71. and computes a port from these two characters.
  72. The default offset is chosen from a Sality implementation in 2011
  73. """
  74. firstLetter = random.choice(string.ascii_letters);
  75. lastLetter = random.choice(string.ascii_letters + string.digits);
  76. return (offset + ord(firstLetter) * ord(lastLetter));
  77. #################################################
  78. ######## MAC address generation ########
  79. #################################################
  80. class MacAddressGenerator:
  81. def __init__(self, include_broadcast_macs=False, include_virtual_macs=False):
  82. self.broadcast = include_broadcast_macs
  83. self.virtual = include_virtual_macs
  84. self.generated = set()
  85. def random_mac(self) -> str:
  86. while True:
  87. mac = self._random_mac()
  88. if mac not in self.generated:
  89. self.generated.add(mac)
  90. return mac
  91. def clear(self):
  92. self.generated.clear()
  93. def generates_broadcast_macs(self) -> bool:
  94. return self.broadcast
  95. def generates_virtual_macs(self) -> bool:
  96. return self.virtual
  97. def set_broadcast_generation(self, broadcast: bool):
  98. self.broadcast = broadcast
  99. def set_virtual_generation(self, virtual: bool):
  100. self.virtual = virtual
  101. def _random_mac(self) -> str:
  102. mac_bytes = bytearray(getrandbits(8) for i in range(6))
  103. if not self.broadcast:
  104. mac_bytes[0] &= ~1 # clear the first bytes' first bit
  105. if not self.virtual:
  106. mac_bytes[0] &= ~2 # clear the first bytes' second bit
  107. return ":".join("%02X" % b for b in mac_bytes)
  108. #################################################
  109. ######## UDP/TCP Packet generation ########
  110. #################################################
  111. class PacketGenerator():
  112. """
  113. Creates packets, based on the set protocol
  114. """
  115. def __init__(self, protocol="udp"):
  116. """
  117. Creates a new Packet_Generator Object
  118. :param protocol: the protocol of the packets to be created, udp or tcp
  119. """
  120. super(PacketGenerator, self).__init__()
  121. self.protocol = protocol
  122. def generate_packet(self, ip_src: str = "192.168.64.32", ip_dst: str = "192.168.64.48",
  123. mac_src: str = "56:6D:D9:BC:70:1C",
  124. mac_dst: str = "F4:2B:95:B3:0E:1A", port_src: int = 1337, port_dst: int = 6442, ttl: int = 64,
  125. tcpflags: str = "S", payload: str = ""):
  126. """
  127. Creates a Packet with the specified Values for the current protocol
  128. :param ip_src: the source IP address of the IP header
  129. :param ip_dst the destination IP address of the IP header
  130. :param mac_src: the source MAC address of the MAC header
  131. :param mac_dst: the destination MAC address of the MAC header
  132. :param port_src: the source port of the header
  133. :param port_dst: the destination port of the header
  134. :param ttl: the ttl Value of the packet
  135. :param tcpflags: the TCP flags of the TCP header
  136. :param payload: the payload of the packet
  137. :return: the corresponding packet
  138. """
  139. if (self.protocol == "udp"):
  140. packet = generate_udp_packet(ip_src=ip_src, ip_dst=ip_dst, mac_src=mac_src, mac_dst=mac_dst, ttl=ttl,
  141. port_src=port_src, port_dst=port_dst, payload=payload)
  142. elif (self.protocol == "tcp"):
  143. packet = generate_tcp_packet(ip_src=ip_src, ip_dst=ip_dst, mac_src=mac_src, mac_dst=mac_dst, ttl=ttl,
  144. port_src=port_src, port_dst=port_dst, tcpflags=tcpflags, payload=payload)
  145. return packet
  146. def generate_mmcom_packet(self, ip_src: str = "192.168.64.32", ip_dst: str = "192.168.64.48",
  147. mac_src: str = "56:6D:D9:BC:70:1C",
  148. mac_dst: str = "F4:2B:95:B3:0E:1A", port_src: int = 1337, port_dst: int = 6442,
  149. tcpflags: str = "S", ttl: int = 64,
  150. message_type: MessageType = MessageType.SALITY_HELLO, neighborlist_entries: int = 1):
  151. """
  152. Creates a Packet for Members-Management-Communication with the specified Values and the current protocol
  153. :param ip_src: the source IP address of the IP header
  154. :param ip_dst the destination IP address of the IP header
  155. :param mac_src: the source MAC address of the MAC header
  156. :param mac_dst: the destination MAC address of the MAC header
  157. :param port_src: the source port of the header
  158. :param port_dst: the destination port of the header
  159. :param tcpflags: the TCP flags of the TCP header, if tcp is selected as protocol
  160. :param ttl: the ttl Value of the packet
  161. :param message_type: affects the size of the payload
  162. :param neighborlist_entries: number of entries of a Neighbourlist-reply, affects the size of the payload
  163. :return: the corresponding packet
  164. """
  165. # Determine length of the payload that has to be generated
  166. if (message_type == MessageType.SALITY_HELLO):
  167. payload_len = 0
  168. elif (message_type == MessageType.SALITY_HELLO_REPLY):
  169. payload_len = 22
  170. elif (message_type == MessageType.SALITY_NL_REQUEST):
  171. payload_len = 28
  172. elif (message_type == MessageType.SALITY_NL_REPLY):
  173. payload_len = 24 + 6 * neighborlist_entries
  174. else:
  175. payload_len = 0
  176. payload = generate_payload(payload_len)
  177. if (self.protocol == "udp"):
  178. packet = generate_udp_packet(ip_src=ip_src, ip_dst=ip_dst, mac_src=mac_src, mac_dst=mac_dst, ttl=ttl,
  179. port_src=port_src, port_dst=port_dst, payload=payload)
  180. elif (self.protocol == "tcp"):
  181. packet = generate_tcp_packet(ip_src=ip_src, ip_dst=ip_dst, mac_src=mac_src, mac_dst=mac_dst, ttl=ttl,
  182. port_src=port_src, port_dst=port_dst, tcpflags=tcpflags, payload=payload)
  183. else:
  184. print("Error: unsupported protocol for generating Packets")
  185. return packet
  186. def generate_tcp_packet(ip_src: str = "192.168.64.32", ip_dst: str = "192.168.64.48",
  187. mac_src: str = "56:6D:D9:BC:70:1C", ttl: int = 64,
  188. mac_dst: str = "F4:2B:95:B3:0E:1A", port_src: int = 1337, port_dst: int = 6442,
  189. tcpflags: str = "S", payload: str = ""):
  190. """
  191. Builds a TCP packet with the values specified by the caller.
  192. :param ip_src: the source IP address of the IP header
  193. :param ip_dst the destination IP address of the IP header
  194. :param mac_src: the source MAC address of the MAC header
  195. :param ttl: the ttl value of the packet
  196. :param mac_dst: the destination MAC address of the MAC header
  197. :param port_src: the source port of the TCP header
  198. :param port_dst: the destination port of the TCP header
  199. :param tcpflags: the TCP flags of the TCP header
  200. :param payload: the payload of the packet
  201. :return: the corresponding TCP packet
  202. """
  203. ether = Ether(src=mac_src, dst=mac_dst)
  204. ip = IP(src=ip_src, dst=ip_dst, ttl=ttl)
  205. tcp = TCP(sport=port_src, dport=port_dst, flags=tcpflags)
  206. packet = ether / ip / tcp / Raw(load=payload)
  207. return packet
  208. def generate_udp_packet(ip_src: str = "192.168.64.32", ip_dst: str = "192.168.64.48",
  209. mac_src: str = "56:6D:D9:BC:70:1C", ttl: int = 64,
  210. mac_dst: str = "F4:2B:95:B3:0E:1A", port_src: int = 1337, port_dst: int = 6442,
  211. payload: str = ""):
  212. """
  213. Builds an UDP packet with the values specified by the caller.
  214. :param ip_src: the source IP address of the IP header
  215. :param ip_dst the destination IP address of the IP header
  216. :param mac_src: the source MAC address of the MAC header
  217. :param ttl: the ttl value of the packet
  218. :param mac_dst: the destination MAC address of the MAC header
  219. :param port_src: the source port of the UDP header
  220. :param port_dst: the destination port of the UDP header
  221. :param payload: the payload of the packet
  222. :return: the corresponding UDP packet
  223. """
  224. ether = Ether(src=mac_src, dst=mac_dst)
  225. ip = IP(src=ip_src, dst=ip_dst, ttl=ttl)
  226. udp = UDP(sport=port_src, dport=port_dst)
  227. packet = ether / ip / udp / Raw(load=payload)
  228. return packet
  229. #################################################
  230. ######## IP address generation ########
  231. #################################################
  232. class IPChooser:
  233. def random_ip(self):
  234. return ip.IPAddress.from_int(random.randrange(0, 1 << 32))
  235. def size(self):
  236. return 1 << 32
  237. def __len__(self):
  238. return self.size()
  239. class IPChooserByRange(IPChooser):
  240. def __init__(self, ip_range):
  241. self.range = ip_range
  242. def random_ip(self):
  243. start = int(self.range.first_address())
  244. end = start + self.range.block_size()
  245. return ip.IPAddress.from_int(random.randrange(start, end))
  246. def size(self):
  247. return self.range.block_size()
  248. class IPChooserByList(IPChooser):
  249. def __init__(self, ips):
  250. self.ips = list(ips)
  251. if not self.ips:
  252. raise ValueError("list of ips must not be empty")
  253. def random_ip(self):
  254. return random.choice(self.ips)
  255. def size(self):
  256. return len(self.ips)
  257. class IPGenerator:
  258. def __init__(self, ip_chooser=IPChooser(), # include all ip-addresses by default (before the blacklist)
  259. include_private_ips=False, include_localhost=False,
  260. include_multicast=False, include_reserved=False,
  261. include_link_local=False, blacklist=None):
  262. self.blacklist = []
  263. self.generated_ips = set()
  264. if not include_private_ips:
  265. for segment in ip.ReservedIPBlocks.PRIVATE_IP_SEGMENTS:
  266. self.add_to_blacklist(segment)
  267. if not include_localhost:
  268. self.add_to_blacklist(ip.ReservedIPBlocks.LOCALHOST_SEGMENT)
  269. if not include_multicast:
  270. self.add_to_blacklist(ip.ReservedIPBlocks.MULTICAST_SEGMENT)
  271. if not include_reserved:
  272. self.add_to_blacklist(ip.ReservedIPBlocks.RESERVED_SEGMENT)
  273. if not include_link_local:
  274. self.add_to_blacklist(ip.ReservedIPBlocks.ZERO_CONF_SEGMENT)
  275. if blacklist:
  276. for segment in blacklist:
  277. self.add_to_blacklist(segment)
  278. self.chooser = ip_chooser
  279. @staticmethod
  280. def from_range(range, *args, **kwargs):
  281. return IPGenerator(IPChooserByRange(range), *args, **kwargs)
  282. def add_to_blacklist(self, ip_segment):
  283. if isinstance(ip_segment, ip.IPAddressBlock):
  284. self.blacklist.append(ip_segment)
  285. else:
  286. self.blacklist.append(ip.IPAddressBlock.parse(ip_segment))
  287. def random_ip(self):
  288. if len(self.generated_ips) == self.chooser.size():
  289. raise ValueError("Exhausted the space of possible ip-addresses, no new unique ip-address can be generated")
  290. while True:
  291. random_ip = self.chooser.random_ip()
  292. if not self._is_in_blacklist(random_ip) and random_ip not in self.generated_ips:
  293. self.generated_ips.add(random_ip)
  294. return str(random_ip)
  295. def clear(self, clear_blacklist=True, clear_generated_ips=True):
  296. if clear_blacklist: self.blacklist.clear()
  297. if clear_generated_ips: self.generated_ips.clear()
  298. def _is_in_blacklist(self, ip: ip.IPAddress):
  299. return any(ip in block for block in self.blacklist)
  300. class MappingIPGenerator(IPGenerator):
  301. def __init__(self, *args, **kwargs):
  302. super().__init__(self, *args, **kwargs)
  303. self.mapping = {}
  304. def clear(self, clear_generated_ips=True, *args, **kwargs):
  305. super().clear(self, clear_generated_ips=clear_generated_ips, *args, **kwargs)
  306. if clear_generated_ips:
  307. self.mapping = {}
  308. def get_mapped_ip(self, key):
  309. if key not in self.mapping:
  310. self.mapping[key] = self.random_ip()
  311. return self.mapping[key]
  312. def __getitem__(self, item):
  313. return self.get_mapped_ip(item)