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
- Stale code in the Windows system info collector that collected installed
packages and WMI info. #1389
- Remove insecure access feature in the Monkey Island. #1418
### Fixed
- 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
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,
edit the `server_config.json` file manually
To reset the credentials, edit the `server_config.json` file manually
(located in the [data directory](/reference/data_directory)).
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
{
...
@ -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
{
...

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.
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
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_import import ConfigurationImport
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.monkey_exploitation import MonkeyExploitation
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(Registration, "/api/registration")
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(Bootloader, "/api/bootloader/<string:os>")
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):
self._testing = value
def save_config(self):
self._config.save_to_file()
def get_config(self) -> EnvironmentConfig:
return self._config

View File

@ -1,16 +1,13 @@
import logging
import monkey_island.cc.resources.auth.user_store as user_store
from monkey_island.cc.environment import EnvironmentConfig, aws, password, standard
from monkey_island.cc.environment import EnvironmentConfig, aws, password
logger = logging.getLogger(__name__)
AWS = "aws"
STANDARD = "standard"
PASSWORD = "password"
ENV_DICT = {
STANDARD: standard.StandardEnvironment,
AWS: aws.AwsEnvironment,
PASSWORD: password.PasswordEnvironment,
}
@ -24,16 +21,6 @@ def set_env(env_type: str, env_config: EnvironmentConfig):
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):
try:
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,
completedSteps: completedSteps,
islandMode: undefined,
noAuthLoginAttempted: undefined
};
this.interval = undefined;
this.setMode();
@ -77,45 +76,44 @@ class AppComponent extends AuthComponent {
if (this.state.isLoggedIn === false) {
return
}
this.auth.loggedIn()
.then(res => {
if (this.state.isLoggedIn !== res) {
this.setState({
isLoggedIn: res
});
}
if (!res) {
this.auth.needsRegistration()
.then(result => {
this.setState({
needsRegistration: result
});
})
}
let res = this.auth.loggedIn();
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();
});
}
)
}
if (this.state.isLoggedIn !== res) {
this.setState({
isLoggedIn: res
});
}
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 = () => {

View File

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

View File

@ -7,8 +7,6 @@ import ParticleBackground from '../ui-components/ParticleBackground';
class RegisterPageComponent extends React.Component {
NO_AUTH_API_ENDPOINT = '/api/environment';
register = (event) => {
event.preventDefault();
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) => {
this.username = evt.target.value;
};
@ -96,13 +70,6 @@ class RegisterPageComponent extends React.Component {
<Button className={'monkey-submit-button'} type={'submit'} >
Let's go!
</Button>
<Row>
<Col>
<a href='#' onClick={this.setNoAuth} className={'no-auth-link'}>
I want anyone to access the island
</a>
</Col>
</Row>
<Row>
<Col>
{

View File

@ -1,4 +1,3 @@
import StandardConfig from './StandardConfig';
import AwsConfig from './AwsConfig';
import PasswordConfig from './PasswordConfig';
@ -6,7 +5,6 @@ import SERVER_CONFIG_JSON from '../../../server_config.json';
const CONFIG_DICT =
{
'standard': StandardConfig,
'aws': AwsConfig,
'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';
export default class AuthService {
NO_AUTH_CREDS = '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()';
SECONDS_BEFORE_JWT_EXPIRES = 20;
AUTHENTICATION_API_ENDPOINT = '/api/auth';
REGISTRATION_API_ENDPOINT = '/api/registration';
@ -16,7 +14,7 @@ export default class AuthService {
};
jwtHeader = () => {
if (this._loggedIn()) {
if (this.loggedIn()) {
return 'Bearer ' + this._getToken();
}
};
@ -68,7 +66,7 @@ export default class AuthService {
'Content-Type': 'application/json'
};
if (this._loggedIn()) {
if (this.loggedIn()) {
headers['Authorization'] = 'Bearer ' + this._getToken();
}
@ -101,19 +99,7 @@ export default class AuthService {
})
};
async 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() {
loggedIn() {
const token = this._getToken();
return ((token !== null) && !this._isTokenExpired(token));
}

View File

@ -35,15 +35,3 @@
margin-bottom: 20px;
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" : {
"server_config": "password",
"deployment": "develop",

View File

@ -16,8 +16,3 @@ def no_credentials(server_configs_dir):
@pytest.fixture(scope="module")
def partial_credentials(server_configs_dir):
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
NO_CREDENTIALS = None
PARTIAL_CREDENTIALS = None
STANDARD_WITH_CREDENTIALS = None
STANDARD_ENV = None
EMPTY_USER_CREDENTIALS = UserCreds("", "")
FULL_USER_CREDENTIALS = UserCreds(username="test", password_hash="1231234")
@ -31,16 +29,10 @@ def configure_resources(server_configs_dir):
global WITH_CREDENTIALS
global NO_CREDENTIALS
global PARTIAL_CREDENTIALS
global STANDARD_WITH_CREDENTIALS
global STANDARD_ENV
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")
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():
@ -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, 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):
env = TestEnvironment.EnvironmentCredentialsRequired()
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, 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):
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, WITH_CREDENTIALS, True)
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(
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"
def test_save_to_file(config_file, standard_with_credentials):
shutil.copyfile(standard_with_credentials, config_file)
def test_save_to_file(config_file, with_credentials):
shutil.copyfile(with_credentials, config_file)
environment_config = EnvironmentConfig(config_file)
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"]
def test_save_to_file_preserve_log_level(config_file, standard_with_credentials):
shutil.copyfile(standard_with_credentials, config_file)
def test_save_to_file_preserve_log_level(config_file, with_credentials):
shutil.copyfile(with_credentials, config_file)
environment_config = EnvironmentConfig(config_file)
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"
def test_add_user(config_file, standard_with_credentials):
def test_add_user(config_file, with_credentials):
new_user = "new_user"
new_password_hash = "fedcba"
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.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
def test_get_users(standard_with_credentials):
environment_config = EnvironmentConfig(standard_with_credentials)
def test_get_users(with_credentials):
environment_config = EnvironmentConfig(with_credentials)
users = environment_config.get_users()
assert len(users) == 1