scanner_wrapper.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import os
  2. import logging
  3. import collections
  4. import threading
  5. import subprocess
  6. import time
  7. from subprocess import PIPE, TimeoutExpired
  8. logger = logging.getLogger("pra_framework")
  9. class ZmapWrapper(object):
  10. def __init__(self, **kwargs):
  11. # there ary many arguments so we take a more efficient approach
  12. timestamp = time.strftime("%Y_%M_%d__%H_%M_%S")
  13. # Default parameters to be set. This is more efficient flexible
  14. # than explicitly setting them in __init__() via self._xyz...
  15. prop_defaults = {
  16. "port_target": 1234,
  17. "filename_output_csv" : None,
  18. "filename_blacklist_target_ip": None,
  19. # TODO: just for online scans
  20. "filename_whitelist_target_ip": None,
  21. "dirname_logs": None,
  22. # scan options
  23. "rate_kbit_per_s": 1000,
  24. "probes_per_target": 1,
  25. "timeout_wait": 0,
  26. # network options
  27. "interface_name": "eth0",
  28. "mac_source": None,
  29. "mac_gw": None,
  30. # probe module, we only use the pra module
  31. "probe_module": "tcp_synscan_pra",
  32. #"probe_module": "tcp_synscan",
  33. # additional options
  34. "marker_encoding": 0,
  35. "markervalue": None,
  36. "markerbits_value": 32,
  37. "markerbits_checksum": 0,
  38. "markerbits_dst_small" : 0,
  39. # disable status outputs, directly exit scanner after finishing (for small address ranges to speed up)
  40. "disable_monitor": 0,
  41. "filename_config": None,
  42. # target adresses as string: "1.2.3.4 1.2.4.0/24 ..."
  43. "target_addresses": None,
  44. "verbosity": 3,
  45. #
  46. "ready_callback": None,
  47. "name_timestamp": timestamp,
  48. # directory which contains "src/zmap wihtout trailing slash"
  49. "base_dir_zmap": "../zmap",
  50. # avoid establishing process communication and don't wait for process to finish
  51. "fast_mode": False,
  52. "scan_timeout": -1
  53. }
  54. for (prop, default) in prop_defaults.items():
  55. setattr(self, prop, kwargs.get(prop, default))
  56. if self.target_addresses is None:
  57. raise Exception("Not address given to scan")
  58. self.filename_zmap_bin = os.path.join(self.base_dir_zmap, "src/zmap")
  59. # use absolute path
  60. #self.filename_zmap_bin = os.path.abspath(self.filename_zmap_bin)
  61. # logger.debug("zmap binary: %s" % self.filename_zmap_bin)
  62. """
  63. if self.filename_blacklist_target_ip is None:
  64. # TODO: change to full blacklist file
  65. self.filename_zmap_blacklist = os.path.join(self.base_dir_zmap, "blacklist.conf")
  66. #self.filename_blacklist_target_ip = os.path.join(self.base_dir_zmap, "blacklist.conf_minimal")
  67. """
  68. self._scan_process = None
  69. self._is_running = False
  70. def start(self):
  71. """
  72. Start a port scan. This call is blocking until the
  73. process is finished. Call stop() to terminate process earlier.
  74. """
  75. if self._is_running:
  76. logger.warning("scanner is allready running")
  77. return
  78. # aggregate scan parameters
  79. cmd = [self.filename_zmap_bin]
  80. # basic arguments
  81. # this is generally not needed when using destination port marker but we set is per default
  82. cmd.extend(["-p", "%d" % self.port_target])
  83. if self.filename_blacklist_target_ip is not None:
  84. cmd.extend(["-b", self.filename_blacklist_target_ip])
  85. """
  86. if self.filename_whitelist_target_ip is not None:
  87. cmd.extend(["-w", self.filename_whitelist_target_ip])
  88. """
  89. if self.filename_output_csv is not None:
  90. cmd.extend(["-o", self.filename_output_csv])
  91. # data output
  92. cmd.extend(["-O", "csv"])
  93. cmd.extend(["-f", "classification,saddr,daddr,daddr_inner_icmp,sport,dport,success"])
  94. # scan options
  95. cmd.extend(["-B", "%dK" % self.rate_kbit_per_s])
  96. cmd.extend(["-P", "%d" % self.probes_per_target])
  97. if self.filename_output_csv is None:
  98. cmd.extend(["-c", "%d" % self.timeout_wait])
  99. else:
  100. # wait at minimum 2 seconds at end of scan to receive feedback
  101. cmd.extend(["-c", "%d" % 2])
  102. #cmd.extend(["--hello-timeout", self._timeout_syn])
  103. # network options
  104. if self.mac_gw is not None:
  105. cmd.extend(["-G", self.mac_gw])
  106. if self.mac_source is not None:
  107. cmd.extend(["--source-mac", self.mac_source])
  108. cmd.extend(["-i", self.interface_name])
  109. # probe modules
  110. cmd.extend(["-M", self.probe_module])
  111. # additional options
  112. cmd.extend(["--marker_encoding", "%d" % self.marker_encoding])
  113. if self.markervalue is not None:
  114. cmd.extend(["--use_markervalue", "1"])
  115. cmd.extend(["--markervalue", "%d" % self.markervalue])
  116. cmd.extend(["--markerbits_value", "%d" % self.markerbits_value])
  117. cmd.extend(["--markerbits_checksum", "%d" % self.markerbits_checksum])
  118. cmd.extend(["--disable_monitor", "%d" % self.disable_monitor])
  119. if self.filename_config is not None:
  120. cmd.extend(["-C", self.filename_config])
  121. cmd.extend(["-v", "%d" % self.verbosity])
  122. # TODO: activate for zmap internal Logging
  123. if self.dirname_logs is not None:
  124. cmd.extend(["-L", self.dirname_logs])
  125. # limit scan time
  126. if self.scan_timeout > 0:
  127. cmd.extend(["-t", "%d" % self.scan_timeout])
  128. # two cores are enough to achieve a maximum speed
  129. #cmd.extend(["--cores", "0"])
  130. #cmd.extend(["--cores", "0,1"])
  131. cmd.extend(["--cores", "0,1,2"])
  132. # addresses as list
  133. cmd.extend(self.target_addresses)
  134. logger.debug("starting scanner: " + " ".join(cmd))
  135. self._is_running = True
  136. #return
  137. # start the process
  138. try:
  139. #self._scan_process = subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
  140. if not self.fast_mode:
  141. self._scan_process = subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
  142. else:
  143. self._scan_process = subprocess.Popen(cmd)
  144. except Exception:
  145. logger.warning("could not start process, killing it")
  146. self._scan_process.kill()
  147. #if self.fast_mode:
  148. # return
  149. # if self._scan_process.returncode != 0:
  150. # logger.warning("something went wrong while scanning, check output")
  151. # wait until scan has finished
  152. try:
  153. self._scan_process.wait()
  154. self._check_process_output()
  155. except TimeoutExpired:
  156. # this will update date for status retrieval (time left)
  157. self._check_process_output()
  158. if self._is_running:
  159. self.stop()
  160. if self.ready_callback is not None:
  161. # self._extract_ips()
  162. self.ready_callback(self)
  163. def _check_process_output(self):
  164. try:
  165. outs, errs = self._scan_process.communicate(timeout=1)
  166. #if self.disable_monitor == 0:
  167. # logger.debug("standard output\n:" + outs.decode("ASCII"))
  168. # logger.debug("error output:\n" + errs.decode("ASCII"))
  169. except:
  170. pass
  171. def stop(self):
  172. if not self._is_running:
  173. logger.warning("scanner was not running (anymore?)")
  174. return
  175. self._is_running = False
  176. # avoid ResourceWarning: unclosed file <_io.BufferedReader name=7>
  177. try:
  178. self._scan_process.stdout.close()
  179. self._scan_process.stdin.close()
  180. self._scan_process.stderr.close()
  181. except:
  182. pass
  183. try:
  184. # self._scan_process.terminate()
  185. # do it the hard way, we don't need any resume storing
  186. self._scan_process.kill()
  187. except ProcessLookupError as e:
  188. # logger.warning("could not terminate process")
  189. # logger.warning(e)
  190. # assume process already finished
  191. pass