Overseer Server - Python Documentation
This commit is contained in:
		
							parent
							
								
									6d4cc4a11b
								
							
						
					
					
						commit
						de195834df
					
				| @ -20,7 +20,8 @@ def create_app(): | ||||
| 
 | ||||
| @click.group(cls=FlaskGroup, create_app=create_app) | ||||
| def cli(): | ||||
|     """Management script for the Wiki application.""" | ||||
|     """Management script for the application.""" | ||||
| 
 | ||||
| 
 | ||||
| # Import all flask views | ||||
| import overseer.overseer  # noqa: E501,F401,E402 | ||||
|  | ||||
| @ -4,8 +4,10 @@ open_websockets = [] | ||||
| def send_websocket_event(data): | ||||
|     """Send an event to all registered websockets. | ||||
| 
 | ||||
|     Arguments: | ||||
|     data -- Data to send over the websocket(s) | ||||
|     Arguments | ||||
|     --------- | ||||
|     data : obj | ||||
|         Data to send over the websocket(s) | ||||
|     """ | ||||
|     for socket in open_websockets: | ||||
|         socket.send(data) | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| from flask import Blueprint | ||||
| 
 | ||||
| # Setup the API blueprint | ||||
| api = Blueprint("v1", __name__, url_prefix="/api/v1") | ||||
| from overseer.api.v1 import routes, events  # noqa: F401,E402 | ||||
|  | ||||
| @ -2,15 +2,18 @@ from overseer import app | ||||
| from overseer.api import open_websockets | ||||
| from flask_socketio import SocketIO | ||||
| 
 | ||||
| # Create and register new websocket for v1 API | ||||
| socketio = SocketIO(app, path="/api/v1/socket.io") | ||||
| open_websockets.append(socketio) | ||||
| 
 | ||||
| 
 | ||||
| @socketio.on("message") | ||||
| def handle_message(data): | ||||
|     """Not Implemented - Used to response to client sent WebSocket messages""" | ||||
|     print("RAW DATA: %s" % data) | ||||
| 
 | ||||
| 
 | ||||
| @socketio.on("json") | ||||
| def handle_json(json): | ||||
|     """Not Implemented - Used to response to client sent WebSocket messages""" | ||||
|     print("JSON DATA: %s" % json) | ||||
|  | ||||
| @ -40,7 +40,7 @@ def get_scans_by_target(target): | ||||
|                  /api/v1/scans/1.1.1.1 | ||||
|         RESPONSE: { "data": [ <ARRAY_OF_SCAN_HISTORY> ] } | ||||
|     """ | ||||
|     page = 1 | ||||
|     page = 1  # TODO: Pagination | ||||
| 
 | ||||
|     if overseer.scan_manager.is_ip(target): | ||||
|         scan_results = overseer.database.get_scan_results_by_target( | ||||
| @ -56,7 +56,7 @@ def get_scans_by_target(target): | ||||
| 
 | ||||
| @api.route("/search", methods=["GET"]) | ||||
| def get_scans(search): | ||||
|     """ | ||||
|     """Not Implemented | ||||
|     GET: | ||||
|         REQUEST: /api/v1/search?query=1.1.1.1 | ||||
|                  /api/v1/search?query=192.168.0.0/24 | ||||
| @ -67,6 +67,7 @@ def get_scans(search): | ||||
| 
 | ||||
| 
 | ||||
| def __normalize_scan_results(scan_results, target): | ||||
|     """Returns a normalized list of objects for ScanHistory items""" | ||||
|     return list( | ||||
|         map( | ||||
|             lambda x: { | ||||
|  | ||||
| @ -2,11 +2,22 @@ import os | ||||
| 
 | ||||
| 
 | ||||
| def get_env(key, default=None, required=False): | ||||
|     """Wrapper for gathering env vars.""" | ||||
|     if required: | ||||
|         assert key in os.environ, "Missing Environment Variable: %s" % key | ||||
|     return os.environ.get(key, default) | ||||
| 
 | ||||
| 
 | ||||
| class EnvConfig: | ||||
|     """Wrap application configurations | ||||
| 
 | ||||
