assemblyline-v4-service 4.6.1.dev231__py3-none-any.whl → 4.6.1.dev248__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- assemblyline_v4_service/VERSION +1 -1
- assemblyline_v4_service/common/api.py +1 -2
- assemblyline_v4_service/common/result.py +594 -3
- {assemblyline_v4_service-4.6.1.dev231.dist-info → assemblyline_v4_service-4.6.1.dev248.dist-info}/METADATA +1 -1
- {assemblyline_v4_service-4.6.1.dev231.dist-info → assemblyline_v4_service-4.6.1.dev248.dist-info}/RECORD +8 -8
- {assemblyline_v4_service-4.6.1.dev231.dist-info → assemblyline_v4_service-4.6.1.dev248.dist-info}/WHEEL +0 -0
- {assemblyline_v4_service-4.6.1.dev231.dist-info → assemblyline_v4_service-4.6.1.dev248.dist-info}/licenses/LICENCE.md +0 -0
- {assemblyline_v4_service-4.6.1.dev231.dist-info → assemblyline_v4_service-4.6.1.dev248.dist-info}/top_level.txt +0 -0
assemblyline_v4_service/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.6.1.
|
|
1
|
+
4.6.1.dev248
|
|
@@ -164,12 +164,11 @@ class PrivilegedServiceAPI:
|
|
|
164
164
|
def get_safelist(self, tag_list=None):
|
|
165
165
|
if DEVELOPMENT_MODE:
|
|
166
166
|
return {}
|
|
167
|
-
tag_types = None
|
|
168
167
|
|
|
169
168
|
if tag_list and not isinstance(tag_list, list):
|
|
170
169
|
raise ValueError("Parameter tag_list should be a list of strings.")
|
|
171
170
|
|
|
172
|
-
return self.safelist_client.get_safelisted_tags(
|
|
171
|
+
return self.safelist_client.get_safelisted_tags(tag_list)
|
|
173
172
|
|
|
174
173
|
def lookup_safelist(self, qhash):
|
|
175
174
|
if DEVELOPMENT_MODE:
|
|
@@ -2,7 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
-
from
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, TextIO, Union
|
|
6
7
|
|
|
7
8
|
from assemblyline.common import log as al_log
|
|
8
9
|
from assemblyline.common.attack_map import attack_map, group_map, revoke_map, software_map
|
|
@@ -39,7 +40,8 @@ BODY_FORMAT = StringTable('BODY_FORMAT', [
|
|
|
39
40
|
('MULTI', 9),
|
|
40
41
|
('DIVIDER', 10), # This is not a real result section and can only be use inside a multi section
|
|
41
42
|
('ORDERED_KEY_VALUE', 11),
|
|
42
|
-
('TIMELINE', 12)
|
|
43
|
+
('TIMELINE', 12),
|
|
44
|
+
('SANDBOX', 13),
|
|
43
45
|
])
|
|
44
46
|
|
|
45
47
|
# This is a StringTable representation of the PROMOTE_TO set of keys in
|
|
@@ -410,6 +412,542 @@ class ProcessTreeSectionBody(SectionBody):
|
|
|
410
412
|
self._data.append(process.as_primitives())
|
|
411
413
|
|
|
412
414
|
|
|
415
|
+
class SandboxMachineMetadata:
|
|
416
|
+
"""Metadata about the machine where the sandbox analysis took place."""
|
|
417
|
+
|
|
418
|
+
def __init__(
|
|
419
|
+
self,
|
|
420
|
+
# The IP of the machine used for analysis.
|
|
421
|
+
ip: Optional[str] = None,
|
|
422
|
+
|
|
423
|
+
# The hypervisor of the machine used for analysis.
|
|
424
|
+
hypervisor: Optional[str] = None,
|
|
425
|
+
|
|
426
|
+
# The name of the machine used for analysis.
|
|
427
|
+
hostname: Optional[str] = None,
|
|
428
|
+
|
|
429
|
+
# The platform of the machine used for analysis.
|
|
430
|
+
platform: Optional[str] = None,
|
|
431
|
+
|
|
432
|
+
# The version of the operating system of the machine used for analysis.
|
|
433
|
+
version: Optional[str] = None,
|
|
434
|
+
|
|
435
|
+
# The architecture of the machine used for analysis.
|
|
436
|
+
architecture: Optional[str] = None,
|
|
437
|
+
):
|
|
438
|
+
self.ip = ip
|
|
439
|
+
self.hypervisor = hypervisor
|
|
440
|
+
self.hostname = hostname
|
|
441
|
+
self.platform = platform
|
|
442
|
+
self.version = version
|
|
443
|
+
self.architecture = architecture
|
|
444
|
+
|
|
445
|
+
def as_primitives(self) -> Dict:
|
|
446
|
+
"""Return a JSON-serializable representation."""
|
|
447
|
+
return {
|
|
448
|
+
"ip": self.ip,
|
|
449
|
+
"hypervisor": self.hypervisor,
|
|
450
|
+
"hostname": self.hostname,
|
|
451
|
+
"platform": self.platform,
|
|
452
|
+
"version": self.version,
|
|
453
|
+
"architecture": self.architecture,
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
class SandboxAnalysisMetadata:
|
|
458
|
+
"""Metadata regarding the sandbox analysis task."""
|
|
459
|
+
|
|
460
|
+
def __init__(
|
|
461
|
+
self,
|
|
462
|
+
# The ID used for identifying the analysis task.
|
|
463
|
+
task_id: Optional[str] = None,
|
|
464
|
+
|
|
465
|
+
# The start time of the analysis (ISO format).
|
|
466
|
+
start_time: str = "",
|
|
467
|
+
|
|
468
|
+
# The end time of the analysis (ISO format).
|
|
469
|
+
end_time: Optional[str] = None,
|
|
470
|
+
|
|
471
|
+
# The routing used in the sandbox setup. (e.g., Spoofed, Internet, Tor, VPN)
|
|
472
|
+
routing: Optional[str] = None,
|
|
473
|
+
|
|
474
|
+
# The resolution used for the analysis.
|
|
475
|
+
window_size: Optional[str] = None,
|
|
476
|
+
):
|
|
477
|
+
self.task_id = task_id
|
|
478
|
+
self.start_time = start_time
|
|
479
|
+
self.end_time = end_time
|
|
480
|
+
self.routing = routing
|
|
481
|
+
self.window_size = window_size
|
|
482
|
+
|
|
483
|
+
def as_primitives(self) -> Dict:
|
|
484
|
+
"""Return a JSON-serializable representation."""
|
|
485
|
+
return {
|
|
486
|
+
"task_id": self.task_id,
|
|
487
|
+
"start_time": self.start_time,
|
|
488
|
+
"end_time": self.end_time,
|
|
489
|
+
"routing": self.routing,
|
|
490
|
+
"window_size": self.window_size,
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
class SandboxProcessItem:
|
|
495
|
+
"""Represents a process observed during sandbox execution."""
|
|
496
|
+
|
|
497
|
+
def __init__(
|
|
498
|
+
self,
|
|
499
|
+
|
|
500
|
+
# The image of the process. Default: "<unknown_image>".
|
|
501
|
+
image: str,
|
|
502
|
+
|
|
503
|
+
# The time of creation for the process. (ISO date format)
|
|
504
|
+
start_time: str,
|
|
505
|
+
|
|
506
|
+
# The process ID of the parent process.
|
|
507
|
+
ppid: Optional[int] = None,
|
|
508
|
+
|
|
509
|
+
# The process ID.
|
|
510
|
+
pid: Optional[int] = None,
|
|
511
|
+
|
|
512
|
+
# The command line that the process ran.
|
|
513
|
+
command_line: Optional[str] = None,
|
|
514
|
+
|
|
515
|
+
# The time of termination for the process. (ISO date format)
|
|
516
|
+
end_time: Optional[str] = None,
|
|
517
|
+
|
|
518
|
+
# The integrity level of the process.
|
|
519
|
+
integrity_level: Optional[str] = None,
|
|
520
|
+
|
|
521
|
+
# The hash of the file run.
|
|
522
|
+
image_hash: Optional[str] = None,
|
|
523
|
+
|
|
524
|
+
# The original name of the file.
|
|
525
|
+
original_file_name: Optional[str] = None,
|
|
526
|
+
|
|
527
|
+
# Whether this process was safelisted.
|
|
528
|
+
safelisted: Optional[bool] = False,
|
|
529
|
+
|
|
530
|
+
# Number of files this process interacted with
|
|
531
|
+
file_count: int = 0,
|
|
532
|
+
|
|
533
|
+
# Number of registries this process interacted with
|
|
534
|
+
registry_count: int = 0,
|
|
535
|
+
):
|
|
536
|
+
# ----------------------------
|
|
537
|
+
# Core process information
|
|
538
|
+
# ----------------------------
|
|
539
|
+
self.image = image or "<unknown_image>"
|
|
540
|
+
self.start_time = start_time
|
|
541
|
+
|
|
542
|
+
# Parent process information
|
|
543
|
+
self.ppid = ppid
|
|
544
|
+
|
|
545
|
+
# Current process information
|
|
546
|
+
self.pid = pid
|
|
547
|
+
self.command_line = command_line
|
|
548
|
+
self.end_time = end_time
|
|
549
|
+
self.integrity_level = integrity_level
|
|
550
|
+
self.image_hash = image_hash
|
|
551
|
+
self.original_file_name = original_file_name
|
|
552
|
+
self.safelisted = safelisted
|
|
553
|
+
|
|
554
|
+
# ----------------------------
|
|
555
|
+
# Relationships & statistics
|
|
556
|
+
# ----------------------------
|
|
557
|
+
self.file_count = file_count
|
|
558
|
+
self.registry_count = registry_count
|
|
559
|
+
|
|
560
|
+
def as_primitives(self) -> Dict:
|
|
561
|
+
"""Return a JSON-serializable dictionary representation of this process."""
|
|
562
|
+
return {
|
|
563
|
+
"image": self.image,
|
|
564
|
+
"start_time": self.start_time,
|
|
565
|
+
"ppid": self.ppid,
|
|
566
|
+
"pid": self.pid,
|
|
567
|
+
"command_line": self.command_line,
|
|
568
|
+
"end_time": self.end_time,
|
|
569
|
+
"integrity_level": self.integrity_level,
|
|
570
|
+
"image_hash": self.image_hash,
|
|
571
|
+
"original_file_name": self.original_file_name,
|
|
572
|
+
"safelisted": self.safelisted,
|
|
573
|
+
"file_count": self.file_count,
|
|
574
|
+
"registry_count": self.registry_count,
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
LookupType = Literal[
|
|
579
|
+
"A", "AAAA", "AFSDB", "APL", "CAA", "CDNSKEY", "CDS", "CERT", "CNAME", "CSYNC",
|
|
580
|
+
"DHCID", "DLV", "DNAME", "DNSKEY", "DS", "EUI48", "EUI64", "HINFO", "HIP",
|
|
581
|
+
"HTTPS", "IPSECKEY", "KEY", "KX", "LOC", "MX", "NAPTR", "NS", "NSEC", "NSEC3",
|
|
582
|
+
"NSEC3PARAM", "OPENPGPKEY", "PTR", "RRSIG", "RP", "SIG", "SMIMEA", "SOA",
|
|
583
|
+
"SRV", "SSHFP", "SVCB", "TA", "TKEY", "TLSA", "TSIG", "TXT", "URI", "ZONEMD"
|
|
584
|
+
]
|
|
585
|
+
|
|
586
|
+
RequestMethod = Literal[
|
|
587
|
+
"GET", "POST", "PUT", "DELETE", "HEAD", "CONNECT", "OPTIONS", "TRACE", "PATCH",
|
|
588
|
+
"BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "COPY", "LOCK",
|
|
589
|
+
"MKCOL", "MOVE", "NOTIFY", "POLL", "PROPFIND", "PROPPATCH", "SEARCH",
|
|
590
|
+
"SUBSCRIBE", "UNLOCK", "UNSUBSCRIBE", "X-MS-ENUMATTS"
|
|
591
|
+
]
|
|
592
|
+
|
|
593
|
+
ConnectionType = Literal["http", "dns", "tls", "smtp"]
|
|
594
|
+
|
|
595
|
+
ConnectionDirection = Literal["outbound", "inbound", "unknown"]
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
class SandboxNetworkDNS:
|
|
599
|
+
"""Details for a DNS request."""
|
|
600
|
+
|
|
601
|
+
def __init__(
|
|
602
|
+
self,
|
|
603
|
+
domain: str,
|
|
604
|
+
lookup_type: LookupType,
|
|
605
|
+
resolved_ips: Optional[List[str]] = None,
|
|
606
|
+
resolved_domains: Optional[List[str]] = None,
|
|
607
|
+
):
|
|
608
|
+
# The domain requested.
|
|
609
|
+
self.domain = domain
|
|
610
|
+
|
|
611
|
+
# A list of IPs that were resolved.
|
|
612
|
+
self.resolved_ips = resolved_ips or []
|
|
613
|
+
|
|
614
|
+
# A list of domains that were resolved.
|
|
615
|
+
self.resolved_domains = resolved_domains or []
|
|
616
|
+
|
|
617
|
+
# The type of DNS request.
|
|
618
|
+
self.lookup_type = lookup_type
|
|
619
|
+
|
|
620
|
+
def as_primitives(self) -> Dict:
|
|
621
|
+
return {
|
|
622
|
+
"domain": self.domain,
|
|
623
|
+
"resolved_ips": self.resolved_ips,
|
|
624
|
+
"resolved_domains": self.resolved_domains,
|
|
625
|
+
"lookup_type": self.lookup_type,
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
class SandboxNetworkHTTP:
|
|
630
|
+
"""Details for an HTTP request."""
|
|
631
|
+
|
|
632
|
+
def __init__(
|
|
633
|
+
self,
|
|
634
|
+
request_uri: str,
|
|
635
|
+
request_headers: Optional[Dict[str, object]] = None,
|
|
636
|
+
request_method: Optional[RequestMethod] = None,
|
|
637
|
+
response_headers: Optional[Dict[str, object]] = None,
|
|
638
|
+
request_body: Optional[str] = None,
|
|
639
|
+
response_status_code: Optional[int] = None,
|
|
640
|
+
response_body: Optional[str] = None,
|
|
641
|
+
response_content_fileinfo: Optional[Dict] = None,
|
|
642
|
+
response_content_mimetype: Optional[str] = None,
|
|
643
|
+
):
|
|
644
|
+
# The URI requested.
|
|
645
|
+
self.request_uri = request_uri
|
|
646
|
+
|
|
647
|
+
# Headers included in the request.
|
|
648
|
+
self.request_headers = request_headers or {}
|
|
649
|
+
|
|
650
|
+
# The method of the request.
|
|
651
|
+
self.request_method = request_method
|
|
652
|
+
|
|
653
|
+
# Headers included in the response.
|
|
654
|
+
self.response_headers = response_headers or {}
|
|
655
|
+
|
|
656
|
+
# The body of the request.
|
|
657
|
+
self.request_body = request_body
|
|
658
|
+
|
|
659
|
+
# The status code of the response.
|
|
660
|
+
self.response_status_code = response_status_code
|
|
661
|
+
|
|
662
|
+
# The body of the response.
|
|
663
|
+
self.response_body = response_body
|
|
664
|
+
|
|
665
|
+
# File information of the response content.
|
|
666
|
+
self.response_content_fileinfo = response_content_fileinfo
|
|
667
|
+
|
|
668
|
+
# MIME type returned by the server.
|
|
669
|
+
self.response_content_mimetype = response_content_mimetype
|
|
670
|
+
|
|
671
|
+
def as_primitives(self) -> Dict:
|
|
672
|
+
return {
|
|
673
|
+
"request_uri": self.request_uri,
|
|
674
|
+
"request_headers": self.request_headers,
|
|
675
|
+
"request_method": self.request_method,
|
|
676
|
+
"response_headers": self.response_headers,
|
|
677
|
+
"request_body": self.request_body,
|
|
678
|
+
"response_status_code": self.response_status_code,
|
|
679
|
+
"response_body": self.response_body,
|
|
680
|
+
"response_content_fileinfo": self.response_content_fileinfo,
|
|
681
|
+
"response_content_mimetype": self.response_content_mimetype,
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
class SandboxNetworkSMTP:
|
|
686
|
+
"""Details for an SMTP request."""
|
|
687
|
+
|
|
688
|
+
def __init__(
|
|
689
|
+
self,
|
|
690
|
+
mail_from: str,
|
|
691
|
+
mail_to: List[str],
|
|
692
|
+
attachments: Optional[List[Dict]] = None,
|
|
693
|
+
):
|
|
694
|
+
# Sender of the email.
|
|
695
|
+
self.mail_from = mail_from
|
|
696
|
+
|
|
697
|
+
# Recipients of the email.
|
|
698
|
+
self.mail_to = mail_to
|
|
699
|
+
|
|
700
|
+
# File information about the attachments.
|
|
701
|
+
self.attachments = attachments or []
|
|
702
|
+
|
|
703
|
+
def as_primitives(self) -> Dict:
|
|
704
|
+
return {
|
|
705
|
+
"mail_from": self.mail_from,
|
|
706
|
+
"mail_to": self.mail_to,
|
|
707
|
+
"attachments": self.attachments,
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
class SandboxNetflowItem:
|
|
712
|
+
"""Details about a low-level network connection by IP."""
|
|
713
|
+
|
|
714
|
+
def __init__(
|
|
715
|
+
self,
|
|
716
|
+
|
|
717
|
+
# The destination IP of the connection.
|
|
718
|
+
destination_ip: Optional[str] = None,
|
|
719
|
+
|
|
720
|
+
# The destination port of the connection.
|
|
721
|
+
destination_port: Optional[int] = None,
|
|
722
|
+
|
|
723
|
+
# The transport layer protocol (e.g., tcp, udp).
|
|
724
|
+
transport_layer_protocol: Optional[Literal["tcp", "udp"]] = None,
|
|
725
|
+
|
|
726
|
+
# The direction of the network connection.
|
|
727
|
+
direction: Optional[ConnectionDirection] = None,
|
|
728
|
+
|
|
729
|
+
# PID of the process that spawned the network connection.
|
|
730
|
+
pid: Optional[int] = None,
|
|
731
|
+
|
|
732
|
+
# The source IP of the connection.
|
|
733
|
+
source_ip: Optional[str] = None,
|
|
734
|
+
|
|
735
|
+
# The source port of the connection.
|
|
736
|
+
source_port: Optional[int] = None,
|
|
737
|
+
|
|
738
|
+
time_observed: str = None,
|
|
739
|
+
|
|
740
|
+
# HTTP-specific details of the request.
|
|
741
|
+
http_details: Optional[SandboxNetworkHTTP] = None,
|
|
742
|
+
|
|
743
|
+
# DNS-specific details of the request.
|
|
744
|
+
dns_details: Optional[SandboxNetworkDNS] = None,
|
|
745
|
+
|
|
746
|
+
# SMTP-specific details of the request.
|
|
747
|
+
smtp_details: Optional[SandboxNetworkSMTP] = None,
|
|
748
|
+
|
|
749
|
+
# Type of connection being made.
|
|
750
|
+
connection_type: Optional[ConnectionType] = None,
|
|
751
|
+
):
|
|
752
|
+
self.destination_ip = destination_ip
|
|
753
|
+
self.destination_port = destination_port
|
|
754
|
+
self.transport_layer_protocol = transport_layer_protocol
|
|
755
|
+
self.direction = direction
|
|
756
|
+
self.pid = pid
|
|
757
|
+
self.source_ip = source_ip
|
|
758
|
+
self.source_port = source_port
|
|
759
|
+
self.time_observed = time_observed
|
|
760
|
+
self.http_details = http_details
|
|
761
|
+
self.dns_details = dns_details
|
|
762
|
+
self.smtp_details = smtp_details
|
|
763
|
+
self.connection_type = connection_type
|
|
764
|
+
|
|
765
|
+
def as_primitives(self) -> Dict:
|
|
766
|
+
"""Return a JSON-serializable representation."""
|
|
767
|
+
data: Dict[str, Any] = {
|
|
768
|
+
"destination_ip": self.destination_ip,
|
|
769
|
+
"destination_port": self.destination_port,
|
|
770
|
+
"transport_layer_protocol": self.transport_layer_protocol,
|
|
771
|
+
"direction": self.direction,
|
|
772
|
+
"pid": self.pid,
|
|
773
|
+
"source_ip": self.source_ip,
|
|
774
|
+
"source_port": self.source_port,
|
|
775
|
+
"time_observed": self.time_observed,
|
|
776
|
+
"connection_type": self.connection_type,
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if self.http_details is not None:
|
|
780
|
+
data["http_details"] = self.http_details.as_primitives()
|
|
781
|
+
|
|
782
|
+
if self.dns_details is not None:
|
|
783
|
+
data["dns_details"] = self.dns_details.as_primitives()
|
|
784
|
+
|
|
785
|
+
if self.smtp_details is not None:
|
|
786
|
+
data["smtp_details"] = self.smtp_details.as_primitives()
|
|
787
|
+
|
|
788
|
+
return data
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
class SandboxAttackItem:
|
|
792
|
+
"""Represents a MITRE ATT&CK technique or pattern."""
|
|
793
|
+
|
|
794
|
+
def __init__(
|
|
795
|
+
self,
|
|
796
|
+
attack_id: str,
|
|
797
|
+
pattern: str = None,
|
|
798
|
+
categories: List[str] = [],
|
|
799
|
+
):
|
|
800
|
+
self.attack_id = attack_id
|
|
801
|
+
self.pattern = pattern
|
|
802
|
+
self.categories = categories or []
|
|
803
|
+
|
|
804
|
+
def as_primitives(self) -> Dict[str, Any]:
|
|
805
|
+
return {
|
|
806
|
+
"attack_id": self.attack_id,
|
|
807
|
+
"pattern": self.pattern,
|
|
808
|
+
"categories": self.categories,
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
class SandboxSignatureItem:
|
|
813
|
+
"""A signature that was raised during the analysis of the task."""
|
|
814
|
+
|
|
815
|
+
def __init__(
|
|
816
|
+
self,
|
|
817
|
+
|
|
818
|
+
# The name of the signature.
|
|
819
|
+
name: str,
|
|
820
|
+
|
|
821
|
+
# Type of signature. One of: "CUCKOO", "YARA", "SIGMA", "SURICATA".
|
|
822
|
+
type: Literal["CUCKOO", "YARA", "SIGMA", "SURICATA"],
|
|
823
|
+
|
|
824
|
+
# Classification of signature (e.g., "malicious", "benign").
|
|
825
|
+
classification: str,
|
|
826
|
+
|
|
827
|
+
# A list of ATT&CK patterns and categories of the signature.
|
|
828
|
+
attacks: Optional[List[SandboxAttackItem]] = [],
|
|
829
|
+
|
|
830
|
+
# List of actors of the signature.
|
|
831
|
+
actors: Optional[List[str]] = [],
|
|
832
|
+
|
|
833
|
+
# List of malware families of the signature.
|
|
834
|
+
malware_families: Optional[List[str]] = [],
|
|
835
|
+
|
|
836
|
+
# ID of the signature.
|
|
837
|
+
signature_id: Optional[str] = None,
|
|
838
|
+
|
|
839
|
+
# Optional human-readable message.
|
|
840
|
+
message: Optional[str] = None,
|
|
841
|
+
|
|
842
|
+
# PIDs of the processes that generated the signature.
|
|
843
|
+
pids: Optional[List[int]] = [],
|
|
844
|
+
|
|
845
|
+
# Score of the heuristic this signature belongs to
|
|
846
|
+
score: int = None,
|
|
847
|
+
):
|
|
848
|
+
self.name = name
|
|
849
|
+
self.type = type
|
|
850
|
+
self.classification = classification
|
|
851
|
+
self.attacks = attacks
|
|
852
|
+
self.actors = actors
|
|
853
|
+
self.malware_families = malware_families
|
|
854
|
+
self.signature_id = signature_id
|
|
855
|
+
self.message = message
|
|
856
|
+
self.pids = pids
|
|
857
|
+
self.score = score
|
|
858
|
+
|
|
859
|
+
def as_primitives(self) -> Dict[str, Any]:
|
|
860
|
+
return {
|
|
861
|
+
"name": self.name,
|
|
862
|
+
"type": self.type,
|
|
863
|
+
"classification": self.classification,
|
|
864
|
+
"attacks": [a.as_primitives() for a in self.attacks] if self.attacks else None,
|
|
865
|
+
"actors": self.actors,
|
|
866
|
+
"malware_families": self.malware_families,
|
|
867
|
+
"signature_id": self.signature_id,
|
|
868
|
+
"message": self.message,
|
|
869
|
+
"pids": self.pids,
|
|
870
|
+
"score": self.score,
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
class SandboxSectionBody(SectionBody):
|
|
877
|
+
"""
|
|
878
|
+
Represents the structured body of a sandbox analysis section.
|
|
879
|
+
Collects all sandbox-relevant entities: sandbox metadata, processes, network flows, and signatures.
|
|
880
|
+
"""
|
|
881
|
+
|
|
882
|
+
def __init__(self) -> None:
|
|
883
|
+
super().__init__(BODY_FORMAT.SANDBOX, body={
|
|
884
|
+
"sandbox_name": None,
|
|
885
|
+
"sandbox_version": None,
|
|
886
|
+
"machine_metadata": None,
|
|
887
|
+
"analysis_metadata": None,
|
|
888
|
+
"processes": [],
|
|
889
|
+
"netflows": [],
|
|
890
|
+
"signatures": [],
|
|
891
|
+
})
|
|
892
|
+
|
|
893
|
+
def set_sandbox(self, name: str, version: Optional[str], machine_metadata: SandboxMachineMetadata, analysis_metadata: SandboxAnalysisMetadata) -> None:
|
|
894
|
+
"""Set the sandbox metadata (name, version, machine info, and analysis info)."""
|
|
895
|
+
self._data["sandbox_name"] = name
|
|
896
|
+
self._data["sandbox_version"] = version
|
|
897
|
+
self._data["machine_metadata"] = (
|
|
898
|
+
machine_metadata.as_primitives() if machine_metadata else None
|
|
899
|
+
)
|
|
900
|
+
self._data["analysis_metadata"] = (
|
|
901
|
+
analysis_metadata.as_primitives() if analysis_metadata else None
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
def add_process(self, process: SandboxProcessItem) -> None:
|
|
905
|
+
"""Add a single process to the sandbox result."""
|
|
906
|
+
if not isinstance(process, SandboxProcessItem):
|
|
907
|
+
raise TypeError("Expected SandboxProcessItem")
|
|
908
|
+
self._data["processes"].append(process.as_primitives())
|
|
909
|
+
|
|
910
|
+
def add_processes(self, processes: List[SandboxProcessItem]) -> None:
|
|
911
|
+
"""Add multiple processes at once."""
|
|
912
|
+
for proc in processes:
|
|
913
|
+
self.add_process(proc)
|
|
914
|
+
|
|
915
|
+
def add_netflow(self, netflow: SandboxNetflowItem) -> None:
|
|
916
|
+
"""Add a network flow to the sandbox result."""
|
|
917
|
+
if not isinstance(netflow, SandboxNetflowItem):
|
|
918
|
+
raise TypeError("Expected SandboxNetflowItem")
|
|
919
|
+
self._data["netflows"].append(netflow.as_primitives())
|
|
920
|
+
|
|
921
|
+
def add_netflows(self, netflows: List[SandboxNetflowItem]) -> None:
|
|
922
|
+
"""Add multiple network flows at once."""
|
|
923
|
+
for nf in netflows:
|
|
924
|
+
self.add_netflow(nf)
|
|
925
|
+
|
|
926
|
+
def add_signature(self, signature: SandboxSignatureItem) -> None:
|
|
927
|
+
"""Add a detection signature to the sandbox result."""
|
|
928
|
+
if not isinstance(signature, SandboxSignatureItem):
|
|
929
|
+
raise TypeError("Expected SandboxSignatureItem")
|
|
930
|
+
self._data["signatures"].append(signature.as_primitives())
|
|
931
|
+
|
|
932
|
+
def add_signatures(self, signatures: List[SandboxSignatureItem]) -> None:
|
|
933
|
+
"""Add multiple detection signatures at once."""
|
|
934
|
+
for sig in signatures:
|
|
935
|
+
self.add_signature(sig)
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
def as_primitives(self) -> Dict[str, Any]:
|
|
939
|
+
"""Return a fully JSON-serializable structure."""
|
|
940
|
+
return {
|
|
941
|
+
"sandbox_name": self._data["sandbox_name"],
|
|
942
|
+
"sandbox_version": self._data["sandbox_version"],
|
|
943
|
+
"machine_metadata": self._data["machine_metadata"],
|
|
944
|
+
"analysis_metadata": self._data["analysis_metadata"],
|
|
945
|
+
"processes": self._data["processes"],
|
|
946
|
+
"netflows": self._data["netflows"],
|
|
947
|
+
"signatures": self._data["signatures"],
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
|
|
413
951
|
class TableRow(dict):
|
|
414
952
|
def __init__(self, *args, **kwargs) -> None:
|
|
415
953
|
data = {}
|
|
@@ -474,7 +1012,7 @@ class TimelineSectionBody(SectionBody):
|
|
|
474
1012
|
def add_node(self, title: str, content: str, opposite_content: str,
|
|
475
1013
|
icon: str = None, signatures: List[str] = [], score: int = 0) -> None:
|
|
476
1014
|
self._data.append(dict(title=title, content=content, opposite_content=opposite_content,
|
|
477
|
-
|
|
1015
|
+
icon=icon, signatures=signatures, score=score))
|
|
478
1016
|
|
|
479
1017
|
|
|
480
1018
|
class ResultSection:
|
|
@@ -795,6 +1333,59 @@ class ResultProcessTreeSection(TypeSpecificResultSection):
|
|
|
795
1333
|
self.section_body.add_process(process)
|
|
796
1334
|
|
|
797
1335
|
|
|
1336
|
+
class ResultSandboxSection(TypeSpecificResultSection):
|
|
1337
|
+
"""
|
|
1338
|
+
Represents a result section specifically designed for sandbox analysis data.
|
|
1339
|
+
Provides a typed interface to manipulate the underlying SandboxSectionBody.
|
|
1340
|
+
"""
|
|
1341
|
+
|
|
1342
|
+
def __init__(self, title_text: Union[str, List], **kwargs):
|
|
1343
|
+
self.section_body: SandboxSectionBody
|
|
1344
|
+
super().__init__(title_text, SandboxSectionBody(), **kwargs)
|
|
1345
|
+
|
|
1346
|
+
def set_sandbox(
|
|
1347
|
+
self,
|
|
1348
|
+
name: str,
|
|
1349
|
+
version: Optional[str],
|
|
1350
|
+
machine_metadata: Optional[SandboxMachineMetadata],
|
|
1351
|
+
analysis_metadata: Optional[SandboxAnalysisMetadata],
|
|
1352
|
+
) -> None:
|
|
1353
|
+
"""Set the sandbox metadata (name, version, machine info, and analysis info)."""
|
|
1354
|
+
self.section_body.set_sandbox(name, version, machine_metadata, analysis_metadata)
|
|
1355
|
+
|
|
1356
|
+
def add_process(self, process: SandboxProcessItem) -> None:
|
|
1357
|
+
"""Add a single process to the sandbox result."""
|
|
1358
|
+
self.section_body.add_process(process)
|
|
1359
|
+
|
|
1360
|
+
def add_processes(self, processes: List[SandboxProcessItem]) -> None:
|
|
1361
|
+
"""Add multiple processes at once."""
|
|
1362
|
+
self.section_body.add_processes(processes)
|
|
1363
|
+
|
|
1364
|
+
def add_netflow(self, netflow: SandboxNetflowItem) -> None:
|
|
1365
|
+
"""Add a single network flow to the sandbox result."""
|
|
1366
|
+
self.section_body.add_netflow(netflow)
|
|
1367
|
+
|
|
1368
|
+
def add_netflows(self, netflows: List[SandboxNetflowItem]) -> None:
|
|
1369
|
+
"""Add multiple network flows at once."""
|
|
1370
|
+
self.section_body.add_netflows(netflows)
|
|
1371
|
+
|
|
1372
|
+
def add_signature(self, signature: SandboxSignatureItem) -> None:
|
|
1373
|
+
"""Add a detection signature to the sandbox result."""
|
|
1374
|
+
self.section_body.add_signature(signature)
|
|
1375
|
+
|
|
1376
|
+
def add_signatures(self, signatures: List[SandboxSignatureItem]) -> None:
|
|
1377
|
+
"""Add multiple detection signatures at once."""
|
|
1378
|
+
self.section_body.add_signatures(signatures)
|
|
1379
|
+
|
|
1380
|
+
def add_heuristic(self, heuristic: SandboxHeuristicItem) -> None:
|
|
1381
|
+
"""Add a heuristic to the sandbox result."""
|
|
1382
|
+
self.section_body.add_heuristic(heuristic)
|
|
1383
|
+
|
|
1384
|
+
def add_heuristics(self, heuristics: List[SandboxHeuristicItem]) -> None:
|
|
1385
|
+
"""Add multiple heuristics at once."""
|
|
1386
|
+
self.section_body.add_heuristics(heuristics)
|
|
1387
|
+
|
|
1388
|
+
|
|
798
1389
|
class ResultTableSection(TypeSpecificResultSection):
|
|
799
1390
|
def __init__(self, title_text: Union[str, List], **kwargs):
|
|
800
1391
|
self.section_body: TableSectionBody
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
assemblyline_v4_service/VERSION,sha256=
|
|
1
|
+
assemblyline_v4_service/VERSION,sha256=SUMkvqKXjKag4psmpJRWlFSvoaGc4RwLKM7YQCSl-zU,13
|
|
2
2
|
assemblyline_v4_service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
assemblyline_v4_service/healthz.py,sha256=3QGBg0EZuXC6UN411HFwpLNEop9UvS9feFhvBUTP-k4,1576
|
|
4
4
|
assemblyline_v4_service/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
assemblyline_v4_service/run_privileged_service.py,sha256=un2zcZjQVKYwMWihLLmeUc3IMJ6ALnFbR1FPeMW1U2A,14486
|
|
6
6
|
assemblyline_v4_service/run_service.py,sha256=XfdABk3hEZsIw31tmFcJc-FbcxvBF9tiDIlg9oHCtZA,5900
|
|
7
7
|
assemblyline_v4_service/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
assemblyline_v4_service/common/api.py,sha256=
|
|
8
|
+
assemblyline_v4_service/common/api.py,sha256=59XcuHxOpR4gSZI0foNqOaRh7IINTGvWD-pjEUrU-jU,6823
|
|
9
9
|
assemblyline_v4_service/common/base.py,sha256=psivTxiOeN2jqL3G3I26oY9JFK-qPuwrg5y_y_d7xYs,14127
|
|
10
10
|
assemblyline_v4_service/common/helper.py,sha256=xs9quuf-M1JOdKieBqOmWaOece0CtzXFhhe85xQYmuY,3289
|
|
11
11
|
assemblyline_v4_service/common/ocr.py,sha256=NgkFqAq2lRzIveYUulKJmiiWYqwf4siYbL59n1Ow02o,8350
|
|
12
12
|
assemblyline_v4_service/common/ontology_helper.py,sha256=9Ad81qbddg_pRMupT8o_KzxbKgpodaRqpc3mPoEKLtw,8494
|
|
13
13
|
assemblyline_v4_service/common/request.py,sha256=W7fqC2xQE3i5i2jlCDyUDp3ZqJQQqSshNW0mQfJMkFg,11792
|
|
14
|
-
assemblyline_v4_service/common/result.py,sha256=
|
|
14
|
+
assemblyline_v4_service/common/result.py,sha256=zxZx8-RggtbB1RXGuZht-jHIFw4lCQBhzlC3aRSJsxo,52754
|
|
15
15
|
assemblyline_v4_service/common/task.py,sha256=dJsvRpW0x88CCF_LW6w87jQ_UKTVaOs2Gb117IDNiU8,14233
|
|
16
16
|
assemblyline_v4_service/common/utils.py,sha256=FDFsFcI6wt-pWyeQYnDWivsPbtme5RqVyofmNiggh6Y,3922
|
|
17
17
|
assemblyline_v4_service/dev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -24,7 +24,7 @@ assemblyline_v4_service/updater/client.py,sha256=tLY84gaGdFBVIDaMgRHIEa7x2S8jBl7
|
|
|
24
24
|
assemblyline_v4_service/updater/gunicorn_config.py,sha256=p3j2KPBeD5jvMw9O5i7vAtlRgPSVVxIG9AO0DfN82J8,1247
|
|
25
25
|
assemblyline_v4_service/updater/helper.py,sha256=OTV6WA77wBDOSVWaxijNg-HpwvEwnZozH03S3Q4oUns,10764
|
|
26
26
|
assemblyline_v4_service/updater/updater.py,sha256=XiqabDp89-t_J6C3U33R-RvA5lMIahFW_MsAVUGyXok,31876
|
|
27
|
-
assemblyline_v4_service-4.6.1.
|
|
27
|
+
assemblyline_v4_service-4.6.1.dev248.dist-info/licenses/LICENCE.md,sha256=NSkYo9EH8h5oOkzg4VhjAHF4339MqPP2cQ8msTPgl-c,1396
|
|
28
28
|
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
test/conftest.py,sha256=W3SieQpZsZpGEmtLqY4aIlxREDSsHceyCrFcFsWUM0U,1851
|
|
30
30
|
test/test_healthz.py,sha256=DkeLUlrb7rGx3nZ04aADU9HXXu5mZTf_DBwT0xhzIv4,7
|
|
@@ -40,7 +40,7 @@ test/test_common/test_request.py,sha256=HiDU1n4Rjso_U0qDME4ohA_9j7rpfqLSD1-e2Rfq
|
|
|
40
40
|
test/test_common/test_result.py,sha256=ZtLUddBDA_BTIjG3Jasbq78_AdEjCRe4cb85XLBwH5o,43585
|
|
41
41
|
test/test_common/test_task.py,sha256=P44mNcSe-3tJgDk9ppN3KbM7oN4LBVIuhONG-Gveh74,19007
|
|
42
42
|
test/test_common/test_utils.py,sha256=TbnBxqpS_ZC5ptXR9XJX3xtbItD0mTbtiBxxdyP8J5k,5904
|
|
43
|
-
assemblyline_v4_service-4.6.1.
|
|
44
|
-
assemblyline_v4_service-4.6.1.
|
|
45
|
-
assemblyline_v4_service-4.6.1.
|
|
46
|
-
assemblyline_v4_service-4.6.1.
|
|
43
|
+
assemblyline_v4_service-4.6.1.dev248.dist-info/METADATA,sha256=rFuHv74YlUeeTNvqE78Vq_3TJKJO_VyVyHfbbPFk5To,5625
|
|
44
|
+
assemblyline_v4_service-4.6.1.dev248.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
45
|
+
assemblyline_v4_service-4.6.1.dev248.dist-info/top_level.txt,sha256=LpTOEaVCatkrvbVq3EZseMSIa2PQZU-2rhuO_FTpZgY,29
|
|
46
|
+
assemblyline_v4_service-4.6.1.dev248.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|