From 21f3d3b399d349d71bffa14af4195b060f2677bf Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 17 Dec 2014 15:47:54 -0500 Subject: [PATCH 01/13] initial commit of xenserver configuration template --- conf/xenserver.conf | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 conf/xenserver.conf diff --git a/conf/xenserver.conf b/conf/xenserver.conf new file mode 100644 index 0000000000..cce36648a0 --- /dev/null +++ b/conf/xenserver.conf @@ -0,0 +1,58 @@ +[xenserver] +# Specify the XenServer username for authentication. +user = root + +# Specify the XenServer password for authentionication. +password = changeme + +# Specify the XenServer URL. The url is the XMLRPC location of the XenServer, +# which can be either a hostname or IP address. +url = https://xenserver + +# Specify a comma-separated list of available machines to be used. For each +# specified ID you have to define a dedicated section containing the details +# on the respective machine. (E.g. cuckoo1,cuckoo2,cuckoo3) +machines = cuckoo1 + +[cuckoo1] +# Specify the virtual machine uuid. +uuid = 00000000-0000-0000-0000-000000000000 + +# Specify the snapshot uuid to use. +snapshot = 00000000-0000-0000-0000-000000000000 + +# Specify the operating system platform used by current machine +# [windows/darwin/linux]. +platform = windows + +# Specify the IP address of the current virtual machine. Make sure that the +# IP address is valid and that the host machine is able to reach it. If not, +# the analysis will fail. +ip = 192.168.54.111 + +# (Optional) Specify the name of the network interface that should be used +# when dumping network traffic from this machine with tcpdump. If specified, +# overrides the default interface specified in cuckoo.conf +# Example (virbr0 is the interface name): +# interface = virbr0 + +# (Optional) Specify the IP of the Result Server, as your virtual machine sees it. +# The Result Server will always bind to the address and port specified in cuckoo.conf, +# however you could set up your virtual network to use NAT/PAT, so you can specify here +# the IP address for the Result Server as your machine sees it. If you don't specify an +# address here, the machine will use the default value from cuckoo.conf. +# NOTE: if you set this option you have to set result server IP to 0.0.0.0 in cuckoo.conf. +# Example: +# resultserver_ip = 192.168.122.101 + +# (Optional) Specify the port for the Result Server, as your virtual machine sees it. +# The Result Server will always bind to the address and port specified in cuckoo.conf, +# however you could set up your virtual network to use NAT/PAT, so you can specify here +# the port for the Result Server as your machine sees it. If you don't specify a port +# here, the machine will use the default value from cuckoo.conf. +# Example: +# resultserver_port = 2042 + +# (Optional) Set your own tags. These are comma separated and help to identify +# specific VMs. You can run samples on VMs with tag you require. +# tags = windows_xp_sp3,32_bit,acrobat_reader_6 From 6219236e11add4ed07ca86fcf0fc2b2d025daae4 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 17 Dec 2014 20:04:44 -0500 Subject: [PATCH 02/13] Added XenServer machinery Able to start VM's from disk and memory snapshots --- modules/machinery/xenserver.py | 197 +++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 modules/machinery/xenserver.py diff --git a/modules/machinery/xenserver.py b/modules/machinery/xenserver.py new file mode 100644 index 0000000000..6a48f07eef --- /dev/null +++ b/modules/machinery/xenserver.py @@ -0,0 +1,197 @@ + + +import logging +from lib.cuckoo.common.abstracts import Machinery +from lib.cuckoo.common.exceptions import CuckooMachineError, CuckooDependencyError + + + +try: + import XenAPI + HAVE_XENAPI = True +except ImportError: + HAVE_XENAPI = False + + +log = logging.getLogger(__name__) + + +class XenServerMachinery(Machinery): + + LABEL = "uuid" + + def _initialize_check(self): + """Check XenServer configuration, initialize a Xen API connection, and + verify machine validity. + """ + + if not HAVE_XENAPI: + raise CuckooDependencyError("Unable to import XenAPI") + + if not self.options.xenserver.user: + raise CuckooMachineError("XenServer username missing, please add " + "it to xenserver.conf.") + + if not self.options.xenserver.password: + raise CuckooMachineError("XenServer password missing, please add " + "it to xenserver.conf") + + if not self.options.xenserver.url: + raise CuckooMachineError("XenServer url missing, please add it to " + "xenserver.conf") + + try: + self.session = XenAPI.Session(self.options.xenserver.url) + except: + raise CuckooMachineError("Could not connect to XenServer: invalid " + "or incorrect urlm please ensure the url " + "is correct in xenserver.conf") + + try: + self.session.xenapi.login_with_password( + self.options.xenserver.user, self.options.xenserver.password + ) + except: + raise CuckooMachineError("Could not connect to XenServer: " + "incorrect credentials, please ensure the " + "user and password are correct in " + "xenserver.conf") + + for machine in self.machines(): + uuid = machine.label + self._check_vm(uuid) + + if machine.snapshot: + self._check_snapshot(uuid, machine.snapshot) + + super(XenServerMachinery, self)._initialize_check() + + def _get_vm_ref(self, uuid): + """Get a virtual machine reference. + @param uuid: vm uuid + """ + + return self.session.xenapi.VM.get_by_uuid(uuid) + + def _get_vm_record(self, ref): + """Get the virtual machine record. + @param ref: vm reference + """ + + return self.session.xenapi.VM.get_record(ref) + + def _check_vm(self, uuid): + """Check vm existence and validity. + @param uuid: vm uuid + """ + + try: + ref = self._get_vm_ref(uuid) + vm = self._get_vm_record(ref) + except XenAPI.Failure as e: + raise CuckooMachineError("Vm not found: %s: %s" % uuid) + + if vm['is_a_snapshot']: + raise CuckooMachineError("Vm is a snapshot: %s" % uuid) + + if vm['is_a_template']: + raise CuckooMachineError("Vm is a template: %s" % uuid) + + if vm['is_control_domain']: + raise CuckooMachineError("Vm is a control domain: %s" % uuid) + + def _check_snapshot(self, vm_uuid, snapshot_uuid): + """Check snapshot existence and that the snapshot is of the specified + vm uuid. + @param vm_uuid: vm uuid + @param snapshot_uuid: snapshot uuid + """ + + try: + snapshot_ref = self._get_vm_ref(snapshot_uuid) + snapshot = self._get_vm_record(snapshot_ref) + except: + raise CuckooMachineError("Snapshot not found: %s" % snapshot_uuid) + + if not snapshot['is_a_snapshot']: + raise CuckooMachineError("Invalid snapshot: %s" % snapshot_uuid) + + try: + parent = self._get_vm_record(snapshot['snapshot_of']) + except: + raise CuckooMachineError("Invalid snapshot: %s" % snapshot_uuid) + + parent_uuid = parent['uuid'] + if parent_uuid != vm_uuid: + raise CuckooMachineError("Snapshot does not belong to specified " + "vm: %s" % snapshot_uuid) + + def _snapshot_from_vm_uuid(self, uuid): + """Get the snapshot uuid from a virtual machine. + @param uuid: vm uuid + """ + + machine = self.db.view_machine_by_label(uuid) + return machine.snapshot + + def _is_halted(self, vm): + """Checks if the virtual machine is running. + @param uuid: vm uuid + """ + + return vm['power_state'] == 'Halted' + + def start(self, uuid): + """Start a virtual machine. + @param uuid: vm uuid + """ + + vm_ref = self._get_vm_ref(uuid) + vm = self._get_vm_record(vm_ref) + + if not self._is_halted(vm): + raise CuckooMachineError("Vm is already running: %s" % uuid) + + snapshot = self._snapshot_from_vm_uuid(uuid) + if snapshot: + snapshot_ref = self._get_vm_ref(snapshot) + try: + log.debug("Reverting vm %s to snapshot %s", uuid, snapshot) + self.session.xenapi.VM.revert(snapshot_ref) + log.debug("Revert completed for vm %s", uuid) + except XenAPI.Failure as e: + raise CuckooMachineError("Unable to revert vm %s: %s" + % (uuid,e.details[0])) + + try: + log.debug("Resuming reverted vm %s" % uuid) + self.session.xenapi.VM.resume(vm_ref, False, False) + except XenAPI.Failure as e: + raise CuckooMachineError("Unable to resume vm %s: %s" + % (uuid, e.details[0])) + else: + log.debug("No snapshot found for vm, booting: %s" % uuid) + try: + self.session.xenapi.VM.start(vm_ref, False, False) + except XenAPI.Failure as e: + raise CuckooMachineError("Unable to start vm %s: %s" + % (uuid, e.details[0])) + + log.debug("Started vm: %s", uuid) + + def stop(self, uuid): + """Stop a virtual machine. + @param uuid: vm uuid + """ + + ref = self._get_vm_ref(uuid) + vm = self._get_vm_record(ref) + if self._is_halted(vm): + log.warning("Trying to stop an already stopped machine: %s", uuid) + else: + try: + self.session.xenapi.VM.hard_shutdown(ref) + except XenAPI.Failure as e: + raise CuckooMachineError("Error shutting down virtual machine: " + "%s: %s" % (uuid, e.details[0])) + From a50373d4cb9f8c045bb9ae25d44327d4f5660658 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 17 Dec 2014 21:42:46 -0500 Subject: [PATCH 03/13] updated xenserver config comments --- conf/xenserver.conf | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/conf/xenserver.conf b/conf/xenserver.conf index cce36648a0..dc0b2da3a7 100644 --- a/conf/xenserver.conf +++ b/conf/xenserver.conf @@ -18,8 +18,14 @@ machines = cuckoo1 # Specify the virtual machine uuid. uuid = 00000000-0000-0000-0000-000000000000 -# Specify the snapshot uuid to use. -snapshot = 00000000-0000-0000-0000-000000000000 +# Specify the snapshot uuid to use. Snapshots are not required, but if they are +# not used, the virtual machine's disks must be configured to reset on boot. +# Resetting the disks on boot ensures that samples cannot permanently modify the +# analysis virtual machine past a shutdown. Refer to the "Saving the Virtual +# Machine" section in the Cuckoo documentation for details on how to enable +# disk resetting on boot. +# Example: +# snapshot = 00000000-0000-0000-0000-000000000000 # Specify the operating system platform used by current machine # [windows/darwin/linux]. From 7ed07441d4242d81243c03a029dbb3046d558a22 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 17 Dec 2014 21:56:01 -0500 Subject: [PATCH 04/13] Updated XenServer machinery to support virtual machines without snapshots When no snapshots are available, the VM's disk must be set to reset on boot via the VDI.on-boot=reset param. This ensures that samples do not permanently modify the analysis VM. --- modules/machinery/xenserver.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/modules/machinery/xenserver.py b/modules/machinery/xenserver.py index 6a48f07eef..8fd9e1114d 100644 --- a/modules/machinery/xenserver.py +++ b/modules/machinery/xenserver.py @@ -59,10 +59,12 @@ def _initialize_check(self): for machine in self.machines(): uuid = machine.label - self._check_vm(uuid) + (ref, vm) = self._check_vm(uuid) if machine.snapshot: self._check_snapshot(uuid, machine.snapshot) + else: + self._check_disks_reset(vm) super(XenServerMachinery, self)._initialize_check() @@ -100,6 +102,8 @@ def _check_vm(self, uuid): if vm['is_control_domain']: raise CuckooMachineError("Vm is a control domain: %s" % uuid) + return (ref, vm) + def _check_snapshot(self, vm_uuid, snapshot_uuid): """Check snapshot existence and that the snapshot is of the specified vm uuid. @@ -126,6 +130,32 @@ def _check_snapshot(self, vm_uuid, snapshot_uuid): raise CuckooMachineError("Snapshot does not belong to specified " "vm: %s" % snapshot_uuid) + def _check_disks_reset(self, vm): + """Check whether each attached disk is set to reset on boot. + @param vm: vm record + """ + + for ref in vm['VBDs']: + try: + vbd = self.session.xenapi.VBD.get_record(ref) + except: + log.warning("Invalid VBD for vm %s: %s", vm['uuid'], ref) + continue + + if vbd['type'] == 'Disk': + vdi_ref = vbd['VDI'] + try: + vdi = self.session.xenapi.VDI.get_record(vdi_ref) + except: + log.warning("Invalid VDI for vm %s: %s", vm['uuid'], vdi_ref) + continue + + if vdi['on_boot'] != 'reset' and vdi['read_only'] == False: + raise CuckooMachineError( + "Vm %s contains invalid VDI %s: disk is not reset on " + "boot. Please set the on-boot parameter to 'reset'." + % (vm['uuid'], vdi['uuid'])) + def _snapshot_from_vm_uuid(self, uuid): """Get the snapshot uuid from a virtual machine. @param uuid: vm uuid From 9fe00943d105fd78210f39040b964f3eff7566cd Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 17 Dec 2014 21:56:30 -0500 Subject: [PATCH 05/13] Added XenServer guest installation documentation --- docs/book/src/installation/guest/saving.rst | 55 ++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/docs/book/src/installation/guest/saving.rst b/docs/book/src/installation/guest/saving.rst index ef687ba041..cedb7c3de4 100644 --- a/docs/book/src/installation/guest/saving.rst +++ b/docs/book/src/installation/guest/saving.rst @@ -30,7 +30,7 @@ restore it:: KVM === -If decided to adopt KVM, you must fist of all be sure to use a disk format for +If decided to adopt KVM, you must first of all be sure to use a disk format for your virtual machines which supports snapshots. By default libvirt tools create RAW virtual disks, and since we need snapshots you'll either have to use QCOW2 or LVM. For the scope of this guide we adopt QCOW2, @@ -96,3 +96,56 @@ Where your_snapshot_name is the name you choose for the snapshot. After that power off the machine from the GUI or from the command line:: $ vmrun stop "/your/disk/image/path/wmware_image_name.vmx" hard + +XenServer +========= + +If you decided to adopt XenServer, the XenServer machinery supports starting +virtual machines from either disk or a memory snapshot. Creating and reverting +memory snapshots require that the Xen guest tools be installed in the +virtual machine. The recommended method of booting XenServer virtual machines is +through memory snapshots because they can greatly reduce the boot time of +virtual machines during analysis. If, however, the option of installing the +guest tools is not available, the virtual machine can be configured to have its +disks reset on boot. Resetting the disk ensures that samples cannot permanently +modify the virtual machine. + +Memory Snapshots +---------------- + +The Xen guest tools can be installed from the XenCenter application that ships +with XenServer. Once installed, restart the virtual machine and ensure that the +Cuckoo agent is running. + +Snapshots can be taken through the XenCenter application and the command line +interface on the control domain (Dom0). When creating the snapshot from +XenCenter, ensure that the "Snapshot disk and memory" is checked. Once created, +right-click on the snapshot and note the snapshot UUID. + +To snapshot from the command line interface, run the following command:: + + $ xe vm-checkpoint vm="vm_uuid_or_name" new-name-label="Snapshot Name/Description" + +The snapshot UUID is printed to the sreen once the command completes. + +Regardless of how the snapshot was created, save the UUID in the virtual +machine's configuration section. Once the snapshot has been created, you can +shutdown the virtual machine. + +Booting from Disk +----------------- + +If you can't install the Xen guest tools or if you don't need to use memory +snapshots, you will need to ensure that the virtual machine's disks are reset on +boot. This can be done with the following commands:: + + $ xe vm-disk-list vm="vm_name_or_uuid" + +For all the VDI's listed for the virtual machine, run the following command to +change the disk to reset on boot:: + + $ xe vdi-param-set uuid="vdi_uuid" on-boot=reset + +After the disk is set to reset on boot, no permanent changes can be made to the +virtual machine's disk. Modifications that occur while a virtual machine is +running wil not persist past shutdown. From bab6b0d054770e7b4663c3f3c8f617ac04568601 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 17 Dec 2014 22:11:48 -0500 Subject: [PATCH 06/13] Fixed PEP8 issues and updated to conform to Cuckoo's coding style --- modules/machinery/xenserver.py | 53 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/modules/machinery/xenserver.py b/modules/machinery/xenserver.py index 8fd9e1114d..b7c679b3cc 100644 --- a/modules/machinery/xenserver.py +++ b/modules/machinery/xenserver.py @@ -1,10 +1,11 @@ - +# Copyright (C) 2010-2014 Cuckoo Foundation. +# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org +# See the file 'docs/LICENSE' for copying permission. import logging from lib.cuckoo.common.abstracts import Machinery -from lib.cuckoo.common.exceptions import CuckooMachineError, CuckooDependencyError - - +from lib.cuckoo.common.exceptions import CuckooMachineError +from lib.cuckoo.common.exceptions import CuckooDependencyError try: import XenAPI @@ -53,8 +54,8 @@ def _initialize_check(self): ) except: raise CuckooMachineError("Could not connect to XenServer: " - "incorrect credentials, please ensure the " - "user and password are correct in " + "incorrect credentials, please ensure " + "the user and password are correct in " "xenserver.conf") for machine in self.machines(): @@ -93,13 +94,13 @@ def _check_vm(self, uuid): except XenAPI.Failure as e: raise CuckooMachineError("Vm not found: %s: %s" % uuid) - if vm['is_a_snapshot']: + if vm["is_a_snapshot"]: raise CuckooMachineError("Vm is a snapshot: %s" % uuid) - if vm['is_a_template']: + if vm["is_a_template"]: raise CuckooMachineError("Vm is a template: %s" % uuid) - if vm['is_control_domain']: + if vm["is_control_domain"]: raise CuckooMachineError("Vm is a control domain: %s" % uuid) return (ref, vm) @@ -117,15 +118,15 @@ def _check_snapshot(self, vm_uuid, snapshot_uuid): except: raise CuckooMachineError("Snapshot not found: %s" % snapshot_uuid) - if not snapshot['is_a_snapshot']: + if not snapshot["is_a_snapshot"]: raise CuckooMachineError("Invalid snapshot: %s" % snapshot_uuid) try: - parent = self._get_vm_record(snapshot['snapshot_of']) + parent = self._get_vm_record(snapshot["snapshot_of"]) except: raise CuckooMachineError("Invalid snapshot: %s" % snapshot_uuid) - parent_uuid = parent['uuid'] + parent_uuid = parent["uuid"] if parent_uuid != vm_uuid: raise CuckooMachineError("Snapshot does not belong to specified " "vm: %s" % snapshot_uuid) @@ -135,26 +136,27 @@ def _check_disks_reset(self, vm): @param vm: vm record """ - for ref in vm['VBDs']: + for ref in vm["VBDs"]: try: vbd = self.session.xenapi.VBD.get_record(ref) except: - log.warning("Invalid VBD for vm %s: %s", vm['uuid'], ref) + log.warning("Invalid VBD for vm %s: %s", vm["uuid"], ref) continue - - if vbd['type'] == 'Disk': - vdi_ref = vbd['VDI'] + + if vbd["type"] == "Disk": + vdi_ref = vbd["VDI"] try: vdi = self.session.xenapi.VDI.get_record(vdi_ref) except: - log.warning("Invalid VDI for vm %s: %s", vm['uuid'], vdi_ref) + log.warning("Invalid VDI for vm %s: %s", vm["uuid"], + vdi_ref) continue - - if vdi['on_boot'] != 'reset' and vdi['read_only'] == False: + + if vdi["on_boot"] != "reset" and vdi["read_only"] is False: raise CuckooMachineError( "Vm %s contains invalid VDI %s: disk is not reset on " "boot. Please set the on-boot parameter to 'reset'." - % (vm['uuid'], vdi['uuid'])) + % (vm["uuid"], vdi["uuid"])) def _snapshot_from_vm_uuid(self, uuid): """Get the snapshot uuid from a virtual machine. @@ -169,7 +171,7 @@ def _is_halted(self, vm): @param uuid: vm uuid """ - return vm['power_state'] == 'Halted' + return vm["power_state"] == "Halted" def start(self, uuid): """Start a virtual machine. @@ -191,7 +193,7 @@ def start(self, uuid): log.debug("Revert completed for vm %s", uuid) except XenAPI.Failure as e: raise CuckooMachineError("Unable to revert vm %s: %s" - % (uuid,e.details[0])) + % (uuid, e.details[0])) try: log.debug("Resuming reverted vm %s" % uuid) @@ -222,6 +224,5 @@ def stop(self, uuid): try: self.session.xenapi.VM.hard_shutdown(ref) except XenAPI.Failure as e: - raise CuckooMachineError("Error shutting down virtual machine: " - "%s: %s" % (uuid, e.details[0])) - + raise CuckooMachineError("Error shutting down virtual machine:" + " %s: %s" % (uuid, e.details[0])) From 51c5e6876213bf5ed1fd038b2f8aea891846bcc9 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 17 Dec 2014 22:31:43 -0500 Subject: [PATCH 07/13] Corrected several spelling errors --- docs/book/src/installation/guest/saving.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/book/src/installation/guest/saving.rst b/docs/book/src/installation/guest/saving.rst index cedb7c3de4..b7e398ee6d 100644 --- a/docs/book/src/installation/guest/saving.rst +++ b/docs/book/src/installation/guest/saving.rst @@ -126,7 +126,7 @@ To snapshot from the command line interface, run the following command:: $ xe vm-checkpoint vm="vm_uuid_or_name" new-name-label="Snapshot Name/Description" -The snapshot UUID is printed to the sreen once the command completes. +The snapshot UUID is printed to the screen once the command completes. Regardless of how the snapshot was created, save the UUID in the virtual machine's configuration section. Once the snapshot has been created, you can @@ -148,4 +148,4 @@ change the disk to reset on boot:: After the disk is set to reset on boot, no permanent changes can be made to the virtual machine's disk. Modifications that occur while a virtual machine is -running wil not persist past shutdown. +running will not persist past shutdown. From 961c2fc4d48388c3c3e989b78e29e706a1bf291a Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Thu, 18 Dec 2014 18:52:00 -0500 Subject: [PATCH 08/13] minor documentation fix --- docs/book/src/installation/guest/saving.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/book/src/installation/guest/saving.rst b/docs/book/src/installation/guest/saving.rst index b7e398ee6d..13894e9e3a 100644 --- a/docs/book/src/installation/guest/saving.rst +++ b/docs/book/src/installation/guest/saving.rst @@ -107,8 +107,8 @@ virtual machine. The recommended method of booting XenServer virtual machines is through memory snapshots because they can greatly reduce the boot time of virtual machines during analysis. If, however, the option of installing the guest tools is not available, the virtual machine can be configured to have its -disks reset on boot. Resetting the disk ensures that samples cannot permanently -modify the virtual machine. +disks reset on boot. Resetting the disk ensures that malware samples cannot +permanently modify the virtual machine. Memory Snapshots ---------------- @@ -137,7 +137,14 @@ Booting from Disk If you can't install the Xen guest tools or if you don't need to use memory snapshots, you will need to ensure that the virtual machine's disks are reset on -boot. This can be done with the following commands:: +boot and that the Cuckoo agent is set to run at boot time. + +Running the agent at boot time can be configured in Windows by adding a startup +item for the agent. + +To set the attached disks to reset on boot, you'll first need to list all the +attached disks for the virtual machine. Ignore disks that are already read only +or CD-ROM drives. To list all attached disks, run the following command:: $ xe vm-disk-list vm="vm_name_or_uuid" From b918e6fd00715e400f0e71bf6f3971c42dedb4e5 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Thu, 25 Dec 2014 03:10:41 -0500 Subject: [PATCH 09/13] addressed pylint warnings --- modules/machinery/xenserver.py | 39 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/modules/machinery/xenserver.py b/modules/machinery/xenserver.py index b7c679b3cc..5f479e72e0 100644 --- a/modules/machinery/xenserver.py +++ b/modules/machinery/xenserver.py @@ -2,6 +2,10 @@ # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org # See the file 'docs/LICENSE' for copying permission. +""" +XenServer machinery. +""" + import logging from lib.cuckoo.common.abstracts import Machinery from lib.cuckoo.common.exceptions import CuckooMachineError @@ -18,6 +22,7 @@ class XenServerMachinery(Machinery): + """Virtualization layer for XenServer using the XenAPI XML-RPC interface.""" LABEL = "uuid" @@ -91,7 +96,7 @@ def _check_vm(self, uuid): try: ref = self._get_vm_ref(uuid) vm = self._get_vm_record(ref) - except XenAPI.Failure as e: + except XenAPI.Failure: raise CuckooMachineError("Vm not found: %s: %s" % uuid) if vm["is_a_snapshot"]: @@ -173,56 +178,56 @@ def _is_halted(self, vm): return vm["power_state"] == "Halted" - def start(self, uuid): + def start(self, label): """Start a virtual machine. @param uuid: vm uuid """ - vm_ref = self._get_vm_ref(uuid) + vm_ref = self._get_vm_ref(label) vm = self._get_vm_record(vm_ref) if not self._is_halted(vm): - raise CuckooMachineError("Vm is already running: %s" % uuid) + raise CuckooMachineError("Vm is already running: %s", label) - snapshot = self._snapshot_from_vm_uuid(uuid) + snapshot = self._snapshot_from_vm_uuid(label) if snapshot: snapshot_ref = self._get_vm_ref(snapshot) try: - log.debug("Reverting vm %s to snapshot %s", uuid, snapshot) + log.debug("Reverting vm %s to snapshot %s", label, snapshot) self.session.xenapi.VM.revert(snapshot_ref) - log.debug("Revert completed for vm %s", uuid) + log.debug("Revert completed for vm %s", label) except XenAPI.Failure as e: raise CuckooMachineError("Unable to revert vm %s: %s" - % (uuid, e.details[0])) + % (label, e.details[0])) try: - log.debug("Resuming reverted vm %s" % uuid) + log.debug("Resuming reverted vm %s", label) self.session.xenapi.VM.resume(vm_ref, False, False) except XenAPI.Failure as e: raise CuckooMachineError("Unable to resume vm %s: %s" - % (uuid, e.details[0])) + % (label, e.details[0])) else: - log.debug("No snapshot found for vm, booting: %s" % uuid) + log.debug("No snapshot found for vm, booting: %s", label) try: self.session.xenapi.VM.start(vm_ref, False, False) except XenAPI.Failure as e: raise CuckooMachineError("Unable to start vm %s: %s" - % (uuid, e.details[0])) + % (label, e.details[0])) - log.debug("Started vm: %s", uuid) + log.debug("Started vm: %s", label) - def stop(self, uuid): + def stop(self, label=None): """Stop a virtual machine. @param uuid: vm uuid """ - ref = self._get_vm_ref(uuid) + ref = self._get_vm_ref(label) vm = self._get_vm_record(ref) if self._is_halted(vm): - log.warning("Trying to stop an already stopped machine: %s", uuid) + log.warning("Trying to stop an already stopped machine: %s", label) else: try: self.session.xenapi.VM.hard_shutdown(ref) except XenAPI.Failure as e: raise CuckooMachineError("Error shutting down virtual machine:" - " %s: %s" % (uuid, e.details[0])) + " %s: %s" % (label, e.details[0])) From 7515c4110f2b0995b00e0782e7901a0b6d200181 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Thu, 8 Jan 2015 14:22:48 -0500 Subject: [PATCH 10/13] Added _status() implementation --- modules/machinery/xenserver.py | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/modules/machinery/xenserver.py b/modules/machinery/xenserver.py index 5f479e72e0..8dec068755 100644 --- a/modules/machinery/xenserver.py +++ b/modules/machinery/xenserver.py @@ -26,6 +26,12 @@ class XenServerMachinery(Machinery): LABEL = "uuid" + # Power States + RUNNING = "Running" + PAUSED = "Paused" + POWEROFF = "Halted" + ABORTED = "Suspended" + def _initialize_check(self): """Check XenServer configuration, initialize a Xen API connection, and verify machine validity. @@ -88,6 +94,13 @@ def _get_vm_record(self, ref): return self.session.xenapi.VM.get_record(ref) + def _get_vm_power_state(self, ref): + """Get the virtual machine power state. + @param ref: vm reference + """ + + return self.session.xenapi.VM.get_power_state(ref) + def _check_vm(self, uuid): """Check vm existence and validity. @param uuid: vm uuid @@ -231,3 +244,30 @@ def stop(self, label=None): except XenAPI.Failure as e: raise CuckooMachineError("Error shutting down virtual machine:" " %s: %s" % (label, e.details[0])) + + def _list(self): + """List available virtual machines. + @raise CuckooMachineError: if unable to list virtual machines. + """ + + try: + vm_list = [] + for ref in self.session.xenapi.VM.get_all(): + vm = self._get_vm_record(ref) + vm_list.append(vm['uuid']) + except: + raise CuckooMachineError("Cannot list domains") + else: + return vm_list + + def _status(self, label): + """Gets current status of a vm. + @param label: virtual machine uuid + @return: status string. + """ + + ref = self._get_vm_ref(label) + state = self._get_vm_power_state(ref) + + return state + From 1297dcf4d11033b385dfdb11c531991b54977b8b Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 28 Jan 2015 17:00:09 -0500 Subject: [PATCH 11/13] Added requirement note for the XenAPI package when using XenServer --- docs/book/src/installation/host/requirements.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/book/src/installation/host/requirements.rst b/docs/book/src/installation/host/requirements.rst index 68b30ef42c..0897ae67c6 100644 --- a/docs/book/src/installation/host/requirements.rst +++ b/docs/book/src/installation/host/requirements.rst @@ -56,7 +56,7 @@ Except for *python-magic*, *python-dpkt* and *python-libvirt*, the others can be *Yara* and *Pydeep* will have to be installed manually, so please refer to their websites. -If want to use KVM it's packaged too and you can install it with the following command:: +If you want to use KVM it's packaged too and you can install it with the following command:: $ sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils @@ -74,6 +74,10 @@ If want to use KVM it's packaged too and you can install it with the following c .. _MAEC Python bindings: https://pypi.python.org/pypi/maec/4.0.1.0 .. _Chardet: https://pypi.python.org/pypi/chardet +If you want to use XenServer you'll have to install the *XenAPI* Python package:: + + $ sudo pip install XenAPI + Virtualization Software ======================= From b790c66ee049c73a578d8027f06af0b4c4d21135 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Thu, 29 Jan 2015 10:24:03 -0500 Subject: [PATCH 12/13] Fixed error when reporting nonexisting VM and added multithread support The XenAPI XMLRPC connection is not thread safe. I modified the machinery to store a single XenAPI connection in each thread. Creating the connection is done on the fly and on demand; a new connection is created when a thread uses the machinery for the first time. --- modules/machinery/xenserver.py | 56 ++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/modules/machinery/xenserver.py b/modules/machinery/xenserver.py index 8dec068755..0562c8ec96 100644 --- a/modules/machinery/xenserver.py +++ b/modules/machinery/xenserver.py @@ -10,6 +10,7 @@ from lib.cuckoo.common.abstracts import Machinery from lib.cuckoo.common.exceptions import CuckooMachineError from lib.cuckoo.common.exceptions import CuckooDependencyError +import threading try: import XenAPI @@ -37,6 +38,8 @@ def _initialize_check(self): verify machine validity. """ + self._sessions = {} + if not HAVE_XENAPI: raise CuckooDependencyError("Unable to import XenAPI") @@ -52,15 +55,38 @@ def _initialize_check(self): raise CuckooMachineError("XenServer url missing, please add it to " "xenserver.conf") + self._make_xenapi_session() + + for machine in self.machines(): + uuid = machine.label + (ref, vm) = self._check_vm(uuid) + + if machine.snapshot: + self._check_snapshot(uuid, machine.snapshot) + else: + self._check_disks_reset(vm) + + super(XenServerMachinery, self)._initialize_check() + + @property + def session(self): + tid = threading.current_thread().ident + sess = self._sessions.get(tid, None) + if sess is None: + sess = self._make_xenapi_session(tid) + return sess + + def _make_xenapi_session(self, tid=None): + tid = tid or threading.current_thread().ident try: - self.session = XenAPI.Session(self.options.xenserver.url) + sess = XenAPI.Session(self.options.xenserver.url) except: raise CuckooMachineError("Could not connect to XenServer: invalid " - "or incorrect urlm please ensure the url " + "or incorrect url, please ensure the url " "is correct in xenserver.conf") try: - self.session.xenapi.login_with_password( + sess.xenapi.login_with_password( self.options.xenserver.user, self.options.xenserver.password ) except: @@ -68,24 +94,15 @@ def _initialize_check(self): "incorrect credentials, please ensure " "the user and password are correct in " "xenserver.conf") - - for machine in self.machines(): - uuid = machine.label - (ref, vm) = self._check_vm(uuid) - - if machine.snapshot: - self._check_snapshot(uuid, machine.snapshot) - else: - self._check_disks_reset(vm) - - super(XenServerMachinery, self)._initialize_check() + self._sessions[tid] = sess + return sess def _get_vm_ref(self, uuid): """Get a virtual machine reference. @param uuid: vm uuid """ - return self.session.xenapi.VM.get_by_uuid(uuid) + return self.session.xenapi.VM.get_by_uuid(uuid.lower()) def _get_vm_record(self, ref): """Get the virtual machine record. @@ -109,8 +126,9 @@ def _check_vm(self, uuid): try: ref = self._get_vm_ref(uuid) vm = self._get_vm_record(ref) - except XenAPI.Failure: - raise CuckooMachineError("Vm not found: %s: %s" % uuid) + except XenAPI.Failure as e: + raise CuckooMachineError("Vm not found: %s: %s" + % (uuid, e.details[0])) if vm["is_a_snapshot"]: raise CuckooMachineError("Vm is a snapshot: %s" % uuid) @@ -193,7 +211,7 @@ def _is_halted(self, vm): def start(self, label): """Start a virtual machine. - @param uuid: vm uuid + @param label: vm uuid """ vm_ref = self._get_vm_ref(label) @@ -231,7 +249,7 @@ def start(self, label): def stop(self, label=None): """Stop a virtual machine. - @param uuid: vm uuid + @param label: vm uuid """ ref = self._get_vm_ref(label) From c416cd7fb128c187ad8362b41bf89378c5870cfa Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Thu, 29 Jan 2015 10:30:51 -0500 Subject: [PATCH 13/13] Clarified XenServer guest virtual machine setup --- docs/book/src/installation/guest/saving.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/book/src/installation/guest/saving.rst b/docs/book/src/installation/guest/saving.rst index 13894e9e3a..2b07c842f6 100644 --- a/docs/book/src/installation/guest/saving.rst +++ b/docs/book/src/installation/guest/saving.rst @@ -142,14 +142,16 @@ boot and that the Cuckoo agent is set to run at boot time. Running the agent at boot time can be configured in Windows by adding a startup item for the agent. -To set the attached disks to reset on boot, you'll first need to list all the -attached disks for the virtual machine. Ignore disks that are already read only -or CD-ROM drives. To list all attached disks, run the following command:: +The following commands must be run while the virtual machine is powered off. + +To set the virtual machine's disks to reset on boot, you'll first need to list +all the attached disks for the virtual machine. To list all attached disks, run +the following command:: $ xe vm-disk-list vm="vm_name_or_uuid" -For all the VDI's listed for the virtual machine, run the following command to -change the disk to reset on boot:: +Ignoring all CD-ROM and read-only disks, run the following command for each +remaining disk to change it's behavior to reset on boot:: $ xe vdi-param-set uuid="vdi_uuid" on-boot=reset