From 96cc4edba93d17258b4da39348e831328b64bbc4 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 2 Oct 2018 14:45:42 +0300 Subject: [PATCH 1/7] Domain name translation fully implemented and displayed in map and report --- monkey/common/network/network_range.py | 29 ++++++++++++++++++- monkey/infection_monkey/model/host.py | 3 +- .../network/network_scanner.py | 6 +++- .../monkey_island/cc/resources/telemetry.py | 6 ++-- monkey/monkey_island/cc/services/node.py | 14 ++++++--- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/monkey/common/network/network_range.py b/monkey/common/network/network_range.py index a2142ce0e..3adace9bc 100644 --- a/monkey/common/network/network_range.py +++ b/monkey/common/network/network_range.py @@ -5,9 +5,13 @@ from abc import ABCMeta, abstractmethod import ipaddress from six import text_type +import logging +import re __author__ = 'itamar' +LOG = logging.getLogger(__name__) + class NetworkRange(object): __metaclass__ = ABCMeta @@ -111,7 +115,7 @@ class IpRange(NetworkRange): class SingleIpRange(NetworkRange): def __init__(self, ip_address, shuffle=True): super(SingleIpRange, self).__init__(shuffle=shuffle) - self._ip_address = ip_address + self._ip_address, self.domain_name = self.string_to_host(ip_address) def __repr__(self): return "" % (self._ip_address,) @@ -121,3 +125,26 @@ class SingleIpRange(NetworkRange): def _get_range(self): return [SingleIpRange._ip_to_number(self._ip_address)] + + @staticmethod + def string_to_host(string): + """ + Converts the string that user entered in "Scan IP/subnet list" to dict of domain name and ip + :param string: String that was entered in "Scan IP/subnet list" + :return: A tuple in format (IP, domain_name). Eg. (192.168.55.1, www.google.com) + """ + # The most common use case is to enter ip/range into "Scan IP/subnet list" + domain_name = '' + ip = string + + # If a string was entered instead of IP we presume that it was domain name and translate it + if re.search('[a-zA-Z]', string): + try: + ip = socket.gethostbyname(string) + domain_name = string + except socket.error: + LOG.error( + "You'r specified host: {} is neither found as domain name nor it's an IP address".format(string)) + return socket.error + return ip, domain_name + diff --git a/monkey/infection_monkey/model/host.py b/monkey/infection_monkey/model/host.py index 00bf08053..592cb290a 100644 --- a/monkey/infection_monkey/model/host.py +++ b/monkey/infection_monkey/model/host.py @@ -2,8 +2,9 @@ __author__ = 'itamar' class VictimHost(object): - def __init__(self, ip_addr): + def __init__(self, ip_addr, domain_name=''): self.ip_addr = ip_addr + self.domain_name = domain_name self.os = {} self.services = {} self.monkey_exe = None diff --git a/monkey/infection_monkey/network/network_scanner.py b/monkey/infection_monkey/network/network_scanner.py index d3a37d48c..8dd429d39 100644 --- a/monkey/infection_monkey/network/network_scanner.py +++ b/monkey/infection_monkey/network/network_scanner.py @@ -7,6 +7,7 @@ from infection_monkey.network.info import local_ips, get_interfaces_ranges from infection_monkey.model import VictimHost from infection_monkey.network import HostScanner from infection_monkey.network import TcpScanner, PingScanner + __author__ = 'itamar' LOG = logging.getLogger(__name__) @@ -78,7 +79,10 @@ class NetworkScanner(object): for net_range in self._ranges: LOG.debug("Scanning for potential victims in the network %r", net_range) for ip_addr in net_range: - victim = VictimHost(ip_addr) + if hasattr(net_range, 'domain_name'): + victim = VictimHost(ip_addr, net_range.domain_name) + else: + victim = VictimHost(ip_addr) if stop_callback and stop_callback(): LOG.debug("Got stop signal") break diff --git a/monkey/monkey_island/cc/resources/telemetry.py b/monkey/monkey_island/cc/resources/telemetry.py index b88acbac6..be363ce33 100644 --- a/monkey/monkey_island/cc/resources/telemetry.py +++ b/monkey/monkey_island/cc/resources/telemetry.py @@ -90,10 +90,11 @@ class Telemetry(flask_restful.Resource): @staticmethod def get_edge_by_scan_or_exploit_telemetry(telemetry_json): dst_ip = telemetry_json['data']['machine']['ip_addr'] + dst_domain_name = telemetry_json['data']['machine']['domain_name'] src_monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) dst_node = NodeService.get_monkey_by_ip(dst_ip) if dst_node is None: - dst_node = NodeService.get_or_create_node(dst_ip) + dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name) return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"]) @@ -144,6 +145,7 @@ class Telemetry(flask_restful.Resource): edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json) data = copy.deepcopy(telemetry_json['data']['machine']) ip_address = data.pop("ip_addr") + domain_name = data.pop("domain_name") new_scan = \ { "timestamp": telemetry_json["timestamp"], @@ -153,7 +155,7 @@ class Telemetry(flask_restful.Resource): mongo.db.edge.update( {"_id": edge["_id"]}, {"$push": {"scans": new_scan}, - "$set": {"ip_address": ip_address}} + "$set": {"ip_address": ip_address, 'domain_name': domain_name}} ) node = mongo.db.node.find_one({"_id": edge["to"]}) diff --git a/monkey/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py index 1f9b68ebe..77bdf6ed7 100644 --- a/monkey/monkey_island/cc/services/node.py +++ b/monkey/monkey_island/cc/services/node.py @@ -41,6 +41,7 @@ class NodeService: # node is uninfected new_node = NodeService.node_to_net_node(node, for_report) new_node["ip_addresses"] = node["ip_addresses"] + new_node["domain_name"] = node["domain_name"] for edge in edges: accessible_from_nodes.append(NodeService.get_monkey_label(NodeService.get_monkey_by_id(edge["from"]))) @@ -62,7 +63,9 @@ class NodeService: @staticmethod def get_node_label(node): - return node["os"]["version"] + " : " + node["ip_addresses"][0] + if node["domain_name"]: + node["domain_name"] = " ("+node["domain_name"]+")" + return node["os"]["version"] + " : " + node["ip_addresses"][0] + node["domain_name"] @staticmethod def _cmp_exploits_by_timestamp(exploit_1, exploit_2): @@ -137,6 +140,7 @@ class NodeService: "group": NodeService.get_monkey_group(monkey), "os": NodeService.get_monkey_os(monkey), "dead": monkey["dead"], + "domain_name": "" } @staticmethod @@ -176,10 +180,11 @@ class NodeService: upsert=False) @staticmethod - def insert_node(ip_address): + def insert_node(ip_address, domain_name=''): new_node_insert_result = mongo.db.node.insert_one( { "ip_addresses": [ip_address], + "domain_name": domain_name, "exploited": False, "creds": [], "os": @@ -191,10 +196,10 @@ class NodeService: return mongo.db.node.find_one({"_id": new_node_insert_result.inserted_id}) @staticmethod - def get_or_create_node(ip_address): + def get_or_create_node(ip_address, domain_name=''): new_node = mongo.db.node.find_one({"ip_addresses": ip_address}) if new_node is None: - new_node = NodeService.insert_node(ip_address) + new_node = NodeService.insert_node(ip_address, domain_name) return new_node @staticmethod @@ -261,6 +266,7 @@ class NodeService: def get_monkey_island_node(): island_node = NodeService.get_monkey_island_pseudo_net_node() island_node["ip_addresses"] = local_ip_addresses() + island_node["domain_name"] = "" return island_node @staticmethod From 847286dec7d66ee167016526725ec3a73f4d30f8 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 2 Oct 2018 15:05:06 +0300 Subject: [PATCH 2/7] Modified front end to inform user that he can enter URL's , not only IP's --- monkey/monkey_island/cc/services/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index b23e5cd19..ae5755174 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -10,6 +10,7 @@ from cc.encryptor import encryptor from cc.environment.environment import env from cc.utils import local_ip_addresses from config_schema import SCHEMA + __author__ = "itay.mizeretz" logger = logging.getLogger(__name__) From 7d34c290cc8b4e613413c657af7543c6dc4fe830 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 2 Oct 2018 16:41:52 +0300 Subject: [PATCH 3/7] Added support for invalid domain AND added front end files not commited in previous commits --- monkey/common/network/network_range.py | 20 +++++++++++++++++-- .../report-components/BreachedServers.js | 3 ++- .../report-components/ScannedServers.js | 3 ++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/monkey/common/network/network_range.py b/monkey/common/network/network_range.py index 3adace9bc..088177f11 100644 --- a/monkey/common/network/network_range.py +++ b/monkey/common/network/network_range.py @@ -120,12 +120,28 @@ class SingleIpRange(NetworkRange): def __repr__(self): return "" % (self._ip_address,) + def __iter__(self): + """ + We have to check if we have an IP to return, because user could have entered invalid + domain name and no IP was found + :return: IP if there is one + """ + if self.ip_found(): + yield self._number_to_ip(self.get_range()[0]) + 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)] + def ip_found(self): + """ + Checks if we could translate domain name entered into IP address + :return: True if dns found domain name and false otherwise + """ + return hasattr(self, "_ip_address") and self._ip_address + @staticmethod def string_to_host(string): """ @@ -144,7 +160,7 @@ class SingleIpRange(NetworkRange): domain_name = string except socket.error: LOG.error( - "You'r specified host: {} is neither found as domain name nor it's an IP address".format(string)) - return socket.error + "You'r specified host: {} is not found as a domain name and it's not an IP address".format(string)) + return None, string return ip, domain_name diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js index d23a14c38..381380af7 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js @@ -10,7 +10,8 @@ const columns = [ Header: 'Breached Servers', columns: [ {Header: 'Machine', accessor: 'label'}, - {Header: 'IP Addresses', id: 'ip_addresses', accessor: x => renderArray(x.ip_addresses)}, + {Header: 'IP Addresses', id: 'ip_addresses', + accessor: x => renderArray(x.ip_addresses)+(x.domain_name ? " ("+x.domain_name+")" : "")}, {Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)} ] } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js index 9b62bbdc5..c81e57340 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js @@ -10,7 +10,8 @@ const columns = [ Header: 'Scanned Servers', columns: [ { Header: 'Machine', accessor: 'label'}, - { Header: 'IP Addresses', id: 'ip_addresses', accessor: x => renderArray(x.ip_addresses)}, + { Header: 'IP Addresses', id: 'ip_addresses', + accessor: x => renderArray(x.ip_addresses)+(x.domain_name ? " ("+x.domain_name+")" : "")}, { Header: 'Accessible From', id: 'accessible_from_nodes', accessor: x => renderArray(x.accessible_from_nodes)}, { Header: 'Services', id: 'services', accessor: x => renderArray(x.services)} ] From d35634b729f6ca7575f2a2f39030ca9606f51b35 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Sat, 15 Dec 2018 16:42:20 +0200 Subject: [PATCH 4/7] Small fixes --- monkey/monkey_island/cc/services/node.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py index 77bdf6ed7..c73fdc5a6 100644 --- a/monkey/monkey_island/cc/services/node.py +++ b/monkey/monkey_island/cc/services/node.py @@ -63,9 +63,10 @@ class NodeService: @staticmethod def get_node_label(node): + domain_name = node["domain_name"] if node["domain_name"]: - node["domain_name"] = " ("+node["domain_name"]+")" - return node["os"]["version"] + " : " + node["ip_addresses"][0] + node["domain_name"] + domain_name = " ("+node["domain_name"]+")" + return node["os"]["version"] + " : " + node["ip_addresses"][0] + domain_name @staticmethod def _cmp_exploits_by_timestamp(exploit_1, exploit_2): From 4f0606d6fb57df40d0b700fc388d7427abe2ccc7 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Sun, 6 Jan 2019 13:49:40 +0200 Subject: [PATCH 5/7] Fixed PR comments (ip casting, typos) --- monkey/common/network/network_range.py | 20 +++++++++++++------- monkey/monkey_island/cc/services/node.py | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/monkey/common/network/network_range.py b/monkey/common/network/network_range.py index 088177f11..e77d09fdf 100644 --- a/monkey/common/network/network_range.py +++ b/monkey/common/network/network_range.py @@ -140,27 +140,33 @@ class SingleIpRange(NetworkRange): Checks if we could translate domain name entered into IP address :return: True if dns found domain name and false otherwise """ - return hasattr(self, "_ip_address") and self._ip_address + return self._ip_address @staticmethod def string_to_host(string): """ - Converts the string that user entered in "Scan IP/subnet list" to dict of domain name and ip + Converts the string that user entered in "Scan IP/subnet list" to a tuple of domain name and ip :param string: String that was entered in "Scan IP/subnet list" :return: A tuple in format (IP, domain_name). Eg. (192.168.55.1, www.google.com) """ # The most common use case is to enter ip/range into "Scan IP/subnet list" domain_name = '' - ip = string - # If a string was entered instead of IP we presume that it was domain name and translate it - if re.search('[a-zA-Z]', string): + # Make sure to have unicode string + user_input = string.decode('utf-8', 'ignore') + + # Try casting user's input as IP + try: + ip = ipaddress.ip_address(user_input).exploded + except ValueError: + # Exception means that it's a domain name try: ip = socket.gethostbyname(string) domain_name = string except socket.error: - LOG.error( - "You'r specified host: {} is not found as a domain name and it's not an IP address".format(string)) + LOG.error("Your specified host: {} is not found as a domain name and" + " it's not an IP address".format(string)) return None, string + # If a string was entered instead of IP we presume that it was domain name and translate it return ip, domain_name diff --git a/monkey/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py index c73fdc5a6..6fc86920c 100644 --- a/monkey/monkey_island/cc/services/node.py +++ b/monkey/monkey_island/cc/services/node.py @@ -63,7 +63,7 @@ class NodeService: @staticmethod def get_node_label(node): - domain_name = node["domain_name"] + domain_name = "" if node["domain_name"]: domain_name = " ("+node["domain_name"]+")" return node["os"]["version"] + " : " + node["ip_addresses"][0] + domain_name From bf26ed88818b619aa005617ee62c8a692980a988 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Sun, 27 Jan 2019 14:21:39 +0200 Subject: [PATCH 6/7] Fixed some errors poined out in PR --- monkey/common/network/network_range.py | 13 ++++++++++++- monkey/infection_monkey/model/host.py | 2 +- monkey/monkey_island/cc/services/node.py | 3 ++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/monkey/common/network/network_range.py b/monkey/common/network/network_range.py index e77d09fdf..1dddbc086 100644 --- a/monkey/common/network/network_range.py +++ b/monkey/common/network/network_range.py @@ -51,12 +51,23 @@ class NetworkRange(object): address_str = address_str.strip() if not address_str: # Empty string return None - if -1 != address_str.find('-'): + if NetworkRange.check_if_range(address_str): 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 check_if_range(address_str): + if -1 != address_str.find('-'): + ips = address_str.split('-') + try: + ipaddress.ip_address(ips[0]) and ipaddress.ip_address(ips[1]) + except ValueError as e: + return False + return True + return False + @staticmethod def _ip_to_number(address): return struct.unpack(">L", socket.inet_aton(address))[0] diff --git a/monkey/infection_monkey/model/host.py b/monkey/infection_monkey/model/host.py index 592cb290a..dcc6e7455 100644 --- a/monkey/infection_monkey/model/host.py +++ b/monkey/infection_monkey/model/host.py @@ -4,7 +4,7 @@ __author__ = 'itamar' class VictimHost(object): def __init__(self, ip_addr, domain_name=''): self.ip_addr = ip_addr - self.domain_name = domain_name + self.domain_name = str(domain_name) self.os = {} self.services = {} self.monkey_exe = None diff --git a/monkey/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py index 6fc86920c..50c921be8 100644 --- a/monkey/monkey_island/cc/services/node.py +++ b/monkey/monkey_island/cc/services/node.py @@ -6,6 +6,7 @@ import cc.services.log from cc.database import mongo from cc.services.edge import EdgeService from cc.utils import local_ip_addresses +import socket __author__ = "itay.mizeretz" @@ -267,7 +268,7 @@ class NodeService: def get_monkey_island_node(): island_node = NodeService.get_monkey_island_pseudo_net_node() island_node["ip_addresses"] = local_ip_addresses() - island_node["domain_name"] = "" + island_node["domain_name"] = socket.gethostname() return island_node @staticmethod From d028c707382bcfda31405b63543c7345561ec54f Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 28 Jan 2019 12:01:56 +0200 Subject: [PATCH 7/7] Fixed bug related to '-' and displaying scanned servers --- monkey/common/network/network_range.py | 1 - monkey/monkey_island/cc/services/report.py | 4 +++- .../ui/src/components/report-components/BreachedServers.js | 6 +++++- .../ui/src/components/report-components/ScannedServers.js | 6 +++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/monkey/common/network/network_range.py b/monkey/common/network/network_range.py index 1dddbc086..de89f7e4a 100644 --- a/monkey/common/network/network_range.py +++ b/monkey/common/network/network_range.py @@ -6,7 +6,6 @@ from abc import ABCMeta, abstractmethod import ipaddress from six import text_type import logging -import re __author__ = 'itamar' diff --git a/monkey/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/report.py index 8e4d42abd..50d24d692 100644 --- a/monkey/monkey_island/cc/services/report.py +++ b/monkey/monkey_island/cc/services/report.py @@ -131,7 +131,8 @@ class ReportService: list((x['hostname'] for x in (NodeService.get_displayed_node_by_id(edge['from'], True) for edge in EdgeService.get_displayed_edges_by_to(node['id'], True)))), - 'services': node['services'] + 'services': node['services'], + 'domain_name': node['domain_name'] }) logger.info('Scanned nodes generated for reporting') @@ -151,6 +152,7 @@ class ReportService: { 'label': monkey['label'], 'ip_addresses': monkey['ip_addresses'], + 'domain_name': node['domain_name'], 'exploits': list(set( [ReportService.EXPLOIT_DISPLAY_DICT[exploit['exploiter']] for exploit in monkey['exploits'] if exploit['result']])) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js index 381380af7..16f445ce9 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/BreachedServers.js @@ -5,13 +5,17 @@ let renderArray = function(val) { return
{val.map(x =>
{x}
)}
; }; +let renderIpAddresses = function (val) { + return
{renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")}
; +}; + const columns = [ { Header: 'Breached Servers', columns: [ {Header: 'Machine', accessor: 'label'}, {Header: 'IP Addresses', id: 'ip_addresses', - accessor: x => renderArray(x.ip_addresses)+(x.domain_name ? " ("+x.domain_name+")" : "")}, + accessor: x => renderIpAddresses(x)}, {Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)} ] } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js index c81e57340..57418e415 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ScannedServers.js @@ -5,13 +5,17 @@ let renderArray = function(val) { return
{val.map(x =>
{x}
)}
; }; +let renderIpAddresses = function (val) { + return
{renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")}
; +}; + const columns = [ { Header: 'Scanned Servers', columns: [ { Header: 'Machine', accessor: 'label'}, { Header: 'IP Addresses', id: 'ip_addresses', - accessor: x => renderArray(x.ip_addresses)+(x.domain_name ? " ("+x.domain_name+")" : "")}, + accessor: x => renderIpAddresses(x)}, { Header: 'Accessible From', id: 'accessible_from_nodes', accessor: x => renderArray(x.accessible_from_nodes)}, { Header: 'Services', id: 'services', accessor: x => renderArray(x.services)} ]