Merge pull request #1439 from guardicore/remove-standard-environment

Remove standard environment (insecure access feature)
This commit is contained in:
Shreya Malviya 2021-09-06 13:18:27 +05:30 committed by GitHub
commit 6740812f4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 57 additions and 229 deletions

View File

@ -18,6 +18,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
- "Back door user" post-breach action. #1410 - "Back door user" post-breach action. #1410
- Stale code in the Windows system info collector that collected installed - Stale code in the Windows system info collector that collected installed
packages and WMI info. #1389 packages and WMI info. #1389
- Remove insecure access feature in the Monkey Island. #1418
### Fixed ### Fixed
- Misaligned buttons and input fields on exploiter and network configuration - Misaligned buttons and input fields on exploiter and network configuration

View File

@ -52,12 +52,11 @@ Monkey in the newly created folder.
## Reset/enable the Monkey Island password ## Reset/enable the Monkey Island password
When you first access the Monkey Island server, you'll be prompted to create an account. When you first access the Monkey Island server, you'll be prompted to create an account.
To reset the credentials or enable/disable the authentication, To reset the credentials, edit the `server_config.json` file manually
edit the `server_config.json` file manually
(located in the [data directory](/reference/data_directory)). (located in the [data directory](/reference/data_directory)).
In order to reset the credentials, the following edits need to be made: In order to reset the credentials, the following edits need to be made:
1. Delete the `user` field if one exists. It will look like this: 1. Delete the `user` field. It will look like this:
```json ```json
{ {
... ...
@ -65,7 +64,7 @@ In order to reset the credentials, the following edits need to be made:
... ...
} }
``` ```
1. Delete the `password_hash` field if one exists. It will look like this: 1. Delete the `password_hash` field. It will look like this:
```json ```json
{ {
... ...

View File

@ -11,8 +11,6 @@ tags: ["usage", "password"]
The first time you launch Monkey Island (the Infection Monkey C&C server), you'll be prompted to create an account and secure your island. After account creation, the server will only be accessible via the credentials you entered. The first time you launch Monkey Island (the Infection Monkey C&C server), you'll be prompted to create an account and secure your island. After account creation, the server will only be accessible via the credentials you entered.
If you want an island to be accessible without credentials, press *I want anyone to access the island*. Please note that this option is insecure, and you should only use it in development environments.
## Resetting your account credentials ## Resetting your account credentials
This procedure is documented in [the FAQ]({{< ref "/faq/#how-do-i-reset-the-monkey-island-password" >}}). This procedure is documented in [the FAQ]({{< ref "/faq/#how-do-i-reset-the-monkey-island-password" >}}).

View File

@ -23,7 +23,6 @@ from monkey_island.cc.resources.client_run import ClientRun
from monkey_island.cc.resources.configuration_export import ConfigurationExport from monkey_island.cc.resources.configuration_export import ConfigurationExport
from monkey_island.cc.resources.configuration_import import ConfigurationImport from monkey_island.cc.resources.configuration_import import ConfigurationImport
from monkey_island.cc.resources.edge import Edge from monkey_island.cc.resources.edge import Edge
from monkey_island.cc.resources.environment import Environment
from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation
from monkey_island.cc.resources.exploitations.monkey_exploitation import MonkeyExploitation from monkey_island.cc.resources.exploitations.monkey_exploitation import MonkeyExploitation
from monkey_island.cc.resources.island_configuration import IslandConfiguration from monkey_island.cc.resources.island_configuration import IslandConfiguration
@ -125,7 +124,6 @@ def init_api_resources(api):
api.add_resource(Root, "/api") api.add_resource(Root, "/api")
api.add_resource(Registration, "/api/registration") api.add_resource(Registration, "/api/registration")
api.add_resource(Authenticate, "/api/auth") api.add_resource(Authenticate, "/api/auth")
api.add_resource(Environment, "/api/environment")
api.add_resource(Monkey, "/api/monkey", "/api/monkey/", "/api/monkey/<string:guid>") api.add_resource(Monkey, "/api/monkey", "/api/monkey/", "/api/monkey/<string:guid>")
api.add_resource(Bootloader, "/api/bootloader/<string:os>") api.add_resource(Bootloader, "/api/bootloader/<string:os>")
api.add_resource(LocalRun, "/api/local-monkey", "/api/local-monkey/") api.add_resource(LocalRun, "/api/local-monkey", "/api/local-monkey/")

View File

@ -77,9 +77,6 @@ class Environment(object, metaclass=ABCMeta):
def testing(self, value): def testing(self, value):
self._testing = value self._testing = value
def save_config(self):
self._config.save_to_file()
def get_config(self) -> EnvironmentConfig: def get_config(self) -> EnvironmentConfig:
return self._config return self._config

View File

@ -1,16 +1,13 @@
import logging import logging
import monkey_island.cc.resources.auth.user_store as user_store from monkey_island.cc.environment import EnvironmentConfig, aws, password
from monkey_island.cc.environment import EnvironmentConfig, aws, password, standard
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
AWS = "aws" AWS = "aws"
STANDARD = "standard"
PASSWORD = "password" PASSWORD = "password"
ENV_DICT = { ENV_DICT = {
STANDARD: standard.StandardEnvironment,
AWS: aws.AwsEnvironment, AWS: aws.AwsEnvironment,
PASSWORD: password.PasswordEnvironment, PASSWORD: password.PasswordEnvironment,
} }
@ -24,16 +21,6 @@ def set_env(env_type: str, env_config: EnvironmentConfig):
env = ENV_DICT[env_type](env_config) env = ENV_DICT[env_type](env_config)
def set_to_standard():
global env
if env:
env_config = env.get_config()
env_config.server_config = "standard"
set_env("standard", env_config)
env.save_config()
user_store.UserStore.set_users(env.get_auth_users())
def initialize_from_file(file_path): def initialize_from_file(file_path):
try: try:
config = EnvironmentConfig(file_path) config = EnvironmentConfig(file_path)

View File

@ -1,12 +0,0 @@
from monkey_island.cc.environment import Environment
from monkey_island.cc.resources.auth.auth_user import User
class StandardEnvironment(Environment):
_credentials_required = False
NO_AUTH_USER = "1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()"
NO_AUTH_SECRET = "$2b$12$frH7uEwV3jkDNGgReW6j2udw8hy/Yw1SWAqytrcBYK48kn1V5lQIa"
def get_auth_users(self):
return [User(1, StandardEnvironment.NO_AUTH_USER, StandardEnvironment.NO_AUTH_SECRET)]

View File

@ -1,22 +0,0 @@
import json
import logging
import flask_restful
from flask import request
import monkey_island.cc.environment.environment_singleton as env_singleton
logger = logging.getLogger(__name__)
class Environment(flask_restful.Resource):
def patch(self):
env_data = json.loads(request.data)
if env_data["server_config"] == "standard":
if env_singleton.env.needs_registration():
env_singleton.set_to_standard()
logger.warning(
"No user registered, Island on standard mode - no credentials required to "
"access."
)
return {}

View File

@ -67,7 +67,6 @@ class AppComponent extends AuthComponent {
loading: true, loading: true,
completedSteps: completedSteps, completedSteps: completedSteps,
islandMode: undefined, islandMode: undefined,
noAuthLoginAttempted: undefined
}; };
this.interval = undefined; this.interval = undefined;
this.setMode(); this.setMode();
@ -77,45 +76,44 @@ class AppComponent extends AuthComponent {
if (this.state.isLoggedIn === false) { if (this.state.isLoggedIn === false) {
return return
} }
this.auth.loggedIn()
.then(res => {
if (this.state.isLoggedIn !== res) {
this.setState({
isLoggedIn: res
});
}
if (!res) { let res = this.auth.loggedIn();
this.auth.needsRegistration()
.then(result => {
this.setState({
needsRegistration: result
});
})
}
if (res) { if (this.state.isLoggedIn !== res) {
this.setMode() this.setState({
.then(() => { isLoggedIn: res
if (this.state.islandMode === null) {
return
}
this.authFetch('/api')
.then(res => res.json())
.then(res => {
let completedSteps = CompletedSteps.buildFromResponse(res.completed_steps);
// This check is used to prevent unnecessary re-rendering
if (_.isEqual(this.state.completedSteps, completedSteps)) {
return;
}
this.setState({completedSteps: completedSteps});
this.showInfectionDoneNotification();
});
}
)
}
}); });
}
if (!res) {
this.auth.needsRegistration()
.then(result => {
this.setState({
needsRegistration: result
});
})
}
if (res) {
this.setMode()
.then(() => {
if (this.state.islandMode === null) {
return
}
this.authFetch('/api')
.then(res => res.json())
.then(res => {
let completedSteps = CompletedSteps.buildFromResponse(res.completed_steps);
// This check is used to prevent unnecessary re-rendering
if (_.isEqual(this.state.completedSteps, completedSteps)) {
return;
}
this.setState({completedSteps: completedSteps});
this.showInfectionDoneNotification();
});
}
)
}
}; };
setMode = () => { setMode = () => {

View File

@ -48,12 +48,11 @@ class LoginPageComponent extends React.Component {
this.redirectToRegistration() this.redirectToRegistration()
} }
}) })
this.auth.loggedIn()
.then(res => { if (this.auth.loggedIn()) {
if (res) { this.redirectToHome();
this.redirectToHome(); }
}
});
} }
render() { render() {

View File

@ -7,8 +7,6 @@ import ParticleBackground from '../ui-components/ParticleBackground';
class RegisterPageComponent extends React.Component { class RegisterPageComponent extends React.Component {
NO_AUTH_API_ENDPOINT = '/api/environment';
register = (event) => { register = (event) => {
event.preventDefault(); event.preventDefault();
this.auth.register(this.username, this.password).then(res => { this.auth.register(this.username, this.password).then(res => {
@ -24,30 +22,6 @@ class RegisterPageComponent extends React.Component {
}); });
}; };
setNoAuth = () => {
let options = {};
options['headers'] = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
options['method'] = 'PATCH';
options['body'] = JSON.stringify({'server_config': 'standard'});
return fetch(this.NO_AUTH_API_ENDPOINT, options)
.then(res => {
if (res.status === 200) {
this.auth.attemptNoAuthLogin().then(() => {
this.redirectToHome();
});
} else {
this.setState({
failed: true,
error: res['error']
});
}
})
}
updateUsername = (evt) => { updateUsername = (evt) => {
this.username = evt.target.value; this.username = evt.target.value;
}; };
@ -96,13 +70,6 @@ class RegisterPageComponent extends React.Component {
<Button className={'monkey-submit-button'} type={'submit'} > <Button className={'monkey-submit-button'} type={'submit'} >
Let's go! Let's go!
</Button> </Button>
<Row>
<Col>
<a href='#' onClick={this.setNoAuth} className={'no-auth-link'}>
I want anyone to access the island
</a>
</Col>
</Row>
<Row> <Row>
<Col> <Col>
{ {

View File

@ -1,4 +1,3 @@
import StandardConfig from './StandardConfig';
import AwsConfig from './AwsConfig'; import AwsConfig from './AwsConfig';
import PasswordConfig from './PasswordConfig'; import PasswordConfig from './PasswordConfig';
@ -6,7 +5,6 @@ import SERVER_CONFIG_JSON from '../../../server_config.json';
const CONFIG_DICT = const CONFIG_DICT =
{ {
'standard': StandardConfig,
'aws': AwsConfig, 'aws': AwsConfig,
'password': PasswordConfig 'password': PasswordConfig
}; };

View File

@ -1,10 +0,0 @@
import BaseConfig from './BaseConfig';
class StandardConfig extends BaseConfig {
isAuthEnabled() {
return false;
}
}
export default StandardConfig;

View File

@ -1,8 +1,6 @@
import decode from 'jwt-decode'; import decode from 'jwt-decode';
export default class AuthService { export default class AuthService {
NO_AUTH_CREDS = '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()';
SECONDS_BEFORE_JWT_EXPIRES = 20; SECONDS_BEFORE_JWT_EXPIRES = 20;
AUTHENTICATION_API_ENDPOINT = '/api/auth'; AUTHENTICATION_API_ENDPOINT = '/api/auth';
REGISTRATION_API_ENDPOINT = '/api/registration'; REGISTRATION_API_ENDPOINT = '/api/registration';
@ -16,7 +14,7 @@ export default class AuthService {
}; };
jwtHeader = () => { jwtHeader = () => {
if (this._loggedIn()) { if (this.loggedIn()) {
return 'Bearer ' + this._getToken(); return 'Bearer ' + this._getToken();
} }
}; };
@ -68,7 +66,7 @@ export default class AuthService {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}; };
if (this._loggedIn()) { if (this.loggedIn()) {
headers['Authorization'] = 'Bearer ' + this._getToken(); headers['Authorization'] = 'Bearer ' + this._getToken();
} }
@ -101,19 +99,7 @@ export default class AuthService {
}) })
}; };
async loggedIn() { loggedIn() {
let token = this._getToken();
if ((token === null) || (this._isTokenExpired(token))) {
await this.attemptNoAuthLogin();
}
return this._loggedIn();
}
attemptNoAuthLogin() {
return this._login(this.NO_AUTH_CREDS, this.NO_AUTH_CREDS);
}
_loggedIn() {
const token = this._getToken(); const token = this._getToken();
return ((token !== null) && !this._isTokenExpired(token)); return ((token !== null) && !this._isTokenExpired(token));
} }

View File

@ -35,15 +35,3 @@
margin-bottom: 20px; margin-bottom: 20px;
text-align: center; text-align: center;
} }
.no-auth-link {
margin-top: 10px;
float: right;
color: $monkey-black;
text-decoration: underline;
}
.no-auth-link:hover {
float: right;
color: $monkey-yellow;
}

View File

@ -1,9 +0,0 @@
{
"environment" : {
"server_config": "standard",
"deployment": "develop"
},
"mongodb": {
"start_mongodb": true
}
}

View File

@ -1,12 +0,0 @@
{
"log_level": "NOTICE",
"environment" : {
"server_config": "standard",
"deployment": "develop",
"user": "test",
"password_hash": "abcdef"
},
"mongodb": {
"start_mongodb": true
}
}

View File

@ -1,4 +1,5 @@
{ {
"log_level": "NOTICE",
"environment" : { "environment" : {
"server_config": "password", "server_config": "password",
"deployment": "develop", "deployment": "develop",

View File

@ -16,8 +16,3 @@ def no_credentials(server_configs_dir):
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def partial_credentials(server_configs_dir): def partial_credentials(server_configs_dir):
return os.path.join(server_configs_dir, "server_config_partial_credentials.json") return os.path.join(server_configs_dir, "server_config_partial_credentials.json")
@pytest.fixture(scope="module")
def standard_with_credentials(server_configs_dir):
return os.path.join(server_configs_dir, "server_config_standard_with_credentials.json")

View File

@ -17,8 +17,6 @@ from monkey_island.cc.environment import Environment, EnvironmentConfig, UserCre
WITH_CREDENTIALS = None WITH_CREDENTIALS = None
NO_CREDENTIALS = None NO_CREDENTIALS = None
PARTIAL_CREDENTIALS = None PARTIAL_CREDENTIALS = None
STANDARD_WITH_CREDENTIALS = None
STANDARD_ENV = None
EMPTY_USER_CREDENTIALS = UserCreds("", "") EMPTY_USER_CREDENTIALS = UserCreds("", "")
FULL_USER_CREDENTIALS = UserCreds(username="test", password_hash="1231234") FULL_USER_CREDENTIALS = UserCreds(username="test", password_hash="1231234")
@ -31,16 +29,10 @@ def configure_resources(server_configs_dir):
global WITH_CREDENTIALS global WITH_CREDENTIALS
global NO_CREDENTIALS global NO_CREDENTIALS
global PARTIAL_CREDENTIALS global PARTIAL_CREDENTIALS
global STANDARD_WITH_CREDENTIALS
global STANDARD_ENV
WITH_CREDENTIALS = os.path.join(server_configs_dir, "server_config_with_credentials.json") WITH_CREDENTIALS = os.path.join(server_configs_dir, "server_config_with_credentials.json")
NO_CREDENTIALS = os.path.join(server_configs_dir, "server_config_no_credentials.json") NO_CREDENTIALS = os.path.join(server_configs_dir, "server_config_no_credentials.json")
PARTIAL_CREDENTIALS = os.path.join(server_configs_dir, "server_config_partial_credentials.json") PARTIAL_CREDENTIALS = os.path.join(server_configs_dir, "server_config_partial_credentials.json")
STANDARD_WITH_CREDENTIALS = os.path.join(
server_configs_dir, "server_config_standard_with_credentials.json"
)
STANDARD_ENV = os.path.join(server_configs_dir, "server_config_standard_env.json")
def get_tmp_file(): def get_tmp_file():
@ -123,29 +115,18 @@ class TestEnvironment(TestCase):
self._test_bool_env_method("needs_registration", env, NO_CREDENTIALS, True) self._test_bool_env_method("needs_registration", env, NO_CREDENTIALS, True)
self._test_bool_env_method("needs_registration", env, PARTIAL_CREDENTIALS, True) self._test_bool_env_method("needs_registration", env, PARTIAL_CREDENTIALS, True)
env = TestEnvironment.EnvironmentCredentialsNotRequired()
self._test_bool_env_method("needs_registration", env, STANDARD_ENV, False)
self._test_bool_env_method("needs_registration", env, STANDARD_WITH_CREDENTIALS, False)
def test_is_registered(self): def test_is_registered(self):
env = TestEnvironment.EnvironmentCredentialsRequired() env = TestEnvironment.EnvironmentCredentialsRequired()
self._test_bool_env_method("_is_registered", env, WITH_CREDENTIALS, True) self._test_bool_env_method("_is_registered", env, WITH_CREDENTIALS, True)
self._test_bool_env_method("_is_registered", env, NO_CREDENTIALS, False) self._test_bool_env_method("_is_registered", env, NO_CREDENTIALS, False)
self._test_bool_env_method("_is_registered", env, PARTIAL_CREDENTIALS, False) self._test_bool_env_method("_is_registered", env, PARTIAL_CREDENTIALS, False)
env = TestEnvironment.EnvironmentCredentialsNotRequired()
self._test_bool_env_method("_is_registered", env, STANDARD_ENV, False)
self._test_bool_env_method("_is_registered", env, STANDARD_WITH_CREDENTIALS, False)
def test_is_credentials_set_up(self): def test_is_credentials_set_up(self):
env = TestEnvironment.EnvironmentCredentialsRequired() env = TestEnvironment.EnvironmentCredentialsRequired()
self._test_bool_env_method("_is_credentials_set_up", env, NO_CREDENTIALS, False) self._test_bool_env_method("_is_credentials_set_up", env, NO_CREDENTIALS, False)
self._test_bool_env_method("_is_credentials_set_up", env, WITH_CREDENTIALS, True) self._test_bool_env_method("_is_credentials_set_up", env, WITH_CREDENTIALS, True)
self._test_bool_env_method("_is_credentials_set_up", env, PARTIAL_CREDENTIALS, False) self._test_bool_env_method("_is_credentials_set_up", env, PARTIAL_CREDENTIALS, False)
env = TestEnvironment.EnvironmentCredentialsNotRequired()
self._test_bool_env_method("_is_credentials_set_up", env, STANDARD_ENV, False)
def _test_bool_env_method( def _test_bool_env_method(
self, method_name: str, env: Environment, config: Dict, expected_result: bool self, method_name: str, env: Environment, config: Dict, expected_result: bool
): ):

View File

@ -40,8 +40,8 @@ def test_get_with_partial_credentials(partial_credentials):
assert config_dict["user"] == "test" assert config_dict["user"] == "test"
def test_save_to_file(config_file, standard_with_credentials): def test_save_to_file(config_file, with_credentials):
shutil.copyfile(standard_with_credentials, config_file) shutil.copyfile(with_credentials, config_file)
environment_config = EnvironmentConfig(config_file) environment_config = EnvironmentConfig(config_file)
environment_config.aws = "test_aws" environment_config.aws = "test_aws"
@ -53,8 +53,8 @@ def test_save_to_file(config_file, standard_with_credentials):
assert environment_config.to_dict() == from_file["environment"] assert environment_config.to_dict() == from_file["environment"]
def test_save_to_file_preserve_log_level(config_file, standard_with_credentials): def test_save_to_file_preserve_log_level(config_file, with_credentials):
shutil.copyfile(standard_with_credentials, config_file) shutil.copyfile(with_credentials, config_file)
environment_config = EnvironmentConfig(config_file) environment_config = EnvironmentConfig(config_file)
environment_config.aws = "test_aws" environment_config.aws = "test_aws"
@ -67,12 +67,12 @@ def test_save_to_file_preserve_log_level(config_file, standard_with_credentials)
assert from_file["log_level"] == "NOTICE" assert from_file["log_level"] == "NOTICE"
def test_add_user(config_file, standard_with_credentials): def test_add_user(config_file, with_credentials):
new_user = "new_user" new_user = "new_user"
new_password_hash = "fedcba" new_password_hash = "fedcba"
new_user_creds = UserCreds(new_user, new_password_hash) new_user_creds = UserCreds(new_user, new_password_hash)
shutil.copyfile(standard_with_credentials, config_file) shutil.copyfile(with_credentials, config_file)
environment_config = EnvironmentConfig(config_file) environment_config = EnvironmentConfig(config_file)
environment_config.add_user(new_user_creds) environment_config.add_user(new_user_creds)
@ -85,8 +85,8 @@ def test_add_user(config_file, standard_with_credentials):
assert from_file["environment"]["password_hash"] == new_password_hash assert from_file["environment"]["password_hash"] == new_password_hash
def test_get_users(standard_with_credentials): def test_get_users(with_credentials):
environment_config = EnvironmentConfig(standard_with_credentials) environment_config = EnvironmentConfig(with_credentials)
users = environment_config.get_users() users = environment_config.get_users()
assert len(users) == 1 assert len(users) == 1