forked from p15670423/monkey
Merge pull request #1884 from guardicore/1813-bb-optimization
1813 bb optimization
This commit is contained in:
commit
a92818645e
|
@ -0,0 +1,42 @@
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class Depth1A(ConfigTemplate):
|
||||||
|
config_values = copy(BaseTemplate.config_values)
|
||||||
|
# Tests:
|
||||||
|
# Hadoop (10.2.2.2, 10.2.2.3)
|
||||||
|
# Log4shell (10.2.3.55, 10.2.3.56, 10.2.3.49, 10.2.3.50, 10.2.3.51, 10.2.3.52)
|
||||||
|
# MSSQL (10.2.2.16)
|
||||||
|
# SMB mimikatz password stealing and brute force (10.2.2.14 and 10.2.2.15)
|
||||||
|
config_values.update(
|
||||||
|
{
|
||||||
|
"basic.exploiters.exploiter_classes": [
|
||||||
|
"HadoopExploiter",
|
||||||
|
"Log4ShellExploiter",
|
||||||
|
"MSSQLExploiter",
|
||||||
|
"SmbExploiter",
|
||||||
|
"SSHExploiter",
|
||||||
|
],
|
||||||
|
"basic_network.scope.subnet_scan_list": [
|
||||||
|
"10.2.2.2",
|
||||||
|
"10.2.2.3",
|
||||||
|
"10.2.3.55",
|
||||||
|
"10.2.3.56",
|
||||||
|
"10.2.3.49",
|
||||||
|
"10.2.3.50",
|
||||||
|
"10.2.3.51",
|
||||||
|
"10.2.3.52",
|
||||||
|
"10.2.2.16",
|
||||||
|
"10.2.2.14",
|
||||||
|
"10.2.2.15",
|
||||||
|
],
|
||||||
|
"basic.credentials.exploit_password_list": ["Ivrrw5zEzs", "Xk8VDTsC"],
|
||||||
|
"basic.credentials.exploit_user_list": ["m0nk3y"],
|
||||||
|
"monkey.system_info.system_info_collector_classes": [
|
||||||
|
"MimikatzCollector",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,23 @@
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class Depth2A(ConfigTemplate):
|
||||||
|
config_values = copy(BaseTemplate.config_values)
|
||||||
|
# SSH password and key brute-force, key stealing (10.2.2.11, 10.2.2.12)
|
||||||
|
config_values.update(
|
||||||
|
{
|
||||||
|
"basic.exploiters.exploiter_classes": [
|
||||||
|
"SSHExploiter",
|
||||||
|
],
|
||||||
|
"basic_network.scope.subnet_scan_list": [
|
||||||
|
"10.2.2.11",
|
||||||
|
"10.2.2.12",
|
||||||
|
],
|
||||||
|
"basic_network.scope.depth": 2,
|
||||||
|
"basic.credentials.exploit_password_list": ["^NgDvY59~8"],
|
||||||
|
"basic.credentials.exploit_user_list": ["m0nk3y"],
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,48 @@
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class Depth3A(ConfigTemplate):
|
||||||
|
config_values = copy(BaseTemplate.config_values)
|
||||||
|
|
||||||
|
# Tests:
|
||||||
|
# Powershell (10.2.3.45, 10.2.3.46, 10.2.3.47, 10.2.3.48)
|
||||||
|
# Tunneling (SSH brute force) (10.2.2.9, 10.2.1.10, 10.2.0.12, 10.2.0.11)
|
||||||
|
# WMI pass the hash (10.2.2.15)
|
||||||
|
config_values.update(
|
||||||
|
{
|
||||||
|
"basic.exploiters.exploiter_classes": [
|
||||||
|
"PowerShellExploiter",
|
||||||
|
"SSHExploiter",
|
||||||
|
"WmiExploiter",
|
||||||
|
],
|
||||||
|
"basic_network.scope.subnet_scan_list": [
|
||||||
|
"10.2.3.45",
|
||||||
|
"10.2.3.46",
|
||||||
|
"10.2.3.47",
|
||||||
|
"10.2.3.48",
|
||||||
|
"10.2.2.9",
|
||||||
|
"10.2.1.10",
|
||||||
|
"10.2.0.12",
|
||||||
|
"10.2.0.11",
|
||||||
|
"10.2.2.15",
|
||||||
|
],
|
||||||
|
"basic.credentials.exploit_password_list": [
|
||||||
|
"Passw0rd!",
|
||||||
|
"3Q=(Ge(+&w]*",
|
||||||
|
"`))jU7L(w}",
|
||||||
|
"t67TC5ZDmz",
|
||||||
|
],
|
||||||
|
"basic_network.scope.depth": 3,
|
||||||
|
"internal.general.keep_tunnel_open_time": 20,
|
||||||
|
"basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"],
|
||||||
|
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
||||||
|
"internal.exploits.exploit_ntlm_hash_list": [
|
||||||
|
"d0f0132b308a0c4e5d1029cc06f48692",
|
||||||
|
"5da0889ea2081aa79f6852294cba4a5e",
|
||||||
|
"50c9987a6bf1ac59398df9f911122c9b",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
|
@ -8,39 +8,20 @@ from typing_extensions import Type
|
||||||
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer
|
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer
|
||||||
from envs.monkey_zoo.blackbox.analyzers.zerologon_analyzer import ZerologonAnalyzer
|
from envs.monkey_zoo.blackbox.analyzers.zerologon_analyzer import ZerologonAnalyzer
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
||||||
from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop
|
from envs.monkey_zoo.blackbox.config_templates.grouped.depth_1_a import Depth1A
|
||||||
from envs.monkey_zoo.blackbox.config_templates.log4j_logstash import Log4jLogstash
|
from envs.monkey_zoo.blackbox.config_templates.grouped.depth_2_a import Depth2A
|
||||||
from envs.monkey_zoo.blackbox.config_templates.log4j_solr import Log4jSolr
|
from envs.monkey_zoo.blackbox.config_templates.grouped.depth_3_a import Depth3A
|
||||||
from envs.monkey_zoo.blackbox.config_templates.log4j_tomcat import Log4jTomcat
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.powershell_credentials_reuse import (
|
||||||
from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.performance import Performance
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.powershell_credentials_reuse import (
|
|
||||||
PowerShellCredentialsReuse,
|
PowerShellCredentialsReuse,
|
||||||
)
|
)
|
||||||
from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.smb_pth import SmbPth
|
||||||
from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.wmi_mimikatz import WmiMimikatz
|
||||||
from envs.monkey_zoo.blackbox.config_templates.ssh import Ssh
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.zerologon import Zerologon
|
||||||
from envs.monkey_zoo.blackbox.config_templates.tunneling import Tunneling
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.wmi_mimikatz import WmiMimikatz
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.wmi_pth import WmiPth
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.zerologon import Zerologon
|
|
||||||
from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_TEST_MACHINE_LIST
|
from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_TEST_MACHINE_LIST
|
||||||
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
||||||
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
|
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
|
||||||
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
|
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.map_generation import MapGenerationTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.map_generation_from_telemetries import (
|
|
||||||
MapGenerationFromTelemetryTest,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.report_generation import ReportGenerationTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.report_generation_from_telemetries import (
|
|
||||||
ReportGenerationFromTelemetryTest,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test import (
|
|
||||||
TelemetryPerformanceTest,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
|
from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
|
||||||
initialize_gcp_client,
|
initialize_gcp_client,
|
||||||
start_machines,
|
start_machines,
|
||||||
|
@ -48,7 +29,7 @@ from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
|
||||||
)
|
)
|
||||||
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
||||||
|
|
||||||
DEFAULT_TIMEOUT_SECONDS = 2 * 60
|
DEFAULT_TIMEOUT_SECONDS = 2 * 60 + 30
|
||||||
MACHINE_BOOTUP_WAIT_SECONDS = 30
|
MACHINE_BOOTUP_WAIT_SECONDS = 30
|
||||||
LOG_DIR_PATH = "./logs"
|
LOG_DIR_PATH = "./logs"
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
@ -125,48 +106,20 @@ class TestMonkeyBlackbox:
|
||||||
log_handler=log_handler,
|
log_handler=log_handler,
|
||||||
).run()
|
).run()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def run_performance_test(
|
|
||||||
performance_test_class,
|
|
||||||
island_client,
|
|
||||||
config_template,
|
|
||||||
timeout_in_seconds,
|
|
||||||
break_on_timeout=False,
|
|
||||||
):
|
|
||||||
raw_config = IslandConfigParser.get_raw_config(config_template, island_client)
|
|
||||||
log_handler = TestLogsHandler(
|
|
||||||
performance_test_class.TEST_NAME, island_client, TestMonkeyBlackbox.get_log_dir_path()
|
|
||||||
)
|
|
||||||
analyzers = [
|
|
||||||
CommunicationAnalyzer(island_client, IslandConfigParser.get_ips_of_targets(raw_config))
|
|
||||||
]
|
|
||||||
performance_test_class(
|
|
||||||
island_client=island_client,
|
|
||||||
raw_config=raw_config,
|
|
||||||
analyzers=analyzers,
|
|
||||||
timeout=timeout_in_seconds,
|
|
||||||
log_handler=log_handler,
|
|
||||||
break_on_timeout=break_on_timeout,
|
|
||||||
).run()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_log_dir_path():
|
def get_log_dir_path():
|
||||||
return os.path.abspath(LOG_DIR_PATH)
|
return os.path.abspath(LOG_DIR_PATH)
|
||||||
|
|
||||||
def test_ssh_exploiter(self, island_client):
|
def test_depth_1_a(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, Ssh, "SSH_exploiter_and_keys")
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Depth1A, "Depth1A test suite")
|
||||||
|
|
||||||
def test_hadoop_exploiter(self, island_client):
|
def test_depth_2_a(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, Hadoop, "Hadoop_exploiter", 6 * 60)
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Depth2A, "Depth2A test suite")
|
||||||
|
|
||||||
def test_mssql_exploiter(self, island_client):
|
def test_depth_3_a(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, Mssql, "MSSQL_exploiter")
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Depth3A, "Depth3A test suite")
|
||||||
|
|
||||||
def test_powershell_exploiter(self, island_client):
|
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
|
||||||
island_client, PowerShell, "PowerShell_Remoting_exploiter"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# Not grouped because can only be ran on windows
|
||||||
@pytest.mark.skip_powershell_reuse
|
@pytest.mark.skip_powershell_reuse
|
||||||
def test_powershell_exploiter_credentials_reuse(self, island_client):
|
def test_powershell_exploiter_credentials_reuse(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
@ -175,42 +128,7 @@ class TestMonkeyBlackbox:
|
||||||
"PowerShell_Remoting_exploiter_credentials_reuse",
|
"PowerShell_Remoting_exploiter_credentials_reuse",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_smb_and_mimikatz_exploiters(self, island_client):
|
# Not grouped because it's slow
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
|
||||||
island_client, SmbMimikatz, "SMB_exploiter_mimikatz"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_smb_pth(self, island_client):
|
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH")
|
|
||||||
|
|
||||||
def test_log4j_solr_exploiter(self, island_client):
|
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
|
||||||
island_client, Log4jSolr, "Log4Shell_Solr_exploiter"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_log4j_tomcat_exploiter(self, island_client):
|
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
|
||||||
island_client, Log4jTomcat, "Log4Shell_tomcat_exploiter"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_log4j_logstash_exploiter(self, island_client):
|
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
|
||||||
island_client, Log4jLogstash, "Log4Shell_logstash_exploiter"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_tunneling(self, island_client):
|
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
|
||||||
island_client, Tunneling, "Tunneling_exploiter", 3 * 60
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_wmi_and_mimikatz_exploiters(self, island_client):
|
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
|
||||||
island_client, WmiMimikatz, "WMI_exploiter,_mimikatz"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_wmi_pth(self, island_client):
|
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, WmiPth, "WMI_PTH")
|
|
||||||
|
|
||||||
def test_zerologon_exploiter(self, island_client):
|
def test_zerologon_exploiter(self, island_client):
|
||||||
test_name = "Zerologon_exploiter"
|
test_name = "Zerologon_exploiter"
|
||||||
expected_creds = [
|
expected_creds = [
|
||||||
|
@ -235,47 +153,13 @@ class TestMonkeyBlackbox:
|
||||||
log_handler=log_handler,
|
log_handler=log_handler,
|
||||||
).run()
|
).run()
|
||||||
|
|
||||||
@pytest.mark.skip(
|
# Not grouped because conflicts with SMB.
|
||||||
reason="Perfomance test that creates env from fake telemetries is faster, use that instead."
|
# Consider grouping when more depth 1 exploiters collide with group depth_1_a
|
||||||
|
def test_wmi_and_mimikatz_exploiters(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, WmiMimikatz, "WMI_exploiter,_mimikatz"
|
||||||
)
|
)
|
||||||
def test_report_generation_performance(self, island_client, quick_performance_tests):
|
|
||||||
"""
|
|
||||||
This test includes the SSH + Hadoop + MSSQL machines all in one test
|
|
||||||
for a total of 8 machines including the Monkey Island.
|
|
||||||
|
|
||||||
Is has 2 analyzers - the regular one which checks all the Monkeys
|
# Not grouped because it's depth 1 but conflicts with SMB exploiter in group depth_1_a
|
||||||
and the Timing one which checks how long the report took to execute
|
def test_smb_pth(self, island_client):
|
||||||
"""
|
TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH")
|
||||||
if not quick_performance_tests:
|
|
||||||
TestMonkeyBlackbox.run_performance_test(
|
|
||||||
ReportGenerationTest, island_client, Performance, timeout_in_seconds=10 * 60
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
LOGGER.error("This test doesn't support 'quick_performance_tests' option.")
|
|
||||||
assert False
|
|
||||||
|
|
||||||
@pytest.mark.skip(
|
|
||||||
reason="Perfomance test that creates env from fake telemetries is faster, use that instead."
|
|
||||||
)
|
|
||||||
def test_map_generation_performance(self, island_client, quick_performance_tests):
|
|
||||||
if not quick_performance_tests:
|
|
||||||
TestMonkeyBlackbox.run_performance_test(
|
|
||||||
MapGenerationTest, island_client, "PERFORMANCE.conf", timeout_in_seconds=10 * 60
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
LOGGER.error("This test doesn't support 'quick_performance_tests' option.")
|
|
||||||
assert False
|
|
||||||
|
|
||||||
@pytest.mark.run_performance_tests
|
|
||||||
def test_report_generation_from_fake_telemetries(self, island_client, quick_performance_tests):
|
|
||||||
ReportGenerationFromTelemetryTest(island_client, quick_performance_tests).run()
|
|
||||||
|
|
||||||
@pytest.mark.run_performance_tests
|
|
||||||
def test_map_generation_from_fake_telemetries(self, island_client, quick_performance_tests):
|
|
||||||
MapGenerationFromTelemetryTest(island_client, quick_performance_tests).run()
|
|
||||||
|
|
||||||
@pytest.mark.run_performance_tests
|
|
||||||
def test_telem_performance(self, island_client, quick_performance_tests):
|
|
||||||
TelemetryPerformanceTest(
|
|
||||||
island_client, quick_performance_tests
|
|
||||||
).test_telemetry_performance()
|
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from typing_extensions import Type
|
||||||
|
|
||||||
|
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer
|
||||||
|
from envs.monkey_zoo.blackbox.analyzers.zerologon_analyzer import ZerologonAnalyzer
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.drupal import Drupal
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.hadoop import Hadoop
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.log4j_logstash import Log4jLogstash
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.log4j_solr import Log4jSolr
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.log4j_tomcat import Log4jTomcat
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.mssql import Mssql
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.performance import Performance
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.powershell import PowerShell
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.powershell_credentials_reuse import (
|
||||||
|
PowerShellCredentialsReuse,
|
||||||
|
)
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.smb_mimikatz import SmbMimikatz
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.smb_pth import SmbPth
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.ssh import Ssh
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.struts2 import Struts2
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.tunneling import Tunneling
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.weblogic import Weblogic
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.wmi_mimikatz import WmiMimikatz
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.wmi_pth import WmiPth
|
||||||
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.zerologon import Zerologon
|
||||||
|
from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_TEST_MACHINE_LIST
|
||||||
|
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
||||||
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
||||||
|
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
|
||||||
|
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
|
||||||
|
from envs.monkey_zoo.blackbox.tests.performance.map_generation import MapGenerationTest
|
||||||
|
from envs.monkey_zoo.blackbox.tests.performance.map_generation_from_telemetries import (
|
||||||
|
MapGenerationFromTelemetryTest,
|
||||||
|
)
|
||||||
|
from envs.monkey_zoo.blackbox.tests.performance.report_generation import ReportGenerationTest
|
||||||
|
from envs.monkey_zoo.blackbox.tests.performance.report_generation_from_telemetries import (
|
||||||
|
ReportGenerationFromTelemetryTest,
|
||||||
|
)
|
||||||
|
from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test import (
|
||||||
|
TelemetryPerformanceTest,
|
||||||
|
)
|
||||||
|
from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
|
||||||
|
initialize_gcp_client,
|
||||||
|
start_machines,
|
||||||
|
stop_machines,
|
||||||
|
)
|
||||||
|
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT_SECONDS = 2 * 60
|
||||||
|
MACHINE_BOOTUP_WAIT_SECONDS = 30
|
||||||
|
LOG_DIR_PATH = "./logs"
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True, scope="session")
|
||||||
|
def GCPHandler(request, no_gcp):
|
||||||
|
if not no_gcp:
|
||||||
|
try:
|
||||||
|
initialize_gcp_client()
|
||||||
|
start_machines(GCP_TEST_MACHINE_LIST)
|
||||||
|
except Exception as e:
|
||||||
|
LOGGER.error("GCP Handler failed to initialize: %s." % e)
|
||||||
|
pytest.exit("Encountered an error while starting GCP machines. Stopping the tests.")
|
||||||
|
wait_machine_bootup()
|
||||||
|
|
||||||
|
def fin():
|
||||||
|
stop_machines(GCP_TEST_MACHINE_LIST)
|
||||||
|
|
||||||
|
request.addfinalizer(fin)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True, scope="session")
|
||||||
|
def delete_logs():
|
||||||
|
LOGGER.info("Deleting monkey logs before new tests.")
|
||||||
|
TestLogsHandler.delete_log_folder_contents(TestMonkeyBlackbox.get_log_dir_path())
|
||||||
|
|
||||||
|
|
||||||
|
def wait_machine_bootup():
|
||||||
|
sleep(MACHINE_BOOTUP_WAIT_SECONDS)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def island_client(island, quick_performance_tests):
|
||||||
|
client_established = False
|
||||||
|
try:
|
||||||
|
island_client_object = MonkeyIslandClient(island)
|
||||||
|
client_established = island_client_object.get_api_status()
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Got an exception while trying to establish connection to the Island.")
|
||||||
|
finally:
|
||||||
|
if not client_established:
|
||||||
|
pytest.exit("BB tests couldn't establish communication to the island.")
|
||||||
|
if not quick_performance_tests:
|
||||||
|
island_client_object.reset_env()
|
||||||
|
island_client_object.set_scenario(IslandModeEnum.ADVANCED.value)
|
||||||
|
yield island_client_object
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("island_client")
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
class TestMonkeyBlackbox:
|
||||||
|
@staticmethod
|
||||||
|
def run_exploitation_test(
|
||||||
|
island_client: MonkeyIslandClient,
|
||||||
|
config_template: Type[ConfigTemplate],
|
||||||
|
test_name: str,
|
||||||
|
timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS,
|
||||||
|
):
|
||||||
|
raw_config = IslandConfigParser.get_raw_config(config_template, island_client)
|
||||||
|
analyzer = CommunicationAnalyzer(
|
||||||
|
island_client, IslandConfigParser.get_ips_of_targets(raw_config)
|
||||||
|
)
|
||||||
|
log_handler = TestLogsHandler(
|
||||||
|
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
|
||||||
|
)
|
||||||
|
ExploitationTest(
|
||||||
|
name=test_name,
|
||||||
|
island_client=island_client,
|
||||||
|
raw_config=raw_config,
|
||||||
|
analyzers=[analyzer],
|
||||||
|
timeout=timeout_in_seconds,
|
||||||
|
log_handler=log_handler,
|
||||||
|
).run()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def run_performance_test(
|
||||||
|
performance_test_class,
|
||||||
|
island_client,
|
||||||
|
config_template,
|
||||||
|
timeout_in_seconds,
|
||||||
|
break_on_timeout=False,
|
||||||
|
):
|
||||||
|
raw_config = IslandConfigParser.get_raw_config(config_template, island_client)
|
||||||
|
log_handler = TestLogsHandler(
|
||||||
|
performance_test_class.TEST_NAME, island_client, TestMonkeyBlackbox.get_log_dir_path()
|
||||||
|
)
|
||||||
|
analyzers = [
|
||||||
|
CommunicationAnalyzer(island_client, IslandConfigParser.get_ips_of_targets(raw_config))
|
||||||
|
]
|
||||||
|
performance_test_class(
|
||||||
|
island_client=island_client,
|
||||||
|
raw_config=raw_config,
|
||||||
|
analyzers=analyzers,
|
||||||
|
timeout=timeout_in_seconds,
|
||||||
|
log_handler=log_handler,
|
||||||
|
break_on_timeout=break_on_timeout,
|
||||||
|
).run()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_log_dir_path():
|
||||||
|
return os.path.abspath(LOG_DIR_PATH)
|
||||||
|
|
||||||
|
def test_ssh_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Ssh, "SSH_exploiter_and_keys")
|
||||||
|
|
||||||
|
def test_hadoop_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Hadoop, "Hadoop_exploiter", 6 * 60)
|
||||||
|
|
||||||
|
def test_mssql_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Mssql, "MSSQL_exploiter")
|
||||||
|
|
||||||
|
def test_powershell_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, PowerShell, "PowerShell_Remoting_exploiter"
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.mark.skip_powershell_reuse
|
||||||
|
def test_powershell_exploiter_credentials_reuse(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client,
|
||||||
|
PowerShellCredentialsReuse,
|
||||||
|
"PowerShell_Remoting_exploiter_credentials_reuse",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_smb_and_mimikatz_exploiters(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, SmbMimikatz, "SMB_exploiter_mimikatz"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_smb_pth(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH")
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="Drupal exploiter is deprecated")
|
||||||
|
def test_drupal_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Drupal, "Drupal_exploiter")
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="Struts2 exploiter is deprecated")
|
||||||
|
def test_struts_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Struts2, "Struts2_exploiter")
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="Weblogic exploiter is deprecated")
|
||||||
|
def test_weblogic_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(island_client, Weblogic, "Weblogic_exploiter")
|
||||||
|
|
||||||
|
def test_log4j_solr_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, Log4jSolr, "Log4Shell_Solr_exploiter"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_log4j_tomcat_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, Log4jTomcat, "Log4Shell_tomcat_exploiter"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_log4j_logstash_exploiter(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, Log4jLogstash, "Log4Shell_logstash_exploiter"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_tunneling(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, Tunneling, "Tunneling_exploiter", 3 * 60
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_wmi_and_mimikatz_exploiters(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, WmiMimikatz, "WMI_exploiter,_mimikatz"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_wmi_pth(self, island_client):
|
||||||
|
TestMonkeyBlackbox.run_exploitation_test(island_client, WmiPth, "WMI_PTH")
|
||||||
|
|
||||||
|
def test_zerologon_exploiter(self, island_client):
|
||||||
|
test_name = "Zerologon_exploiter"
|
||||||
|
expected_creds = [
|
||||||
|
"Administrator",
|
||||||
|
"aad3b435b51404eeaad3b435b51404ee",
|
||||||
|
"2864b62ea4496934a5d6e86f50b834a5",
|
||||||
|
]
|
||||||
|
raw_config = IslandConfigParser.get_raw_config(Zerologon, island_client)
|
||||||
|
zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds)
|
||||||
|
communication_analyzer = CommunicationAnalyzer(
|
||||||
|
island_client, IslandConfigParser.get_ips_of_targets(raw_config)
|
||||||
|
)
|
||||||
|
log_handler = TestLogsHandler(
|
||||||
|
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
|
||||||
|
)
|
||||||
|
ExploitationTest(
|
||||||
|
name=test_name,
|
||||||
|
island_client=island_client,
|
||||||
|
raw_config=raw_config,
|
||||||
|
analyzers=[zero_logon_analyzer, communication_analyzer],
|
||||||
|
timeout=DEFAULT_TIMEOUT_SECONDS,
|
||||||
|
log_handler=log_handler,
|
||||||
|
).run()
|
||||||
|
|
||||||
|
@pytest.mark.skip(
|
||||||
|
reason="Perfomance test that creates env from fake telemetries is faster, use that instead."
|
||||||
|
)
|
||||||
|
def test_report_generation_performance(self, island_client, quick_performance_tests):
|
||||||
|
"""
|
||||||
|
This test includes the SSH + Hadoop + MSSQL machines all in one test
|
||||||
|
for a total of 8 machines including the Monkey Island.
|
||||||
|
|
||||||
|
Is has 2 analyzers - the regular one which checks all the Monkeys
|
||||||
|
and the Timing one which checks how long the report took to execute
|
||||||
|
"""
|
||||||
|
if not quick_performance_tests:
|
||||||
|
TestMonkeyBlackbox.run_performance_test(
|
||||||
|
ReportGenerationTest, island_client, Performance, timeout_in_seconds=10 * 60
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
LOGGER.error("This test doesn't support 'quick_performance_tests' option.")
|
||||||
|
assert False
|
||||||
|
|
||||||
|
@pytest.mark.skip(
|
||||||
|
reason="Perfomance test that creates env from fake telemetries is faster, use that instead."
|
||||||
|
)
|
||||||
|
def test_map_generation_performance(self, island_client, quick_performance_tests):
|
||||||
|
if not quick_performance_tests:
|
||||||
|
TestMonkeyBlackbox.run_performance_test(
|
||||||
|
MapGenerationTest, island_client, "PERFORMANCE.conf", timeout_in_seconds=10 * 60
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
LOGGER.error("This test doesn't support 'quick_performance_tests' option.")
|
||||||
|
assert False
|
||||||
|
|
||||||
|
@pytest.mark.run_performance_tests
|
||||||
|
def test_report_generation_from_fake_telemetries(self, island_client, quick_performance_tests):
|
||||||
|
ReportGenerationFromTelemetryTest(island_client, quick_performance_tests).run()
|
||||||
|
|
||||||
|
@pytest.mark.run_performance_tests
|
||||||
|
def test_map_generation_from_fake_telemetries(self, island_client, quick_performance_tests):
|
||||||
|
MapGenerationFromTelemetryTest(island_client, quick_performance_tests).run()
|
||||||
|
|
||||||
|
@pytest.mark.run_performance_tests
|
||||||
|
def test_telem_performance(self, island_client, quick_performance_tests):
|
||||||
|
TelemetryPerformanceTest(
|
||||||
|
island_client, quick_performance_tests
|
||||||
|
).test_telemetry_performance()
|
|
@ -5,7 +5,7 @@ from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandCo
|
||||||
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
||||||
from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
|
from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
|
||||||
|
|
||||||
MAX_TIME_FOR_MONKEYS_TO_DIE = 5 * 60
|
MAX_TIME_FOR_MONKEYS_TO_DIE = 2 * 60
|
||||||
WAIT_TIME_BETWEEN_REQUESTS = 1
|
WAIT_TIME_BETWEEN_REQUESTS = 1
|
||||||
TIME_FOR_MONKEY_PROCESS_TO_FINISH = 5
|
TIME_FOR_MONKEY_PROCESS_TO_FINISH = 5
|
||||||
DELAY_BETWEEN_ANALYSIS = 1
|
DELAY_BETWEEN_ANALYSIS = 1
|
||||||
|
@ -89,6 +89,7 @@ class ExploitationTest(BasicTest):
|
||||||
if time_passed > MAX_TIME_FOR_MONKEYS_TO_DIE:
|
if time_passed > MAX_TIME_FOR_MONKEYS_TO_DIE:
|
||||||
LOGGER.error("Some monkeys didn't die after the test, failing")
|
LOGGER.error("Some monkeys didn't die after the test, failing")
|
||||||
assert False
|
assert False
|
||||||
|
LOGGER.info(f"After {time_passed} seconds all monkeys have died")
|
||||||
|
|
||||||
def parse_logs(self):
|
def parse_logs(self):
|
||||||
LOGGER.info("Parsing test logs:")
|
LOGGER.info("Parsing test logs:")
|
||||||
|
|
|
@ -3,20 +3,15 @@ import pathlib
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
||||||
from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop
|
from envs.monkey_zoo.blackbox.config_templates.grouped.depth_1_a import Depth1A
|
||||||
from envs.monkey_zoo.blackbox.config_templates.log4j_logstash import Log4jLogstash
|
from envs.monkey_zoo.blackbox.config_templates.grouped.depth_2_a import Depth2A
|
||||||
from envs.monkey_zoo.blackbox.config_templates.log4j_solr import Log4jSolr
|
from envs.monkey_zoo.blackbox.config_templates.grouped.depth_3_a import Depth3A
|
||||||
from envs.monkey_zoo.blackbox.config_templates.log4j_tomcat import Log4jTomcat
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.powershell_credentials_reuse import (
|
||||||
from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql
|
PowerShellCredentialsReuse,
|
||||||
from envs.monkey_zoo.blackbox.config_templates.performance import Performance
|
)
|
||||||
from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.smb_pth import SmbPth
|
||||||
from envs.monkey_zoo.blackbox.config_templates.smb_mimikatz import SmbMimikatz
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.wmi_mimikatz import WmiMimikatz
|
||||||
from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth
|
from envs.monkey_zoo.blackbox.config_templates.single_tests.zerologon import Zerologon
|
||||||
from envs.monkey_zoo.blackbox.config_templates.ssh import Ssh
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.tunneling import Tunneling
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.wmi_mimikatz import WmiMimikatz
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.wmi_pth import WmiPth
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.zerologon import Zerologon
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
||||||
|
|
||||||
|
@ -33,22 +28,14 @@ parser.add_argument(
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
island_client = MonkeyIslandClient(args.island_ip)
|
island_client = MonkeyIslandClient(args.island_ip)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_TEMPLATES = [
|
CONFIG_TEMPLATES = [
|
||||||
Hadoop,
|
Depth1A,
|
||||||
Mssql,
|
Depth2A,
|
||||||
Performance,
|
Depth3A,
|
||||||
PowerShell,
|
|
||||||
SmbMimikatz,
|
|
||||||
SmbPth,
|
|
||||||
Ssh,
|
|
||||||
Tunneling,
|
|
||||||
WmiMimikatz,
|
|
||||||
WmiPth,
|
|
||||||
Zerologon,
|
Zerologon,
|
||||||
Log4jLogstash,
|
SmbPth,
|
||||||
Log4jTomcat,
|
WmiMimikatz,
|
||||||
Log4jSolr,
|
PowerShellCredentialsReuse,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -771,7 +771,9 @@ Accessibale through Island using m0nk3y-user.</td>
|
||||||
<tr class="even">
|
<tr class="even">
|
||||||
<td>Notes:</td>
|
<td>Notes:</td>
|
||||||
<td>User: m0nk3y, Password: Passw0rd!<br>
|
<td>User: m0nk3y, Password: Passw0rd!<br>
|
||||||
Accessiable through cached credentials (Windows Island)</td>
|
Accessible using the same m0nk3y user from island, in other words powershell exploiter can exploit
|
||||||
|
this machine without credentials as long as the user running the agent is the same on both
|
||||||
|
machines</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -38,6 +38,7 @@ pytest-cov = "*"
|
||||||
isort = "==5.10.1"
|
isort = "==5.10.1"
|
||||||
coverage = "*"
|
coverage = "*"
|
||||||
vulture = "==2.3"
|
vulture = "==2.3"
|
||||||
|
tqdm = "*" # Used in BB tests
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.7"
|
python_version = "3.7"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "260be37685cd94ec3e28773e82834ee6564462ace9b7b1c9242dcf611e33fd25"
|
"sha256": "48c3a77a6022276d2607c19ae66490310fa8fa99e07e888b416c181e5ec0b534"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -759,11 +759,11 @@
|
||||||
},
|
},
|
||||||
"setuptools": {
|
"setuptools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:425ec0e0014c5bcc1104dd1099de6c8f0584854fc9a4f512575f5ed5ee399fb9",
|
"sha256:7999cbd87f1b6e1f33bf47efa368b224bed5e27b5ef2c4d46580186cbcb1a86a",
|
||||||
"sha256:6d59c30ce22dd583b42cacf51eebe4c6ea72febaa648aa8b30e5015d23a191fe"
|
"sha256:a65e3802053e99fc64c6b3b29c11132943d5b8c8facbcc461157511546510967"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==61.3.0"
|
"version": "==62.0.0"
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -791,11 +791,11 @@
|
||||||
},
|
},
|
||||||
"werkzeug": {
|
"werkzeug": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:094ecfc981948f228b30ee09dbfe250e474823b69b9b1292658301b5894bbf08",
|
"sha256:3c5493ece8268fecdcdc9c0b112211acd006354723b280d643ec732b6d4063d6",
|
||||||
"sha256:9b55466a3e99e13b1f0686a66117d39bda85a992166e0a79aedfcf3586328f7a"
|
"sha256:f8e89a20aeabbe8a893c24a461d3ee5dad2123b05cc6abd73ceed01d39c3ae74"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.1.0"
|
"version": "==2.1.1"
|
||||||
},
|
},
|
||||||
"wirerope": {
|
"wirerope": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -805,11 +805,11 @@
|
||||||
},
|
},
|
||||||
"zipp": {
|
"zipp": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d",
|
"sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad",
|
||||||
"sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"
|
"sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==3.7.0"
|
"version": "==3.8.0"
|
||||||
},
|
},
|
||||||
"zope.event": {
|
"zope.event": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1208,6 +1208,14 @@
|
||||||
"markers": "python_version < '3.11'",
|
"markers": "python_version < '3.11'",
|
||||||
"version": "==2.0.1"
|
"version": "==2.0.1"
|
||||||
},
|
},
|
||||||
|
"tqdm": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d",
|
||||||
|
"sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==4.64.0"
|
||||||
|
},
|
||||||
"typed-ast": {
|
"typed-ast": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0eb77764ea470f14fcbb89d51bc6bbf5e7623446ac4ed06cbd9ca9495b62e36e",
|
"sha256:0eb77764ea470f14fcbb89d51bc6bbf5e7623446ac4ed06cbd9ca9495b62e36e",
|
||||||
|
@ -1272,11 +1280,11 @@
|
||||||
},
|
},
|
||||||
"zipp": {
|
"zipp": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d",
|
"sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad",
|
||||||
"sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"
|
"sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==3.7.0"
|
"version": "==3.8.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue