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
23 changes: 15 additions & 8 deletions analyzer/windows/lib/common/abstracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,25 @@ def enum_paths(self):
basedir = path[0]
sys32 = len(path) > 1 and path[1].lower() == "system32"
if basedir == "SystemRoot":
if not sys32 or "PE32+" not in self.config.file_type:
yield os.path.join(os.getenv("SystemRoot"), *path[1:])
yield os.path.join(os.getenv("SystemRoot"), "sysnative", *path[2:])
system_root = os.getenv("SystemRoot")
if system_root:
file_type = getattr(self.config, "file_type", "") or ""
if not sys32 or "PE32+" not in file_type:
yield os.path.join(system_root, *path[1:])
if sys32:
yield os.path.join(system_root, "sysnative", *path[2:])
# Fallback for 64-bit Python where sysnative is not available
if "PE32+" in file_type:
yield os.path.join(system_root, *path[1:])
elif basedir == "ProgramFiles":
if os.getenv("ProgramFiles(x86)"):
yield os.path.join(os.getenv("ProgramFiles(x86)"), *path[1:])
yield os.path.join(os.getenv("ProgramFiles").replace(" (x86)", ""), *path[1:])
if os.getenv("ProgramFiles"):
yield os.path.join(os.getenv("ProgramFiles").replace(" (x86)", ""), *path[1:])
elif basedir == "HomeDrive":
# os.path.join() does not work well when giving just C:
# instead of C:\\, so we manually add the backslash.
homedrive = "{}\\".format(os.getenv("HomeDrive"))
yield os.path.join(homedrive, *path[1:])
homedrive = os.getenv("HomeDrive")
if homedrive:
yield os.path.join(f"{homedrive}\\", *path[1:])
elif os.getenv(basedir):
yield os.path.join(os.getenv(basedir), *path[1:])
else:
Expand Down
15 changes: 0 additions & 15 deletions data/yara/CAPE/Sedreco.yar

This file was deleted.

15 changes: 9 additions & 6 deletions lib/cuckoo/common/gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,26 @@
gcp_cfg = Config("gcp")


def download_from_gcs(gcs_uri, destination_path):
def download_from_gcs(gcs_uri, destination_path, logger=None):
"""
Downloads a file from GCS.
gcs_uri: gs://bucket_name/object_name
"""
if logger is None:
logger = log

if not HAVE_GCP:
log.error("Google Cloud Storage dependencies not installed. Please run `poetry install --extras gcp` or `pip install google-cloud-storage`")
logger.error("Google Cloud Storage dependencies not installed. Please run `poetry install --extras gcp` or `pip install google-cloud-storage`")
return False

try:
if not gcs_uri.startswith("gs://"):
log.error("Invalid GCS URI: %s", gcs_uri)
logger.error("Invalid GCS URI: %s", gcs_uri)
return False

path_parts = gcs_uri[5:].split("/", 1)
if len(path_parts) != 2:
log.error("Invalid GCS URI: %s", gcs_uri)
logger.error("Invalid GCS URI: %s", gcs_uri)
return False

bucket_name, blob_name = path_parts
Expand All @@ -56,11 +59,11 @@ def download_from_gcs(gcs_uri, destination_path):
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(blob_name)

log.info("Downloading %s to %s", gcs_uri, destination_path)
logger.info("Downloading %s to %s", gcs_uri, destination_path)
blob.download_to_filename(destination_path)
return True
except Exception as e:
log.error("Failed to download from GCS %s: %s", gcs_uri, e)
logger.error("Failed to download from GCS %s: %s", gcs_uri, e)
return False

def check_node_up(host: str) -> bool:
Expand Down
9 changes: 3 additions & 6 deletions utils/fstab.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
import tempfile
import threading

if sys.version_info[:2] < (3, 8):
sys.exit("You are running an incompatible version of Python, please use >= 3.8")

CUCKOO_ROOT = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")
sys.path.append(CUCKOO_ROOT)

Expand Down Expand Up @@ -58,8 +55,8 @@ def add_nfs_entry(hostname: str, worker_folder: str):
if any(hostname in entry for entry in fstab if not entry.startswith("#")):
return

# hostname:/opt/CAPEv2 /opt/CAPEv2/2 nfs, auto,users,nofail,noatime,nolock,intr,tcp,actimeo=1800, 0 0
fstab.append(f"{hostname}:/opt/CAPEv2 {worker_path} nfs, auto,user,users,nofail,noatime,nolock,intr,tcp,actimeo=1800, 0 0")
# hostname:/opt/CAPEv2 /opt/CAPEv2/2 nfs _netdev,nofail,noatime,nolock,intr,tcp,actimeo=1800,x-systemd.automount,x-systemd.mount-timeout=30s 0 0
fstab.append(f"{hostname}:/opt/CAPEv2 {worker_path} nfs _netdev,nofail,noatime,nolock,intr,tcp,actimeo=1800,x-systemd.automount,x-systemd.mount-timeout=30s 0 0")
_ = path_write_file("/etc/fstab", "\n".join(fstab), mode="text")

try:
Expand All @@ -74,7 +71,7 @@ def remove_nfs_entry(hostname: str):
with lock:
fstab = path_read_file("/etc/fstab", mode="text").split("\n")
for entry in fstab:
if entry.startswith(hostname) and " nfs, " in entry:
if entry.startswith(hostname) and " nfs " in entry:
fstab.remove(entry)
_ = path_write_file("/etc/fstab", "\n".join(fstab), mode="text")
break
Expand Down
48 changes: 38 additions & 10 deletions utils/gcp_pubsub_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
import sys
import tempfile
import threading
import warnings

# Mute Google Cloud's Python version support warning for Python 3.10
warnings.filterwarnings("ignore", category=FutureWarning, module="google.api_core")

sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))

Expand All @@ -25,6 +29,13 @@
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s: %(message)s")
log = logging.getLogger("gcp_pubsub_service")

class GCPServiceLogger(logging.LoggerAdapter):
def process(self, msg, kwargs):
correlation_id = self.extra.get("correlation_id")
if correlation_id:
msg = f"[{correlation_id}] {msg}"
return msg, kwargs

class GCPPubSubService:
def __init__(self):
self.gcp_cfg = Config("gcp")
Expand Down Expand Up @@ -57,21 +68,38 @@ def __init__(self):
self.db = Database()

def process_message(self, message):
correlation_id = message.message_id
try:
payload = json.loads(message.data.decode("utf-8"))
log.info("Received payload: %s", payload.get("uuid"))
correlation_id = payload.get("uuid") or payload.get("transaction_id") or message.message_id

# Create a localized logger with correlation_id
mlog = GCPServiceLogger(log, {"correlation_id": correlation_id})

sample_hash = payload.get("sample_hash")
gcs_uri = payload.get("gcs_uri")
if not sample_hash or not gcs_uri:
mlog.error("Missing sample_hash or gcs_uri in payload")
message.nack()
return
sandbox_options = payload.get("sandbox_options", "")
parent_id = payload.get("parent_id", "")
transaction_id = payload.get("transaction_id", "")
sample_name = payload.get("name", "sample")
source = payload.get("source", "")

mlog.info("Received message for sample: %s (name: %s, source: %s)", sample_hash, sample_name, source)

category = None
if "category=static" in sandbox_options:
category = "static"

sandbox_options = sandbox_options or ""
if sandbox_options:
sandbox_options += f",name={sample_name}"
else:
sandbox_options += f"name={sample_name}"

# Format custom fields with truncation to fit 255 chars
custom_parts = []
if parent_id:
Expand All @@ -93,14 +121,14 @@ def process_message(self, message):
is_temp = False

if not path_exists(local_path):
log.info("Sample %s not found locally, fetching from GCS: %s", sample_hash, gcs_uri)
fd, temp_path = tempfile.mkstemp()
mlog.info("Sample %s not found locally, fetching from GCS: %s", sample_hash, gcs_uri)
fd, temp_path = tempfile.mkstemp(prefix=sample_name)
os.close(fd)
if download_from_gcs(gcs_uri, temp_path):
if download_from_gcs(gcs_uri, temp_path, logger=mlog):
local_path = temp_path
is_temp = True
else:
log.error("Failed to download sample from GCS")
mlog.error("Failed to download sample from GCS: %s", gcs_uri)
message.nack()
return

Expand All @@ -115,23 +143,23 @@ def process_message(self, message):
filename=sample_name,
)
if task_ids:
log.info("Successfully submitted task(s): %s", task_ids)
mlog.info("Successfully submitted task(s) %s for sample %s", task_ids, sample_hash)
message.ack()
else:
log.error("Failed to add task to database")
mlog.error("Failed to add task to database for sample %s", sample_hash)
message.nack()
except Exception as e:
log.error("Failed to add task to database: %s", e)
mlog.error("Failed to add task to database: %s", e)
message.nack()
finally:
if is_temp and path_exists(local_path):
try:
os.unlink(local_path)
except Exception as e:
log.warning("Failed to delete temp file %s for task, %s: %s", local_path, payload.get("uuid"), e)
mlog.warning("Failed to delete temp file %s: %s", local_path, e)

except Exception as e:
log.error("Error processing message: %s", e)
log.error("[%s] Error processing message: %s", correlation_id, e)
message.nack()

def start(self):
Expand Down
12 changes: 10 additions & 2 deletions web/apiv2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
import sys
import tempfile
import zipfile
from contextlib import suppress
from datetime import datetime, timedelta
from io import BytesIO
from urllib.parse import quote, urljoin
from wsgiref.util import FileWrapper

import plyara
import plyara.utils
import pyzipper
import requests
import yara
Expand Down Expand Up @@ -88,6 +87,12 @@
except ImportError:
import re

HAVE_PLYARA = False
with suppress(ImportError):
import plyara
import plyara.utils
HAVE_PLYARA = True

# FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'

# Config variables
Expand Down Expand Up @@ -2909,6 +2914,9 @@ def yara_uploader(request):
if not apiconf.yara_uploader.get("enabled"):
return Response({"error": True, "error_value": "Yara Uploader API is Disabled"})

if not HAVE_PLYARA:
return Response({"error": True, "error_value": "Missing dependency. Contact your administrator."})

category = request.data.get("category")
if not category or category not in ALLOWED_YARA_CATEGORIES:
return Response(
Expand Down
Loading
Loading