maco 1.2.14__py3-none-any.whl → 1.2.15__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.
- demo_extractors/complex/complex.py +12 -0
- demo_extractors/complex/complex_utils.py +11 -2
- demo_extractors/elfy.py +12 -0
- demo_extractors/limit_other.py +15 -0
- demo_extractors/nothing.py +11 -3
- demo_extractors/shared.py +6 -0
- demo_extractors/terminator.py +12 -1
- maco/base_test.py +24 -7
- maco/cli.py +19 -5
- maco/collector.py +25 -9
- maco/exceptions.py +31 -1
- maco/extractor.py +7 -8
- maco/model/model.py +22 -0
- maco/utils.py +94 -16
- maco/yara.py +47 -5
- {maco-1.2.14.dist-info → maco-1.2.15.dist-info}/METADATA +3 -3
- maco-1.2.15.dist-info/RECORD +49 -0
- {maco-1.2.14.dist-info → maco-1.2.15.dist-info}/WHEEL +1 -1
- model_setup/maco/base_test.py +24 -7
- model_setup/maco/cli.py +19 -5
- model_setup/maco/collector.py +25 -9
- model_setup/maco/exceptions.py +31 -1
- model_setup/maco/extractor.py +7 -8
- model_setup/maco/model/model.py +22 -0
- model_setup/maco/utils.py +94 -16
- model_setup/maco/yara.py +47 -5
- tests/extractors/basic.py +10 -2
- tests/extractors/basic_longer.py +9 -2
- tests/extractors/bob/bob.py +2 -0
- tests/extractors/import_rewriting/__init__.py +0 -0
- tests/extractors/import_rewriting/importer.py +10341 -0
- tests/extractors/test_basic.py +4 -0
- maco-1.2.14.dist-info/RECORD +0 -47
- {maco-1.2.14.dist-info → maco-1.2.15.dist-info}/entry_points.txt +0 -0
- {maco-1.2.14.dist-info → maco-1.2.15.dist-info/licenses}/LICENSE.md +0 -0
- {maco-1.2.14.dist-info → maco-1.2.15.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Demo complex extractor."""
|
|
2
|
+
|
|
1
3
|
from io import BytesIO
|
|
2
4
|
from typing import List, Optional
|
|
3
5
|
|
|
@@ -39,6 +41,16 @@ class Complex(extractor.Extractor):
|
|
|
39
41
|
"""
|
|
40
42
|
|
|
41
43
|
def run(self, stream: BytesIO, matches: List[yara.Match]) -> Optional[model.ExtractorModel]:
|
|
44
|
+
"""Run the analysis process.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
stream (BytesIO): file object from disk/network/memory.
|
|
48
|
+
matches (List[yara.Match]): yara rule matches
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
(Optional[model.ExtractorModel]): model of results
|
|
52
|
+
|
|
53
|
+
"""
|
|
42
54
|
self.logger.info("starting run")
|
|
43
55
|
self.logger.debug(f"{[x.rule for x in matches]=}")
|
|
44
56
|
data = stream.read()
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"""Example of a complex function invoked by the extractor."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def getdata() -> Dict[str, int]:
|
|
7
|
+
"""This could be some complex and long function to support the main script.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
(Dict[str, int]): returns mock results
|
|
11
|
+
"""
|
|
3
12
|
return {"result": 5}
|
demo_extractors/elfy.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Demo extractor that targets ELF files."""
|
|
2
|
+
|
|
1
3
|
from io import BytesIO
|
|
2
4
|
from typing import List, Optional
|
|
3
5
|
|
|
@@ -21,6 +23,16 @@ class Elfy(extractor.Extractor):
|
|
|
21
23
|
"""
|
|
22
24
|
|
|
23
25
|
def run(self, stream: BytesIO, matches: List[yara.Match]) -> Optional[model.ExtractorModel]:
|
|
26
|
+
"""Run the analysis process.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
stream (BytesIO): file object from disk/network/memory.
|
|
30
|
+
matches (List[yara.Match]): yara rule matches
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
(Optional[model.ExtractorModel]): model of results
|
|
34
|
+
|
|
35
|
+
"""
|
|
24
36
|
# return config model formatted results
|
|
25
37
|
ret = model.ExtractorModel(family=self.family)
|
|
26
38
|
# the list for campaign_id already exists and is empty, so we just add an item
|
demo_extractors/limit_other.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Demo extractor to show the usage of the other field in the model."""
|
|
2
|
+
|
|
1
3
|
from io import BytesIO
|
|
2
4
|
from typing import List, Optional
|
|
3
5
|
|
|
@@ -23,6 +25,19 @@ class LimitOther(extractor.Extractor):
|
|
|
23
25
|
"""
|
|
24
26
|
|
|
25
27
|
def run(self, stream: BytesIO, matches: List[yara.Match]) -> Optional[model.ExtractorModel]:
|
|
28
|
+
"""Run the analysis process.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
stream (BytesIO): file object from disk/network/memory.
|
|
32
|
+
matches (List[yara.Match]): yara rule matches
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
(Optional[model.ExtractorModel]): model of results
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
Exception: if the httpx library is not installed
|
|
39
|
+
|
|
40
|
+
"""
|
|
26
41
|
# import httpx at runtime so we can test that requirements.txt is installed dynamically without breaking
|
|
27
42
|
# the tests that do direct importing
|
|
28
43
|
import httpx
|
demo_extractors/nothing.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
"""Demo extractor that returns nothing."""
|
|
2
|
+
|
|
1
3
|
from io import BytesIO
|
|
2
|
-
from typing import List
|
|
4
|
+
from typing import List
|
|
3
5
|
|
|
4
|
-
from maco import extractor,
|
|
6
|
+
from maco import extractor, yara
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
class Nothing(extractor.Extractor):
|
|
@@ -21,6 +23,12 @@ class Nothing(extractor.Extractor):
|
|
|
21
23
|
}
|
|
22
24
|
"""
|
|
23
25
|
|
|
24
|
-
def run(self, stream: BytesIO, matches: List[yara.Match])
|
|
26
|
+
def run(self, stream: BytesIO, matches: List[yara.Match]):
|
|
27
|
+
"""Run the analysis process.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
stream (BytesIO): file object from disk/network/memory.
|
|
31
|
+
matches (List[yara.Match]): yara rule matches
|
|
32
|
+
"""
|
|
25
33
|
# return config model formatted results
|
|
26
34
|
return
|
demo_extractors/shared.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Custom model based on Maco's model."""
|
|
2
|
+
|
|
1
3
|
from typing import Optional
|
|
2
4
|
|
|
3
5
|
import pydantic
|
|
@@ -6,7 +8,11 @@ from maco import model
|
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class MyCustomModel(model.ExtractorModel):
|
|
11
|
+
"""Custom model based on Maco's model."""
|
|
12
|
+
|
|
9
13
|
class Other(pydantic.BaseModel):
|
|
14
|
+
"""Custom 'other' class."""
|
|
15
|
+
|
|
10
16
|
key1: str
|
|
11
17
|
key2: bool
|
|
12
18
|
key3: int
|
demo_extractors/terminator.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Example extractor that terminates early during extraction."""
|
|
2
|
+
|
|
1
3
|
from io import BytesIO
|
|
2
4
|
from typing import List, Optional
|
|
3
5
|
|
|
@@ -6,12 +8,21 @@ from maco.exceptions import AnalysisAbortedException
|
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class Terminator(extractor.Extractor):
|
|
9
|
-
"""Terminates early during extraction"""
|
|
11
|
+
"""Terminates early during extraction."""
|
|
10
12
|
|
|
11
13
|
family = "terminator"
|
|
12
14
|
author = "skynet"
|
|
13
15
|
last_modified = "1997-08-29"
|
|
14
16
|
|
|
15
17
|
def run(self, stream: BytesIO, matches: List[yara.Match]) -> Optional[model.ExtractorModel]:
|
|
18
|
+
"""Run the analysis process but terminate early.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
stream (BytesIO): file object from disk/network/memory.
|
|
22
|
+
matches (List[yara.Match]): yara rule matches
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
AnalysisAbortedException: Extractor has decided to terminate early
|
|
26
|
+
"""
|
|
16
27
|
# Terminate early and indicate I can't run on this sample
|
|
17
28
|
raise AnalysisAbortedException("I can't run on this sample")
|
maco/base_test.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Foundation for unit testing an extractor.
|
|
2
2
|
|
|
3
3
|
Example:
|
|
4
|
-
|
|
5
4
|
from maco import base_test
|
|
6
5
|
class TestExample(base_test.BaseTest):
|
|
7
6
|
name = "Example"
|
|
@@ -20,13 +19,12 @@ import unittest
|
|
|
20
19
|
import cart
|
|
21
20
|
|
|
22
21
|
from maco import collector
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class NoHitException(Exception):
|
|
26
|
-
pass
|
|
22
|
+
from maco.exceptions import NoHitException
|
|
27
23
|
|
|
28
24
|
|
|
29
25
|
class BaseTest(unittest.TestCase):
|
|
26
|
+
"""Base test class."""
|
|
27
|
+
|
|
30
28
|
name: str = None # name of the extractor
|
|
31
29
|
# folder and/or file where extractor is.
|
|
32
30
|
# I recommend something like os.path.join(__file__, "../../extractors")
|
|
@@ -36,6 +34,11 @@ class BaseTest(unittest.TestCase):
|
|
|
36
34
|
|
|
37
35
|
@classmethod
|
|
38
36
|
def setUpClass(cls) -> None:
|
|
37
|
+
"""Initialization of class.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
Exception: when name or path is not set.
|
|
41
|
+
"""
|
|
39
42
|
if not cls.name or not cls.path:
|
|
40
43
|
raise Exception("name and path must be set")
|
|
41
44
|
cls.c = collector.Collector(cls.path, include=[cls.name], create_venv=cls.create_venv)
|
|
@@ -47,7 +50,11 @@ class BaseTest(unittest.TestCase):
|
|
|
47
50
|
self.assertEqual(len(self.c.extractors), 1)
|
|
48
51
|
|
|
49
52
|
def extract(self, stream):
|
|
50
|
-
"""Return results for running extractor over stream, including yara check.
|
|
53
|
+
"""Return results for running extractor over stream, including yara check.
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
NoHitException: when yara rule doesn't hit.
|
|
57
|
+
"""
|
|
51
58
|
runs = self.c.match(stream)
|
|
52
59
|
if not runs:
|
|
53
60
|
raise NoHitException("no yara rule hit")
|
|
@@ -65,7 +72,17 @@ class BaseTest(unittest.TestCase):
|
|
|
65
72
|
|
|
66
73
|
@classmethod
|
|
67
74
|
def load_cart(cls, filepath: str) -> io.BytesIO:
|
|
68
|
-
"""Load and unneuter a test file (likely malware) into memory for processing.
|
|
75
|
+
"""Load and unneuter a test file (likely malware) into memory for processing.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
filepath (str): Path to carted sample
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
(io.BytesIO): Buffered stream containing the un-carted sample
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
FileNotFoundError: if the path to the sample doesn't exist
|
|
85
|
+
"""
|
|
69
86
|
# it is nice if we can load files relative to whatever is implementing base_test
|
|
70
87
|
dirpath = os.path.split(cls._get_location())[0]
|
|
71
88
|
# either filepath is absolute, or should be loaded relative to child of base_test
|
maco/cli.py
CHANGED
|
@@ -3,19 +3,18 @@
|
|
|
3
3
|
import argparse
|
|
4
4
|
import base64
|
|
5
5
|
import binascii
|
|
6
|
-
import cart
|
|
7
6
|
import hashlib
|
|
8
7
|
import io
|
|
9
8
|
import json
|
|
10
9
|
import logging
|
|
11
10
|
import os
|
|
12
11
|
import sys
|
|
13
|
-
|
|
14
12
|
from importlib.metadata import version
|
|
15
13
|
from typing import BinaryIO, List, Tuple
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
import cart
|
|
18
16
|
|
|
17
|
+
from maco import collector
|
|
19
18
|
|
|
20
19
|
logger = logging.getLogger("maco.lib.cli")
|
|
21
20
|
|
|
@@ -29,7 +28,20 @@ def process_file(
|
|
|
29
28
|
force: bool,
|
|
30
29
|
include_base64: bool,
|
|
31
30
|
):
|
|
32
|
-
"""Process a filestream with the extractors and rules.
|
|
31
|
+
"""Process a filestream with the extractors and rules.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
collected (collector.Collector): a Collector instance
|
|
35
|
+
path_file (str): path to sample to be analyzed
|
|
36
|
+
stream (BinaryIO): binary stream to be analyzed
|
|
37
|
+
pretty (bool): Pretty print the JSON output
|
|
38
|
+
force (bool): Run all extractors regardless of YARA rule match
|
|
39
|
+
include_base64 (bool): include base64'd data in output
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
(dict): The output from the extractors analyzing the sample
|
|
43
|
+
|
|
44
|
+
"""
|
|
33
45
|
unneutered = io.BytesIO()
|
|
34
46
|
try:
|
|
35
47
|
cart.unpack_stream(stream, unneutered)
|
|
@@ -98,7 +110,8 @@ def process_filesystem(
|
|
|
98
110
|
) -> Tuple[int, int, int]:
|
|
99
111
|
"""Process filesystem with extractors and print results of extraction.
|
|
100
112
|
|
|
101
|
-
Returns
|
|
113
|
+
Returns:
|
|
114
|
+
(Tuple[int, int, int]): Total number of analysed files, yara hits and successful maco extractions.
|
|
102
115
|
"""
|
|
103
116
|
if force:
|
|
104
117
|
logger.warning("force execute will cause errors if an extractor requires a yara rule hit during execution")
|
|
@@ -163,6 +176,7 @@ def process_filesystem(
|
|
|
163
176
|
|
|
164
177
|
|
|
165
178
|
def main():
|
|
179
|
+
"""Main block for CLI."""
|
|
166
180
|
parser = argparse.ArgumentParser(description="Run extractors over samples.")
|
|
167
181
|
parser.add_argument("extractors", type=str, help="path to extractors")
|
|
168
182
|
parser.add_argument("samples", type=str, help="path to samples")
|
maco/collector.py
CHANGED
|
@@ -13,18 +13,20 @@ from multiprocess import Manager, Process, Queue
|
|
|
13
13
|
from pydantic import BaseModel
|
|
14
14
|
|
|
15
15
|
from maco import extractor, model, utils, yara
|
|
16
|
-
from maco.exceptions import AnalysisAbortedException
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class ExtractorLoadError(Exception):
|
|
20
|
-
pass
|
|
21
|
-
|
|
16
|
+
from maco.exceptions import AnalysisAbortedException, ExtractorLoadError
|
|
22
17
|
|
|
23
18
|
logger = logging.getLogger("maco.lib.helpers")
|
|
24
19
|
|
|
25
20
|
|
|
26
21
|
def _verify_response(resp: Union[BaseModel, dict]) -> Dict:
|
|
27
|
-
"""Enforce types and verify properties, and remove defaults.
|
|
22
|
+
"""Enforce types and verify properties, and remove defaults.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
resp (Union[BaseModel, dict])): results from extractor
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
(Dict): results from extractor after verification
|
|
29
|
+
"""
|
|
28
30
|
if not resp:
|
|
29
31
|
return None
|
|
30
32
|
# check the response is valid for its own model
|
|
@@ -62,6 +64,8 @@ class ExtractorRegistration(TypedDict):
|
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
class Collector:
|
|
67
|
+
"""Discover and load extractors from file system."""
|
|
68
|
+
|
|
65
69
|
def __init__(
|
|
66
70
|
self,
|
|
67
71
|
path_extractors: str,
|
|
@@ -70,7 +74,11 @@ class Collector:
|
|
|
70
74
|
create_venv: bool = False,
|
|
71
75
|
skip_install: bool = False,
|
|
72
76
|
):
|
|
73
|
-
"""Discover and load extractors from file system.
|
|
77
|
+
"""Discover and load extractors from file system.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ExtractorLoadError: when no extractors are found
|
|
81
|
+
"""
|
|
74
82
|
# maco requires the extractor to be imported directly, so ensure they are available on the path
|
|
75
83
|
full_path_extractors = os.path.abspath(path_extractors)
|
|
76
84
|
full_path_above_extractors = os.path.dirname(full_path_extractors)
|
|
@@ -175,7 +183,15 @@ class Collector:
|
|
|
175
183
|
stream: BinaryIO,
|
|
176
184
|
extractor_name: str,
|
|
177
185
|
) -> Dict[str, Any]:
|
|
178
|
-
"""Run extractor with stream and verify output matches the model.
|
|
186
|
+
"""Run extractor with stream and verify output matches the model.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
stream (BinaryIO): Binary stream to analyze
|
|
190
|
+
extractor_name (str): Name of extractor to analyze stream
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
(Dict[str, Any]): Results from extractor
|
|
194
|
+
"""
|
|
179
195
|
extractor = self.extractors[extractor_name]
|
|
180
196
|
try:
|
|
181
197
|
# Run extractor on a copy of the sample
|
maco/exceptions.py
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
"""Exception classes for extractors."""
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
# Can be raised by extractors to abort analysis of a sample
|
|
2
5
|
# ie. Can abort if preliminary checks at start of run indicate the file shouldn't be analyzed by extractor
|
|
3
|
-
class AnalysisAbortedException(Exception):
|
|
6
|
+
class AnalysisAbortedException(Exception):
|
|
7
|
+
"""Raised when extractors voluntarily abort analysis of a sample."""
|
|
8
|
+
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExtractorLoadError(Exception):
|
|
13
|
+
"""Raised when extractors cannot be loaded."""
|
|
14
|
+
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class InvalidExtractor(ValueError):
|
|
19
|
+
"""Raised when an extractor is invalid."""
|
|
20
|
+
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class NoHitException(Exception):
|
|
25
|
+
"""Raised when the YARA rule of an extractor doesn't hit."""
|
|
26
|
+
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SyntaxError(Exception):
|
|
31
|
+
"""Raised when there's a syntax error in the YARA rule."""
|
|
32
|
+
|
|
33
|
+
pass
|
maco/extractor.py
CHANGED
|
@@ -4,14 +4,8 @@ import logging
|
|
|
4
4
|
import textwrap
|
|
5
5
|
from typing import BinaryIO, List, Optional, Union
|
|
6
6
|
|
|
7
|
-
from maco import yara
|
|
8
|
-
|
|
9
|
-
from . import model
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class InvalidExtractor(ValueError):
|
|
13
|
-
pass
|
|
14
|
-
|
|
7
|
+
from maco import model, yara
|
|
8
|
+
from maco.exceptions import InvalidExtractor
|
|
15
9
|
|
|
16
10
|
DEFAULT_YARA_RULE = """
|
|
17
11
|
rule {name}
|
|
@@ -37,6 +31,11 @@ class Extractor:
|
|
|
37
31
|
logger: logging.Logger = None # logger for use when debugging
|
|
38
32
|
|
|
39
33
|
def __init__(self) -> None:
|
|
34
|
+
"""Initialise the extractor.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
InvalidExtractor: When the extractor is invalid.
|
|
38
|
+
"""
|
|
40
39
|
self.name = name = type(self).__name__
|
|
41
40
|
self.logger = logging.getLogger(f"maco.extractor.{name}")
|
|
42
41
|
self.logger.debug(f"initialise '{name}'")
|
maco/model/model.py
CHANGED
|
@@ -29,6 +29,8 @@ class Encryption(ForbidModel):
|
|
|
29
29
|
"""Encryption usage."""
|
|
30
30
|
|
|
31
31
|
class UsageEnum(str, Enum):
|
|
32
|
+
"""Purpose of the encryption."""
|
|
33
|
+
|
|
32
34
|
config = "config"
|
|
33
35
|
communication = "communication"
|
|
34
36
|
binary = "binary"
|
|
@@ -52,6 +54,8 @@ class Encryption(ForbidModel):
|
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
class CategoryEnum(str, Enum):
|
|
57
|
+
"""Category of the malware."""
|
|
58
|
+
|
|
55
59
|
# Software that shows you extra promotions that you cannot control as you use your PC.
|
|
56
60
|
# You wouldn't see the extra ads if you didn't have adware installed.
|
|
57
61
|
adware = "adware"
|
|
@@ -274,6 +278,8 @@ class ExtractorModel(ForbidModel):
|
|
|
274
278
|
"""Binary data extracted by decoder."""
|
|
275
279
|
|
|
276
280
|
class TypeEnum(str, Enum):
|
|
281
|
+
"""Type of binary data."""
|
|
282
|
+
|
|
277
283
|
payload = "payload" # contained within the original file
|
|
278
284
|
config = "config" # sometimes malware uses json/formatted text for config
|
|
279
285
|
other = "other"
|
|
@@ -289,6 +295,8 @@ class ExtractorModel(ForbidModel):
|
|
|
289
295
|
# convenience for ret.encryption.append(ret.Encryption(*properties))
|
|
290
296
|
# Define as class as only way to allow for this to be accessed and not have pydantic try to parse it.
|
|
291
297
|
class Encryption(Encryption):
|
|
298
|
+
"""Encryption usage."""
|
|
299
|
+
|
|
292
300
|
pass
|
|
293
301
|
|
|
294
302
|
encryption: Union[List[Encryption], Encryption, None] = None # encryption information for the binary
|
|
@@ -436,6 +444,8 @@ class ExtractorModel(ForbidModel):
|
|
|
436
444
|
"""Direct usage of DNS."""
|
|
437
445
|
|
|
438
446
|
class RecordTypeEnum(str, Enum):
|
|
447
|
+
"""DNS record types."""
|
|
448
|
+
|
|
439
449
|
A = "A"
|
|
440
450
|
AAAA = "AAAA"
|
|
441
451
|
AFSDB = "AFSDB"
|
|
@@ -512,6 +522,8 @@ class ExtractorModel(ForbidModel):
|
|
|
512
522
|
# convenience for ret.encryption.append(ret.Encryption(*properties))
|
|
513
523
|
# Define as class as only way to allow for this to be accessed and not have pydantic try to parse it.
|
|
514
524
|
class Encryption(Encryption):
|
|
525
|
+
"""Encryption usage."""
|
|
526
|
+
|
|
515
527
|
pass
|
|
516
528
|
|
|
517
529
|
encryption: List[Encryption] = []
|
|
@@ -530,6 +542,8 @@ class ExtractorModel(ForbidModel):
|
|
|
530
542
|
"""Cryptocoin usage (ransomware/miner)."""
|
|
531
543
|
|
|
532
544
|
class UsageEnum(str, Enum):
|
|
545
|
+
"""Cryptocoin usage."""
|
|
546
|
+
|
|
533
547
|
ransomware = "ransomware" # request money to unlock
|
|
534
548
|
miner = "miner" # use gpu/cpu to mint coins
|
|
535
549
|
other = "other"
|
|
@@ -543,7 +557,11 @@ class ExtractorModel(ForbidModel):
|
|
|
543
557
|
cryptocurrency: List[Cryptocurrency] = []
|
|
544
558
|
|
|
545
559
|
class Path(ForbidModel):
|
|
560
|
+
"""Path used by malware."""
|
|
561
|
+
|
|
546
562
|
class UsageEnum(str, Enum):
|
|
563
|
+
"""Purpose of the path."""
|
|
564
|
+
|
|
547
565
|
c2 = "c2" # file/folder issues commands to malware
|
|
548
566
|
config = "config" # config is loaded from this path
|
|
549
567
|
install = "install" # install directory/filename for malware
|
|
@@ -559,7 +577,11 @@ class ExtractorModel(ForbidModel):
|
|
|
559
577
|
paths: List[Path] = [] # files/directories used by malware
|
|
560
578
|
|
|
561
579
|
class Registry(ForbidModel):
|
|
580
|
+
"""Registry usage by malware."""
|
|
581
|
+
|
|
562
582
|
class UsageEnum(str, Enum):
|
|
583
|
+
"""Registry usage."""
|
|
584
|
+
|
|
563
585
|
persistence = "persistence" # stay alive
|
|
564
586
|
store_data = "store_data" # generated encryption keys or config
|
|
565
587
|
store_payload = "store_payload" # malware hidden in registry key
|