Merge pull request #2133 from guardicore/2004-scan-target-configuration

2004 scan target configuration
This commit is contained in:
VakarisZ 2022-08-01 17:05:38 +03:00 committed by GitHub
commit fd1c10cd73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 195 additions and 44 deletions

View File

@ -1,6 +1,4 @@
import re
from marshmallow import Schema, ValidationError, fields, post_load, validate, validates
from marshmallow import Schema, fields, post_load, validate
from .agent_sub_configurations import (
CustomPBAConfiguration,
@ -14,49 +12,19 @@ from .agent_sub_configurations import (
TCPScanConfiguration,
)
from .utils import freeze_lists
valid_windows_custom_pba_filename_regex = re.compile(r"^[^<>:\"\\\/|?*]*[^<>:\"\\\/|?* \.]+$|^$")
valid_linux_custom_pba_filename_regex = re.compile(r"^[^\0/]*$")
from .validators import (
validate_ip,
validate_linux_filename,
validate_subnet_range,
validate_windows_filename,
)
class CustomPBAConfigurationSchema(Schema):
linux_command = fields.Str()
linux_filename = fields.Str(
validate=validate.Regexp(regex=valid_linux_custom_pba_filename_regex)
)
linux_filename = fields.Str(validate=validate_linux_filename)
windows_command = fields.Str()
windows_filename = fields.Str(
validate=validate.Regexp(regex=valid_windows_custom_pba_filename_regex)
)
@validates("windows_filename")
def validate_windows_filename_not_reserved(self, windows_filename):
# filename shouldn't start with any of these and be followed by a period
if windows_filename.split(".")[0].upper() in [
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
]:
raise ValidationError("Invalid Windows filename: reserved name used")
windows_filename = fields.Str(validate=validate_windows_filename)
@post_load
def _make_custom_pba_configuration(self, data, **kwargs):
@ -73,10 +41,10 @@ class PluginConfigurationSchema(Schema):
class ScanTargetConfigurationSchema(Schema):
blocked_ips = fields.List(fields.Str())
inaccessible_subnets = fields.List(fields.Str())
blocked_ips = fields.List(fields.Str(validate=validate_ip))
inaccessible_subnets = fields.List(fields.Str(validate=validate_subnet_range))
local_network_scan = fields.Bool()
subnets = fields.List(fields.Str())
subnets = fields.List(fields.Str(validate=validate_subnet_range))
@post_load
@freeze_lists

View File

@ -54,6 +54,20 @@ class PluginConfiguration:
@dataclass(frozen=True)
class ScanTargetConfiguration:
"""
Configuration of network targets to scan and exploit
Attributes:
:param blocked_ips: IP's that won't be scanned
Example: ("1.1.1.1", "2.2.2.2")
:param inaccessible_subnets: Subnet ranges that shouldn't be accessible for the agent
Example: ("1.1.1.1", "2.2.2.2/24", "myserver")
:param local_network_scan: Whether or not the agent should scan the local network
:param subnets: Subnet ranges to scan
Example: ("192.168.1.1-192.168.2.255", "3.3.3.3", "2.2.2.2/24",
"myHostname")
"""
blocked_ips: Tuple[str, ...]
inaccessible_subnets: Tuple[str, ...]
local_network_scan: bool

View File

@ -0,0 +1,8 @@
from .filenames import validate_linux_filename, validate_windows_filename
from .ip_ranges import (
validate_ip,
validate_hostname,
validate_ip_range,
validate_subnet_range,
validate_ip_network,
)

View File

@ -0,0 +1,24 @@
import re
from pathlib import PureWindowsPath
from marshmallow import ValidationError
_valid_windows_filename_regex = re.compile(r"^[^<>:\"\\\/|?*]*[^<>:\"\\\/|?* \.]+$|^$")
_valid_linux_filename_regex = re.compile(r"^[^\0/]*$")
def validate_linux_filename(linux_filename: str):
if not re.match(_valid_linux_filename_regex, linux_filename):
raise ValidationError(f"Invalid Unix filename {linux_filename}: illegal characters")
def validate_windows_filename(windows_filename: str):
_validate_windows_filename_not_reserved(windows_filename)
if not re.match(_valid_windows_filename_regex, windows_filename):
raise ValidationError(f"Invalid Windows filename {windows_filename}: illegal characters")
def _validate_windows_filename_not_reserved(windows_filename: str):
# filename shouldn't start with any of these and be followed by a period
if PureWindowsPath(windows_filename).is_reserved():
raise ValidationError(f"Invalid Windows filename {windows_filename}: reserved name used")

View File

@ -0,0 +1,67 @@
import re
from ipaddress import AddressValueError, IPv4Address, IPv4Network, NetmaskValueError
from marshmallow import ValidationError
def validate_subnet_range(subnet_range: str):
try:
return validate_ip(subnet_range)
except ValidationError:
pass
try:
return validate_ip_range(subnet_range)
except ValidationError:
pass
try:
return validate_ip_network(subnet_range)
except ValidationError:
pass
try:
return validate_hostname(subnet_range)
except ValidationError:
raise ValidationError(f"Invalid subnet range {subnet_range}")
def validate_hostname(hostname: str):
# Based on hostname syntax: https://www.rfc-editor.org/rfc/rfc1123#page-13
hostname_segments = hostname.split(".")
if any((part.endswith("-") or part.startswith("-") for part in hostname_segments)):
raise ValidationError(f"Hostname segment can't start or end with a hyphen: {hostname}")
if not any((char.isalpha() for char in hostname_segments[-1])):
raise ValidationError(f"Last segment of a hostname must contain a letter: {hostname}")
valid_characters_pattern = r"^[A-Za-z0-9\-]+$"
valid_characters_regex = re.compile(valid_characters_pattern)
matches = (
re.match(valid_characters_regex, hostname_segment) for hostname_segment in hostname_segments
)
if not all(matches):
raise ValidationError(f"Hostname contains invalid characters: {hostname}")
def validate_ip_network(ip_network: str):
try:
IPv4Network(ip_network, strict=False)
except (NetmaskValueError, AddressValueError):
raise ValidationError(f"Invalid IPv4 network {ip_network}")
def validate_ip_range(ip_range: str):
ip_range = ip_range.replace(" ", "")
ips = ip_range.split("-")
if len(ips) != 2:
raise ValidationError(f"Invalid IP range {ip_range}")
validate_ip(ips[0])
validate_ip(ips[1])
def validate_ip(ip: str):
try:
IPv4Address(ip)
except AddressValueError:
raise ValidationError(f"Invalid IP address {ip}")

View File

@ -0,0 +1,70 @@
import pytest
from marshmallow import ValidationError
from common.agent_configuration.validators.ip_ranges import validate_ip, validate_subnet_range
@pytest.mark.parametrize("ip", ["192.168.56.1", "0.0.0.0"])
def test_validate_ip_valid(ip):
validate_ip(ip)
@pytest.mark.parametrize("ip", ["1.1.1", "257.256.255.255", "1.1.1.1.1"])
def test_validate_ip_invalid(ip):
with pytest.raises(ValidationError):
validate_ip(ip)
@pytest.mark.parametrize("ip", ["192.168.56.1", "0.0.0.0"])
def test_validate_subnet_range__ip_valid(ip):
validate_subnet_range(ip)
@pytest.mark.parametrize("ip", ["1.1.1", "257.256.255.255", "1.1.1.1.1"])
def test_validate_subnet_range__ip_invalid(ip):
with pytest.raises(ValidationError):
validate_subnet_range(ip)
@pytest.mark.parametrize("ip_range", ["1.1.1.1 - 2.2.2.2", "1.1.1.255-1.1.1.1"])
def test_validate_subnet_range__ip_range_valid(ip_range):
validate_subnet_range(ip_range)
@pytest.mark.parametrize(
"ip_range",
[
"1.1.1-2.2.2.2",
"0-.1.1.1-2.2.2.2",
"a..1.1.1-2.2.2.2",
"257.1.1.1-2.2.2.2",
"1.1.1.1-2.2.2.2-3.3.3.3",
],
)
def test_validate_subnet_range__ip_range_invalid(ip_range):
with pytest.raises(ValidationError):
validate_subnet_range(ip_range)
@pytest.mark.parametrize("hostname", ["infection.monkey", "1nfection-Monkey", "1.1.1.1a"])
def test_validate_subnet_range__hostname_valid(hostname):
validate_subnet_range(hostname)
@pytest.mark.parametrize(
"hostname", ["hy&!he.host", "čili-peppers.are-hot", "one.two-", "one-.two", "one@two", ""]
)
def test_validate_subnet_range__hostname_invalid(hostname):
with pytest.raises(ValidationError):
validate_subnet_range(hostname)
@pytest.mark.parametrize("cidr_range", ["1.1.1.1/24", "1.1.1.1/0"])
def test_validate_subnet_range__cidr_valid(cidr_range):
validate_subnet_range(cidr_range)
@pytest.mark.parametrize("cidr_range", ["1.1.1/24", "1.1.1.1/-1", "1.1.1.1/33", "1.1.1.1/222"])
def test_validate_subnet_range__cidr_invalid(cidr_range):
with pytest.raises(ValidationError):
validate_subnet_range(cidr_range)