forked from p15670423/monkey
Added the logic of assigning states to nodes, state(node group) parsing and icon placeholders
This commit is contained in:
parent
f792572079
commit
a0d4b825e9
|
@ -1,35 +1,23 @@
|
||||||
import json
|
import json
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
import flask_restful
|
import flask_restful
|
||||||
from flask import request, make_response
|
from flask import request, make_response
|
||||||
|
|
||||||
from monkey_island.cc.database import mongo
|
from monkey_island.cc.services.bootloader import BootloaderService
|
||||||
from monkey_island.cc.services.node import NodeService
|
|
||||||
|
|
||||||
WINDOWS_VERSIONS = {
|
|
||||||
"5.0": "Windows 2000",
|
|
||||||
"5.1": "Windows XP",
|
|
||||||
"5.2": "Windows XP/server 2003",
|
|
||||||
"6.0": "Windows Vista/server 2008",
|
|
||||||
"6.1": "Windows 7/server 2008R2",
|
|
||||||
"6.2": "Windows 8/server 2012",
|
|
||||||
"6.3": "Windows 8.1/server 2012R2",
|
|
||||||
"10.0": "Windows 10/server 2016-2019"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Bootloader(flask_restful.Resource):
|
class Bootloader(flask_restful.Resource):
|
||||||
|
|
||||||
# Used by monkey. can't secure.
|
# Used by monkey. can't secure.
|
||||||
def post(self, **kw):
|
def post(self, **kw):
|
||||||
data = json.loads(request.data.decode().replace("\n", "").replace("NAME=\"", "").replace("\"\"", "\""))
|
data = Bootloader.parse_bootloader_request(request.data)
|
||||||
|
resp = BootloaderService.parse_bootloader_data(data)
|
||||||
# Remove local ips
|
|
||||||
local_addr = [i for i in data["ips"] if i.startswith("127")]
|
|
||||||
if local_addr:
|
|
||||||
data["ips"].remove(local_addr[0])
|
|
||||||
|
|
||||||
mongo.db.bootloader_telems.insert(data)
|
|
||||||
node_id = NodeService.get_or_create_node_from_bootloader_telem(data)
|
|
||||||
|
|
||||||
return make_response({"status": "OK"}, 200)
|
return make_response({"status": "OK"}, 200)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_bootloader_request(request_data: bytes) -> Dict[str, str]:
|
||||||
|
parsed_data = json.loads(request_data.decode().replace("\n", "")
|
||||||
|
.replace("NAME=\"", "")
|
||||||
|
.replace("\"\"", "\""))
|
||||||
|
return parsed_data
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
from monkey_island.cc.database import mongo
|
||||||
|
from monkey_island.cc.services.node import NodeService
|
||||||
|
from monkey_island.cc.services.utils.node_groups import NodeGroups
|
||||||
|
|
||||||
|
WINDOWS_VERSIONS = {
|
||||||
|
"5.0": "Windows 2000",
|
||||||
|
"5.1": "Windows XP",
|
||||||
|
"5.2": "Windows XP/server 2003",
|
||||||
|
"6.0": "Windows Vista/server 2008",
|
||||||
|
"6.1": "Windows 7/server 2008R2",
|
||||||
|
"6.2": "Windows 8/server 2012",
|
||||||
|
"6.3": "Windows 8.1/server 2012R2",
|
||||||
|
"10.0": "Windows 10/server 2016-2019"
|
||||||
|
}
|
||||||
|
|
||||||
|
MIN_GLIBC_VERSION = 2.14
|
||||||
|
|
||||||
|
|
||||||
|
class BootloaderService:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_bootloader_data(data: Dict) -> str:
|
||||||
|
data['ips'] = BootloaderService.remove_local_ips(data['ips'])
|
||||||
|
mongo.db.bootloader_telems.insert(data)
|
||||||
|
will_monkey_run = BootloaderService.is_glibc_supported(data['glibc_version'])
|
||||||
|
node = NodeService.get_or_create_node_from_bootloader_data(data, will_monkey_run)
|
||||||
|
group_keywords = [data['system'], 'monkey']
|
||||||
|
group_keywords.append('starting') if will_monkey_run else group_keywords.append('old')
|
||||||
|
NodeService.set_node_group(node['_id'], NodeGroups.get_group_by_keywords(group_keywords))
|
||||||
|
return "abc"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_glibc_supported(glibc_version_string) -> bool:
|
||||||
|
glibc_version_string = glibc_version_string.lower()
|
||||||
|
glibc_version = glibc_version_string.split(' ')[-1]
|
||||||
|
return glibc_version >= str(MIN_GLIBC_VERSION) and 'eglibc' not in glibc_version_string
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def remove_local_ips(ip_list) -> List[str]:
|
||||||
|
return [i for i in ip_list if not i.startswith("127")]
|
|
@ -0,0 +1,35 @@
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from monkey_island.cc.services.bootloader import BootloaderService
|
||||||
|
|
||||||
|
WINDOWS_VERSIONS = {
|
||||||
|
"5.0": "Windows 2000",
|
||||||
|
"5.1": "Windows XP",
|
||||||
|
"5.2": "Windows XP/server 2003",
|
||||||
|
"6.0": "Windows Vista/server 2008",
|
||||||
|
"6.1": "Windows 7/server 2008R2",
|
||||||
|
"6.2": "Windows 8/server 2012",
|
||||||
|
"6.3": "Windows 8.1/server 2012R2",
|
||||||
|
"10.0": "Windows 10/server 2016-2019"
|
||||||
|
}
|
||||||
|
|
||||||
|
MIN_GLIBC_VERSION = 2.14
|
||||||
|
|
||||||
|
|
||||||
|
class TestBootloaderService(TestCase):
|
||||||
|
|
||||||
|
def test_is_glibc_supported(self):
|
||||||
|
str1 = "ldd (Ubuntu EGLIBC 2.15-0ubuntu10) 2.15"
|
||||||
|
str2 = "ldd (GNU libc) 2.12"
|
||||||
|
str3 = "ldd (GNU libc) 2.28"
|
||||||
|
str4 = "ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23"
|
||||||
|
self.assertTrue(not BootloaderService.is_glibc_supported(str1) and
|
||||||
|
not BootloaderService.is_glibc_supported(str2) and
|
||||||
|
BootloaderService.is_glibc_supported(str3) and
|
||||||
|
BootloaderService.is_glibc_supported(str4))
|
||||||
|
|
||||||
|
def test_remove_local_ips(self):
|
||||||
|
ips = ["127.1.1.1", "127.0.0.1", "192.168.56.1"]
|
||||||
|
ips = BootloaderService.remove_local_ips(ips)
|
||||||
|
self.assertEqual(["192.168.56.1"], ips)
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict
|
||||||
|
import socket
|
||||||
|
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
|
|
||||||
|
@ -7,8 +9,8 @@ from monkey_island.cc.database import mongo
|
||||||
from monkey_island.cc.models import Monkey
|
from monkey_island.cc.models import Monkey
|
||||||
from monkey_island.cc.services.edge import EdgeService
|
from monkey_island.cc.services.edge import EdgeService
|
||||||
from monkey_island.cc.utils import local_ip_addresses
|
from monkey_island.cc.utils import local_ip_addresses
|
||||||
import socket
|
|
||||||
from monkey_island.cc import models
|
from monkey_island.cc import models
|
||||||
|
from monkey_island.cc.services.utils.node_groups import NodeGroups
|
||||||
|
|
||||||
__author__ = "itay.mizeretz"
|
__author__ = "itay.mizeretz"
|
||||||
|
|
||||||
|
@ -122,20 +124,25 @@ class NodeService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_monkey_group(monkey):
|
def get_monkey_group(monkey):
|
||||||
|
keywords = []
|
||||||
if len(set(monkey["ip_addresses"]).intersection(local_ip_addresses())) != 0:
|
if len(set(monkey["ip_addresses"]).intersection(local_ip_addresses())) != 0:
|
||||||
monkey_type = "island_monkey"
|
keywords.extend(["island", "monkey"])
|
||||||
else:
|
else:
|
||||||
monkey_type = "manual" if NodeService.get_monkey_manual_run(monkey) else "monkey"
|
monkey_type = "manual" if NodeService.get_monkey_manual_run(monkey) else "monkey"
|
||||||
|
keywords.append(monkey_type)
|
||||||
|
|
||||||
monkey_os = NodeService.get_monkey_os(monkey)
|
keywords.append(NodeService.get_monkey_os(monkey))
|
||||||
monkey_running = "" if Monkey.get_single_monkey_by_id(monkey["_id"]).is_dead() else "_running"
|
if not Monkey.get_single_monkey_by_id(monkey["_id"]).is_dead():
|
||||||
return "%s_%s%s" % (monkey_type, monkey_os, monkey_running)
|
keywords.append("running")
|
||||||
|
return NodeGroups.get_group_by_keywords(keywords).value
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_node_group(node):
|
def get_node_group(node) -> str:
|
||||||
|
if node['group']:
|
||||||
|
return node['group']
|
||||||
node_type = "exploited" if node.get("exploited") else "clean"
|
node_type = "exploited" if node.get("exploited") else "clean"
|
||||||
node_os = NodeService.get_node_os(node)
|
node_os = NodeService.get_node_os(node)
|
||||||
return "%s_%s" % (node_type, node_os)
|
return NodeGroups.get_group_by_keywords([node_type, node_os]).value
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def monkey_to_net_node(monkey, for_report=False):
|
def monkey_to_net_node(monkey, for_report=False):
|
||||||
|
@ -166,6 +173,12 @@ class NodeService:
|
||||||
"os": NodeService.get_node_os(node)
|
"os": NodeService.get_node_os(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_node_group(node_id: str, node_group: NodeGroups):
|
||||||
|
mongo.db.node.update({"_id": node_id},
|
||||||
|
{'$set': {'group': node_group.value}},
|
||||||
|
upsert=False)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unset_all_monkey_tunnels(monkey_id):
|
def unset_all_monkey_tunnels(monkey_id):
|
||||||
mongo.db.monkey.update(
|
mongo.db.monkey.update(
|
||||||
|
@ -208,37 +221,38 @@ class NodeService:
|
||||||
return mongo.db.node.find_one({"_id": new_node_insert_result.inserted_id})
|
return mongo.db.node.find_one({"_id": new_node_insert_result.inserted_id})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_node_from_bootloader_telem(bootloader_telem):
|
def create_node_from_bootloader_data(bootloader_data: Dict, will_monkey_run: bool):
|
||||||
new_node_insert_result = mongo.db.node.insert_one(
|
new_node_insert_result = mongo.db.node.insert_one(
|
||||||
{
|
{
|
||||||
"ip_addresses": bootloader_telem['ips'],
|
"ip_addresses": bootloader_data['ips'],
|
||||||
"domain_name": bootloader_telem['hostname'],
|
"domain_name": bootloader_data['hostname'],
|
||||||
|
"will_monkey_run": will_monkey_run,
|
||||||
"exploited": False,
|
"exploited": False,
|
||||||
"creds": [],
|
"creds": [],
|
||||||
"os":
|
"os":
|
||||||
{
|
{
|
||||||
"type": bootloader_telem['system'],
|
"type": bootloader_data['system'],
|
||||||
"version": bootloader_telem['os_version']
|
"version": bootloader_data['os_version']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return mongo.db.node.find_one({"_id": new_node_insert_result.inserted_id})
|
return mongo.db.node.find_one({"_id": new_node_insert_result.inserted_id})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_or_create_node_from_bootloader_telem(bootloader_telem):
|
def get_or_create_node_from_bootloader_data(bootloader_data: Dict, will_monkey_run: bool) -> Dict:
|
||||||
new_node = mongo.db.node.find_one({"domain_name": bootloader_telem['hostname'],
|
new_node = mongo.db.node.find_one({"domain_name": bootloader_data['hostname'],
|
||||||
"ip_addresses": bootloader_telem['ips']})
|
"ip_addresses": bootloader_data['ips']})
|
||||||
if new_node is None:
|
if new_node is None:
|
||||||
new_node = NodeService.create_node_from_bootloader_telem(bootloader_telem)
|
new_node = NodeService.create_node_from_bootloader_data(bootloader_data, will_monkey_run)
|
||||||
if bootloader_telem['tunnel']:
|
if bootloader_data['tunnel']:
|
||||||
dst_node = NodeService.get_node_or_monkey_by_ip(bootloader_telem['tunnel'])
|
dst_node = NodeService.get_node_or_monkey_by_ip(bootloader_data['tunnel'])
|
||||||
else:
|
else:
|
||||||
dst_node = NodeService.get_monkey_island_node()
|
dst_node = NodeService.get_monkey_island_node()
|
||||||
edge = EdgeService.get_or_create_edge(new_node['_id'], dst_node['id'])
|
edge = EdgeService.get_or_create_edge(new_node['_id'], dst_node['id'])
|
||||||
mongo.db.edge.update({"_id": edge["_id"]},
|
mongo.db.edge.update({"_id": edge["_id"]},
|
||||||
{'$set': {'tunnel': bool(bootloader_telem['tunnel']),
|
{'$set': {'tunnel': bool(bootloader_data['tunnel']),
|
||||||
'exploited': (not bool(bootloader_telem['tunnel'])),
|
'exploited': (not bool(bootloader_data['tunnel'])),
|
||||||
'ip_address': bootloader_telem['ips'][0],
|
'ip_address': bootloader_data['ips'][0],
|
||||||
'group': 'island'}},
|
'group': NodeGroups.get_group_by_keywords(['island']).value}},
|
||||||
upsert=False)
|
upsert=False)
|
||||||
|
|
||||||
return new_node
|
return new_node
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List
|
||||||
|
import collections
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroups(Enum):
|
||||||
|
CLEAN_UNKNOWN = 'clean_unknown'
|
||||||
|
CLEAN_LINUX = 'clean_linux'
|
||||||
|
CLEAN_WINDOWS = 'clean_windows'
|
||||||
|
EXPLOITED_LINUX = 'exploited_linux'
|
||||||
|
EXPLOITED_WINDOWS = 'exploited_windows'
|
||||||
|
ISLAND = 'island'
|
||||||
|
ISLAND_MONKEY_LINUX = 'island_monkey_linux'
|
||||||
|
ISLAND_MONKEY_LINUX_RUNNING = 'island_monkey_linux_running'
|
||||||
|
ISLAND_MONKEY_WINDOWS = 'island_monkey_windows'
|
||||||
|
ISLAND_MONKEY_WINDOWS_RUNNING = 'island_monkey_windows_running'
|
||||||
|
MANUAL_LINUX = 'manual_linux'
|
||||||
|
MANUAL_LINUX_RUNNING = 'manual_linux_running'
|
||||||
|
MANUAL_WINDOWS = 'manual_windows'
|
||||||
|
MANUAL_WINDOWS_RUNNING = 'manual_windows_running'
|
||||||
|
MONKEY_LINUX = 'monkey_linux'
|
||||||
|
MONKEY_LINUX_RUNNING = 'monkey_linux_running'
|
||||||
|
MONKEY_WINDOWS = 'monkey_windows'
|
||||||
|
MONKEY_WINDOWS_RUNNING = 'monkey_windows_running'
|
||||||
|
MONKEY_WINDOWS_STARTING = 'monkey_windows_starting'
|
||||||
|
MONKEY_LINUX_STARTING = 'monkey_linux_starting'
|
||||||
|
MONKEY_WINDOWS_OLD = 'monkey_windows_old'
|
||||||
|
MONKEY_LINUX_OLD = 'monkey_linux_old'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_group_by_keywords(keywords: List) -> NodeGroups:
|
||||||
|
potential_groups = [i for i in NodeGroups if NodeGroups._is_group_from_keywords(i, keywords)]
|
||||||
|
if len(potential_groups) > 1:
|
||||||
|
raise MultipleGroupsFoundException("Multiple groups contain provided keywords. "
|
||||||
|
"Manually build group string to ensure keyword order.")
|
||||||
|
elif len(potential_groups) == 0:
|
||||||
|
raise NoGroupsFoundException("No groups found with provided keywords. "
|
||||||
|
"Check for typos and make sure group codes want to find exists.")
|
||||||
|
return potential_groups[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_group_from_keywords(group, keywords) -> bool:
|
||||||
|
group_keywords = group.value.split("_")
|
||||||
|
return collections.Counter(group_keywords) == collections.Counter(keywords)
|
||||||
|
|
||||||
|
|
||||||
|
class MultipleGroupsFoundException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoGroupsFoundException(Exception):
|
||||||
|
pass
|
|
@ -0,0 +1,18 @@
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from monkey_island.cc.services.utils.node_groups import NodeGroups, NoGroupsFoundException
|
||||||
|
|
||||||
|
|
||||||
|
class TestNodeGroups(TestCase):
|
||||||
|
|
||||||
|
def test_get_group_by_keywords(self):
|
||||||
|
tst1 = NodeGroups.get_group_by_keywords(['island']) == NodeGroups.ISLAND
|
||||||
|
tst2 = NodeGroups.get_group_by_keywords(['running', 'linux', 'monkey']) == NodeGroups.MONKEY_LINUX_RUNNING
|
||||||
|
tst3 = NodeGroups.get_group_by_keywords(['monkey', 'linux', 'running']) == NodeGroups.MONKEY_LINUX_RUNNING
|
||||||
|
tst4 = False
|
||||||
|
try:
|
||||||
|
NodeGroups.get_group_by_keywords(['bogus', 'values', 'from', 'long', 'list', 'should', 'fail'])
|
||||||
|
except NoGroupsFoundException:
|
||||||
|
tst4 = True
|
||||||
|
self.assertTrue(tst1 and tst2 and tst3 and tst4)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
const groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island',
|
const groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island',
|
||||||
'island_monkey_linux', 'island_monkey_linux_running', 'island_monkey_windows', 'island_monkey_windows_running',
|
'island_monkey_linux', 'island_monkey_linux_running', 'island_monkey_windows', 'island_monkey_windows_running',
|
||||||
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
|
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
|
||||||
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
|
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running', 'monkey_windows_starting',
|
||||||
|
'monkey_linux_starting', 'monkey_windows_old', 'monkey_linux_old' ];
|
||||||
|
|
||||||
let getGroupsOptions = () => {
|
let getGroupsOptions = () => {
|
||||||
let groupOptions = {};
|
let groupOptions = {};
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 145 KiB |
Binary file not shown.
After Width: | Height: | Size: 111 KiB |
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
Loading…
Reference in New Issue