|     Attributes | ||||
|     ---------- | ||||
|     DATABASE : str | ||||
|         The specied desired database (default: sqlite) | ||||
|     DATA_PATH : str | ||||
|         The path where to store any resources (default: ./) | ||||
|     """ | ||||
| 
 | ||||
|     DATABASE = get_env("OVERSEER_DB", default="sqlite") | ||||
|     DATA_PATH = get_env("OVERSEER_DATA_PATH", default="./") | ||||
|  | ||||
| @ -8,23 +8,52 @@ from sqlalchemy.orm.exc import NoResultFound | ||||
| 
 | ||||
| 
 | ||||
| class DatabaseConnector: | ||||
|     """Used to manipulate the database | ||||
| 
 | ||||
|     Methods | ||||
|     ------- | ||||
|     create_scan_target(**kwargs) | ||||
|         Create a new ScanTarget database row with the provided hostname or | ||||
|         ip_addr | ||||
|     get_scan_target(**kwargs) | ||||
|         Get a ScanTarget by either its hostname or ip_addr | ||||
|     get_all_scan_targets(page=1) | ||||
|         Get all ScanTargets ordered by most recent, limited to 25 / page | ||||
|     create_scan_result(status, results=[], error=None, **kwargs) | ||||
|         Create a new ScanHistory for the provided hostname or ip_addr | ||||
|     update_scan_result(history_id, status, results=None, error=None) | ||||
|         Update the referenced ScanHistory ID with the provided values | ||||
|     get_scan_results_by_target(page=1, **kwargs) | ||||
|         Get all ScanHistory for the provided hostname or ip_add, limited to | ||||
|         25 / page | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, data_path, in_memory=False): | ||||
|         """ | ||||
|         Parameters | ||||
|         ---------- | ||||
|         data_path : str | ||||
|             Directory to store the sqlite file | ||||
|         in_memory : bool, optional | ||||
|             Directive to store the DB in memory | ||||
|         """ | ||||
|         if in_memory: | ||||
|             self.engine = create_engine( | ||||
|             self.__engine = create_engine( | ||||
|                 "sqlite+pysqlite:///:memory:", echo=False, future=True | ||||
|             ) | ||||
|         else: | ||||
|             db_path = path.join(data_path, "overseer.sqlite") | ||||
|             self.engine = create_engine( | ||||
|             self.__engine = create_engine( | ||||
|                 "sqlite+pysqlite:///%s" % db_path, | ||||
|                 echo=False, | ||||
|                 future=True, | ||||
|             ) | ||||
|         Base.metadata.create_all(self.engine) | ||||
|         Base.metadata.create_all(self.__engine) | ||||
|         self.__cleanup_stale_records() | ||||
| 
 | ||||
|     def __cleanup_stale_records(self): | ||||
|         session = Session(bind=self.engine) | ||||
|         """Cleans up any stale ScanHistory records""" | ||||
|         session = Session(bind=self.__engine) | ||||
|         history_filter = ScanHistory.status == "IN_PROGRESS" | ||||
|         all_stale = session.query(ScanHistory).filter(history_filter).all() | ||||
| 
 | ||||
| @ -36,6 +65,19 @@ class DatabaseConnector: | ||||
|         session.close() | ||||
| 
 | ||||
|     def create_scan_target(self, **kwargs): | ||||
|         """Create a new ScanTarget database row with the provided hostname or | ||||
|         ip_addr | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         **kwargs | ||||
|             Either hostname or ip_addr | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         ScanTarget | ||||
|             The created ScanTarget | ||||
|         """ | ||||
|         if len(kwargs.keys() & {"ip_addr", "hostname"}) != 1: | ||||
|             raise ValueError("Missing keyword argument: ip_addr or hostname") | ||||
| 
 | ||||
| @ -47,13 +89,25 @@ class DatabaseConnector: | ||||
|                 hostname=kwargs["hostname"], updated_at=datetime.now() | ||||
|             ) | ||||
| 
 | ||||
|         session = Session(bind=self.engine, expire_on_commit=False) | ||||
|         session = Session(bind=self.__engine, expire_on_commit=False) | ||||
|         session.add(scan_target) | ||||
|         session.commit() | ||||
|         session.close() | ||||
|         return scan_target | ||||
| 
 | ||||
|     def get_scan_target(self, **kwargs): | ||||
|         """Get a ScanTarget by either its hostname or ip_addr | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         **kwargs | ||||
|             Either hostname or ip_addr | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         ScanTarget | ||||
|             The requested ScanTarget | ||||
|         """ | ||||
|         if len(kwargs.keys() & {"ip_addr", "hostname"}) != 1: | ||||
|             raise ValueError("Missing keyword argument: ip_addr or hostname") | ||||
| 
 | ||||
| @ -63,13 +117,25 @@ class DatabaseConnector: | ||||
|         elif "hostname" in kwargs: | ||||
|             target_filter = ScanTarget.hostname == kwargs["hostname"] | ||||
| 
 | ||||
|         session = Session(bind=self.engine) | ||||
|         session = Session(bind=self.__engine) | ||||
|         scan_target = session.query(ScanTarget).filter(target_filter).first() | ||||
|         session.close() | ||||
|         return scan_target | ||||
| 
 | ||||
|     def get_all_scan_targets(self, page=1): | ||||
|         session = Session(bind=self.engine) | ||||
|         """Get all ScanTargets ordered by most recent, limited to 25 / page | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         page : int, optional | ||||
|             The desired ScanTarget page | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         list | ||||
|             List of ScanTarget | ||||
|         """ | ||||
|         session = Session(bind=self.__engine) | ||||
|         scan_targets = ( | ||||
|             session.query(ScanTarget) | ||||
|             .order_by(ScanTarget.updated_at.desc()) | ||||
| @ -81,6 +147,24 @@ class DatabaseConnector: | ||||
|         return scan_targets | ||||
| 
 | ||||
|     def create_scan_result(self, status, results=[], error=None, **kwargs): | ||||
|         """Create a new ScanHistory for the provided hostname or ip_addr | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         status : str | ||||
|             The status of the scan (IN_PROGRESS, FAILED, COMPLETE) | ||||
|         results : list | ||||
|             List of strings of open ports (E.g. ["53 UDP", "53 TCP"] | ||||
|         error : str, optional | ||||
|             Error message, if any | ||||
|         **kwargs | ||||
|             Either hostname or ip_addr | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         ScanHistory | ||||
|             The created ScanHistory | ||||
|         """ | ||||
|         scan_target = self.get_scan_target(**kwargs) | ||||
|         if not scan_target: | ||||
|             scan_target = self.create_scan_target(**kwargs) | ||||
| @ -92,14 +176,32 @@ class DatabaseConnector: | ||||
|             error=error, | ||||
|             created_at=datetime.now(), | ||||
|         ) | ||||
|         session = Session(bind=self.engine, expire_on_commit=False) | ||||
|         session = Session(bind=self.__engine, expire_on_commit=False) | ||||
|         session.add(scan_history) | ||||
|         session.commit() | ||||
|         session.close() | ||||
|         return scan_history | ||||
| 
 | ||||
|     def update_scan_result(self, history_id, status, results=None, error=None): | ||||
|         session = Session(bind=self.engine, expire_on_commit=False) | ||||
|         """Update the referenced ScanHistory ID with the provided values | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         history_id : int | ||||
|             The ScanHistory ID to update | ||||
|         status : str | ||||
|             The status of the scan (IN_PROGRESS, FAILED, COMPLETE) | ||||
|         results : list, optional | ||||
|             List of strings of open ports (E.g. ["53 UDP", "53 TCP"] | ||||
|         error : str, optional | ||||
|             Error message, if any | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         ScanHistory | ||||
|             The updated ScanHistory | ||||
|         """ | ||||
|         session = Session(bind=self.__engine, expire_on_commit=False) | ||||
|         scan_history = session.query(ScanHistory).get(history_id) | ||||
| 
 | ||||
|         if scan_history is None: | ||||
| @ -116,6 +218,19 @@ class DatabaseConnector: | ||||
|         return scan_history | ||||
| 
 | ||||
|     def get_scan_results_by_target(self, page=1, **kwargs): | ||||
|         """Get all ScanHistory for the provided hostname or ip_add, limited to | ||||
|         25 / page | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         page : int, optional | ||||
|             The desired ScanResult page | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         list | ||||
|             List of ScanHistory | ||||
|         """ | ||||
|         if len(kwargs.keys() & {"ip_addr", "hostname"}) != 1: | ||||
|             raise ValueError("Missing keyword argument: ip_addr or hostname") | ||||
| 
 | ||||
| @ -125,7 +240,7 @@ class DatabaseConnector: | ||||
|         elif "hostname" in kwargs: | ||||
|             history_filter = ScanTarget.hostname == kwargs["hostname"] | ||||
| 
 | ||||
|         session = Session(bind=self.engine) | ||||
|         session = Session(bind=self.__engine) | ||||
|         scan_histories = ( | ||||
|             session.query(ScanHistory) | ||||
|             .join(ScanHistory.target) | ||||
|  | ||||
| @ -5,6 +5,20 @@ Base = declarative_base() | ||||
| 
 | ||||
| 
 | ||||
| class ScanTarget(Base): | ||||
|     """ScanTarget DB Model | ||||
| 
 | ||||
|     Attributes | ||||
|     ------- | ||||
|     id : Column(Integer) | ||||
|         The ID in the database | ||||
|     ip : Column(Integer), optional | ||||
|         The integer represented IP Address in the database | ||||
|     hostname : Column(String) | ||||
|         The hostname in the database | ||||
|     updated_at : Column(DateTime) | ||||
|         The DateTime when the ScanTarget was last updated | ||||
|     """ | ||||
| 
 | ||||
|     __tablename__ = "scan_target" | ||||
| 
 | ||||
|     # Unique ID | ||||
| @ -20,6 +34,7 @@ class ScanTarget(Base): | ||||
|     updated_at = Column(DateTime()) | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         """The string representation of the class.""" | ||||
|         return ( | ||||
|             f"ScanTarget(id={self.id!r}, ip={self.ip!r}, " | ||||
|             f"hostname={self.hostname!r}, updated_at={self.updated_at!r})" | ||||
| @ -27,6 +42,26 @@ class ScanTarget(Base): | ||||
| 
 | ||||
| 
 | ||||
| class ScanHistory(Base): | ||||
|     """ScanHistory DB Model | ||||
| 
 | ||||
|     Attributes | ||||
|     ------- | ||||
|     id : Column(Integer) | ||||
|         The ID in the database | ||||
|     target_id : Column(Integer) | ||||
|         The ScanTarget ID reference | ||||
|     results : Column(String) | ||||
|         The CSV delimited string representing all open ports. | ||||
|     created_at : Column(DateTime) | ||||
|         The DateTime when the ScanHistory was created | ||||
|     status : Column(String) | ||||
|         The status of the ScanHistory (IN_PROGRESS, COMPLETE, FAILED) | ||||
|     error : Column(String) | ||||
|         The error, if any, of the ScanHistory | ||||
|     target : relationship | ||||
|         The ScanTarget relationship reference | ||||
|     """ | ||||
| 
 | ||||
|     __tablename__ = "scan_history" | ||||
| 
 | ||||
|     # Unique ID | ||||
| @ -51,6 +86,7 @@ class ScanHistory(Base): | ||||
|     target = relationship("ScanTarget", foreign_keys=[target_id]) | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         """The string representation of the class.""" | ||||
|         return ( | ||||
|             f"ScanHistory(id={self.id!r}, target_id={self.target_id!r}, " | ||||
|             f"results={self.results!r}, created_at={self.created_at!r}, " | ||||
|  | ||||
| @ -5,25 +5,19 @@ from overseer.api.v1 import api as api_v1 | ||||
| 
 | ||||
| @app.route("/", methods=["GET"]) | ||||
| def main_entry(): | ||||
|     """ | ||||
|     Initial Entrypoint to the SPA (i.e. 'index.html') | ||||
|     """ | ||||
|     """Initial Entrypoint to the SPA (i.e. 'index.html')""" | ||||
|     return make_response(render_template("index.html")) | ||||
| 
 | ||||
| 
 | ||||
| @app.route("/<path:path>", methods=["GET"]) | ||||
| def catch_all(path): | ||||
|     """ | ||||
|     Necessary due to client side SPA route handling. | ||||
|     """ | ||||
|     """Necessary due to client side SPA route handling""" | ||||
|     return make_response(render_template("index.html")) | ||||
| 
 | ||||
| 
 | ||||
| @app.route("/static/<path:path>") | ||||
| def static_resources(path): | ||||
|     """ | ||||
|     Front End Static Resources | ||||
|     """ | ||||
|     """Front end static resources""" | ||||
|     return send_from_directory("static", path) | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -9,19 +9,39 @@ import time | ||||
| 
 | ||||
| 
 | ||||
| class ScanManager: | ||||
|     def __init__(self): | ||||
|         self.pending_shutdown = False | ||||
|         self.active_scans = [] | ||||
|         self.broadcast_thread = Thread(target=self.__broadcast_thread) | ||||
|         self.broadcast_thread.start() | ||||
|     """Used to manage any ongoing scans | ||||
| 
 | ||||
|     def __broadcast_thread(self): | ||||
|         while not self.pending_shutdown: | ||||
|     Methods | ||||
|     ------- | ||||
|     shutdown() | ||||
|         Shutdown and cleanup the scan monitor thread | ||||
|     get_status() | ||||
|         Get a normalized list of dicts detailing outstanding scans | ||||
|     perform_scan(target) | ||||
|         Initiate a scan on target (Can be a hostname, or ip_addr) | ||||
|     is_ip(target) | ||||
|         Determines if a target is a hostname of ip_addr | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         """Create instance and start thread""" | ||||
|         self.__pending_shutdown = False | ||||
|         self.__active_scans = [] | ||||
|         self.__monitor_thread = Thread(target=self.__scan_monitor) | ||||
|         self.__monitor_thread.start() | ||||
| 
 | ||||
|     def __scan_monitor(self): | ||||
|         """Monitors active and completed scans | ||||
| 
 | ||||
|         Responsible for publishing status to the websockets, cleaning up | ||||
|         completed scans, and updating the database accordingly. | ||||
|         """ | ||||
|         while not self.__pending_shutdown: | ||||
|             time.sleep(1) | ||||
|             if len(self.active_scans) == 0: | ||||
|             if len(self.__active_scans) == 0: | ||||
|                 continue | ||||
| 
 | ||||
|             for scan in self.active_scans: | ||||
|             for scan in self.__active_scans: | ||||
|                 # WebSocket progress | ||||
|                 total_progress = (scan.tcp_progress + scan.udp_progress) / 2 | ||||
|                 results = scan.get_results() | ||||
| @ -61,13 +81,21 @@ class ScanManager: | ||||
| 
 | ||||
|                 # Cleanup active scan | ||||
|                 scan.join() | ||||
|                 self.active_scans.remove(scan) | ||||
|                 self.__active_scans.remove(scan) | ||||
| 
 | ||||
|     def shutdown(self): | ||||
|         self.pending_shutdown = True | ||||
|         self.broadcast_thread.join() | ||||
|         """Shutdown and cleanup the scan monitor thread""" | ||||
|         self.__pending_shutdown = True | ||||
|         self.__monitor_thread.join() | ||||
| 
 | ||||
|     def get_status(self): | ||||
|         """Get a normalized list of dicts detailing outstanding scans | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         list | ||||
|             List of normalized details on current active scans | ||||
|         """ | ||||
|         return list( | ||||
|             map( | ||||
|                 lambda x: { | ||||
| @ -83,11 +111,18 @@ class ScanManager: | ||||
|                         (x.tcp_progress + x.udp_progress) / 2 | ||||
|                     ),  # noqa: E501 | ||||
|                 }, | ||||
|                 self.active_scans, | ||||
|                 self.__active_scans, | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|     def perform_scan(self, target): | ||||
|         """Initiate a scan on target (Can be a hostname, or ip_addr) | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         target : str | ||||
|             Either a hostname or IP address of the endpoint to scan | ||||
|         """ | ||||
|         try: | ||||
|             target = socket.gethostbyname(target) | ||||
|         except socket.error: | ||||
| @ -104,10 +139,22 @@ class ScanManager: | ||||
| 
 | ||||
|         new_scan = Scanner(target, scan_history) | ||||
|         new_scan.start() | ||||
|         self.active_scans.append(new_scan) | ||||
|         self.__active_scans.append(new_scan) | ||||
|         return scan_history | ||||
| 
 | ||||
|     def is_ip(self, target): | ||||
|         """Determines if a target is a hostname of ip_addr | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         target : str | ||||
|             Either a hostname or IP address of the endpoint to scan | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         bool | ||||
|             Whether the target is an IP or not | ||||
|         """ | ||||
|         try: | ||||
|             ipaddress.ip_address(target) | ||||
|             return True | ||||
| @ -116,12 +163,46 @@ class ScanManager: | ||||
| 
 | ||||
| 
 | ||||
| class Scanner(Thread): | ||||
|     """Subclass of Thread - used to perform a TCP and UDP scan | ||||
| 
 | ||||
|     Attributes | ||||
|     ---------- | ||||
|     target : str | ||||
|         The hostname or ip_addr to scan | ||||
|     scan_history : ScanHistory | ||||
|         The ScanHistory DB model reference for this scan | ||||
|     tcp_progress : int | ||||
|         The current progress percentage of the threaded TCP scan | ||||
|     udp_progress : int | ||||
|         The current progress percentage of the threaded UDP scan | ||||
|     tcp_results : list | ||||
|         The current list if ints of open TCP ports | ||||
|     udp_results : list | ||||
|         The current list if ints of open UDP ports | ||||
| 
 | ||||
|     Methods | ||||
|     ------- | ||||
|     run() | ||||
|         Overridden run method from the Thread superclass. Starts the scan. | ||||
|     get_results() | ||||
|         Returns a normalized list of string of open ports | ||||
|         (E.g. ["53 UDP", "53 TCP"]) | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, target, scan_history): | ||||
|         """ | ||||
|         Parameters | ||||
|         ---------- | ||||
|         target : str | ||||
|             The hostname or ip_addr to scan | ||||
|         scan_history : ScanHistory | ||||
|             The ScanHistory DB model reference for this scan | ||||
|         """ | ||||
|         Thread.__init__(self) | ||||
|         self.target = target | ||||
|         self.scan_history = scan_history | ||||
| 
 | ||||
|         self.port_count = 1000 | ||||
|         self.__port_count = 1000 | ||||
| 
 | ||||
|         self.tcp_progress = 0 | ||||
|         self.udp_progress = 0 | ||||
| @ -129,11 +210,19 @@ class Scanner(Thread): | ||||
|         self.tcp_results = [] | ||||
|         self.udp_results = [] | ||||
| 
 | ||||
|         self.udp_payloads = {} | ||||
|         self.__udp_payloads = {} | ||||
|         self.__load_nmap_payloads() | ||||
| 
 | ||||
|     def __load_nmap_payloads(self): | ||||
|         """Load and parse nmap UDP payloads""" | ||||
|         """Load and parse nmap UDP payloads | ||||
| 
 | ||||
|         Because of how UDP is designed, we have to test with specific payloads. | ||||
|         This parses nmaps protocol specific payloads [0] in preperation. | ||||
| 
 | ||||
|         TODO: This should be cached. No need to parse it on every scan. | ||||
| 
 | ||||
|         [0] https://nmap.org/book/nmap-payloads.html | ||||
|         """ | ||||
| 
 | ||||
|         # Open file & remove comments | ||||
|         nmap_payloads = os.path.join( | ||||
| @ -165,25 +254,33 @@ class Scanner(Thread): | ||||
|                     start_port = int(port_range[0]) | ||||
|                     end_port = int(port_range[1]) | ||||
|                     for port_match in range(start_port, end_port + 1): | ||||
|                         if port_match not in self.udp_payloads: | ||||
|                             self.udp_payloads[port_match] = [] | ||||
|                         self.udp_payloads[port_match].extend(match_payloads) | ||||
|                         if port_match not in self.__udp_payloads: | ||||
|                             self.__udp_payloads[port_match] = [] | ||||
|                         self.__udp_payloads[port_match].extend(match_payloads) | ||||
|                 else: | ||||
|                     port_match = int(raw_port) | ||||
|                     if port_match not in self.udp_payloads: | ||||
|                         self.udp_payloads[port_match] = [] | ||||
|                     self.udp_payloads[port_match].extend(match_payloads) | ||||
|                     if port_match not in self.__udp_payloads: | ||||
|                         self.__udp_payloads[port_match] = [] | ||||
|                     self.__udp_payloads[port_match].extend(match_payloads) | ||||
| 
 | ||||
|     def run(self): | ||||
|         """Overridden run method from the Thread superclass. Starts the scan""" | ||||
|         tcp_thread = Thread(target=self.__scan_tcp) | ||||
|         udp_thread = Thread(target=self.__scan_udp) | ||||
|         tcp_thread.start() | ||||
|         udp_thread.start() | ||||
|         tcp_thread.join() | ||||
|         udp_thread.join() | ||||
|         return {"TCP": self.tcp_results, "UDP": self.udp_results} | ||||
| 
 | ||||
|     def get_results(self): | ||||
|         """Returns a normalized list of string of open ports | ||||
| 
 | ||||
|         Returns | ||||
|         ------- | ||||
|         list | ||||
|             List of open ports (E.g. ["53 UDP", "53 TCP"]) | ||||
|         """ | ||||
| 
 | ||||
|         results = list(map(lambda x: "%s UDP" % x, self.udp_results)) | ||||
|         results.extend( | ||||
|             list(map(lambda x: "%s TCP" % x, self.tcp_results)) | ||||
| @ -192,23 +289,24 @@ class Scanner(Thread): | ||||
|         return results | ||||
| 
 | ||||
|     def __scan_tcp(self): | ||||
|         for port in range(1, self.port_count): | ||||
|         """Threaded TCP Scanner""" | ||||
|         for port in range(1, self.__port_count): | ||||
|             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||||
|             s.settimeout(0.1) | ||||
|             result = s.connect_ex((self.target, port)) | ||||
|             if result == 0: | ||||
|                 self.tcp_results.append(port) | ||||
|             s.close() | ||||
|             self.tcp_progress = round(port / self.port_count * 100) | ||||
|             self.tcp_progress = round(port / self.__port_count * 100) | ||||
| 
 | ||||
|     def __scan_udp(self): | ||||
|         for port in range(1, self.port_count): | ||||
|             # print("UDP port %s..." % port) | ||||
|         """Threaded UDP Scanner""" | ||||
|         for port in range(1, self.__port_count): | ||||
|             s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||||
|             s.settimeout(0.01) | ||||
|             payloads = ["\x00"] | ||||
|             if port in self.udp_payloads: | ||||
|                 payloads = self.udp_payloads[port] | ||||
|             if port in self.__udp_payloads: | ||||
|                 payloads.extend(self.__udp_payloads[port]) | ||||
|             for payload in payloads: | ||||
|                 s.sendto(payload.encode("utf-8"), (self.target, port)) | ||||
|                 try: | ||||
| @ -220,16 +318,4 @@ class Scanner(Thread): | ||||
|                 except socket.timeout: | ||||
|                     pass | ||||
|             s.close() | ||||
|             self.udp_progress = round(port / self.port_count * 100) | ||||
| 
 | ||||
| 
 | ||||
| # FOR TESTING PURPOSES | ||||
| def main(): | ||||
|     sm = ScanManager() | ||||
|     sm.perform_scan("localhost") | ||||
|     # sm.perform_scan("10.0.20.254") | ||||
|     # sm.perform_scan("10.0.21.20") | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
|             self.udp_progress = round(port / self.__port_count * 100) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user