├── instantiation ├── vm_clone │ ├── create_bridges.sh │ ├── initif │ │ ├── initif.conf │ │ ├── ip_calc.sh │ │ └── initif │ ├── create_bridges │ │ ├── 02_up_bridge.sh │ │ └── 01_write_bridge_config.sh │ ├── create_vms.sh │ ├── vm_destroy_xml.sh │ ├── check_ping.py │ └── vm_clone_xml.sh ├── logs_preparation │ ├── noise_low.pcap │ ├── noise_high.pcap │ ├── noise_medium.pcap │ ├── pcap_sshattack_generator.sh │ ├── pcap_ddosattack_generator.sh │ ├── pcap_dosattack_generator.sh │ └── mergePcap.py ├── malware_creation │ ├── dummy_malware │ ├── dummy_malware.c │ └── malware_launch.sh ├── attacks_emulation │ ├── launch_ddos.sh │ ├── launch_dos.sh │ ├── install_paramiko.sh │ └── attack_paramiko_ssh.py ├── sshkey_hostname_setup │ ├── hostname_setup.sh │ ├── create_ch_acl.ps1 │ ├── sshkey_setup.sh │ ├── sshkey_setup_win_cmd.sh │ └── sshkey_setup_win_unix.sh ├── ruleset_modification │ ├── iptables_template │ ├── append_ruleset.py │ ├── firewall_ruleset │ ├── firewall_ruleset.sh │ └── ruleset_modify.sh ├── users_managing │ ├── add_user.sh │ └── modify_user.sh └── content_copy_program_run │ ├── copy_content_win.sh │ ├── copy_content.sh │ ├── run_program.py │ └── limitedstringqueue.py ├── cyris_workflow.png ├── logs └── README ├── settings └── README ├── cyber_range └── README ├── CONTRIBUTORS ├── CONFIG ├── main ├── mail_template ├── aws_image.py ├── aws_info.py ├── aws_sg.py ├── parse_config.py ├── storyboard.py ├── cyvar.py ├── aws_cleanup.py ├── aws_instances.py ├── range_cleanup.py ├── modules.py ├── clone_environment.py └── entities.py ├── examples ├── basic-aws.yml ├── basic.yml ├── basic-windows7.yml ├── basic-multi_host.yml ├── dmz.yml └── full.yml ├── destroy_all_cr.sh ├── HOST-PREPARE.sh ├── cleanup └── downbridges.py ├── LICENSE ├── CHANGES └── README.md /instantiation/vm_clone/create_bridges.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /instantiation/vm_clone/initif/initif.conf: -------------------------------------------------------------------------------- 1 | 2 | eth0 192.168.122.0/24 3 | 4 | -------------------------------------------------------------------------------- /cyris_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crond-jaist/cyris/HEAD/cyris_workflow.png -------------------------------------------------------------------------------- /logs/README: -------------------------------------------------------------------------------- 1 | This directory is required for CyRIS to work correctly. Do not remove it. 2 | -------------------------------------------------------------------------------- /settings/README: -------------------------------------------------------------------------------- 1 | This directory is required for CyRIS to work correctly. Do not remove it. 2 | -------------------------------------------------------------------------------- /cyber_range/README: -------------------------------------------------------------------------------- 1 | This directory is required for CyRIS to work correctly. Do not remove it. 2 | -------------------------------------------------------------------------------- /instantiation/logs_preparation/noise_low.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crond-jaist/cyris/HEAD/instantiation/logs_preparation/noise_low.pcap -------------------------------------------------------------------------------- /instantiation/malware_creation/dummy_malware: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crond-jaist/cyris/HEAD/instantiation/malware_creation/dummy_malware -------------------------------------------------------------------------------- /instantiation/logs_preparation/noise_high.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crond-jaist/cyris/HEAD/instantiation/logs_preparation/noise_high.pcap -------------------------------------------------------------------------------- /instantiation/logs_preparation/noise_medium.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crond-jaist/cyris/HEAD/instantiation/logs_preparation/noise_medium.pcap -------------------------------------------------------------------------------- /instantiation/vm_clone/create_bridges/02_up_bridge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bridge_id=$1 4 | 5 | # bring up interfaces and bridges 6 | ifup eth${bridge_id} 7 | ifup br${bridge_id} 8 | -------------------------------------------------------------------------------- /instantiation/attacks_emulation/launch_ddos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | attack_addr=$1 4 | 5 | bash -c "exec -a ddos_attack hping3 -c 10000 -d 120 -S -w 64 -p 80 --flood --rand-source ${attack_addr} &"; 6 | 7 | sleep 2; 8 | pkill -f ddos_attack; 9 | -------------------------------------------------------------------------------- /instantiation/sshkey_hostname_setup/hostname_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | vm_addr=$1 4 | vm_passwd=$2 5 | host_name=$3 6 | 7 | # copy it to basevm 8 | sshpass -p ${vm_passwd} ssh -o StrictHostKeyChecking=no root@${vm_addr} "echo $host_name > /etc/hostname" 9 | -------------------------------------------------------------------------------- /instantiation/attacks_emulation/launch_dos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source_addr=$1 4 | victim_addr=$2 5 | dport=$3 6 | 7 | bash -c "exec -a dos_attack hping3 -c 10000 -d 120 -S -w 64 -p ${dport} --flood -a ${source_addr} ${victim_addr} &"; 8 | 9 | sleep 2; 10 | pkill -f dos_attack; 11 | -------------------------------------------------------------------------------- /instantiation/sshkey_hostname_setup/create_ch_acl.ps1: -------------------------------------------------------------------------------- 1 | $authorizedKeyPath = "C:\users\root\.ssh\authorized_keys" 2 | $acl = Get-Acl $authorizedKeyPath 3 | $ar = New-Object System.Security.AccessControl.FileSystemAccessRule("NT Service\sshd", "Read", "Allow") 4 | $acl.SetAccessRule($ar) 5 | Set-Acl $authorizedKeyPath $acl -------------------------------------------------------------------------------- /instantiation/ruleset_modification/iptables_template: -------------------------------------------------------------------------------- 1 | 2 | # Required configuration for iptables service 3 | *filter 4 | :INPUT ACCEPT [0:0] 5 | :FORWARD ACCEPT [0:0] 6 | :OUTPUT ACCEPT [0:0] 7 | 8 | # Add below any firewall rules that you would like to appear for all the cyber range guests, 9 | # as the firewall rules provided in the 'firewall_rules' task are appended to this file 10 | 11 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | This file includes the main contributors to the CyRIS project. 2 | 3 | Initial implementation: 4 | Cuong Pham 5 | Razvan Beuran 6 | 7 | Current maintainers: 8 | Razvan Beuran 9 | Ken-chi Chinen 10 | 11 | Various contributions: 12 | Yuichiro Sakamoto (Windows support) 13 | Fumikazu Awa (testing) 14 | Masanori Sunagawa 15 | Zhe Zhang (AWS support) 16 | Zhenguo Hu 17 | -------------------------------------------------------------------------------- /instantiation/attacks_emulation/install_paramiko.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | host_passwd=$1 4 | 5 | # check if paramiko has been installed 6 | python -c "import paramiko" > /dev/null 2>&1; 7 | if [ $? -eq 1 ] 8 | then 9 | #echo "* DEBUG: paramiko is not yet installed" 10 | sudo -S apt-get install python-paramiko 11 | else 12 | #echo "* DEBUG: paramiko is already installed" 13 | : 14 | fi 15 | 16 | -------------------------------------------------------------------------------- /instantiation/vm_clone/create_vms.sh: -------------------------------------------------------------------------------- 1 | # This script is to create new virtual machines from the image base by running scripts in xml folder. 2 | 3 | HOST_ID=$1 4 | VM_ID=$2 5 | BASE_IMG_NAME=$3 6 | CYRIS_ABSPATH=$4 7 | CYBERRANGE_ABSPATH=$5 8 | BRIDGE_ID_STR=$6 9 | ADDR_STR=$7 10 | 11 | inst_dir="instantiation" 12 | 13 | ${CYRIS_ABSPATH}${inst_dir}/vm_clone/vm_clone_xml.sh ${HOST_ID} ${VM_ID} ${BASE_IMG_NAME} ${CYBERRANGE_ABSPATH} ${BRIDGE_ID_STR} ${ADDR_STR}; 14 | 15 | -------------------------------------------------------------------------------- /CONFIG: -------------------------------------------------------------------------------- 1 | [config] 2 | 3 | # The absolute path of the top CyRIS directory 4 | # (remember to have the slash "/" at the end of the path) 5 | cyris_path = /home/cyuser/cyris/ 6 | 7 | # The absolute path where cyber ranges are to be instantiated 8 | # (remember to have the slash "/" at the end of the path) 9 | cyber_range_dir = /home/cyuser/cyris/cyber_range/ 10 | 11 | # Information regarding the gateway 12 | # (details are not used if gw_mode is set to "off") 13 | gw_mode = off 14 | #gw_account = gw_user 15 | #gw_mgmt_addr = gw_hostname 16 | #gw_inside_addr = 172.16.1.1 17 | -------------------------------------------------------------------------------- /main/mail_template: -------------------------------------------------------------------------------- 1 | Dear {instructor}, 2 | 3 | Thank you very much for using our cybersecurity training framework. 4 | We would like to inform you that Training Session #{ID} is ready to 5 | use. Please find below detailed information about how to access the 6 | created cyber range instances: 7 | 8 | {num_cr_instances} 9 | {info_cr_instances} 10 | 11 | We hope you will gain valuable knowledge about cybersecurity through 12 | this training. Feel free to contact us if you want to share with us 13 | what you think about our training framework. 14 | 15 | Best of luck, 16 | The Administration Team 17 | -------------------------------------------------------------------------------- /instantiation/sshkey_hostname_setup/sshkey_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | vm_addr=$1 4 | vm_passwd=$2 5 | mstnode_account=$3 6 | 7 | # Clear old ssh keys from base VM if they exist 8 | ssh-keygen -f "/home/${mstnode_account}/.ssh/known_hosts" -R ${vm_addr} > /dev/null 9 | 10 | # Copy new ssh key to base VM 11 | # NOTE: We are sending both stdout and stderr to /dev/null via the '&>' redirector 12 | # to get rid of INFO messages, but this may cause actual errors to be hidden 13 | COMMAND="ssh-copy-id -o StrictHostKeyChecking=no root@${vm_addr}" 14 | sshpass -p ${vm_passwd} ${COMMAND} &> /dev/null 15 | -------------------------------------------------------------------------------- /instantiation/vm_clone/vm_destroy_xml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | id="cr${1}_" 4 | dir=${2} 5 | 6 | virsh list --all | awk '{ 7 | if($0 ~ /'${id}'/ ) { 8 | destroy_cmd = "virsh shutdown " $2; 9 | echo system(destroy_cmd); 10 | undefine_cmd = "virsh undefine " $2; 11 | echo system(undefine_cmd); 12 | } 13 | }' 14 | ps auxwww | grep diff_${id} | awk '{system("sudo kill -9 " $2)}' 15 | 16 | 17 | #sudo rm ${dir}images/desktop${id}*.xml 18 | #sudo rm ${dir}images/desktop_diff_${id}* 19 | #sudo rm ${dir}images/webserver${id}*.xml 20 | #sudo rm ${dir}images/webserver_diff_${id}* 21 | -------------------------------------------------------------------------------- /examples/basic-aws.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - host_settings: 3 | - id: host_1 4 | mgmt_addr: localhost 5 | account: cyuser 6 | 7 | - guest_settings: 8 | - id: desktop 9 | basevm_host: host_1 10 | basevm_type: aws 11 | basevm_os_type: amazon_linux 12 | 13 | - clone_settings: 14 | - range_id: 123 15 | hosts: 16 | - host_id: host_1 17 | instance_number: 1 18 | guests: 19 | - guest_id: desktop 20 | number: 1 21 | entry_point: yes 22 | topology: 23 | - type: custom 24 | networks: 25 | - name: office 26 | members: desktop.eth0 27 | -------------------------------------------------------------------------------- /main/aws_image.py: -------------------------------------------------------------------------------- 1 | 2 | # Create a new AWS image 3 | def create_img(client, ins_id, name, des='New image created from the previous_instance(BaseVM)'): 4 | 5 | response = client.create_image( 6 | Description=des, 7 | InstanceId=ins_id, 8 | Name=name, 9 | #NoReboot=True|False 10 | ) 11 | img_id = response['ImageId'] 12 | return img_id 13 | 14 | 15 | # Get AWS image description 16 | def describe_image(client, img_id): 17 | response = client.describe_images( 18 | ImageIds=[ 19 | img_id, 20 | ] 21 | ) 22 | state = response['Images'][0]['State'] 23 | return state 24 | -------------------------------------------------------------------------------- /instantiation/users_managing/add_user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | username=$1 4 | passwd=$2 5 | root_privilege=$3 6 | full_name=${@:4:99} 7 | 8 | # add new user with passwd 9 | if id -u "$username" >/dev/null 2>&1; then 10 | echo "user exists"; 11 | exit 1; 12 | else 13 | useradd -s /bin/bash -p $(echo $passwd | openssl passwd -1 -stdin) $username; 14 | fi 15 | 16 | # provide root privilege to that user 17 | if [[ "${root_privilege}" == "yes" ]] 18 | then 19 | bash -c 'echo -e "'$username'\tALL=(ALL:ALL)\tALL" | (EDITOR="tee -a" visudo)' 20 | else 21 | : 22 | fi 23 | 24 | if [[ ! -z $full_name ]]; then 25 | chfn -f "$full_name" $username 26 | fi 27 | -------------------------------------------------------------------------------- /instantiation/vm_clone/create_bridges/01_write_bridge_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | bridge_id=$1 4 | bridge_addr=$2 5 | 6 | # create logical interfaces and bridges configuration 7 | NEWLINE=$'\n' 8 | config="${NEW_LINE} 9 | auto eth${bridge_id}${NEW_LINE} 10 | iface eth${bridge_id} inet manual${NEW_LINE} 11 | ${NEW_LINE} 12 | auto br${bridge_id}${NEW_LINE} 13 | iface br${bridge_id} inet static${NEW_LINE} 14 | address ${bridge_addr}${NEW_LINE} 15 | netmask 255.255.255.0${NEW_LINE} 16 | bridge_ports eth${bridge_id}${NEW_LINE} 17 | bridge_stp off${NEW_LINE} 18 | bridge_fd 0" 19 | 20 | flock -x /etc/network/interfaces echo "${config}" >> /etc/network/interfaces 21 | -------------------------------------------------------------------------------- /examples/basic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - host_settings: 3 | - id: host_1 4 | mgmt_addr: localhost 5 | virbr_addr: 192.168.122.1 6 | account: cyuser 7 | 8 | - guest_settings: 9 | - id: desktop 10 | basevm_host: host_1 11 | basevm_config_file: /home/cyuser/images/basevm.xml 12 | basevm_type: kvm 13 | 14 | - clone_settings: 15 | - range_id: 123 16 | hosts: 17 | - host_id: host_1 18 | instance_number: 1 19 | guests: 20 | - guest_id: desktop 21 | number: 1 22 | entry_point: yes 23 | topology: 24 | - type: custom 25 | networks: 26 | - name: office 27 | members: desktop.eth0 28 | -------------------------------------------------------------------------------- /instantiation/users_managing/modify_user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | old_account=$1 4 | new_account=$2 5 | new_passwd=$3 6 | 7 | # edit new user with passwd 8 | if id -u "$old_account" >/dev/null 2>&1; then 9 | if [[ "${new_account}" == "null" ]] 10 | then 11 | echo "${old_account}:${new_passwd}" | chpasswd; 12 | elif [[ "${new_passwd}" == "null" ]] 13 | then 14 | pkill -u ${old_account} pid; 15 | pkill -9 -u ${old_account}; 16 | usermod -l ${new_account} ${old_account}; 17 | ls -ld /home/${old_account}; 18 | usermod -d /home/${new_account} -m ${new_account}; 19 | fi 20 | else 21 | echo "account does not exist"; 22 | exit 1; 23 | fi 24 | -------------------------------------------------------------------------------- /examples/basic-windows7.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - host_settings: 3 | - id: host_1 4 | mgmt_addr: localhost 5 | virbr_addr: 192.168.122.1 6 | account: cyuser 7 | 8 | - guest_settings: 9 | - id: windows7 10 | basevm_host: host_1 11 | basevm_config_file: /home/cyuser/images/windows7.xml 12 | basevm_type: kvm 13 | basevm_os_type: windows.7 14 | 15 | - clone_settings: 16 | - range_id: 123 17 | hosts: 18 | - host_id: host_1 19 | instance_number: 1 20 | guests: 21 | - guest_id: windows7 22 | number: 1 23 | entry_point: yes 24 | topology: 25 | - type: custom 26 | networks: 27 | - name: office 28 | members: windows7.eth0 29 | -------------------------------------------------------------------------------- /destroy_all_cr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $# -eq 0 ]] ; then 4 | echo 'Please specify locations of abs_path and cr_path' 5 | echo 'Example: ./destroy_all_cr.sh /home/cyuser/cyris/ /home/cyuser/cyris/cyber_range/' 6 | exit 0 7 | fi 8 | 9 | abs_path=${1} 10 | cr_path=${2} 11 | 12 | for dir in ${cr_path}*; do 13 | if [[ -d "$dir" && ! -L "$dir" ]]; then 14 | echo "* INFO: destroy_all_cr: Destroying cyber range in ${dir}..." 15 | for script in ${dir}/*.sh; do 16 | if [[ "${script}" == *whole-controlled* ]]; then 17 | ${script} 18 | fi 19 | done 20 | rm -rf ${dir} 21 | fi 22 | done 23 | 24 | echo "* INFO: destroy_all_cr: Deleting ALL temporary cyber range setting files..." 25 | rm ${abs_path}settings/*.txt 26 | -------------------------------------------------------------------------------- /examples/basic-multi_host.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - host_settings: 3 | - id: host_1 4 | mgmt_addr: localhost 5 | virbr_addr: 192.168.122.1 6 | account: cyuser 7 | - id: host_2 8 | mgmt_addr: 172.16.1.2 9 | virbr_addr: 192.168.122.1 10 | account: cyuser 11 | 12 | - guest_settings: 13 | - id: desktop 14 | basevm_host: host_1 15 | basevm_config_file: /home/cyuser/images/basevm.xml 16 | basevm_type: kvm 17 | 18 | - clone_settings: 19 | - range_id: 123 20 | hosts: 21 | - host_id: host_1, host_2 22 | instance_number: 1 23 | guests: 24 | - guest_id: desktop 25 | number: 1 26 | entry_point: yes 27 | topology: 28 | - type: custom 29 | networks: 30 | - name: office 31 | members: desktop.eth0 32 | -------------------------------------------------------------------------------- /instantiation/content_copy_program_run/copy_content_win.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script is for copying content from outside to cyber range 4 | 5 | src=$1 6 | dst=$2 7 | image_addr=$3 8 | image_passwd=$4 9 | 10 | # check if the dst directory exists 11 | dir_exist=`sshpass -p ${image_passwd} ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${image_addr} "powershell Test-Path ${dst}"` 12 | if [ $(echo "${dir_exist}" | grep -e 'True') ]; then 13 | : 14 | else 15 | sshpass -p ${image_passwd} ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${image_addr} "mkdir ${dst}" 16 | fi 17 | 18 | # copy content from src to dst 19 | sshpass -p ${image_passwd} scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -r $src root@${image_addr}:"${dst}" 20 | 21 | -------------------------------------------------------------------------------- /instantiation/vm_clone/check_ping.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import sys 5 | 6 | def check_ping(addr): 7 | 8 | return_value = os.system("ping -c 1 " + addr) 9 | exit_status = os.WEXITSTATUS(return_value) 10 | if exit_status == 0: 11 | pingstatus = 1 12 | else: 13 | pingstatus = 0 14 | return pingstatus 15 | 16 | 17 | addr_file = sys.argv[1] 18 | addr_list = [] 19 | try: 20 | with open(addr_file) as f: 21 | addr_list = f.readlines() 22 | except IOError: 23 | print "Could not read file:", addr_file 24 | for addr in addr_list: 25 | addr = addr.rstrip("\n") 26 | print addr 27 | if not addr: 28 | print "WARNING: Address to ping is empty." 29 | continue 30 | while True: 31 | status = check_ping(addr) 32 | if status == 1: 33 | print addr," connectable" 34 | break 35 | 36 | -------------------------------------------------------------------------------- /instantiation/ruleset_modification/append_ruleset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | RULESET_FILE = sys.argv[1] 6 | IPCONFIGS_TEMP = sys.argv[2] 7 | 8 | class AppendRuleset(): 9 | def readRuleset(self): 10 | list_rules = [] 11 | f = open(RULESET_FILE, "r") 12 | if not f: 13 | print("Cannot open firewall ruleset file '{}' => abort ruleset append".format(RULESET_FILE)); 14 | return None 15 | for line in f: 16 | list_rules.append(line) 17 | return list_rules 18 | 19 | def appendRuleset(self): 20 | list_rules = self.readRuleset() 21 | 22 | if list_rules: 23 | with open(IPCONFIGS_TEMP, "a") as f: 24 | for rule in list_rules: 25 | f.write(rule) 26 | f.write("COMMIT\n") 27 | 28 | appendRuleset = AppendRuleset() 29 | appendRuleset.appendRuleset() 30 | -------------------------------------------------------------------------------- /instantiation/vm_clone/initif/ip_calc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #argument="192.168.122.0/24" 4 | argument=${1} 5 | 6 | ip_address=`echo ${argument} | cut -d '/' -f 1` 7 | ip_bytes=`echo ${ip_address} | sed "s/\./ /g"` 8 | netmask_suffix=`echo ${argument} | cut -d '/' -f 2` 9 | 10 | netmask_address="" 11 | broadcast_address="" 12 | 13 | for byte in ${ip_bytes}; 14 | do 15 | if [ ${byte} -ne 0 ]; 16 | then 17 | netmask_address="${netmask_address}.255" 18 | broadcast_address="${broadcast_address}.${byte}" 19 | else 20 | netmask_address="${netmask_address}.0" 21 | broadcast_address="${broadcast_address}.255" 22 | fi 23 | done 24 | 25 | netmask_address=`echo ${netmask_address} | cut -d '.' -f 2-` 26 | broadcast_address=`echo ${broadcast_address} | cut -d '.' -f 2-` 27 | 28 | echo -e "Address:\t${ip_address}" 29 | echo -e "Netmask:\t${netmask_address} / ${netmask_suffix}" 30 | echo -e "Broadcast:\t${broadcast_address}" 31 | echo -e "Network:\t${argument}" 32 | 33 | -------------------------------------------------------------------------------- /HOST-PREPARE.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is for preparing host Ubuntu Server 16.04 ready for CyRIS. 4 | 5 | sudo apt-get update 6 | 7 | # 1. Install kvm. 8 | sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils 9 | 10 | # 2. Install virt-manager. 11 | sudo apt-get install virt-manager 12 | 13 | # 3. Install pip. 14 | sudo apt-get install python-pip 15 | 16 | # 4. Install python-paramiko. 17 | sudo apt-get install python-paramiko 18 | 19 | # 5. Install tcpreplay. 20 | sudo apt-get install tcpreplay 21 | 22 | # 6. Install wireshark. 23 | sudo apt-get install wireshark 24 | 25 | # 7. Install sshpass. 26 | sudo apt-get install sshpass 27 | 28 | # 8. Install pssh. 29 | sudo apt-get install pssh 30 | 31 | # 9. Install yaml for python. 32 | sudo apt-get install python-yaml 33 | 34 | # 10. Install scapy for python. 35 | sudo apt-get install python-scapy 36 | 37 | # 11. Install sendemail 38 | sudo apt-get install sendemail 39 | 40 | # 12. ssh-copy-id to itself and other hosts. 41 | ssh-copy-id localhost 42 | 43 | -------------------------------------------------------------------------------- /instantiation/sshkey_hostname_setup/sshkey_setup_win_cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ABS_PATH=$1 4 | INSTANTIATION_DIR=$2 5 | vm_addr=$3 6 | vm_passwd=$4 7 | mstnode_account=$5 8 | 9 | echo "windows sshkey setup("${vm_addr}")" 10 | 11 | # clear old ssh keys if exist 12 | ssh-keygen -f "/home/${mstnode_account}/.ssh/known_hosts" -R ${vm_addr} > /dev/null 13 | 14 | #echo "## make .ssh dir" 15 | sshpass -p ${vm_passwd} ssh -o StrictHostKeyChecking=no root@${vm_addr} "mkdir C:\Users\root\.ssh" 16 | 17 | #echo "## add sshkey in authorized_keys" 18 | sshkey=`cat ~/.ssh/id_rsa.pub` 19 | sshpass -p ${vm_passwd} ssh root@${vm_addr} "echo ${sshkey} >> C:\Users\root\.ssh\authorized_keys" 20 | 21 | #echo "## create change acl script" 22 | sshpass -p ${vm_passwd} scp ${ABS_PATH}${INSTANTIATION_DIR}/sshkey_hostname_setup/create_ch_acl.ps1 root@${vm_addr}:"C:\Users\root\.ssh\create_ch_acl.ps1" 23 | 24 | #echo "## exe change acl script" 25 | sshpass -p ${vm_passwd} ssh root@${vm_addr} "powershell C:\Users\root\.ssh\create_ch_acl.ps1" 26 | 27 | #echo "## delete acl script" 28 | sshpass -p ${vm_passwd} ssh root@${vm_addr} "del /Q C:\Users\root\.ssh\create_ch_acl.ps1" -------------------------------------------------------------------------------- /instantiation/sshkey_hostname_setup/sshkey_setup_win_unix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ABS_PATH=$1 4 | INSTANTIATION_DIR=$2 5 | vm_addr=$3 6 | vm_passwd=$4 7 | mstnode_account=$5 8 | 9 | echo "windows sshkey setup("${vm_addr}")" 10 | 11 | # clear old ssh keys if exist 12 | ssh-keygen -f "/home/${mstnode_account}/.ssh/known_hosts" -R ${vm_addr} > /dev/null 13 | 14 | #echo "## make .ssh dir" 15 | sshpass -p ${vm_passwd} ssh -o StrictHostKeyChecking=no root@${vm_addr} "mkdir C:\Users\root\.ssh" 16 | 17 | #echo "## add sshkey in authorized_keys" 18 | sshkey=`cat ~/.ssh/id_rsa.pub` 19 | sshpass -p ${vm_passwd} ssh root@${vm_addr} "echo ${sshkey} >> C:\Users\root\.ssh\authorized_keys" 20 | 21 | #echo "## create change acl script" 22 | sshpass -p ${vm_passwd} scp ${ABS_PATH}${INSTANTIATION_DIR}/sshkey_hostname_setup/create_ch_acl.ps1 root@${vm_addr}:"C:\Users\root\.ssh\create_ch_acl.ps1" 23 | 24 | #echo "## exe change acl script" 25 | sshpass -p ${vm_passwd} ssh root@${vm_addr} "powershell C:\Users\root\.ssh\create_ch_acl.ps1" 26 | 27 | #echo "## delete acl script" 28 | sshpass -p ${vm_passwd} ssh root@${vm_addr} "del /Q C:\Users\root\.ssh\create_ch_acl.ps1" -------------------------------------------------------------------------------- /main/aws_info.py: -------------------------------------------------------------------------------- 1 | 2 | # Edit the tags of an AWS client 3 | def edit_tags(client, ins_id, v): 4 | # ins_id : string 5 | # v: string 6 | response = client.create_tags( 7 | Resources=[ins_id], 8 | Tags=[ 9 | { 10 | 'Key': 'Name', 11 | 'Value': v 12 | }, 13 | ] 14 | ) 15 | return response 16 | 17 | # Get info about the instances in an AWS client 18 | def get_info(client): 19 | response = client.describe_instances() 20 | 21 | m = len(response['Reservations']) 22 | 23 | lst = [] 24 | 25 | for i in range(m): 26 | n = len(response['Reservations'][i]['Instances']) 27 | for j in range(n): 28 | dic = {} 29 | if 'PublicIpAddress' in response['Reservations'][i]['Instances'][j]: 30 | 31 | dic['Id'] = response['Reservations'][i]['Instances'][j]['InstanceId'] 32 | dic['IpAddress'] = response['Reservations'][i]['Instances'][j]['PublicIpAddress'] 33 | dic['status'] = response['Reservations'][i]['Instances'][j]['State']['Name'] 34 | dic['tags'] = response['Reservations'][i]['Instances'][j]['Tags'] 35 | lst.append(dic) 36 | return lst 37 | -------------------------------------------------------------------------------- /instantiation/ruleset_modification/firewall_ruleset: -------------------------------------------------------------------------------- 1 | # Allow Loopback access 2 | -A INPUT -i lo -j ACCEPT 3 | -A OUTPUT -o lo -j ACCEPT 4 | 5 | # Allow ALL incoming SSH 6 | -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT 7 | -A OUTPUT -o eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT 8 | 9 | # Allow ALL outgoing SSH 10 | -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT 11 | -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT 12 | 13 | # Allow ALL outgoing HTTP 14 | -A OUTPUT -o eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT 15 | -A INPUT -i eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT 16 | 17 | # Allow ALL outgoing HTTPS 18 | -A OUTPUT -o eth0 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT 19 | -A INPUT -i eth0 -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT 20 | 21 | # Allow outgoing DNS connections 22 | -A OUTPUT -p udp -o eth0 --dport 53 -j ACCEPT 23 | -A INPUT -p udp -i eth0 --sport 53 -j ACCEPT 24 | -A OUTPUT -p tcp -o eth0 --dport 53 -j ACCEPT 25 | -A INPUT -p tcp -i eth0 --sport 53 -j ACCEPT 26 | 27 | # Block every other type of connection 28 | -A INPUT -i eth0 -j DROP 29 | -A OUTPUT -o eth0 -j DROP 30 | -------------------------------------------------------------------------------- /main/aws_sg.py: -------------------------------------------------------------------------------- 1 | 2 | # Create an AWS security group 3 | def create_security_group(client,gName): 4 | # input: gName: string 5 | # output: 6 | groupId = client.create_security_group( 7 | Description='SG for SSH-access', 8 | GroupName=gName 9 | ) 10 | return groupId 11 | 12 | ''' 13 | # example 14 | import boto3 15 | client = boto3.client('ec2', region_name='us-east-1') 16 | gName = 'cr01-sg' 17 | s = create_security_group(client, gName) 18 | print(s) 19 | ''' 20 | 21 | # Authorize the security group for an AWS client ingress 22 | def edit_ingress(client, gName): 23 | response = client.authorize_security_group_ingress( 24 | CidrIp='0.0.0.0/0', 25 | FromPort=22, 26 | GroupName=gName, 27 | IpProtocol='tcp', 28 | ToPort=22 29 | ) 30 | return response 31 | 32 | ''' 33 | # example: 34 | import boto3 35 | client = boto3.client('ec2', region_name='us-east-1') 36 | gName = 'cr01-sg' 37 | s = edit_ingress(client, gName) 38 | print(s) 39 | ''' 40 | 41 | # Describe the security groups for an AWS client 42 | def describe_security_groups(client, gNames): 43 | response = client.describe_security_groups( 44 | GroupNames=gNames 45 | ) 46 | # GroupID = [] 47 | # GroupID.append(response['SecurityGroups'][0]['GroupId']) 48 | return response 49 | 50 | ''' 51 | # example: 52 | import boto3 53 | gNames = ['cr01-sg'] 54 | client = boto3.client('ec2', region_name='us-east-1') 55 | r = describe_security_groups(client, gNames) 56 | print(r) 57 | ''' 58 | -------------------------------------------------------------------------------- /instantiation/ruleset_modification/firewall_ruleset.sh: -------------------------------------------------------------------------------- 1 | # Allow Loopback access 2 | iptables -A INPUT -i lo -j ACCEPT 3 | iptables -A OUTPUT -o lo -j ACCEPT 4 | 5 | # Allow ALL incoming SSH 6 | iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT 7 | iptables -A OUTPUT -o eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT 8 | 9 | # Allow ALL outgoing SSH 10 | iptables -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT 11 | iptables -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT 12 | 13 | # Allow ALL outgoing HTTP 14 | iptables -A OUTPUT -o eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT 15 | iptables -A INPUT -i eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT 16 | 17 | # Allow ALL outgoing HTTPS 18 | iptables -A OUTPUT -o eth0 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT 19 | iptables -A INPUT -i eth0 -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT 20 | 21 | # Allow ALL outgoing HTTP 22 | iptables -A OUTPUT -o eth0 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT 23 | iptables -A INPUT -i eth0 -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT 24 | 25 | # Allow outgoing DNS connections 26 | iptables -A OUTPUT -p udp -o eth0 --dport 53 -j ACCEPT 27 | iptables -A INPUT -p udp -i eth0 --sport 53 -j ACCEPT 28 | iptables -A OUTPUT -p tcp -o eth0 --dport 53 -j ACCEPT 29 | iptables -A INPUT -p tcp -i eth0 --sport 53 -j ACCEPT 30 | 31 | # Block every other type of connection 32 | iptables -A INPUT -i eth0 -j DROP 33 | iptables -A OUTPUT -o eth0 -j DROP 34 | -------------------------------------------------------------------------------- /cleanup/downbridges.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # this script is to bring down cyber range 's bridges. It gets the job done by open and see how many bridges are there in the create_bridges.sh file 4 | 5 | import sys 6 | import fcntl 7 | 8 | filename = sys.argv[1] 9 | clone_id = sys.argv[2] 10 | 11 | def down_bridges(): 12 | number = 0 13 | count = 0 14 | # Open the /etc/network/interfaces with atomic mode. 15 | with open(filename, "r+") as my_file: 16 | fcntl.flock(my_file, fcntl.LOCK_EX | fcntl.LOCK_NB) 17 | # Read content of the file. 18 | file_lines = my_file.readlines() 19 | for line in file_lines: 20 | if "auto eth{0}".format(clone_id) in line: 21 | if count == 0: 22 | number = file_lines.index(line) 23 | count = count + 1 24 | 25 | if number != 0: 26 | gap = 0 27 | if count != 1: 28 | gap = 11 * count 29 | else: 30 | gap = 10 31 | print "starting line: ", number - 1 32 | print "ending line: ", number + gap + 1 33 | first_part = file_lines[:(number-1)] 34 | second_part = file_lines[(number+gap):] 35 | my_file.seek(0) 36 | if first_part[-1] != "\n" and second_part and second_part[0] != "\n": 37 | my_file.writelines(first_part) 38 | my_file.writelines("\n") 39 | my_file.writelines(second_part) 40 | else: 41 | my_file.writelines(first_part+second_part) 42 | my_file.truncate() 43 | fcntl.flock(my_file, fcntl.LOCK_UN) 44 | 45 | down_bridges() 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016-2021, Japan Advanced Institute of Science and Technology 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /instantiation/logs_preparation/pcap_sshattack_generator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | abs_path=$1 4 | virbr_addr=$2 5 | host_account=$3 6 | image_addr=$4 7 | image_passwd=$5 8 | attack_source=$6 9 | num=$7 10 | noise_level=$8 11 | file_path=$9 12 | file_name=${10} 13 | cr_dir=${11} 14 | basevm_type=${12} 15 | 16 | inst_dir="instantiation" 17 | 18 | # opens tcpdump to start capturing packets on two interfaces: eth0 and virbr0 19 | sudo bash -c "exec -a virbr0_ssh_pcap tcpdump -i virbr0 -w ${cr_dir}virbr0_ssh.pcap &"; 20 | 21 | # base image starts attacking the host by ssh 22 | sshpass -p ${image_passwd} scp ${abs_path}${inst_dir}/attacks_emulation/attack_paramiko_ssh.py root@${image_addr}:/bin/cyberrange/; 23 | sshpass -p ${image_passwd} ssh -o StrictHostKeyChecking=no root@${image_addr} "python /bin/cyberrange/attack_paramiko_ssh.py ${virbr_addr} ${host_account} ${num} none ${basevm_type}"; 24 | 25 | sudo pkill -f virbr0_ssh_pcap; 26 | 27 | #echo "done" 28 | sudo apt-get install -y tcpreplay; 29 | 30 | # changes ipaddresses in two pcap files and merges them as one 31 | sudo tcprewrite -S ${image_addr}/32:${attack_source}/32 -i ${cr_dir}virbr0_ssh.pcap -o ${cr_dir}virbr0_1.pcap; 32 | sudo tcprewrite -D ${image_addr}/32:${attack_source}/32 -i ${cr_dir}virbr0_1.pcap -o ${cr_dir}virbr0_2.pcap; 33 | sudo tcprewrite -S ${virbr_addr}/32:${image_addr}/32 -i ${cr_dir}virbr0_2.pcap -o ${cr_dir}virbr0_3.pcap; 34 | sudo tcprewrite -D ${virbr_addr}/32:${image_addr}/32 -i ${cr_dir}virbr0_3.pcap -o ${cr_dir}attack.pcapng; 35 | 36 | # change timestamp of noise file and merge with the attack pcap file 37 | sudo python ${abs_path}${inst_dir}/logs_preparation/mergePcap.py ${noise_level} ${file_name} ${abs_path} ${cr_dir}; 38 | 39 | # remove unecessary pcap files 40 | sudo rm ${cr_dir}virbr0*.pcap; 41 | 42 | # copy pcap file to trainee's directory 43 | sshpass -p ${image_passwd} scp ${cr_dir}${file_name} root@${image_addr}:${file_path}; 44 | sudo rm ${cr_dir}${file_name}; 45 | -------------------------------------------------------------------------------- /main/parse_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import ConfigParser 4 | import logging 5 | import os.path 6 | 7 | from storyboard import Storyboard 8 | 9 | ################################################################## 10 | # Function for parsing parameters provided in CONFIG file 11 | ################################################################## 12 | 13 | def parse_config(config_file): 14 | 15 | abs_path = None 16 | cr_dir = None 17 | gw_mode = False 18 | gw_account = None 19 | gw_mgmt_addr = None 20 | gw_inside_addr = None 21 | user_email = None 22 | 23 | if os.path.exists(config_file): 24 | 25 | # Create object and read config file 26 | config = ConfigParser.ConfigParser() 27 | config.read(config_file) 28 | 29 | # Process the options 30 | for option in config.options(Storyboard.SECTION_NAME): 31 | value = config.get(Storyboard.SECTION_NAME, option) 32 | logging.debug("Option '{0}' => {1}".format(option, value)) 33 | if option == Storyboard.CYRIS_PATH: 34 | abs_path = value 35 | elif option == Storyboard.CYBER_RANGE_DIR: 36 | cr_dir = value 37 | elif option == Storyboard.GW_MODE: 38 | # gw_mode is a boolean, so we get again the value as boolean 39 | gw_mode = config.getboolean(Storyboard.SECTION_NAME, option) 40 | elif option == Storyboard.GW_ACCOUNT: 41 | gw_account = value 42 | elif option == Storyboard.GW_MGMT_ADDR: 43 | gw_mgmt_addr = value 44 | elif option == Storyboard.GW_INSIDE_ADDR: 45 | gw_inside_addr = value 46 | elif option == Storyboard.USER_EMAIL: 47 | user_email = value 48 | else: 49 | logging.warning("Unknown configuration option: " + option) 50 | 51 | else: 52 | logging.error("Configuration file not found: " + config_file) 53 | return [False] * 7 54 | 55 | return abs_path, cr_dir, gw_mode, gw_account, gw_mgmt_addr, gw_inside_addr, user_email 56 | -------------------------------------------------------------------------------- /instantiation/logs_preparation/pcap_ddosattack_generator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The flow of the emulation will be as followed: 4 | # - Install the tool hping3 for the base image 5 | # - Start tcpdump on the host to listen to traffic comming to the virtual bridge virbr0 (kvm) 6 | # - Copy the script attacks_emulation/launch_ddos.sh to the base image and start the attack to nic virbr0 of the host 7 | # - Terminate the attack after two seconds 8 | # - Change the victim address in the pcap file (currently virbr0 addr) to image's address 9 | # - Use mergePcap.py to add noise to the pcap file, and copy it to the base image 10 | 11 | abs_path=$1 12 | virbr_addr=$2 13 | image_addr=$3 14 | image_passwd=$4 15 | noise_level=$5 16 | file_path=$6 17 | file_name=$7 18 | cr_dir=$8 19 | 20 | inst_dir="instantiation" 21 | 22 | # install hping3 for base image 23 | sshpass -p ${image_passwd} ssh root@${image_addr} yum install -y hping3; 24 | 25 | # opens tcpdump to start capturing packets on two interfaces: eth0 and virbr0 26 | sudo bash -c "exec -a virbr0_ddos_pcap tcpdump -i virbr0 -c 1000 port 80 -w ${cr_dir}virbr0_ddos.pcap &"; 27 | 28 | # base image starts attacking the host by ddos on port 80 29 | sshpass -p ${image_passwd} scp ${abs_path}${inst_dir}/attacks_emulation/launch_ddos.sh root@${image_addr}:/bin/cyberrange; 30 | sshpass -p ${image_passwd} ssh root@${image_addr} /bin/cyberrange/launch_ddos.sh ${virbr_addr}; 31 | 32 | sudo pkill -f virbr0_ddos_pcap; 33 | 34 | echo "done" 35 | sudo apt-get install -y tcpreplay; 36 | 37 | # changes ipaddresses in two pcap files and merges them as one 38 | tcprewrite -S ${virbr_addr}/32:${image_addr}/32 -i ${cr_dir}virbr0_ddos.pcap -o ${cr_dir}virbr0_3.pcap; 39 | tcprewrite -D ${virbr_addr}/32:${image_addr}/32 -i ${cr_dir}virbr0_3.pcap -o ${cr_dir}attack.pcapng; 40 | 41 | # change timestamp of noise file and merge with the attack pcap file 42 | sudo python ${abs_path}${inst_dir}/logs_preparation/mergePcap.py ${noise_level} ${file_name} ${abs_path} ${cr_dir}; 43 | 44 | sudo rm -f ${cr_dir}virbr0*.pcap; 45 | 46 | # copy pcap file to trainee's directory 47 | sshpass -p ${image_passwd} scp ${cr_dir}${file_name} root@${image_addr}:${file_path}; 48 | sudo rm -f ${cr_dir}${file_name}; 49 | -------------------------------------------------------------------------------- /instantiation/logs_preparation/pcap_dosattack_generator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The flow of the emulation will be as followed: 4 | # - Install the tool hping3 for the base image 5 | # - Start tcpdump on the host to listen to traffic comming to the virtual bridge virbr0 (kvm) 6 | # - Copy the script attacks_emulation/launch_dos.sh to the base image and start the attack to nic virbr0 of the host 7 | # with user-specified source address (source_addr) and destination port (dport) 8 | # - Terminate the attack after two seconds 9 | # - Change the victim address in the pcap file (currently virbr0 addr) to image's address 10 | # - Use mergePcap.py to add noise to the pcap file, and copy it to the base image 11 | # 12 | abs_path=$1 13 | virbr_addr=$2 14 | image_addr=$3 15 | image_passwd=$4 16 | noise_level=$5 17 | file_path=$6 18 | file_name=$7 19 | source_addr=$8 # the address that starts the attack 20 | dport=$9 21 | cr_dir=${10} 22 | 23 | inst_dir="instantiation" 24 | 25 | # install hping3 for base image 26 | sshpass -p ${image_passwd} ssh root@${image_addr} yum install -y hping3; 27 | 28 | # opens tcpdump to start capturing packets on two interfaces: eth0 and virbr0 29 | sudo bash -c "exec -a virbr0_dos_pcap tcpdump -i virbr0 -c 1000 port ${dport} -w ${cr_dir}virbr0_dos.pcap &"; 30 | 31 | # base image starts attacking the host by dos 32 | sshpass -p ${image_passwd} scp ${abs_path}${inst_dir}/attacks_emulation/launch_dos.sh root@${image_addr}:/bin/cyberrange; 33 | sshpass -p ${image_passwd} ssh root@${image_addr} /bin/cyberrange/launch_dos.sh ${source_addr} ${virbr_addr} ${dport}; 34 | 35 | sudo pkill -f virbr0_dos_pcap; 36 | 37 | echo "done" 38 | sudo apt-get install -y tcpreplay; 39 | 40 | # changes ipaddresses in two pcap files and merges them as one 41 | tcprewrite -S ${virbr_addr}/32:${image_addr}/32 -i ${cr_dir}virbr0_dos.pcap -o ${cr_dir}virbr0_3.pcap; 42 | tcprewrite -D ${virbr_addr}/32:${image_addr}/32 -i ${cr_dir}virbr0_3.pcap -o ${cr_dir}attack.pcapng; 43 | 44 | # change timestamp of noise file and merge with the attack pcap file 45 | sudo python ${abs_path}${inst_dir}/logs_preparation/mergePcap.py ${noise_level} ${file_name} ${abs_path} ${cr_dir}; 46 | 47 | sudo rm -f ${cr_dir}virbr0*.pcap; 48 | 49 | # copy pcap file to trainee's directory 50 | sshpass -p ${image_passwd} scp ${cr_dir}${file_name} root@${image_addr}:${file_path}; 51 | sudo rm -f ${cr_dir}${file_name}; 52 | -------------------------------------------------------------------------------- /instantiation/content_copy_program_run/copy_content.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this script is for copying content from outside to cyber range 4 | 5 | src=$1 6 | dst=$2 7 | image_addr=$3 8 | basevm_type=$4 9 | os_type=$5 10 | 11 | # Check whether the specified source file or directory exists 12 | if [ ! -f "${src}" -a ! -d "${src}" ]; then 13 | echo 14 | echo "copy_content.sh: File or directory '${src}' doesn't exist => abort copy" 15 | exit 1 16 | fi 17 | 18 | if [ ${basevm_type} = "kvm" ]; then 19 | # check if the dst directory exists 20 | if (ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${image_addr} "[ -d ${dst} ]") 21 | then 22 | : 23 | else 24 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${image_addr} "mkdir -p ${dst}" 25 | fi 26 | # copy content from src to dst 27 | scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -r ${src} root@${image_addr}:${dst} 28 | elif [ ${basevm_type} = "aws" ]; then 29 | if [ ${os_type} = "red_hat" -o ${os_type} = "amazon_linux" -o ${os_type} = "amazon_linux2" ]; then 30 | if (ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ec2-user@${image_addr} "[ -d ${dst} ]") 31 | then 32 | : 33 | else 34 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ec2-user@${image_addr} "sudo mkdir -p ${dst}" 35 | fi 36 | scp -r -i TESTKEY.pem -o StrictHostKeyChecking=no ${src} ec2-user@${image_addr}: 37 | # Special syntax for ${src} to preserve the last part of the name (after /) for the local 'mv' 38 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ec2-user@${image_addr} "sudo mv ${src##*/} ${dst}" 39 | elif [ ${os_type} = "ubuntu_20" -o ${os_type} = "ubuntu_18" -o ${os_type} = "ubuntu_16" ]; then 40 | if (ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${image_addr} "[ -d ${dst} ]") 41 | then 42 | : 43 | else 44 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${image_addr} "sudo mkdir -p ${dst}" 45 | fi 46 | scp -r -i TESTKEY.pem -o StrictHostKeyChecking=no ${src} ubuntu@${image_addr}: 47 | # Special syntax for ${src} to preserve the last part of the name (after /) for the local 'mv' 48 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${image_addr} "sudo mv ${src##*/} ${dst}" 49 | fi 50 | fi 51 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | 2 | CyRIS v1.2 3 | ---------- 4 | * Improved execution messages and logged information 5 | * Improved robustness of certain task handling by checking whether 6 | specified files exist before attempting execution 7 | * Added support for the Amazon Web Services (AWS) virtualization 8 | platform in addition to KVM 9 | * Added example range file to illustrate the use of AWS virtualization 10 | * Included noise PCAP files in the release, so that the 11 | 'emulate_traffic_capture_file' task can now be used without any 12 | additional setup on the user side 13 | * Improved robustness of range description checking 14 | * Other minor improvements and bug fixes 15 | 16 | CyRIS v1.1 17 | ---------- 18 | * Added support for Windows as guest VM operating system 19 | * Introduced the keyword 'basevm_os_type' to specify the guest VM OS 20 | * Fixed a bug related to providing multiple arguments in the 21 | 'execute_program' task 22 | * Other minor improvements and bug fixes 23 | 24 | CyRIS v1.0 25 | ---------- 26 | * Modified the settings of the sample base VM to make it easier 27 | to use by external parties 28 | * Improved the verification mechanism for the shutdown state of VMs 29 | * Updated the description file examples to latest syntax 30 | * Improved the connectivity checking for VMs (now using actual ssh 31 | connections for validation) 32 | * Improved the syntax checking for description files, including 33 | detection of unknown tags, invalid values, undefined guests, etc. 34 | * Other minor improvements and bug fixes 35 | 36 | CyRIS v0.2 37 | ---------- 38 | * Simplified README.md as most information is in User Guide 39 | * Added a generic multi-host description sample 40 | * Modified some directory and file names for consistency 41 | * Eliminated use of literal 'host_1' id in code related to pscp 42 | * Changed format of configuration file CONFIG, and the section name 43 | [config] is now required; parameter names use lower case, and 44 | ABS_PATH has been renamed to cyris_path for clarity 45 | * Fixed error in DMZ example by which the "forwarding_rules" field in 46 | the "guests" section of "clone_setttings" was incorrectly written as 47 | "firewall_rules" 48 | * Set "yum" as default package manager, hence the "package_manager" 49 | field can now be omitted in "install_package" if the target OS is 50 | CentOS or equivalent 51 | * Added the tag "full_name" to the "add_account" operation, so that 52 | the user's full name can also be specified for new accounts. 53 | * Other minor improvements and bug fixes 54 | 55 | CyRIS v0.1 56 | ---------- 57 | * First public release 58 | -------------------------------------------------------------------------------- /instantiation/logs_preparation/mergePcap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | from time import sleep 7 | from scapy.all import PcapReader 8 | #from scapy.error import Scapy_Exception 9 | 10 | NOISE = sys.argv[1] 11 | FILE_NAME = sys.argv[2] 12 | ABSPATH = sys.argv[3] 13 | CR_DIR = sys.argv[4] 14 | 15 | INSTANTIATION_DIR="instantiation" 16 | 17 | class PcapCreation(): 18 | def execute_command(self, command): 19 | subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 20 | 21 | def get_pcap_timestamp(self, pcapfile): 22 | try: 23 | pcap_content = PcapReader(pcapfile) 24 | list_timestamp = list(pcap_content) 25 | start_time = list_timestamp[0].time 26 | end_time = list_timestamp[-1].time 27 | except IOError: 28 | pcap_content = PcapReader(pcapfile) 29 | list_timestamp = list(pcap_content) 30 | start_time = list_timestamp[0].time 31 | end_time = list_timestamp[-1].time 32 | 33 | print "{0}: {1} - {2}".format(pcapfile, start_time, end_time) 34 | return (start_time + end_time)/2 35 | 36 | def merge_pcap(self): 37 | noise_file = "" 38 | 39 | # convert pcapng to pcap 40 | command = "editcap -F libpcap {0}attack.pcapng {0}attack.pcap;\n".format(CR_DIR) 41 | os.system(command) 42 | 43 | path = "{0}attack.pcap".format(CR_DIR) 44 | 45 | sleep(0.5) 46 | if os.path.isfile(path): 47 | print "yes" 48 | else: 49 | print "no" 50 | 51 | time1 = self.get_pcap_timestamp(path) 52 | if NOISE == "low": 53 | noise_file = "{0}{1}/logs_preparation/noise_low.pcap".format(ABSPATH, INSTANTIATION_DIR) 54 | elif NOISE == "medium": 55 | noise_file = "{0}{1}/logs_preparation/noise_medium.pcap".format(ABSPATH, INSTANTIATION_DIR) 56 | elif NOISE == "high": 57 | noise_file = "{0}{1}/logs_preparation/noise_high.pcap".format(ABSPATH, INSTANTIATION_DIR) 58 | print "{0} \n".format(noise_file) 59 | time2 = self.get_pcap_timestamp(noise_file) 60 | 61 | print "{0}\n".format(FILE_NAME) 62 | # shift time of noise pcap file 63 | command = "editcap -t {0} {1} {2}noise.pcap;\n".format(time1-time2, noise_file, CR_DIR) 64 | # merge pcap files 65 | command += "mergecap {0}noise.pcap {0}attack.pcap -w {0}{1};\n".format(CR_DIR, FILE_NAME) 66 | # clean folder 67 | command += "rm -f {0}noise.pcap; rm -f {0}attack.*; \n".format(CR_DIR) 68 | os.system(command) 69 | 70 | a = PcapCreation() 71 | a.merge_pcap() 72 | -------------------------------------------------------------------------------- /instantiation/content_copy_program_run/run_program.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import subprocess 5 | import urllib 6 | from limitedstringqueue import LimitedStringQueue 7 | 8 | PROGRAM = sys.argv[1] 9 | COMPILER = sys.argv[2] 10 | #ARGS = sys.argv[3] 11 | ARGS = urllib.unquote(sys.argv[3]) 12 | IMAGE_ADDR = sys.argv[4] 13 | IMAGE_PASSWD = sys.argv[5] 14 | LOG_FILE = sys.argv[6] 15 | OS_TYPE=sys.argv[7] 16 | EXECID = sys.argv[8] 17 | 18 | # this program is for executing outside program on cyber range 19 | class RunProgram(): 20 | # this def allows program to run shell commands in python 21 | def execute_command(self, command): 22 | #p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 23 | p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=None) 24 | q = LimitedStringQueue() 25 | with open(LOG_FILE, "a") as myfile: 26 | for line in p.stdout.readlines(): 27 | q.push(line) 28 | myfile.write(line,) 29 | myfile.write("\n") # separate previous outputs 30 | myfile.write("exec-result: "+EXECID+" "+q.concaturlenc()) 31 | myfile.write("\n") # separate following outputs 32 | # Waiting for a return code would not allow background execution, so we don't do it 33 | 34 | # execute commands to run the program on cyber range 35 | def runProgram(self): 36 | program_compiler = "" 37 | 38 | # get the appropriate compiler 39 | if COMPILER == "python": 40 | program_compiler = "python" 41 | if COMPILER == "ruby": 42 | program_compiler = "ruby" 43 | if COMPILER == "powershell": 44 | program_compiler = "powershell" 45 | if COMPILER == "bash": 46 | program_compiler = "bash" 47 | # process args 48 | if ARGS == "none": 49 | program_args = "" 50 | else: 51 | program_args = ARGS 52 | 53 | # execute program on virtual machine 54 | defined_aws_version = ["amazon_linux", "amazon_linux2", "red_hat", "ubuntu_16", "ubuntu_18", "ubuntu_20"] 55 | if OS_TYPE=="windows.7": 56 | command = "sshpass -p {0} ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@{1} {2} \"{3}\" {4}".format(IMAGE_PASSWD, IMAGE_ADDR, program_compiler, PROGRAM, program_args) 57 | elif OS_TYPE in defined_aws_version: 58 | command = "sshpass -p {0} ssh -i TESTKEY.pem -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ec2-user@{1} {2} {3} {4}".format(IMAGE_PASSWD, IMAGE_ADDR, program_compiler, PROGRAM, program_args) 59 | else: 60 | command = "sshpass -p {0} ssh -E /dev/null -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@{1} {2} '{3} {4}'".format(IMAGE_PASSWD, IMAGE_ADDR, program_compiler, PROGRAM, program_args) 61 | self.execute_command(command) 62 | print command 63 | 64 | runProgram = RunProgram() 65 | runProgram.runProgram() 66 | -------------------------------------------------------------------------------- /instantiation/vm_clone/initif/initif: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | Set_Addr() 4 | { 5 | DEV=`echo ${line} | gawk '{ 6 | print $1; 7 | }'` 8 | ADDR=`echo ${line} | gawk '{ 9 | print $2; 10 | }'` 11 | MAC=`/sbin/ip link show dev ${DEV} | gawk '{ 12 | if($1 ~ /link\/ether/) { 13 | print $2; 14 | } 15 | }'` 16 | 17 | bash /bin/cyberrange/initif/ip_calc.sh ${ADDR} | gawk 'BEGIN { 18 | dev = "'${DEV}'"; 19 | ipcmd = "/sbin/ip"; 20 | macaddr = "'"${MAC}"'"; 21 | split(macaddr, macoct, ":"); 22 | for(i = 1; i <= 6; i++) { 23 | macoct[i] = "0x" macoct[i]; 24 | } 25 | } 26 | { 27 | if($1 ~ /Address/) { 28 | ip = $2; 29 | split(ip, ipoct, "."); 30 | } 31 | else if($1 ~ /Netmask/) { 32 | mask = $2; 33 | prefix = $4; 34 | } 35 | else if($1 ~ /Broadcast/) { 36 | brd = $2; 37 | } 38 | else if($1 ~ /Network/) { 39 | network = $2; 40 | } 41 | else if($1 ~ /Netmask/) { 42 | prefix = int($2); 43 | } 44 | } 45 | END { 46 | if(prefix == 8) { 47 | ip = ipoct[1] "." strtonum(macoct[4]) "." strtonum(macoct[5]) "." strtonum(macoct[6]); 48 | gw = ipoct[1] "." ipoct[2] "." strtonum(macoct[5]) ".1"; 49 | } 50 | else if(prefix == 16) { 51 | ip = ipoct[1] "." ipoct[2] "." strtonum(macoct[5]) "." strtonum(macoct[6]); 52 | gw = ipoct[1] "." ipoct[2] "." strtonum(macoct[5]) ".1"; 53 | } 54 | else if(prefix == 24) { 55 | ip = ipoct[1] "." ipoct[2] "." ipoct[3] "." strtonum(macoct[6]); 56 | gw = ipoct[1] "." ipoct[2] "." strtonum(macoct[5]) ".1"; 57 | } 58 | cmd = "service NetworkManager stop"; 59 | system(cmd); 60 | cmd = "chkconfig NetworkManager off"; 61 | system(cmd); 62 | cmd = ipcmd " addr add " ip "/" 24 " broadcast " brd " dev " dev; 63 | system(cmd); 64 | cmd = "route add default gw " gw; 65 | system(cmd); 66 | # cmd = "ifup " dev; 67 | # system(cmd); 68 | cmd = ipcmd " link set up " dev; 69 | system(cmd); 70 | }' 71 | } 72 | 73 | Set_Route() 74 | { 75 | echo "${line}" | gawk '{ 76 | dst = $2; 77 | gw = $3; 78 | cmd = "/sbin/ip route add " dst " via " gw; 79 | system(cmd); 80 | }'; 81 | } 82 | 83 | CONF=$1 84 | cat $CONF | while read line 85 | do 86 | case ${line} in 87 | \#*) 88 | ;; 89 | eth*) 90 | Set_Addr "${line}"; 91 | ;; 92 | route*) 93 | Set_Route "${line}"; 94 | ;; 95 | esac 96 | done 97 | 98 | #set hostname 99 | #hostname=`/sbin/ifconfig -a | grep eth0 | gawk '{ 100 | # split($5, macaddr, ":"); 101 | # hostname = "centos." macaddr[6]; 102 | # print hostname; 103 | #}'` 104 | #echo ${hostname} > /proc/sys/kernel/hostname; 105 | -------------------------------------------------------------------------------- /instantiation/malware_creation/dummy_malware.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int port_listening(int portno) 9 | { 10 | int sockfd, newsockfd, clilen; 11 | char buffer[256]; 12 | struct sockaddr_in serv_addr, cli_addr; 13 | int n; 14 | 15 | /* First call to socket() function */ 16 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 17 | 18 | if (sockfd < 0) { 19 | perror("ERROR opening socket"); 20 | return -1; 21 | } 22 | 23 | /* Initialize socket structure */ 24 | bzero((char *) &serv_addr, sizeof(serv_addr)); 25 | 26 | serv_addr.sin_family = AF_INET; 27 | serv_addr.sin_addr.s_addr = INADDR_ANY; 28 | serv_addr.sin_port = htons(portno); 29 | 30 | /* Now bind the host address using bind() call.*/ 31 | if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 32 | perror("ERROR on binding"); 33 | return -1; 34 | } 35 | 36 | /* Now start listening for the clients, here process will 37 | * go in sleep mode and will wait for the incoming connection 38 | */ 39 | 40 | listen(sockfd,5); 41 | clilen = sizeof(cli_addr); 42 | 43 | /* Accept actual connection from the client */ 44 | newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen); 45 | 46 | if (newsockfd < 0) { 47 | perror("ERROR on accept"); 48 | return -1; 49 | } 50 | 51 | /* If connection is established then start communicating */ 52 | bzero(buffer,256); 53 | n = read( newsockfd,buffer,255 ); 54 | 55 | if (n < 0) { 56 | perror("ERROR reading from socket"); 57 | return -1; 58 | } 59 | } 60 | 61 | void dummy_calculation(int time_sleep) 62 | { 63 | while (1) 64 | { 65 | unsigned int n = 100000000; 66 | unsigned int a = 1; 67 | unsigned int b = 1; 68 | for (unsigned int i = 0; i < n; i++) 69 | { 70 | unsigned int temp = a; 71 | a = b; 72 | b += temp; 73 | //printf("%d %d\n",a,b); 74 | } 75 | usleep(time_sleep); 76 | } 77 | } 78 | 79 | int is_prime(int num) 80 | { 81 | if (num == 1 || num == 0) 82 | return 0; 83 | for (int i = 2; i < num; i++) 84 | if (num % i == 0) 85 | return 0; 86 | return 1; 87 | } 88 | 89 | void prime_generator(int time_sleep) 90 | { 91 | while (1) 92 | { 93 | for (int i = 1; i < 9999; i++) 94 | //if (is_prime(i)) 95 | //printf("%d\n",i); 96 | is_prime(i); 97 | usleep(time_sleep); 98 | } 99 | } 100 | 101 | int main(int argc, char *argv[]) 102 | { 103 | int time_sleep; 104 | int port; 105 | 106 | if (argc != 3) { 107 | fprintf(stderr,"usage: %s mode option\n", argv[0]); 108 | return -1; 109 | } 110 | if (!strcmp(argv[1], "port_listening")) { 111 | printf("mode: port listening\n"); 112 | port = atoi(argv[2]); 113 | port_listening(port); 114 | } else if (!strcmp(argv[1], "calculation")) { 115 | printf("mode: calculation\n"); 116 | time_sleep = atoi(argv[2]); 117 | dummy_calculation(time_sleep); 118 | } 119 | return 1; 120 | 121 | } 122 | -------------------------------------------------------------------------------- /examples/dmz.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - host_settings: 3 | - id: host_1 4 | mgmt_addr: localhost 5 | virbr_addr: 192.168.122.1 6 | account: cyuser 7 | 8 | - guest_settings: 9 | - id: firewall 10 | basevm_host: host_1 11 | basevm_config_file: /home/cyuser/images/basevm_firewall.xml 12 | basevm_type: kvm 13 | tasks: 14 | - add_account: 15 | - account: robot.abc 16 | passwd: abcrb1357 17 | - modify_account: 18 | - account: root 19 | new_passwd: abcd.1234 20 | 21 | - id: dnsmail 22 | basevm_host: host_1 23 | basevm_config_file: /home/cyuser/images/basevm_dnsmail.xml 24 | basevm_type: kvm 25 | tasks: 26 | - add_account: 27 | - account: robot.abc 28 | passwd: abcrb1357 29 | - modify_account: 30 | - account: root 31 | new_passwd: abcd.1234 32 | - install_package: 33 | - package_manager: yum 34 | name: wget 35 | - package_manager: yum 36 | name: telnet 37 | 38 | - id: filesrv 39 | basevm_host: host_1 40 | basevm_config_file: /home/cyuser/images/basevm_filesrv.xml 41 | basevm_type: kvm 42 | tasks: 43 | - add_account: 44 | - account: robot.abc 45 | passwd: abcrb1357 46 | - modify_account: 47 | - account: root 48 | new_passwd: abcd.1234 49 | - install_package: 50 | - package_manager: yum 51 | name: samba samba-client samba-common 52 | - package_manager: yum 53 | name: wget 54 | 55 | - id: dbsrv 56 | basevm_host: host_1 57 | basevm_config_file: /home/cyuser/images/basevm_dbsrv.xml 58 | basevm_type: kvm 59 | tasks: 60 | - add_account: 61 | - account: robot.abc 62 | passwd: abcrb1357 63 | - modify_account: 64 | - account: root 65 | new_passwd: abcd.1234 66 | - install_package: 67 | - package_manager: yum 68 | name: wget 69 | 70 | - id: desktop 71 | basevm_host: host_1 72 | basevm_config_file: /home/cyuser/images/basevm_desktop.xml 73 | basevm_type: kvm 74 | tasks: 75 | - add_account: 76 | - account: daniel 77 | passwd: JamesBond 78 | - install_package: 79 | - package_manager: yum 80 | name: nmap 81 | - package_manager: yum 82 | name: telnet 83 | 84 | - clone_settings: 85 | - range_id: 124 86 | hosts: 87 | - host_id: host_1 88 | instance_number: 1 89 | guests: 90 | - guest_id: firewall 91 | number: 1 92 | forwarding_rules: 93 | - rule: src=office,external dst=internal.dbsrv dport=3306 94 | - rule: src=office,external dst=internal.filesrv dport=139,445 95 | - rule: src=office dst=external dport=25,53 96 | entry_point: yes 97 | - guest_id: dnsmail 98 | number: 1 99 | - guest_id: filesrv 100 | number: 1 101 | - guest_id: dbsrv 102 | number: 1 103 | - guest_id: desktop 104 | number: 1 105 | topology: 106 | - type: custom 107 | networks: 108 | - name: external 109 | members: dnsmail.eth0 110 | gateway: firewall.eth0 111 | - name: internal 112 | members: filesrv.eth0, dbsrv.eth0 113 | gateway: firewall.eth1 114 | - name: office 115 | members: desktop.eth0 116 | gateway: firewall.eth2 117 | -------------------------------------------------------------------------------- /instantiation/attacks_emulation/attack_paramiko_ssh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import paramiko, sys, os, socket 3 | import threading 4 | import subprocess 5 | 6 | attacked_addr = sys.argv[1] 7 | username = sys.argv[2] 8 | number = sys.argv[3] 9 | time = sys.argv[4] 10 | basevm_type = sys.argv[5] 11 | 12 | class myThread (threading.Thread): 13 | def __init__(self, threadID, name): 14 | threading.Thread.__init__(self) 15 | self.threadID = threadID 16 | self.name = name 17 | self.assign_number = 0 18 | 19 | def run(self): 20 | print "Starting " + self.name 21 | if(self.threadID != 5): 22 | self.assign_number = int(number)/5 23 | else: 24 | self.assign_number = int(number) - (int(number)/5)*4 25 | 26 | for i in range(0, self.assign_number): 27 | try: 28 | response = ssh_connect() 29 | if response == 1: 30 | print "{}: {}".format(self.name, i) 31 | elif response == 2: 32 | print "socket error" 33 | except Exception, e: 34 | print e 35 | pass 36 | print "Exiting " + self.name 37 | 38 | def ssh_connect(): 39 | ssh = paramiko.SSHClient() 40 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 41 | 42 | try: 43 | ssh.connect(attacked_addr, port=22, username=username, password="abcd") 44 | except paramiko.AuthenticationException: 45 | response = 1 46 | except socket.error: 47 | response = 2 48 | 49 | ssh.close() 50 | return response 51 | 52 | # Set system date as the same as input 53 | if time != "none": 54 | if basevm_type == 'kvm': 55 | os.system("ssh root@{0} date +%Y%m%d -s {1}".format(attacked_addr, time)) 56 | elif basevm_type == 'aws': 57 | os.system("ssh -i TESTKEY.pem ec2-user@{0} date +%Y%m%d -s {1}".format(attacked_addr, time)) 58 | 59 | # Create new threads 60 | thread1 = myThread(1, "Thread-1") 61 | thread2 = myThread(2, "Thread-2") 62 | thread3 = myThread(3, "Thread-3") 63 | thread4 = myThread(4, "Thread-4") 64 | thread5 = myThread(5, "Thread-5") 65 | 66 | # Start new Threads 67 | thread1.start() 68 | thread2.start() 69 | thread3.start() 70 | thread4.start() 71 | thread5.start() 72 | 73 | # Wait until threads are finished 74 | thread1.join() 75 | thread2.join() 76 | thread3.join() 77 | thread4.join() 78 | thread5.join() 79 | 80 | # Set system date to the correct value. 81 | if time != "none": 82 | if basevm_type == 'kvm': 83 | correct_date = subprocess.check_output("date +%Y%m%d", shell=True) 84 | correct_time = subprocess.check_output("date +%T", shell=True) 85 | os.system("ssh root@{0} date +%Y%m%d -s {1}".format(attacked_addr, correct_date)) 86 | os.system("ssh root@{0} date +%T -s {1}".format(attacked_addr, correct_time)) 87 | os.system("ssh root@{0} sort --stable --reverse --key=1,2 /var/log/secure -o /var/log/secure".format(attacked_addr)) 88 | elif basevm_type == 'aws': 89 | correct_date = subprocess.check_output("date +%Y%m%d", shell=True) 90 | correct_time = subprocess.check_output("date +%T", shell=True) 91 | os.system("ssh -i TESTKEY.pem ec2-user@{0} sudo date +%Y%m%d -s {1}".format(attacked_addr, correct_date)) 92 | os.system("ssh -i TESTKEY.pem ec2-user@{0} sudo date +%T -s {1}".format(attacked_addr, correct_time)) 93 | os.system("ssh -i TESTKEY.pem ec2-user@{0} sudo sort --stable --reverse --key=1,2 /var/log/secure -o /var/log/secure".format(attacked_addr)) 94 | -------------------------------------------------------------------------------- /main/storyboard.py: -------------------------------------------------------------------------------- 1 | class Storyboard: 2 | # Constants regarding the input file 3 | ## Host settings 4 | HOST_SETTINGS = "host_settings" 5 | ID = "id" 6 | MGMT_ADDR = "mgmt_addr" 7 | VIRBR_ADDR = "virbr_addr" 8 | ACCOUNT = "account" 9 | NOT_AVAIL = "N/A" 10 | 11 | ## Guest settings 12 | GUEST_SETTINGS = "guest_settings" 13 | ID4GUEST = "id" 14 | IP_ADDR = "ip_addr" 15 | BASEVM_HOST = "basevm_host" 16 | BASEVM_CONFIG_FILE = "basevm_config_file" 17 | BASEVM_TYPE = "basevm_type" 18 | BASEVM_OS_TYPE = "basevm_os_type" 19 | TASKS = "tasks" 20 | 21 | ADD_ACCOUNT = "add_account" 22 | ACCOUNT = "account" 23 | PASSWD = "passwd" 24 | FULL_NAME = "full_name" 25 | MODIFY_ACCOUNT = "modify_account" 26 | NEW_ACCOUNT = "new_account" 27 | NEW_PASSWD = "new_passwd" 28 | 29 | INSTALL_PACKAGE = "install_package" 30 | PACKAGE_MANAGER = "package_manager" 31 | NAME4PACKAGE = "name" 32 | VERSION = "version" 33 | 34 | EMULATE_ATTACK = "emulate_attack" 35 | ATTACK_TYPE ="attack_type" 36 | TARGET_ACCOUNT = "target_account" 37 | ATTEMPT_NUMBER = "attempt_number" 38 | ATTACK_TIME = "attack_time" 39 | 40 | EMULATE_TRAFFIC_CAPTURE_FILE = "emulate_traffic_capture_file" 41 | FORMAT = "format" 42 | FILE_NAME = "file_name" 43 | ATTACK_TYPE = "attack_type" 44 | SSH_ATTACK = "ssh_attack" 45 | DOS_ATTACK = "dos_attack" 46 | DDOS_ATTACK = "ddos_attack" 47 | ATTACK_SOURCE = "attack_source" 48 | NOISE_LEVEL = "noise_level" 49 | 50 | EMULATE_MALWARE = "emulate_malware" 51 | NAME4MALWARE = "name" 52 | MODE = "mode" 53 | DUMMY_CALCULATION = "dummy_calculation" 54 | PORT_LISTENING = "port_listening" 55 | CPU_UTILIZATION ="cpu_utilization" 56 | PORT = "port" 57 | 58 | COPY_CONTENT = "copy_content" 59 | SRC = "src" 60 | DST = "dst" 61 | 62 | EXECUTE_PROGRAM = "execute_program" 63 | PROGRAM = "program" 64 | ARGS ="args" 65 | INTERPRETER = "interpreter" 66 | EXECUTE_TIME = "execute_time" 67 | 68 | FIREWALL_RULES = "firewall_rules" 69 | RULE = "rule" 70 | 71 | ## Clone settings 72 | CLONE_SETTINGS = "clone_settings" 73 | RANGE_ID = "range_id" 74 | HOSTS = "hosts" 75 | HOST_ID = "host_id" 76 | INSTANCE_NUMBER = "instance_number" 77 | GUESTS = "guests" 78 | GUEST_ID = "guest_id" 79 | NUMBER = "number" 80 | ENTRY_POINT = "entry_point" 81 | FORWARDING_RULES = "forwarding_rules" 82 | #RULE = "rule" # Also defined in guests... 83 | TOPOLOGY = "topology" 84 | TYPE = "type" 85 | NETWORKS = "networks" 86 | NAME = "name" 87 | MEMBERS = "members" 88 | GATEWAY = "gateway" 89 | 90 | # Constants regarding the output range details file 91 | # (values that appear identically above are not repeated) 92 | 93 | #RANGE_ID = "range_id" 94 | #HOSTS = "hosts" 95 | #HOST_ID = "host_id" 96 | INSTANCE_COUNT = "instance_count" 97 | INSTANCES = "instances" 98 | INSTANCE_INDEX = "instance_index" 99 | #GUESTS = "guests" 100 | #GUEST_ID = "guest_id" 101 | KVM_DOMAIN = "kvm_domain" 102 | AWS_DOMAIN = "aws_domain" 103 | IP_ADDRS = "ip_addrs" 104 | GATEWAYS = "gateways" 105 | FIREWALL_RULE = "firewall_rule" 106 | NETWORK_MEMBERSHIP = "networks" 107 | 108 | # Constants regarding the configuration file 109 | SECTION_NAME = "config" 110 | CYRIS_PATH = "cyris_path" 111 | CYBER_RANGE_DIR = "cyber_range_dir" 112 | GW_MODE = "gw_mode" 113 | GW_ACCOUNT = "gw_account" 114 | GW_MGMT_ADDR = "gw_mgmt_addr" 115 | GW_INSIDE_ADDR = "gw_inside_addr" 116 | USER_EMAIL = "user_email" 117 | -------------------------------------------------------------------------------- /main/cyvar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Variables for Cy series 4 | # 5 | 6 | import urllib 7 | import string 8 | 9 | class CyVarBase(string.Template): 10 | delimiter = '@' 11 | 12 | class CyVarForm: 13 | def __init__(self, formsource): 14 | self.formbackup = formsource 15 | self.form = CyVarBase(formsource) 16 | self.dict = {} 17 | 18 | # entry dictionary enititis 1 or many by dictionary style 19 | def entry1(self, key, val): 20 | self.dict[key] = val 21 | 22 | def entrymany(self, **kwargs): 23 | for k,v in kwargs.items(): 24 | self.dict[k] = v 25 | 26 | # output form content with dictionary values 27 | def output(self): 28 | return self.form.substitute(self.dict) 29 | 30 | def safe_output(self): 31 | return self.form.safe_substitute(self.dict) 32 | 33 | # print contents 34 | def dump(self): 35 | print "form |"+self.formbackup+"|" 36 | for k in sorted(self.dict.keys()): 37 | print " {0:<16} {1:<16}".format(k,str(self.dict[k])) 38 | 39 | class CyVarBox: 40 | def __init__(self): 41 | self.dict = {} 42 | 43 | # entry dictionary enititis 1 or many by dictionary style 44 | def entry1(self, key, val): 45 | self.dict[key] = val 46 | 47 | def entrymany(self, **kwargs): 48 | for k,v in kwargs.items(): 49 | self.dict[k] = v 50 | 51 | # output form content with dictionary values 52 | def project(self, source, safe=0): 53 | box = CyVarBase(source) 54 | if(safe==0): 55 | rv = box.substitute(self.dict) 56 | else: 57 | rv = box.safe_substitute(self.dict) 58 | return rv 59 | 60 | def safe_project(self, source): 61 | return self.project(source, safe=1) 62 | 63 | def project_URL(self, source, safe=0): 64 | owr = x 65 | if "%" in x: 66 | orw = urllib.unquote(owr) 67 | crw = self.project(orw, safe) 68 | cwr = urllib.quote(crw) 69 | else: 70 | cwr = owr 71 | return cwr 72 | 73 | def safe_project_URL(self, source): 74 | return self.project_URL(source, safe=1) 75 | 76 | def project_URLchunks(self, source, safe=0): 77 | parts = [] 78 | for x in source.split(" "): 79 | owr = x 80 | if "%" in x: 81 | orw = urllib.unquote(owr) 82 | crw = self.project(orw, safe) 83 | cwr = urllib.quote(crw) 84 | else: 85 | cwr = owr 86 | parts.append(cwr) 87 | return " ".join(parts) 88 | 89 | def safe_project_URLchunks(self, source): 90 | return self.project_URLchunks(source, safe=1) 91 | 92 | 93 | ### 94 | ### TEST CODE 95 | ### 96 | if __name__ == '__main__': 97 | 98 | form = CyVarBase('cr@crid,ins@incid,@guestname,@guestindex') 99 | a1 = form.substitute(crid='3', incid='1', guestname="desktop", guestindex="1") 100 | print "a1 ",a1 101 | 102 | v = CyVarForm('cr@crid,ins@incid,@guestname,@guestindex\n@{incid}name') 103 | v.entry1("guestname","websrv") 104 | v.entrymany(crid=4,incid=3) 105 | v.dump() 106 | b1 = v.safe_output() 107 | print "b1 ",b1 108 | 109 | v.entry1("guestindex","8") 110 | v.dump() 111 | b1 = v.output() 112 | print "b1 ",b1 113 | 114 | qbox = CyVarBox() 115 | qbox.entry1("name","orange") 116 | mixin = "abc def %40name ghi" 117 | mixou = qbox.safe_project(mixin) 118 | print "mixin |"+mixin+"|" 119 | print "mixou |"+mixou+"|" 120 | mixin = "abc def %40name ghi" 121 | mixou = qbox.safe_project_URLchunks(mixin) 122 | print "mixin |"+mixin+"|" 123 | print "mixou |"+mixou+"|" 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /main/aws_cleanup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import boto3 4 | import time 5 | import sys 6 | 7 | # Can be used to assign a default AWS account ID if the program 8 | # is only used by one user 9 | AWS_ACCOUNT_ID = '123456789012' 10 | 11 | # Describe relevant properties of AWS instances in a client 12 | # Return 2 lists containing instance ids and security group names 13 | def describe(client): 14 | response = client.describe_instances( 15 | Filters=[ 16 | { 17 | 'Name': 'instance-state-name', 18 | 'Values': [ 19 | 'running', 'stopped' 20 | ] 21 | }, 22 | ], 23 | ) 24 | ins_ids = {} 25 | gNames = [] 26 | ins_names = {} 27 | for x in response['Reservations']: 28 | ins_ids.update({x['Instances'][0]['Tags'][0]['Value']: x['Instances'][0]['InstanceId']}) 29 | ins_names.update({x['Instances'][0]['Tags'][0]['Value']: x['Instances'][0]['ImageId']}) 30 | gName = x['Instances'][0]['SecurityGroups'][0]['GroupName'] 31 | if gName not in gNames: 32 | gNames.append(gName) 33 | return ins_ids,gNames,ins_names 34 | 35 | # Terminate the AWS instances identified in the 'ins_ids' list 36 | def terminate_ins(client,ins_ids): 37 | response = client.terminate_instances( 38 | InstanceIds=ins_ids 39 | ) 40 | for i in range(10): 41 | status = describe_instance_status(client,ins_ids) 42 | status = list(set(status)) 43 | if len(status) == 1: 44 | if status[0] == 'terminated': 45 | print('Instances terminated!') 46 | break 47 | time.sleep(10) 48 | 49 | # Delete the security group for an AWS client 50 | def delete_SG(client,gName): 51 | response = client.delete_security_group( 52 | GroupName=gName 53 | ) 54 | 55 | # Describe the status of the AWS instances in the 'ins_ids' list 56 | def describe_instance_status(client,ins_ids): 57 | response = client.describe_instance_status( 58 | InstanceIds=ins_ids, 59 | IncludeAllInstances=True 60 | ) 61 | status = [] 62 | 63 | for idx, value in enumerate(response['InstanceStatuses']): 64 | status.append(value['InstanceState']['Name']) 65 | return status 66 | 67 | ''' 68 | def get_img_id(client,img_name) 69 | response = client.describe_images( 70 | Filters=[ 71 | { 72 | 'Name': 'name', 73 | 'Values': [img_name] 74 | } 75 | ] 76 | ) 77 | return response['Images'][0]['ImageId'] 78 | ''' 79 | 80 | # Delete all AWS images associated to an owner id 81 | def del_img(client,OwnerId,Image_ID): 82 | response = client.describe_images( 83 | Owners=[OwnerId] 84 | ) 85 | for x in response['Images']: 86 | for i in Image_ID: 87 | if i == str(x['ImageId']): 88 | client.deregister_image( 89 | ImageId=x['ImageId'] 90 | ) 91 | 92 | # Main function of the program 93 | def main(argv): 94 | 95 | # Initialize the AWS account ID with a default value 96 | aws_account_id = AWS_ACCOUNT_ID 97 | image_names = [] 98 | image_id = [] 99 | security_name = [] 100 | ins_list = [] 101 | 102 | # Get the AWS account ID from the first command-line argument (if provided) 103 | if len(argv) >= 1: 104 | aws_account_id = argv[0] 105 | for input_name in argv[1:]: 106 | image_names.append('cr' + input_name) 107 | security_name.append('cr' + input_name + '-sg' ) 108 | 109 | print('* Clean up the AWS cyber range...') 110 | client = boto3.client('ec2', region_name='us-east-1') 111 | ins_ids, gNames, ins_names = describe(client) 112 | # Build list of related AMI ids 113 | for i in ins_names.items(): 114 | for j in image_names: 115 | if j in i[0]: 116 | image_id.append(i[1]) 117 | 118 | print('* Terminate instances...') 119 | for ins in ins_ids.items(): 120 | for i in image_names: 121 | if i in ins[0]: 122 | ins_list.append(ins[1]) 123 | #print(ins_list) 124 | terminate_ins(client, ins_list) 125 | 126 | print('* Delete security groups...') 127 | for gName in gNames: 128 | if gName in security_name: 129 | #print(gName) 130 | delete_SG(client, gName) 131 | 132 | print('* Deregister AMIs...') 133 | #print(image_id) 134 | del_img(client, aws_account_id, image_id) 135 | print('* AWS clean up completed.') 136 | 137 | if __name__ == '__main__': 138 | main(sys.argv[1:]) 139 | -------------------------------------------------------------------------------- /instantiation/content_copy_program_run/limitedstringqueue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # LimitedStringQueue: 4 | # - store result string lines as queue 5 | # - if orient is 0, store first several lines 6 | # - if orient is 1, store last several lines 7 | # - "several" is exprain by qnummax 8 | # 9 | # typical example: 10 | # x = LimitedStringQueue() 11 | # x.push("a") 12 | # x.push("b") 13 | # ... 14 | # x.dump() 15 | # x.dumpconcat() 16 | # x.dumpconcaturlenc() 17 | # 18 | 19 | import urllib 20 | 21 | class LimitedStringQueue: 22 | def __init__(self, xqorient=0, xqnummax=10, xqelelenmax=10): 23 | self.q = [] 24 | self.qorient = xqorient 25 | self.qnummax = xqnummax 26 | self.qelelenmax = xqelelenmax 27 | 28 | def printprop(self,xprefix=""): 29 | if(xprefix==""): 30 | pre = "" 31 | else: 32 | pre = xprefix + ": " 33 | print pre+"orient ",self.qorient 34 | print pre+"num* ",len(self.q) 35 | print pre+"nummax ",self.qnummax 36 | # print pre+"elelenmax ",self.qelelenmax 37 | 38 | def clear(self): 39 | self.q = [] 40 | 41 | def push(self, x): 42 | # print self.qorient," ",len(self.q)," vs ",self.qnummax 43 | if(self.qorient==0): 44 | if(len(self.q)=self.qnummax): 48 | self.q.pop(0) 49 | self.q.append(x) 50 | else: 51 | self.q.append(x) 52 | 53 | def dump(self): 54 | for x in self.q: 55 | print x 56 | 57 | def dumpwc(self): 58 | i = 0 59 | for x in self.q: 60 | print i,x 61 | i = i + 1 62 | 63 | def dumpconcat(self,xprefix="",xsep=""): 64 | i = 0 65 | cont = "" 66 | for x in self.q: 67 | if(xsep==""): 68 | cont = cont + x 69 | else: 70 | if(i==0): 71 | cont = x 72 | else: 73 | cont = cont + xsep + x 74 | i = i + 1 75 | if(xprefix==""): 76 | print cont 77 | else: 78 | print xprefix,cont 79 | 80 | def dumpconcaturlenc(self,xprefix="",xsep=""): 81 | i = 0 82 | cont = "" 83 | for x in self.q: 84 | if(xsep==""): 85 | cont = cont + x 86 | else: 87 | if(i==0): 88 | cont = x 89 | else: 90 | cont = cont + xsep + x 91 | i = i + 1 92 | if(xprefix==""): 93 | print urllib.quote(cont) 94 | else: 95 | print xprefix,urllib.quote(cont) 96 | 97 | 98 | def concat(self,xsep=""): 99 | i = 0 100 | cont = "" 101 | for x in self.q: 102 | if(xsep==""): 103 | cont = cont + x 104 | else: 105 | if(i==0): 106 | cont = x 107 | else: 108 | cont = cont + xsep + x 109 | i = i + 1 110 | return cont 111 | 112 | def concaturlenc(self,xsep=""): 113 | i = 0 114 | cont = "" 115 | for x in self.q: 116 | if(xsep==""): 117 | cont = cont + x 118 | else: 119 | if(i==0): 120 | cont = x 121 | else: 122 | cont = cont + xsep + x 123 | i = i + 1 124 | return urllib.quote(cont) 125 | 126 | ### 127 | ### TEST CODE 128 | ### 129 | if __name__ == '__main__': 130 | 131 | q1 = LimitedStringQueue() 132 | q1.push("a") 133 | q1.push("b") 134 | q1.push("c") 135 | q1.push("d") 136 | # q1.dumpwc() 137 | 138 | q2 = LimitedStringQueue(xqnummax=3) 139 | q2.push("a") 140 | q2.push("b") 141 | q2.push("c") 142 | q2.push("d") 143 | # q2.dumpwc() 144 | 145 | q3 = LimitedStringQueue(xqnummax=3,xqorient=1) 146 | q3.push("a") 147 | q3.push("b") 148 | q3.push("c") 149 | q3.push("d") 150 | # q3.dumpwc() 151 | 152 | q1.printprop(xprefix="q1") 153 | q2.printprop(xprefix="q2") 154 | q3.printprop(xprefix="q3") 155 | 156 | q1.dumpconcat() 157 | q2.dumpconcat(xprefix="q2") 158 | q3.dumpconcat(xprefix="q3",xsep="|") 159 | q3.dumpconcaturlenc(xprefix="q3-urlenc",xsep="|") 160 | q3.dumpconcaturlenc(xprefix="q3-urlenc",xsep="\n") 161 | 162 | print "q3-urlenc",q3.concaturlenc(xsep="\n")," <- print w/o dump*" 163 | 164 | -------------------------------------------------------------------------------- /instantiation/malware_creation/malware_launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | addr=$1 4 | malware_name=$2 5 | mode=$3 6 | # Corresponding option of each mode: either cpu usage for calculation mode, or portno for port_listening mode. 7 | crspd_option=$4 8 | basevm_type=$5 9 | abs_path=$6 10 | os_type=$7 11 | 12 | inst_dir="instantiation" 13 | 14 | if [ ${basevm_type} = "kvm" ]; then 15 | # Copy the source code to the /usr/bin/ of base image. 16 | scp ${abs_path}${inst_dir}/malware_creation/dummy_malware root@${addr}:/usr/bin/${malware_name} 17 | 18 | # Add code to rc.d/rc.local file. 19 | if [ ${mode} = "calculation" ]; then 20 | scp ${abs_path}${inst_dir}/malware_creation/cpulimit/src/cpulimit root@${addr}:/usr/bin/ 21 | command="\"exec -a cpusage cpulimit -l ${crspd_option} ${malware_name} ${mode} 1000 &\""; 22 | ssh root@${addr} "echo 'bash -c ${command}' >> /etc/rc.d/rc.local"; 23 | else 24 | command="${malware_name} ${mode} ${crspd_option} &"; 25 | ssh root@${addr} "echo '${command}' >> /etc/rc.d/rc.local"; 26 | fi 27 | elif [ ${basevm_type} = "aws" ]; then 28 | if [ ${os_type} = "red_hat" -o ${os_type} = "amazon_linux" -o ${os_type} = "amazon_linux2" ]; then 29 | # Copy the source code to the /usr/bin/ of base image. 30 | scp -r -i TESTKEY.pem -o StrictHostKeyChecking=no ${abs_path}${inst_dir}/malware_creation/dummy_malware ec2-user@${addr}:${malware_name} 31 | ssh -i TESTKEY.pem ec2-user@${addr} "chmod +x ${malware_name}; sudo mv ${malware_name} /usr/bin" 32 | # Add code to rc.d/rc.local file. 33 | if [ ${mode} = "calculation" ]; then 34 | scp -r -i TESTKEY.pem -o StrictHostKeyChecking=no ${abs_path}${inst_dir}/malware_creation/cpulimit/src/cpulimit ec2-user@${addr}: 35 | command="\"exec -a cpusage cpulimit -l ${crspd_option} ${malware_name} ${mode} 1000 &\""; 36 | #ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ec2-user@${addr} "chmod +x cpulimit; sudo mv cpulimit /usr/bin" 37 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ec2-user@${addr} "chmod +x cpulimit; sudo mv cpulimit /usr/bin; sudo chmod 777 /etc/rc.d/rc.local; echo 'bash -c ${command}' >> /etc/rc.d/rc.local"; 38 | else 39 | command="${malware_name} ${mode} ${crspd_option} &"; 40 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ec2-user@${addr} "echo '${command}' >> /etc/rc.d/rc.local"; 41 | fi 42 | elif [ ${os_type} = "ubuntu_20" -o ${os_type} = "ubuntu_18" ]; then 43 | # Copy the source code to the /usr/bin/ of base image. 44 | scp -r -i TESTKEY.pem -o StrictHostKeyChecking=no ${abs_path}${inst_dir}/malware_creation/dummy_malware ubuntu@${addr}:${malware_name} 45 | ssh -i TESTKEY.pem ubuntu@${addr} "chmod +x ${malware_name}; sudo mv ${malware_name} /usr/bin" 46 | # Add code to rc.d/rc.local file. 47 | if [ ${mode} = "calculation" ]; then 48 | scp -r -i TESTKEY.pem -o StrictHostKeyChecking=no ${abs_path}${inst_dir}/malware_creation/cpulimit/src/cpulimit ubuntu@${addr}: 49 | command="\"exec -a cpusage cpulimit -l ${crspd_option} ${malware_name} ${mode} 1000 &\""; 50 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${addr} "chmod +x cpulimit; sudo mv cpulimit /usr/bin" 51 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${addr} "sudo touch /etc/rc.local; sudo chmod 777 /etc/rc.local; echo -e '#!/bin/bash\nbash -c ${command}' >> /etc/rc.local"; 52 | else 53 | command="${malware_name} ${mode} ${crspd_option} &"; 54 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${addr} "echo '${command}' >> /etc/rc.local"; 55 | fi 56 | elif [ ${os_type} = "ubuntu_16" ]; then 57 | scp -r -i TESTKEY.pem -o StrictHostKeyChecking=no ${abs_path}${inst_dir}/malware_creation/dummy_malware ubuntu@${addr}:${malware_name} 58 | ssh -i TESTKEY.pem ubuntu@${addr} "chmod +x ${malware_name}; sudo mv ${malware_name} /usr/bin" 59 | if [ ${mode} = "calculation" ]; then 60 | scp -r -i TESTKEY.pem -o StrictHostKeyChecking=no ${abs_path}${inst_dir}/malware_creation/cpulimit/src/cpulimit ubuntu@${addr}: 61 | command="\"exec -a cpusage cpulimit -l ${crspd_option} ${malware_name} ${mode} 1000 &\""; 62 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${addr} "chmod +x cpulimit; sudo mv cpulimit /usr/bin; touch rc.local; sudo chmod 777 rc.local /etc/rc.local; sudo echo -e '#!/bin/bash\nbash -c ${command}' >> rc.local; sudo mv rc.local /etc/rc.local; " 63 | #ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${addr} "sudo sed 14d /etc/rc.local; echo -e 'bash -c ${command}' >> /etc/rc.local"; 64 | else 65 | command="${malware_name} ${mode} ${crspd_option} &"; 66 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ubuntu@${addr} "echo '${command}' >> /etc/rc.local"; 67 | fi 68 | fi 69 | fi 70 | -------------------------------------------------------------------------------- /instantiation/ruleset_modification/ruleset_modify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | abs_path=$1 4 | image_addr=$2 5 | ruleset_file=$3 6 | os_type=$4 7 | basevm_type=$5 8 | 9 | inst_dir="instantiation" 10 | 11 | echo "-- Firewall ruleset modification started" 12 | 13 | # Check whether the specified ruleset file actually exists 14 | if [ ! -f "${ruleset_file}" ]; then 15 | echo 16 | echo "ruleset_modify.sh: Firewall ruleset file '${ruleset_file}' doesn't exist => abort firewall setting" 17 | exit 1 18 | fi 19 | 20 | # Apply rules depending on what virtualization technology is used 21 | if [ ${basevm_type} = "kvm" ]; then 22 | 23 | # Set up iptables 24 | ## Install iptables-service package in CentOS 7 (if it is not installed already) 25 | #echo "ruleset_modify.sh: Install iptables-services..." 26 | #sshpass -p ${image_passwd} ssh root@${image_addr} "yum install iptables-services -y" 27 | ## Stop the default CentOS 7 firewall 'firewalld' and start iptables 28 | echo "ruleset_modify.sh: Stop firewalld and start iptables services..." 29 | ssh root@${image_addr} "systemctl stop firewalld; systemctl start iptables; systemctl start ip6tables" 30 | ## Disable firewalld and enable iptables 31 | echo "ruleset_modify.sh: Disable firewalld and enable iptables services..." 32 | ssh root@${image_addr} "systemctl disable firewalld; systemctl mask firewalld; systemctl enable iptables; systemctl enable ip6tables" 33 | 34 | # Prepare the new iptables configuration 35 | ## Copy 'iptables_template' ruleset base to a new file called 'iptables' 36 | echo "ruleset_modify.sh: Prepare iptables file from template..." 37 | cp ${abs_path}${inst_dir}/ruleset_modification/iptables_template ${abs_path}${inst_dir}/ruleset_modification/iptables; 38 | ## Append 'firewall_rules' task rules to the 'iptables' file 39 | echo "ruleset_modify.sh: Append 'firewall_rules' task rules to file..." 40 | python ${abs_path}${inst_dir}/ruleset_modification/append_ruleset.py ${ruleset_file} ${abs_path}${inst_dir}/ruleset_modification/iptables; 41 | ## Copy the 'iptables' file to the base VM 42 | echo "ruleset_modify.sh: Copy iptables configuration file to base VM..." 43 | scp ${abs_path}${inst_dir}/ruleset_modification/iptables root@${image_addr}:/etc/sysconfig; 44 | # Restore iptables rules from configuration file 45 | echo "ruleset_modify.sh: Restore iptables configuration from file..." 46 | echo " (will also appear in the cloned VMs on reboot)" 47 | ssh root@${image_addr} "iptables-restore /etc/sysconfig/iptables"; 48 | 49 | elif [ ${basevm_type} = "aws" ]; then 50 | if [ ${os_type} = "amazon_linux" -o ${os_type} = "amazon_linux2" ]; then 51 | cp ${abs_path}${inst_dir}/ruleset_modification/iptables_template ${abs_path}${inst_dir}/ruleset_modification/iptables; 52 | python ${abs_path}${inst_dir}/ruleset_modification/append_ruleset.py ${ruleset_file} ${abs_path}${inst_dir}/ruleset_modification/iptables; 53 | scp -i TESTKEY.pem -o StrictHostKeyChecking=no ${abs_path}${inst_dir}/ruleset_modification/iptables ec2-user@${image_addr}: 54 | ssh -i TESTKEY.pem ec2-user@${image_addr} "sudo mv iptables /etc/sysconfig/iptables" 55 | ssh -i TESTKEY.pem ec2-user@${image_addr} "sudo iptables-restore /etc/sysconfig/iptables"; 56 | elif [ ${os_type} = "red_hat" ]; then 57 | ssh -i TESTKEY.pem -o StrictHostKeyChecking=no ec2-user@${image_addr} "sudo yum install iptables-services -y"; 58 | ssh -i TESTKEY.pem ec2-user@${image_addr} "sudo systemctl start iptables; sudo systemctl start ip6tables"; 59 | ssh -i TESTKEY.pem ec2-user@${image_addr} "sudo systemctl enable iptables; sudo systemctl enable ip6tables"; 60 | cp ${abs_path}${inst_dir}/ruleset_modification/iptables_template ${abs_path}${inst_dir}/ruleset_modification/iptables; 61 | python ${abs_path}${inst_dir}/ruleset_modification/append_ruleset.py ${ruleset_file} ${abs_path}${inst_dir}/ruleset_modification/iptables; 62 | scp -i TESTKEY.pem ${abs_path}${inst_dir}/ruleset_modification/iptables ec2-user@${image_addr}: 63 | ssh -i TESTKEY.pem ec2-user@${image_addr} "sudo mv iptables /etc/sysconfig/iptables" 64 | ssh -i TESTKEY.pem ec2-user@${image_addr} "sudo iptables-restore /etc/sysconfig/iptables"; 65 | elif [ ${os_type} = "ubuntu_20" -o ${os_type} = "ubuntu_18" -o ${os_type} = "ubuntu_16" ]; then 66 | cp ${abs_path}${inst_dir}/ruleset_modification/iptables_template ${abs_path}${inst_dir}/ruleset_modification/iptables; 67 | python ${abs_path}${inst_dir}/ruleset_modification/append_ruleset.py ${ruleset_file} ${abs_path}${inst_dir}/ruleset_modification/iptables; 68 | scp -i TESTKEY.pem -o StrictHostKeyChecking=no ${abs_path}${inst_dir}/ruleset_modification/iptables ubuntu@${image_addr}: 69 | ssh -i TESTKEY.pem ubuntu@${image_addr} "sudo mv iptables /etc/iptables" 70 | ssh -i TESTKEY.pem ubuntu@${image_addr} "sudo iptables-restore /etc/iptables"; 71 | fi 72 | fi 73 | 74 | echo "-- Firewall ruleset modification ended" 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Important Notice 2 | 3 | ### The CROND NEC-endowed chair at JAIST that has originally developed CyRIS ceased to exist in March 2021, and future development will be carried out by the Cybersecurity Lab at JAIST in a [new repository](https://github.com/cyb3rlab/cyris). The original CyRIS will not be receiving any future updates, so please switch over to the new version as soon as you are able to do so. 4 | 5 | 6 | # CyRIS: Cyber Range Instantiation System 7 | 8 | CyRIS is a tool for facilitating cybersecurity training by automating 9 | the creation and management of the corresponding training environments 10 | (a.k.a., cyber ranges) based on a description in YAML format. CyRIS is 11 | being developed by the Cyber Range Organization and Design (CROND) 12 | NEC-endowed chair at the Japan Advanced Institute of Science and 13 | Technology (JAIST). 14 | 15 | An overview of the CyRIS workflow is provided below. Based on the 16 | input cyber range description, and a collection of virtual machine 17 | base images, CyRIS performs preparation, content installation and 18 | cloning in order to deploy the cyber range on a given server 19 | infrastructure. 20 | 21 | ![CyRIS workflow](https://github.com/crond-jaist/cyris/blob/master/cyris_workflow.png "CyRIS workflow") 22 | 23 | CyRIS is written in Python, and has various features, including system 24 | configuration, tool installation, incident emulation, content 25 | management, and clone management. If interested, please download the 26 | [latest release](https://github.com/crond-jaist/cyris/releases/) and 27 | let us know if you have any issues; a sample virtual machine base 28 | image and a user guide are also provided for your convenience. 29 | 30 | The procedure for installing and configuring CyRIS is rather complex, 31 | therefore you should refer to the User Guide. In particular, the 32 | following issues are to be considered: 33 | 34 | * _Hardware requirements_: Hardware vrtualization support, Internet 35 | connection (optional) -- See Section 3.1 of the User Guide. 36 | * _Software installation_: Host preparation, base image preparation, 37 | CyRIS configuration -- See Section 3.2 of the User Guide. 38 | 39 | 40 | ## Quick Start 41 | 42 | This section provides some basic instructions on how to run a basic 43 | test in order to make sure CyRIS operates correctly. In what follows 44 | we assume that the installation procedure mentioned above was 45 | conducted successfully, and the current directory is the directory 46 | where CyRIS was installed. Please refer to the accompanying User Guide 47 | for details. 48 | 49 | ### Preliminary checks 50 | 51 | Some key issues that must not be forgotten before proceeding to 52 | running CyRIS are: 53 | 54 | * The configuration file `CONFIG` needs to reflect your actual CyRIS 55 | installation, in particular paying attention to the constants below: 56 | 57 | `cyris_path = ...` 58 | 59 | `cyber_range_dir = ...` 60 | 61 | * The sample KVM base image must be present on the CyRIS host, and the 62 | content of the file `basevm_small.xml` must reflect the actual 63 | location of the base image: 64 | 65 | `` 66 | 67 | * The content of sample file `examples/basic.yml` should reflect the 68 | actual host properties, and the actual location of the file 69 | `basevm_small.xml` in the corresponding sections: 70 | 71 | `mgmt_addr: ...` 72 | 73 | `account: ...` 74 | 75 | `basevm_config_file: ...` 76 | 77 | ### Basic operation 78 | 79 | A typical sequence of operations is as follows: 80 | 81 | * Create a cyber range using the basic description edited above: 82 | 83 | `$ main/cyris.py examples/basic.yml CONFIG` 84 | 85 | * Check the details regarding the created cyber range: 86 | 87 | `$ cat cyber_range/123/range_details-cr123.yml` 88 | 89 | * Check the notification about how to login to the cyber range: 90 | 91 | `$ cat cyber_range/123/range_notification-cr123.txt` 92 | 93 | * Try to login into the cyber range: 94 | 95 | `$ ssh trainee01@... -p ...` 96 | 97 | * Destroy the cyber range: 98 | 99 | `$ main/range_cleanup.sh 123 CONFIG` 100 | 101 | ### Recovery from errors 102 | 103 | Ocasionally an error such as `No route to host` appears. We are 104 | currently investigating its exact cause, but for the moment you should 105 | just destroy the partially created cyber range and repeat the creation 106 | process. 107 | 108 | In case you encounter subsequent errors due to mis-configurations, and 109 | the range cleanup command above is insufficient to restore correct 110 | operation, you can also clean up the temporary files via a special 111 | cleanup script (two arguments are required): 112 | 113 | `$ ./destroy_all_cr.sh CYRIS_PATH CYBER_RANGE_PATH` 114 | 115 | 116 | ## References 117 | 118 | For a research background about CyRIS, please consult the following 119 | paper: 120 | 121 | * R. Beuran, C. Pham, D. Tang, K. Chinen, Y. Tan, Y. Shinoda, 122 | "Cybersecurity Education and Training Support System: CyRIS", IEICE 123 | Transactions on Information and Systems, vol. E101-D, no. 3, March 124 | 2018, pp. 740-749. 125 | 126 | For the list of contributors, please check the file CONTRIBUTORS. 127 | -------------------------------------------------------------------------------- /instantiation/vm_clone/vm_clone_xml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # $1: id of server 4 | # $2: id of vm 5 | # $3: third bit of vm 6 | # $4: fourth bit of vm 7 | # $5: name of base image 8 | # $6: absolute path 9 | HOST_ID=$(printf "%x" $1); 10 | VM_ID=$2; 11 | IMAGE_NAME=$3 12 | ABSPATH=$4 13 | BRIDGE_ID_STR=$5 14 | ADDR_STR=$6 15 | 16 | echo -e "\n* Enter VM cloning script 'vm_clone_xml.sh'" 17 | 18 | # Create addr list for network interfaces 19 | IFS="," read -r -a ADDR_LIST <<< "${ADDR_STR}" 20 | # Create bridge_id for network interfaces 21 | IFS="," read -r -a BRIDGE_ID_LIST <<< "${BRIDGE_ID_STR}" 22 | 23 | # Remove the previous config file if it's still present 24 | if [ -e ${ABSPATH}images/${VM_ID}_config.xml ]; 25 | then 26 | rm ${ABSPATH}images/${VM_ID}_config.xml; 27 | fi 28 | 29 | echo "** Create disk image '${VM_ID}_img' for the cloned VM" 30 | # Create overlay image from base image 31 | qemu-img create -b ${ABSPATH}${IMAGE_NAME} -f qcow2 ${ABSPATH}images/${VM_ID}_img 32 | sudo chown libvirt-qemu: ${ABSPATH}images/${VM_ID}_img 33 | 34 | echo "** Create XML config file '${VM_ID}_config.xml' for the cloned VM" 35 | # Create XML config file for starting new VM 36 | echo "" >> ${ABSPATH}images/${VM_ID}_config.xml; 37 | echo " ${VM_ID}" >> ${ABSPATH}images/${VM_ID}_config.xml; 38 | echo " 1024000" >> ${ABSPATH}images/${VM_ID}_config.xml; 39 | echo " 1" >> ${ABSPATH}images/${VM_ID}_config.xml; 40 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 41 | echo " hvm" >> ${ABSPATH}images/${VM_ID}_config.xml; 42 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 43 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 44 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 45 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 46 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 47 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 48 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 49 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 50 | echo " destroy" >> ${ABSPATH}images/${VM_ID}_config.xml; 51 | echo " restart" >> ${ABSPATH}images/${VM_ID}_config.xml; 52 | echo " restart" >> ${ABSPATH}images/${VM_ID}_config.xml; 53 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 54 | echo " /usr/bin/kvm" >> ${ABSPATH}images/${VM_ID}_config.xml; 55 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 56 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 57 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 58 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 59 | echo "
" >> ${ABSPATH}images/${VM_ID}_config.xml; 60 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 61 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 62 | echo "
" >> ${ABSPATH}images/${VM_ID}_config.xml; 63 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 64 | # This inteface setup is for connecting one vm from one server to another vm in another server 65 | for i in "${!ADDR_LIST[@]}" 66 | do 67 | IFS="." read -r -a BIT_LIST <<< "${ADDR_LIST[i]}" 68 | MAC_THIRDLAST_BIT=$(printf "%x" ${BIT_LIST[1]}); 69 | MAC_SECONDLAST_BIT=$(printf "%x" ${BIT_LIST[2]}); 70 | MAC_LAST_BIT=$(printf "%x" ${BIT_LIST[3]}); 71 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 72 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 73 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 74 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 75 | done 76 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 77 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 78 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 79 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 80 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 81 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 82 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 83 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 84 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 85 | echo "
" >> ${ABSPATH}images/${VM_ID}_config.xml; 86 | echo " " >> ${ABSPATH}images/${VM_ID}_config.xml; 87 | echo "