forked from p34709852/monkey
Refactored configuration import and added a check to decide if configuration is encrypted or not. This solved a bug where invalid json was treated as credential error.
This commit is contained in:
parent
b30de00305
commit
a36fc81755
|
@ -54,13 +54,5 @@ class DomainControllerNameFetchError(FailedExploitationError):
|
|||
""" Raise on failed attempt to extract domain controller's name """
|
||||
|
||||
|
||||
class InvalidCredentialsError(Exception):
|
||||
""" Raise when credentials supplied are invalid """
|
||||
|
||||
|
||||
class NoCredentialsError(Exception):
|
||||
""" Raise when no credentials have been supplied """
|
||||
|
||||
|
||||
class InvalidConfigurationError(Exception):
|
||||
""" Raise when configuration is invalid """
|
||||
|
|
|
@ -6,14 +6,14 @@ from json.decoder import JSONDecodeError
|
|||
import flask_restful
|
||||
from flask import request
|
||||
|
||||
from common.utils.exceptions import (
|
||||
InvalidConfigurationError,
|
||||
InvalidCredentialsError,
|
||||
NoCredentialsError,
|
||||
)
|
||||
from common.utils.exceptions import InvalidConfigurationError
|
||||
from monkey_island.cc.resources.auth.auth import jwt_required
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.services.utils.config_encryption import decrypt_config
|
||||
from monkey_island.cc.services.utils.config_encryption import (
|
||||
InvalidCredentialsError,
|
||||
decrypt_config,
|
||||
is_encrypted,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -21,8 +21,7 @@ logger = logging.getLogger(__name__)
|
|||
class ImportStatuses:
|
||||
UNSAFE_OPTION_VERIFICATION_REQUIRED = "unsafe_options_verification_required"
|
||||
INVALID_CONFIGURATION = "invalid_configuration"
|
||||
PASSWORD_REQUIRED = "password_required"
|
||||
WRONG_PASSWORD = "wrong_password"
|
||||
INVALID_CREDENTIALS = "invalid_credentials"
|
||||
IMPORTED = "imported"
|
||||
|
||||
|
||||
|
@ -57,7 +56,8 @@ class ConfigurationImport(flask_restful.Resource):
|
|||
).form_response()
|
||||
except InvalidCredentialsError:
|
||||
return ResponseContents(
|
||||
import_status=ImportStatuses.WRONG_PASSWORD, message="Wrong password supplied"
|
||||
import_status=ImportStatuses.INVALID_CREDENTIALS,
|
||||
message="Invalid credentials provided",
|
||||
).form_response()
|
||||
except InvalidConfigurationError:
|
||||
return ResponseContents(
|
||||
|
@ -65,20 +65,30 @@ class ConfigurationImport(flask_restful.Resource):
|
|||
message="Invalid configuration supplied. "
|
||||
"Maybe the format is outdated or the file has been corrupted.",
|
||||
).form_response()
|
||||
except NoCredentialsError:
|
||||
return ResponseContents(
|
||||
import_status=ImportStatuses.PASSWORD_REQUIRED,
|
||||
).form_response()
|
||||
|
||||
@staticmethod
|
||||
def _get_plaintext_config_from_request(request_contents: dict) -> dict:
|
||||
try:
|
||||
config = json.loads(request_contents["config"])
|
||||
except JSONDecodeError:
|
||||
config = decrypt_config(request_contents["config"], request_contents["password"])
|
||||
return config
|
||||
if ConfigurationImport.is_config_encrypted(request_contents["config"]):
|
||||
return decrypt_config(request_contents["config"], request_contents["password"])
|
||||
else:
|
||||
try:
|
||||
return json.loads(request_contents["config"])
|
||||
except JSONDecodeError:
|
||||
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
|
||||
|
|
|
@ -6,7 +6,7 @@ from typing import Dict
|
|||
|
||||
import pyAesCrypt
|
||||
|
||||
from common.utils.exceptions import InvalidCredentialsError, NoCredentialsError
|
||||
from common.utils.exceptions import InvalidConfigurationError
|
||||
|
||||
BUFFER_SIZE = pyAesCrypt.crypto.bufferSizeDef
|
||||
|
||||
|
@ -28,9 +28,6 @@ def encrypt_config(config: Dict, password: str) -> str:
|
|||
|
||||
|
||||
def decrypt_config(cyphertext: str, password: str) -> Dict:
|
||||
if not password:
|
||||
raise NoCredentialsError
|
||||
|
||||
cyphertext = base64.b64decode(cyphertext)
|
||||
ciphertext_config_stream = io.BytesIO(cyphertext)
|
||||
dec_plaintext_config_stream = io.BytesIO()
|
||||
|
@ -51,6 +48,15 @@ def decrypt_config(cyphertext: str, password: str) -> Dict:
|
|||
raise InvalidCredentialsError
|
||||
else:
|
||||
logger.info("The provided configuration file is corrupt.")
|
||||
raise ex
|
||||
raise InvalidConfigurationError
|
||||
plaintext_config = json.loads(dec_plaintext_config_stream.getvalue().decode("utf-8"))
|
||||
return plaintext_config
|
||||
|
||||
|
||||
def is_encrypted(ciphertext: str) -> bool:
|
||||
ciphertext = base64.b64decode(ciphertext)
|
||||
return ciphertext.startswith(b"AES")
|
||||
|
||||
|
||||
class InvalidCredentialsError(Exception):
|
||||
""" Raise when credentials supplied are invalid """
|
||||
|
|
|
@ -40,8 +40,8 @@ const ConfigImportModal = (props: Props) => {
|
|||
}, [configContents])
|
||||
|
||||
|
||||
function sendConfigToServer(): Promise<string> {
|
||||
return authComponent.authFetch(configImportEndpoint,
|
||||
function sendConfigToServer() {
|
||||
authComponent.authFetch(configImportEndpoint,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
|
@ -53,27 +53,30 @@ const ConfigImportModal = (props: Props) => {
|
|||
}
|
||||
).then(res => res.json())
|
||||
.then(res => {
|
||||
if (res['import_status'] === 'password_required') {
|
||||
if (res['import_status'] === 'invalid_credentials') {
|
||||
setUploadStatus(UploadStatuses.success);
|
||||
setShowPassword(true);
|
||||
} else if (res['import_status'] === 'wrong_password') {
|
||||
setErrorMessage(res['message']);
|
||||
if (showPassword){
|
||||
setErrorMessage(res['message']);
|
||||
} else {
|
||||
setShowPassword(true);
|
||||
setErrorMessage('');
|
||||
}
|
||||
} else if (res['import_status'] === 'invalid_configuration') {
|
||||
setUploadStatus(UploadStatuses.error);
|
||||
setErrorMessage(res['message']);
|
||||
} else if (res['import_status'] === 'unsafe_options_verification_required') {
|
||||
setUploadStatus(UploadStatuses.success);
|
||||
setErrorMessage('');
|
||||
if (isUnsafeOptionSelected(res['config_schema'], JSON.parse(res['config']))) {
|
||||
setShowUnsafeOptionsConfirmation(true);
|
||||
setCandidateConfig(res['config']);
|
||||
} else {
|
||||
setUnsafeOptionsVerified(true);
|
||||
setConfigContents(res['config']);
|
||||
}
|
||||
} else if (res['import_status'] === 'imported'){
|
||||
resetState();
|
||||
props.onClose(true);
|
||||
}
|
||||
return res['import_status'];
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -93,6 +96,7 @@ const ConfigImportModal = (props: Props) => {
|
|||
}
|
||||
|
||||
function uploadFile(event) {
|
||||
setShowPassword(false);
|
||||
let reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
setConfigContents(event.target.result);
|
||||
|
|
Loading…
Reference in New Issue