From b16ab85c4fd2ef88d1d41bc8d9ce073fed2d249e Mon Sep 17 00:00:00 2001 From: Noam Blitz Date: Wed, 19 Jul 2023 10:26:15 +0200 Subject: [PATCH 1/7] make ports configurable --- .../port_classification_ip.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/octopoes/bits/port_classification_ip/port_classification_ip.py b/octopoes/bits/port_classification_ip/port_classification_ip.py index c3ae78d0102..64ff6409934 100644 --- a/octopoes/bits/port_classification_ip/port_classification_ip.py +++ b/octopoes/bits/port_classification_ip/port_classification_ip.py @@ -37,13 +37,24 @@ ] +def get_ports_from_config(config, config_key, default): + ports = config.get(config_key, "") + return list(map(int, ports.split(","))) if ports else default + + def run(input_ooi: IPPort, additional_oois: List, config: Dict[str, str]) -> Iterator[OOI]: aggregate_findings = config.get("aggregate_findings", "False").lower() == "true" if config else False open_ports = [] + + common_tcp_ports = get_ports_from_config(config, "common_tcp_ports", COMMON_TCP_PORTS) + common_udp_ports = get_ports_from_config(config, "common_udp_ports", COMMON_UDP_PORTS) + sa_tcp_ports = get_ports_from_config(config, "sa_tcp_ports", SA_TCP_PORTS) + db_tcp_ports = get_ports_from_config(config, "db_tcp_ports", DB_TCP_PORTS) + for ip_port in additional_oois: port = ip_port.port protocol = ip_port.protocol - if protocol == Protocol.TCP and port in SA_TCP_PORTS: + if protocol == Protocol.TCP and port in sa_tcp_ports: open_sa_port = KATFindingType(id="KAT-OPEN-SYSADMIN-PORT") if aggregate_findings: open_ports.append(ip_port.port) @@ -54,7 +65,7 @@ def run(input_ooi: IPPort, additional_oois: List, config: Dict[str, str]) -> Ite ooi=ip_port.reference, description=f"Port {port}/{protocol.value} is a system administrator port and should not be open.", ) - elif protocol == Protocol.TCP and port in DB_TCP_PORTS: + elif protocol == Protocol.TCP and port in db_tcp_ports: ft = KATFindingType(id="KAT-OPEN-DATABASE-PORT") if aggregate_findings: open_ports.append(ip_port.port) @@ -65,8 +76,8 @@ def run(input_ooi: IPPort, additional_oois: List, config: Dict[str, str]) -> Ite ooi=ip_port.reference, description=f"Port {port}/{protocol.value} is a database port and should not be open.", ) - elif (protocol == Protocol.TCP and port not in COMMON_TCP_PORTS) or ( - protocol == Protocol.UDP and port not in COMMON_UDP_PORTS + elif (protocol == Protocol.TCP and port not in common_tcp_ports) or ( + protocol == Protocol.UDP and port not in common_udp_ports ): kat = KATFindingType(id="KAT-UNCOMMON-OPEN-PORT") if aggregate_findings: From 5ca15527702e338fb91c174cee7fc5710afb7da5 Mon Sep 17 00:00:00 2001 From: Noam Blitz Date: Wed, 19 Jul 2023 10:35:51 +0200 Subject: [PATCH 2/7] update port classification docs --- docs/source/manual/usermanual.rst | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/source/manual/usermanual.rst b/docs/source/manual/usermanual.rst index ec8ebdf5f7a..8c72b34f505 100644 --- a/docs/source/manual/usermanual.rst +++ b/docs/source/manual/usermanual.rst @@ -420,10 +420,10 @@ You can currently configure the ``max-age`` before HSTS headers will be consider "config": {"max-age": "4153600"} } -Aggregate findings ------------------- +Port classification +------------------- -Setting this to ``True`` will aggregate all findings of the same type into one finding, +Setting aggregate_findings to ``True`` will aggregate all findings of the same type into one finding, resulting in cleaner finding reports (both in the web UI and in PDF's). For example, ``KAT-UNCOMMON-OPEN-PORT`` will be aggregated into one finding, instead of one separate finding per port. @@ -435,3 +435,15 @@ will be aggregated into one finding, instead of one separate finding per port. "bit-id": "port-classification-ip", "config": {"aggregate_findings": "True"} } + +Also you can configure which open ports should create findings and which ports should not. This is done by settings +common_tcp_ports, common_udp_ports, sa_tcp_ports and/or db_tcp_ports. As an example: + +.. code-block:: json + + { + "object_type": "Config", + "ooi": "Network|internet", + "bit-id": "port-classification-ip", + "config": {"common_tcp_ports": "1,2,3", "sa_tcp_ports": "4,5,6"} + } From ec5df6c0b59762444c4ae9a6df9c3b4588640a23 Mon Sep 17 00:00:00 2001 From: Noam Blitz Date: Wed, 19 Jul 2023 10:45:22 +0200 Subject: [PATCH 3/7] unit test for port config --- octopoes/tests/test_bit_ports.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/octopoes/tests/test_bit_ports.py b/octopoes/tests/test_bit_ports.py index 78e845659c1..09c9a86c483 100644 --- a/octopoes/tests/test_bit_ports.py +++ b/octopoes/tests/test_bit_ports.py @@ -54,6 +54,17 @@ def test_port_classification_tcp_12345(): assert finding.description == "Port 12345/tcp is not a common port and should possibly not be open." +def test_port_classification_tcp_3306_with_config(): + address = IPAddressV4(address="8.8.8.8", network="fake") + port = IPPort(address=address.reference, protocol="tcp", port=3306) + results = list(run_port_classification(address, [port], {"db_tcp_ports": "1234"})) + + assert len(results) == 2 + finding = results[-1] + assert isinstance(finding, Finding) + assert finding.description == "Port 3306/tcp is not a common port and should possibly not be open." + + def test_port_classification_udp_80(): address = IPAddressV4(address="8.8.8.8", network="fake") port = IPPort(address=address.reference, protocol="udp", port=80) From 05084993da371449dd337e24817c63ee00d2a7e7 Mon Sep 17 00:00:00 2001 From: noamblitz <43830693+noamblitz@users.noreply.github.com> Date: Mon, 24 Jul 2023 11:53:47 +0200 Subject: [PATCH 4/7] add more config explanation --- docs/source/manual/usermanual.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/manual/usermanual.rst b/docs/source/manual/usermanual.rst index 8c72b34f505..1354b334958 100644 --- a/docs/source/manual/usermanual.rst +++ b/docs/source/manual/usermanual.rst @@ -437,7 +437,8 @@ will be aggregated into one finding, instead of one separate finding per port. } Also you can configure which open ports should create findings and which ports should not. This is done by settings -common_tcp_ports, common_udp_ports, sa_tcp_ports and/or db_tcp_ports. As an example: +common_tcp_ports, common_udp_ports, sa_tcp_ports and/or db_tcp_ports. Common TCP ports are ports that will never trigger a finding. A good example is 443. Same counts for common udp ports. +SA (system administrator) ports will trigger a medium finding that a system administrator port is open, for example, port 22 is usually is SA port. Lastly, DB (database) ports trigger a more severe finding when a database port is open. As an of the configuration example: .. code-block:: json From 494e5e8386b09ea1b344d806f5dc4223dbde1763 Mon Sep 17 00:00:00 2001 From: noamblitz <43830693+noamblitz@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:06:41 +0200 Subject: [PATCH 5/7] trailing whitespace --- docs/source/manual/usermanual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/manual/usermanual.rst b/docs/source/manual/usermanual.rst index 1354b334958..667ec6656c1 100644 --- a/docs/source/manual/usermanual.rst +++ b/docs/source/manual/usermanual.rst @@ -437,7 +437,7 @@ will be aggregated into one finding, instead of one separate finding per port. } Also you can configure which open ports should create findings and which ports should not. This is done by settings -common_tcp_ports, common_udp_ports, sa_tcp_ports and/or db_tcp_ports. Common TCP ports are ports that will never trigger a finding. A good example is 443. Same counts for common udp ports. +common_tcp_ports, common_udp_ports, sa_tcp_ports and/or db_tcp_ports. Common TCP ports are ports that will never trigger a finding. A good example is 443. Same counts for common udp ports. SA (system administrator) ports will trigger a medium finding that a system administrator port is open, for example, port 22 is usually is SA port. Lastly, DB (database) ports trigger a more severe finding when a database port is open. As an of the configuration example: .. code-block:: json From 2dff7a08486a59c1666821020774d0aa1679bb07 Mon Sep 17 00:00:00 2001 From: Noam Blitz Date: Mon, 24 Jul 2023 14:28:09 +0200 Subject: [PATCH 6/7] allow empty strings in config --- docs/source/manual/usermanual.rst | 38 +++++++++++++++++++ .../port_classification_ip.py | 6 ++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/source/manual/usermanual.rst b/docs/source/manual/usermanual.rst index 667ec6656c1..626fc32b804 100644 --- a/docs/source/manual/usermanual.rst +++ b/docs/source/manual/usermanual.rst @@ -448,3 +448,41 @@ SA (system administrator) ports will trigger a medium finding that a system admi "bit-id": "port-classification-ip", "config": {"common_tcp_ports": "1,2,3", "sa_tcp_ports": "4,5,6"} } + +Defaults are: + +.. code-block:: python + + COMMON_TCP_PORTS = [ + 25, # SMTP + 53, # DNS + 80, # HTTP + 110, # POP3 + 143, # IMAP + 443, # HTTPS + 465, # SMTPS + 587, # SMTP (message submmission) + 993, # IMAPS + 995, # POP3S + ] + + COMMON_UDP_PORTS = [ + 53, # DNS + ] + + SA_TCP_PORTS = [ + 21, # FTP + 22, # SSH + 23, # Telnet + 3389, # Remote Desktop + 5900, # VNC + ] + DB_TCP_PORTS = [ + 1433, # MS SQL Server + 1434, # MS SQL Server + 3050, # Interbase/Firebase + 3306, # MySQL + 5432, # PostgreSQL + ] + +You can set the ports to an empty string to disable the check. diff --git a/octopoes/bits/port_classification_ip/port_classification_ip.py b/octopoes/bits/port_classification_ip/port_classification_ip.py index 64ff6409934..2daf35e3e79 100644 --- a/octopoes/bits/port_classification_ip/port_classification_ip.py +++ b/octopoes/bits/port_classification_ip/port_classification_ip.py @@ -38,8 +38,10 @@ def get_ports_from_config(config, config_key, default): - ports = config.get(config_key, "") - return list(map(int, ports.split(","))) if ports else default + ports = config.get(config_key, None) + if ports is None: + return default + return list(map(int, ports.split(","))) if ports else [] def run(input_ooi: IPPort, additional_oois: List, config: Dict[str, str]) -> Iterator[OOI]: From 0d11b19b0cf8870b1ffdae9c0588fc86cc931061 Mon Sep 17 00:00:00 2001 From: Noam Blitz Date: Mon, 24 Jul 2023 14:29:12 +0200 Subject: [PATCH 7/7] clear up wording --- docs/source/manual/usermanual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/manual/usermanual.rst b/docs/source/manual/usermanual.rst index 626fc32b804..77a6a681da6 100644 --- a/docs/source/manual/usermanual.rst +++ b/docs/source/manual/usermanual.rst @@ -485,4 +485,4 @@ Defaults are: 5432, # PostgreSQL ] -You can set the ports to an empty string to disable the check. +You can set the ports of SA and DB to an empty string to disable the check.