Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/cuckoo/core/analysis_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,15 +422,15 @@ def run_analysis_on_guest(self) -> None:
options["clock"] = self.db.update_clock(self.task.id)
self.db.guest_set_status(self.task.id, "starting")
guest_manager.start_analysis(options)

try:
if guest_manager.get_status_from_db() == "starting":
guest_manager.set_status_in_db("running")
guest_manager.wait_for_completion()
guest_manager.set_status_in_db("stopping")
except Exception as e:
guest_manager.set_status_in_db("failed")
self.log.exception(f"Unknown exception waiting for guest completion: {e}")
self.log.exception("Unknown exception waiting for guest completion: %s", str(e))

return

Expand Down
97 changes: 94 additions & 3 deletions modules/machinery/gcp.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import logging
import random
import string

from lib.cuckoo.common.config import Config
from typing import List
Expand Down Expand Up @@ -33,6 +35,8 @@ class GCP(Machinery):
ABORTED = "ABORTED"
ERROR = "ERROR"

OPERATION_TIMEOUT = 120 # 2 minutes

def _initialize_check(self):
"""Runs all checks when a machine manager is initialized.
@raise CuckooDependencyError: if google-cloud-compute is not installed
Expand All @@ -50,16 +54,17 @@ def _initialize_check(self):
log.info("Connecting to GCP Project: %s, Zone: %s", self.project, self.zone)

# Initialize Clients
creds = None
if self.json_key_path:
creds = service_account.Credentials.from_service_account_file(self.json_key_path)
self.instances_client = compute_v1.InstancesClient(credentials=creds)
elif self.running_in_gcp:
log.info("Using Compute Engine credentials")
creds = compute_engine.Credentials()
self.instances_client = compute_v1.InstancesClient(credentials=creds)
else:
log.info("No Service Account JSON provided; using Application Default Credentials")
self.instances_client = compute_v1.InstancesClient()

self.instances_client = compute_v1.InstancesClient(credentials=creds)
self.disks_client = compute_v1.DisksClient(credentials=creds)

super()._initialize_check()

Expand Down Expand Up @@ -145,5 +150,91 @@ def stop(self, label):
instance=label
)
self.instances_client.stop(request=request)
snapshot = self.db.view_machine_by_label(label).snapshot
if snapshot is not None:
self._wait_status(label, self.POWEROFF)
self._restore(label, snapshot)
except Exception as e:
raise CuckooMachineError(f"Unable to stop machine '{label}': {e}") from e

def _restore(self, label, snapshot):
"""
Restore a virtual machine according to the configured snapshot.
@param label: virtual machine label.
@raise CuckooMachineError: if unable to restore.
"""
log.debug("Restoring VM %s", label)
try:
request = compute_v1.GetInstanceRequest(
project=self.project,
zone=self.zone,
instance=label
)
instance = self.instances_client.get(request=request)
if len(instance.disks) > 1:
log.error("Unable to restore machine '%s': wrong number of disks", label)
raise CuckooMachineError(f"Unable to restore machine '{label}': wrong number of disks")
elif len(instance.disks) == 1:
# Detach old disk
old_disk = instance.disks[0]
log.debug("Detaching disk %s", old_disk.device_name)
request = compute_v1.DetachDiskInstanceRequest(
project=self.project,
zone=self.zone,
instance=label,
device_name=old_disk.device_name
)
operation = self.instances_client.detach_disk(request=request)
operation.result(timeout=self.OPERATION_TIMEOUT)
self._wait_and_check_operation(operation, label, "unable to detach disk")

# Delete old disk
log.debug("Deleting disk %s", old_disk.device_name)
request = compute_v1.DeleteDiskRequest(
project=self.project,
zone=self.zone,
disk=old_disk.device_name
)
operation = self.disks_client.delete(request=request)
self._wait_and_check_operation(operation, label, "unable to delete disk")

# Create disk from snapshot
new_disk_name = instance.name + ''.join(random.choices(string.ascii_lowercase, k=5))
log.debug("Creating disk %s from snapshot %s", new_disk_name, snapshot)
new_disk = compute_v1.Disk(
name=new_disk_name,
source_snapshot=f"projects/{self.project}/global/snapshots/{snapshot}",
zone=instance.zone
)
operation = self.disks_client.insert(
project=self.project,
zone=self.zone,
disk_resource=new_disk
)
operation.result(timeout=self.OPERATION_TIMEOUT)
self._wait_and_check_operation(operation, label, "unable to create disk")

# Attach new disk
log.debug("Attaching disk %s", new_disk.name)
request = compute_v1.AttachDiskInstanceRequest(
project=self.project,
zone=self.zone,
instance=label,
attached_disk_resource=compute_v1.AttachedDisk(
source=f"/projects/{self.project}/zones/{self.zone}/disks/{new_disk.name}",
mode="READ_WRITE",
auto_delete=True,
boot=True
)
)
operation = self.instances_client.attach_disk(request=request)
self._wait_and_check_operation(operation, label, "unable to attach disk")
except Exception as e:
raise CuckooMachineError(f"Unable to restore machine '{label}': {e}") from e

def _wait_and_check_operation(self, operation, label: str, error_message: str):
"""Waits for a GCP operation to complete and raises an error if it fails."""
operation.result(timeout=self.OPERATION_TIMEOUT)
if operation.error_code:
log.error("Unable to restore machine '%s': %s. Error: %s", label, error_message, operation.error_message)
raise CuckooMachineError(f"Unable to restore machine '{label}': {error_message}")
4 changes: 4 additions & 0 deletions modules/processing/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,10 @@ def run(self):
connection["sport"] = tcp.sport
connection["dport"] = tcp.dport

if tcp.flags & dpkt.tcp.TH_SYN and tcp.flags & dpkt.tcp.TH_ACK:
connection["src"], connection["dst"] = connection["dst"], connection["src"]
connection["sport"], connection["dport"] = connection["dport"], connection["sport"]

if tcp.data:
self._tcp_dissect(connection, tcp.data, ts)
src, sport, dst, dport = connection["src"], connection["sport"], connection["dst"], connection["dport"]
Expand Down