Merge pull request #2048 from guardicore/2002-remove-backend-encryption

Remove backend encryption
This commit is contained in:
Shreya Malviya 2022-06-28 09:41:51 -07:00 committed by GitHub
commit c4d5e58486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 4 additions and 379 deletions

View File

@ -83,6 +83,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- "+dev" from version numbers. #1553
- agent's "--config" argument. #906
- Option to export monkey telemetries. #1998
- "/api/configuration/import" endpoint. #2002
- "/api/configuration/export" endpoint. #2002
### Fixed
- A bug in network map page that caused delay of telemetry log loading. #1545

View File

@ -23,8 +23,6 @@ from monkey_island.cc.resources.blackbox.monkey_blackbox_endpoint import MonkeyB
from monkey_island.cc.resources.blackbox.telemetry_blackbox_endpoint import (
TelemetryBlackboxEndpoint,
)
from monkey_island.cc.resources.configuration_export import ConfigurationExport
from monkey_island.cc.resources.configuration_import import ConfigurationImport
from monkey_island.cc.resources.edge import Edge
from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation
from monkey_island.cc.resources.exploitations.monkey_exploitation import MonkeyExploitation
@ -154,8 +152,6 @@ def init_api_resources(api: FlaskDIWrapper):
api.add_resource(IslandMode)
api.add_resource(IslandConfiguration)
api.add_resource(ConfigurationExport)
api.add_resource(ConfigurationImport)
api.add_resource(AgentConfiguration)
api.add_resource(AgentBinaries)
api.add_resource(NetMap)

View File

@ -1,29 +0,0 @@
import json
from flask import request
from monkey_island.cc.resources.AbstractResource import AbstractResource
from monkey_island.cc.resources.request_authentication import jwt_required
from monkey_island.cc.server_utils.encryption import PasswordBasedStringEncryptor
from monkey_island.cc.services.config import ConfigService
class ConfigurationExport(AbstractResource):
urls = ["/api/configuration/export"]
@jwt_required
def post(self):
data = json.loads(request.data)
should_encrypt = data["should_encrypt"]
plaintext_config = ConfigService.get_config()
config_export = plaintext_config
if should_encrypt:
password = data["password"]
plaintext_config = json.dumps(plaintext_config)
pb_encryptor = PasswordBasedStringEncryptor(password)
config_export = pb_encryptor.encrypt(plaintext_config)
return {"config_export": config_export, "encrypted": should_encrypt}

View File

@ -1,103 +0,0 @@
import json
import logging
from dataclasses import dataclass
from json.decoder import JSONDecodeError
from flask import request
from common.utils.exceptions import InvalidConfigurationError
from monkey_island.cc.resources.AbstractResource import AbstractResource
from monkey_island.cc.resources.request_authentication import jwt_required
from monkey_island.cc.server_utils.encryption import (
InvalidCiphertextError,
InvalidCredentialsError,
PasswordBasedStringEncryptor,
is_encrypted,
)
from monkey_island.cc.services.config import ConfigService
logger = logging.getLogger(__name__)
class ImportStatuses:
UNSAFE_OPTION_VERIFICATION_REQUIRED = "unsafe_options_verification_required"
INVALID_CONFIGURATION = "invalid_configuration"
INVALID_CREDENTIALS = "invalid_credentials"
IMPORTED = "imported"
@dataclass
class ResponseContents:
import_status: str = ImportStatuses.IMPORTED
message: str = ""
status_code: int = 200
config: str = ""
config_schema: str = ""
def form_response(self):
return self.__dict__
class ConfigurationImport(AbstractResource):
# API Spec: Should probably be merged with IslandConfiguration
urls = ["/api/configuration/import"]
SUCCESS = False
@jwt_required
def post(self):
request_contents = json.loads(request.data)
try:
config = ConfigurationImport._get_plaintext_config_from_request(request_contents)
if request_contents["unsafeOptionsVerified"]:
ConfigurationImport.import_config(config)
return ResponseContents().form_response()
else:
return ResponseContents(
config=json.dumps(config),
config_schema=ConfigService.get_config_schema(),
import_status=ImportStatuses.UNSAFE_OPTION_VERIFICATION_REQUIRED,
).form_response()
# API Spec: HTTP status code should be 401 here
except InvalidCredentialsError:
return ResponseContents(
import_status=ImportStatuses.INVALID_CREDENTIALS,
message="Invalid credentials provided",
).form_response()
# API Spec: HTTP status code should be 400 (or something else) here
except InvalidConfigurationError:
return ResponseContents(
import_status=ImportStatuses.INVALID_CONFIGURATION,
message="Invalid configuration supplied. "
"Maybe the format is outdated or the file has been corrupted.",
).form_response()
@staticmethod
def _get_plaintext_config_from_request(request_contents: dict) -> dict:
try:
config = request_contents["config"]
if ConfigurationImport.is_config_encrypted(request_contents["config"]):
pb_encryptor = PasswordBasedStringEncryptor(request_contents["password"])
config = pb_encryptor.decrypt(config)
return json.loads(config)
except (JSONDecodeError, InvalidCiphertextError):
logger.exception(
"Exception encountered when trying to extract plaintext configuration."
)
raise InvalidConfigurationError
@staticmethod
def import_config(config_json):
if not ConfigService.update_config(config_json, should_encrypt=True):
raise InvalidConfigurationError
@staticmethod
def is_config_encrypted(config: str):
try:
if config.startswith("{"):
return False
elif is_encrypted(config):
return True
else:
raise InvalidConfigurationError
except Exception:
raise InvalidConfigurationError

