JoomlaRegPrivExploit.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. # Created by Aidmar
  2. import logging
  3. import math
  4. from operator import itemgetter
  5. import operator
  6. from random import randint, uniform
  7. from lea import Lea
  8. from Attack import BaseAttack
  9. from Attack.AttackParameters import Parameter as Param
  10. from Attack.AttackParameters import ParameterTypes
  11. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  12. # noinspection PyPep8
  13. from scapy.utils import RawPcapReader
  14. from scapy.layers.inet import IP, Ether, TCP, RandShort
  15. #from scapy.all import *
  16. class JoomlaRegPrivExploit(BaseAttack.BaseAttack):
  17. # Metasploit default packet rate
  18. maxDefaultPPS = 55
  19. minDefaultPPS = 5
  20. # HTTP port
  21. http_port = 80
  22. # Metasploit experiments show this range of ports
  23. minDefaultPort = 30000
  24. maxDefaultPort = 50000
  25. def __init__(self, statistics, pcap_file_path):
  26. """
  27. Creates a new instance of the Joomla Registeration Privileges Escalation Exploit.
  28. :param statistics: A reference to the statistics class.
  29. """
  30. # Initialize attack
  31. super(JoomlaRegPrivExploit, self).__init__(statistics, "JoomlaRegPrivesc Exploit", "Injects an JoomlaRegPrivesc exploit'",
  32. "Resource Exhaustion")
  33. # Define allowed parameters and their type
  34. self.supported_params = {
  35. Param.MAC_SOURCE: ParameterTypes.TYPE_MAC_ADDRESS,
  36. Param.IP_SOURCE: ParameterTypes.TYPE_IP_ADDRESS,
  37. Param.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS,
  38. Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS,
  39. Param.TARGET_HOST: ParameterTypes.TYPE_URI,
  40. Param.TARGET_URI: ParameterTypes.TYPE_URI,
  41. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  42. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  43. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT
  44. }
  45. # PARAMETERS: initialize with default utilsvalues
  46. # (values are overwritten if user specifies them)
  47. most_used_ip_address = self.statistics.get_most_used_ip_address()
  48. if isinstance(most_used_ip_address, list):
  49. most_used_ip_address = most_used_ip_address[0]
  50. self.add_param_value(Param.IP_SOURCE, most_used_ip_address)
  51. self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  52. self.add_param_value(Param.TARGET_URI, "/")
  53. self.add_param_value(Param.TARGET_HOST, "www.hackme.com")
  54. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  55. self.add_param_value(Param.PACKETS_PER_SECOND,self.maxDefaultPPS)
  56. # victim configuration
  57. # consider that the destination has port 80 opened
  58. random_ip_address = self.statistics.get_random_ip_address()
  59. self.add_param_value(Param.IP_DESTINATION, random_ip_address)
  60. destination_mac = self.statistics.get_mac_address(random_ip_address)
  61. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  62. destination_mac = self.generate_random_mac_address()
  63. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  64. @property
  65. def generate_attack_pcap(self):
  66. def update_timestamp(timestamp, pps, maxdelay):
  67. """
  68. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  69. :return: Timestamp to be used for the next packet.
  70. """
  71. return timestamp + uniform(1 / pps, maxdelay)
  72. # Timestamp
  73. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  74. # TO-DO: find better pkt rate
  75. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  76. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 30, 5 / pps: 15, 10 / pps: 3})
  77. # Aidmar
  78. # Calculate the complement packet rates of the background traffic packet rates per interval
  79. result = self.statistics.process_db_query(
  80. "SELECT timestamp,pktsCount FROM interval_statistics ORDER BY timestamp")
  81. print(result)
  82. bg_interval_pps = []
  83. intervalsSum = 0
  84. if result:
  85. # Get the interval in seconds
  86. for i, row in enumerate(result):
  87. if i < len(result) - 1:
  88. intervalsSum += math.ceil((int(result[i + 1][0]) * 10 ** -6) - (int(row[0]) * 10 ** -6))
  89. interval = intervalsSum / (len(result) - 1)
  90. # Convert timestamp from micro to seconds, convert packet rate "per interval" to "per second"
  91. for row in result:
  92. bg_interval_pps.append((int(row[0]) * 10 ** -6, int(row[1] / interval)))
  93. # Find max PPS
  94. maxPPS = max(bg_interval_pps, key=itemgetter(1))[1]
  95. complement_interval_pps = []
  96. for row in bg_interval_pps:
  97. complement_interval_pps.append((row[0], int(pps * (maxPPS - row[1]) / maxPPS)))
  98. print(complement_interval_pps)
  99. def getIntervalPPS(timestamp):
  100. for row in complement_interval_pps:
  101. if timestamp <= row[0]:
  102. return row[1]
  103. return complement_interval_pps[-1][1] # in case the timstamp > capture max timestamp
  104. # Initialize parameters
  105. packets = []
  106. mac_source = self.get_param_value(Param.MAC_SOURCE)
  107. ip_source = self.get_param_value(Param.IP_SOURCE)
  108. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  109. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  110. target_host = self.get_param_value(Param.TARGET_HOST)
  111. target_uri = self.get_param_value(Param.TARGET_URI)
  112. # Aidmar - check ip.src == ip.dst
  113. if ip_source == ip_destination:
  114. print("\nERROR: Invalid IP addresses; source IP is the same as destination IP: " + ip_source + ".")
  115. import sys
  116. sys.exit(0)
  117. path_attack_pcap = None
  118. replayDelay = self.get_reply_delay(ip_destination)
  119. # Inject Joomla_registration_privesc
  120. # Read joomla_registration_privesc pcap file
  121. orig_ip_dst = None
  122. exploit_raw_packets = RawPcapReader("joomla_registration_privesc.pcap")
  123. port_source = randint(self.minDefaultPort,self.maxDefaultPort) # experiments show this range of ports
  124. for pkt_num, pkt in enumerate(exploit_raw_packets):
  125. eth_frame = Ether(pkt[0])
  126. ip_pkt = eth_frame.payload
  127. tcp_pkt = ip_pkt.payload
  128. str_http_pkt = str(tcp_pkt.payload)
  129. if pkt_num == 0:
  130. prev_orig_port_source = tcp_pkt.getfieldval("sport")
  131. if tcp_pkt.getfieldval("dport") == self.http_port:
  132. orig_ip_dst = ip_pkt.getfieldval("dst") # victim IP
  133. # Request
  134. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  135. # There are 7 TCP connections with different source ports, for each of them we generate random port
  136. if tcp_pkt.getfieldval("sport") != prev_orig_port_source:
  137. port_source = randint(self.minDefaultPort, self.maxDefaultPort)
  138. prev_orig_port_source = tcp_pkt.getfieldval("sport")
  139. # Ether
  140. eth_frame.setfieldval("src", mac_source)
  141. eth_frame.setfieldval("dst", mac_destination)
  142. # IP
  143. ip_pkt.setfieldval("src", ip_source)
  144. ip_pkt.setfieldval("dst", ip_destination)
  145. # TCP
  146. tcp_pkt.setfieldval("sport",port_source)
  147. eth_frame.payload = b''
  148. ip_pkt.payload = b''
  149. tcp_pkt.payload = b''
  150. #temp = "GET / HTTP/1.0\r\n\r\n"
  151. #temp = "GET / HTTP/1.1\r\nHost: 192.168.189.1\r\nUser-Agent: Mozilla/4.0(compatible;MSIE6.0;WindowsNT5.1)\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n"
  152. if len(str_http_pkt) > 0:
  153. # convert payload bytes to str => str = "b'..\\r\\n..'"
  154. str_http_pkt = str_http_pkt[2:-1]
  155. str_http_pkt = str_http_pkt.replace('/joomla360', target_uri)
  156. str_http_pkt = str_http_pkt.replace(orig_ip_dst, target_host)
  157. str_http_pkt = str_http_pkt.replace("\\n", "\n")
  158. str_http_pkt = str_http_pkt.replace("\\r", "\r")
  159. new_pkt = (eth_frame / ip_pkt/ tcp_pkt / str_http_pkt)
  160. new_pkt.time = timestamp_next_pkt
  161. maxdelay = randomdelay.random()
  162. pps = self.minDefaultPPS if getIntervalPPS(timestamp_next_pkt) is None else max(
  163. getIntervalPPS(timestamp_next_pkt), self.minDefaultPPS)
  164. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
  165. # Reply
  166. else:
  167. # Ether
  168. eth_frame.setfieldval("src", mac_destination)
  169. eth_frame.setfieldval("dst", mac_source)
  170. # IP
  171. ip_pkt.setfieldval("src", ip_destination)
  172. ip_pkt.setfieldval("dst", ip_source)
  173. # TCP
  174. tcp_pkt.setfieldval("dport", port_source)
  175. eth_frame.payload = b''
  176. ip_pkt.payload = b''
  177. tcp_pkt.payload = b''
  178. if len(str_http_pkt) > 0:
  179. # convert payload bytes to str => str = "b'..\\r\\n..'"
  180. str_http_pkt = str_http_pkt[2:-1]
  181. str_http_pkt = str_http_pkt.replace('/joomla360', target_uri)
  182. str_http_pkt = str_http_pkt.replace(orig_ip_dst, target_host)
  183. str_http_pkt = str_http_pkt.replace("\\n", "\n")
  184. str_http_pkt = str_http_pkt.replace("\\r", "\r")
  185. new_pkt = (eth_frame / ip_pkt / tcp_pkt / str_http_pkt)
  186. timestamp_next_pkt = timestamp_next_pkt + uniform(replayDelay, 2 * replayDelay)
  187. new_pkt.time = timestamp_next_pkt
  188. packets.append(new_pkt)
  189. # Store timestamp of first packet (for attack label)
  190. self.attack_start_utime = packets[0].time
  191. self.attack_end_utime = packets[-1].time
  192. if len(packets) > 0:
  193. packets = sorted(packets, key=lambda pkt: pkt.time)
  194. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  195. # return packets sorted by packet time_sec_start
  196. # pkt_num+1: because pkt_num starts at 0
  197. return pkt_num + 1, path_attack_pcap