View File

@ -2,10 +2,6 @@ from .i_encryptor import IEncryptor
from .key_based_encryptor import (
KeyBasedEncryptor,
)
from .password_based_string_encryptor import (
PasswordBasedStringEncryptor,
is_encrypted,
)
from .password_based_bytes_encryptor import (
PasswordBasedBytesEncryptor,
InvalidCredentialsError,

View File

@ -1,33 +0,0 @@
import base64
import logging
import pyAesCrypt
from .i_encryptor import IEncryptor
from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor
logger = logging.getLogger(__name__)
class PasswordBasedStringEncryptor(IEncryptor):
_BUFFER_SIZE = pyAesCrypt.crypto.bufferSizeDef
def __init__(self, password: str):
self.password = password
def encrypt(self, plaintext: str) -> str:
ciphertext = PasswordBasedBytesEncryptor(self.password).encrypt(plaintext.encode())
return base64.b64encode(ciphertext).decode()
def decrypt(self, ciphertext: str) -> str:
ciphertext = base64.b64decode(ciphertext)
plaintext_stream = PasswordBasedBytesEncryptor(self.password).decrypt(ciphertext)
return plaintext_stream.decode()
def is_encrypted(ciphertext: str) -> bool:
ciphertext = base64.b64decode(ciphertext)
return ciphertext.startswith(b"AES")

View File

@ -12,6 +12,7 @@ type Props = {
}
const ConfigExportModal = (props: Props) => {
// TODO: Change this endpoint to new agent-configuration endpoint
const configExportEndpoint = '/api/configuration/export';
const [pass, setPass] = useState('');

View File

@ -18,6 +18,7 @@ type Props = {
const ConfigImportModal = (props: Props) => {
// TODO: change this endpoint to the new configuration import endpoint
const configImportEndpoint = '/api/configuration/import';
const [uploadStatus, setUploadStatus] = useState(UploadStatuses.clean);

View File

@ -1,113 +0,0 @@
{
"basic": {
"exploiters": {
"exploiter_classes": [
"SmbExploiter",
"WmiExploiter",
"SSHExploiter",
"HadoopExploiter",
"MSSQLExploiter"
]
},
"credentials": {
"exploit_user_list": [
"Administrator",
"root",
"user"
],
"exploit_password_list": [
"root",
"123456",
"password",
"123456789",
"qwerty",
"111111",
"iloveyou"
]
}
},
"basic_network": {
"scope": {
"blocked_ips": [],
"local_network_scan": true,
"depth": 2,
"subnet_scan_list": []
},
"network_analysis": {
"inaccessible_subnets": []
}
},
"internal": {
"general": {
"keep_tunnel_open_time": 60
},
"network": {
"tcp_scanner": {
"HTTP_PORTS": [
80,
8080,
443,
8008,
7001,
9200
],
"tcp_target_ports": [
22,
2222,
445,
135,
3389,
80,
8080,
443,
8008,
3306,
7001,
8088
],
"tcp_scan_timeout": 3000
},
"ping_scanner": {
"ping_scan_timeout": 1000
}
},
"classes": {
"finger_classes": [
"SMBFinger",
"SSHFinger",
"HTTPFinger",
"MSSQLFinger",
"ElasticFinger"
]
},
"exploits": {
"exploit_lm_hash_list": [],
"exploit_ntlm_hash_list": [],
"exploit_ssh_keys": []
}
},
"monkey": {
"post_breach": {
"custom_pba_linux_cmd": "",
"custom_pba_windows_cmd": "",
"pba_windows_filename": "",
"pba_linux_filename": "",
"post_breach_actions": [
"communicateasbackdooruser",
"modifyshellstartupfiles",
"hiddenfiles",
"trapcommand",
"changesetuidsetgid",
"schedulejobs",
"timestomping",
"accountdiscovery"
]
},
"system_info": {
"system_info_collector_classes": [
"MimikatzCollector",
"SSHCollector"
]
}
}
}

View File

@ -1,28 +1,14 @@
# Without these imports pytests can't use fixtures,
# because they are not found
import json
import pytest
from tests.unit_tests.monkey_island.cc.mongomock_fixtures import * # noqa: F401,F403,E402
from monkey_island.cc.server_utils.encryption import unlock_datastore_encryptor
@pytest.fixture
def monkey_config(load_monkey_config):
return load_monkey_config("monkey_config_standard.json")
@pytest.fixture
def flat_monkey_config(load_monkey_config):
return load_monkey_config("flat_config.json")
@pytest.fixture
def monkey_config_json(monkey_config):
return json.dumps(monkey_config)
@pytest.fixture
def uses_encryptor(data_for_tests_dir):
secret = "m0nk3y_u53r:3cr3t_p455w0rd"

View File

@ -1,32 +0,0 @@
import pytest
from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_based_encryption import ( # noqa: E501
PASSWORD,
)
from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption_test import (
MALFORMED_CIPHER_TEXT_CORRUPTED,
)
from common.utils.exceptions import InvalidConfigurationError
from monkey_island.cc.resources.configuration_import import ConfigurationImport
from monkey_island.cc.server_utils.encryption import PasswordBasedStringEncryptor
def test_is_config_encrypted__json(monkey_config_json):
assert not ConfigurationImport.is_config_encrypted(monkey_config_json)
@pytest.mark.slow
def test_is_config_encrypted__ciphertext(monkey_config_json):
pb_encryptor = PasswordBasedStringEncryptor(PASSWORD)
encrypted_config = pb_encryptor.encrypt(monkey_config_json)
assert ConfigurationImport.is_config_encrypted(encrypted_config)
def test_is_config_encrypted__corrupt_ciphertext():
with pytest.raises(InvalidConfigurationError):
assert ConfigurationImport.is_config_encrypted(MALFORMED_CIPHER_TEXT_CORRUPTED)
def test_is_config_encrypted__unknown_format():
with pytest.raises(InvalidConfigurationError):
assert ConfigurationImport.is_config_encrypted("ABC")

View File

@ -1,47 +0,0 @@
import pytest
from tests.unit_tests.monkey_island.cc.services.utils.ciphertexts_for_encryption_test import (
MALFORMED_CIPHER_TEXT_CORRUPTED,
VALID_CIPHER_TEXT,
)
from monkey_island.cc.server_utils.encryption import (
InvalidCiphertextError,
InvalidCredentialsError,
PasswordBasedStringEncryptor,
)
# Mark all tests in this module as slow
pytestmark = pytest.mark.slow
PASSWORD = "hello123"
INCORRECT_PASSWORD = "goodbye321"
def test_encrypt_decrypt_string(monkey_config_json):
pb_encryptor = PasswordBasedStringEncryptor(PASSWORD)
encrypted_config = pb_encryptor.encrypt(monkey_config_json)
assert pb_encryptor.decrypt(encrypted_config) == monkey_config_json
def test_decrypt_string__wrong_password(monkey_config_json):
pb_encryptor = PasswordBasedStringEncryptor(INCORRECT_PASSWORD)
with pytest.raises(InvalidCredentialsError):
pb_encryptor.decrypt(VALID_CIPHER_TEXT)
def test_decrypt_string__malformed_corrupted():
pb_encryptor = PasswordBasedStringEncryptor(PASSWORD)
with pytest.raises(ValueError):
pb_encryptor.decrypt(MALFORMED_CIPHER_TEXT_CORRUPTED)
def test_decrypt_string__no_password(monkey_config_json):
pb_encryptor = PasswordBasedStringEncryptor("")
with pytest.raises(InvalidCredentialsError):
pb_encryptor.decrypt(VALID_CIPHER_TEXT)
def test_decrypt_string__invalid_cyphertext(monkey_config_json):
pb_encryptor = PasswordBasedStringEncryptor("")
with pytest.raises(InvalidCiphertextError):
pb_encryptor.decrypt("")