assemblyline-v4-service 4.4.0.24__py3-none-any.whl → 4.4.0.26__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.
Potentially problematic release.
This version of assemblyline-v4-service might be problematic. Click here for more details.
- assemblyline_v4_service/VERSION +1 -1
- assemblyline_v4_service/common/api.py +3 -2
- assemblyline_v4_service/common/base.py +3 -4
- assemblyline_v4_service/common/helper.py +1 -2
- assemblyline_v4_service/common/{extractor/ocr.py → ocr.py} +0 -1
- assemblyline_v4_service/common/ontology_helper.py +7 -8
- assemblyline_v4_service/common/request.py +4 -5
- assemblyline_v4_service/common/result.py +3 -3
- assemblyline_v4_service/common/task.py +3 -3
- assemblyline_v4_service/common/utils.py +2 -2
- assemblyline_v4_service/updater/helper.py +4 -0
- {assemblyline_v4_service-4.4.0.24.dist-info → assemblyline_v4_service-4.4.0.26.dist-info}/METADATA +1 -1
- assemblyline_v4_service-4.4.0.26.dist-info/RECORD +28 -0
- assemblyline_v4_service/common/balbuzard/__init__.py +0 -0
- assemblyline_v4_service/common/balbuzard/balbuzard.py +0 -656
- assemblyline_v4_service/common/balbuzard/bbcrack.py +0 -830
- assemblyline_v4_service/common/balbuzard/patterns.py +0 -650
- assemblyline_v4_service/common/dynamic_service_helper.py +0 -3631
- assemblyline_v4_service/common/extractor/__init__.py +0 -1
- assemblyline_v4_service/common/extractor/base64.py +0 -86
- assemblyline_v4_service/common/extractor/pe_file.py +0 -51
- assemblyline_v4_service/common/icap.py +0 -149
- assemblyline_v4_service/common/keytool_parse.py +0 -66
- assemblyline_v4_service/common/pestudio/__init__.py +0 -0
- assemblyline_v4_service/common/pestudio/xml/__init__.py +0 -0
- assemblyline_v4_service/common/pestudio/xml/features.xml +0 -5607
- assemblyline_v4_service/common/pestudio/xml/functions.xml +0 -5824
- assemblyline_v4_service/common/pestudio/xml/languages.xml +0 -375
- assemblyline_v4_service/common/pestudio/xml/resources.xml +0 -511
- assemblyline_v4_service/common/pestudio/xml/signatures.xml +0 -29105
- assemblyline_v4_service/common/pestudio/xml/strings.xml +0 -2379
- assemblyline_v4_service/common/safelist_helper.py +0 -73
- assemblyline_v4_service/common/section_reducer.py +0 -43
- assemblyline_v4_service/common/tag_helper.py +0 -117
- assemblyline_v4_service/common/tag_reducer.py +0 -242
- assemblyline_v4_service/testing/__init__.py +0 -0
- assemblyline_v4_service/testing/helper.py +0 -463
- assemblyline_v4_service/testing/regenerate_results.py +0 -37
- assemblyline_v4_service-4.4.0.24.dist-info/RECORD +0 -53
- {assemblyline_v4_service-4.4.0.24.dist-info → assemblyline_v4_service-4.4.0.26.dist-info}/LICENCE.md +0 -0
- {assemblyline_v4_service-4.4.0.24.dist-info → assemblyline_v4_service-4.4.0.26.dist-info}/WHEEL +0 -0
- {assemblyline_v4_service-4.4.0.24.dist-info → assemblyline_v4_service-4.4.0.26.dist-info}/top_level.txt +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Base 64 encoded text
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import binascii
|
|
6
|
-
import regex as re
|
|
7
|
-
import warnings
|
|
8
|
-
|
|
9
|
-
from typing import Dict, List, Tuple
|
|
10
|
-
|
|
11
|
-
HTML_ESCAPE_RE = rb'&#(?:x[a-fA-F0-9]{1,4}|\d{1,4});'
|
|
12
|
-
BASE64_RE = rb'(?:[A-Za-z0-9+/]{4,}(?:<\x00 \x00)?(?: |
)?(?: |
)?\r?\n?){5,}' \
|
|
13
|
-
rb'[A-Za-z0-9+/]{2,}={0,2}'
|
|
14
|
-
|
|
15
|
-
CAMEL_RE = rb'(?i)[a-z]+'
|
|
16
|
-
HEX_RE = rb'(?i)[a-f0-9]+'
|
|
17
|
-
MIN_B64_CHARS = 6
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def base64_search(text: bytes) -> Dict[bytes, bytes]:
|
|
21
|
-
"""
|
|
22
|
-
Find all base64 encoded sections in a text.
|
|
23
|
-
Args:
|
|
24
|
-
text: The text to search.
|
|
25
|
-
Returns:
|
|
26
|
-
A dictionary with the original base64 encoded sections as keys
|
|
27
|
-
and the corresponding decoded data as values.
|
|
28
|
-
"""
|
|
29
|
-
warnings.warn("base64_search is depricated, use find_base64 instead", DeprecationWarning)
|
|
30
|
-
b64_matches = {}
|
|
31
|
-
for b64_match in re.findall(BASE64_RE, text):
|
|
32
|
-
if b64_match in b64_matches:
|
|
33
|
-
continue
|
|
34
|
-
b64_string = re.sub(HTML_ESCAPE_RE, b'', b64_match).replace(b'\n', b'').replace(b'\r', b'') \
|
|
35
|
-
.replace(b'<\x00 \x00', b'')
|
|
36
|
-
if re.fullmatch(HEX_RE, b64_string):
|
|
37
|
-
# Hexadecimal characters are a subset of base64
|
|
38
|
-
# Hashes commonly are hex and have multiple of 4 lengths
|
|
39
|
-
continue
|
|
40
|
-
if re.fullmatch(CAMEL_RE, b64_string):
|
|
41
|
-
# Camel case text can be confused for base64
|
|
42
|
-
# It is common in scripts as names
|
|
43
|
-
continue
|
|
44
|
-
uniq_char = set(b64_string)
|
|
45
|
-
if len(uniq_char) > MIN_B64_CHARS and len(b64_string) % 4 == 0:
|
|
46
|
-
try:
|
|
47
|
-
b64_result = binascii.a2b_base64(b64_string)
|
|
48
|
-
b64_matches[b64_match] = b64_result
|
|
49
|
-
except binascii.Error:
|
|
50
|
-
pass
|
|
51
|
-
return b64_matches
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def find_base64(data: bytes) -> List[Tuple[bytes, int, int]]:
|
|
55
|
-
"""
|
|
56
|
-
Find all base64 encoded sections in some data.
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
data: The data to search.
|
|
60
|
-
Returns:
|
|
61
|
-
A list of decoded base64 sections and the location indexes of the section
|
|
62
|
-
in the original data.
|
|
63
|
-
"""
|
|
64
|
-
b64_matches = []
|
|
65
|
-
for b64_match in re.finditer(BASE64_RE, data):
|
|
66
|
-
b64_string = re.sub(HTML_ESCAPE_RE, b'', b64_match.group()).replace(b'\n', b'').replace(b'\r', b'') \
|
|
67
|
-
.replace(b'<\x00 \x00', b'')
|
|
68
|
-
if len(b64_string) % 4 != 0 or len(set(b64_string)) <= MIN_B64_CHARS:
|
|
69
|
-
continue
|
|
70
|
-
if re.fullmatch(HEX_RE, b64_string):
|
|
71
|
-
# Hexadecimal characters are a subset of base64
|
|
72
|
-
# Hashes commonly are hex and have multiple of 4 lengths
|
|
73
|
-
continue
|
|
74
|
-
if re.fullmatch(CAMEL_RE, b64_string):
|
|
75
|
-
# Camel case text can be confused for base64
|
|
76
|
-
# It is common in scripts as names
|
|
77
|
-
continue
|
|
78
|
-
if b64_string.count(b'/')/len(b64_string) > 3/32:
|
|
79
|
-
# If there are a lot of / it as more likely a path
|
|
80
|
-
continue
|
|
81
|
-
try:
|
|
82
|
-
b64_result = binascii.a2b_base64(b64_string)
|
|
83
|
-
b64_matches.append((b64_result, b64_match.start(), b64_match.end()))
|
|
84
|
-
except binascii.Error:
|
|
85
|
-
pass
|
|
86
|
-
return b64_matches
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import regex as re
|
|
2
|
-
|
|
3
|
-
from typing import List, Tuple
|
|
4
|
-
|
|
5
|
-
import pefile
|
|
6
|
-
|
|
7
|
-
EXEDOS_RE = rb'(?s)This program cannot be run in DOS mode'
|
|
8
|
-
EXEHEADER_RE = rb'(?s)MZ.{32,1024}PE\000\000'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def _find_pe_files_with_offset(data: bytes) -> List[Tuple[bytes, int, int]]:
|
|
12
|
-
"""
|
|
13
|
-
Searches for any PE files within data
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
data: The data to search
|
|
17
|
-
Returns:
|
|
18
|
-
A list tuples containing: The found PE file, the starting offset and the end offset
|
|
19
|
-
"""
|
|
20
|
-
pe_files: List[Tuple[bytes, int, int]] = []
|
|
21
|
-
offset = 0
|
|
22
|
-
while offset < len(data):
|
|
23
|
-
match = re.search(EXEHEADER_RE, data)
|
|
24
|
-
if not match:
|
|
25
|
-
return pe_files
|
|
26
|
-
pe_data = data[offset:]
|
|
27
|
-
if not re.search(EXEDOS_RE, pe_data):
|
|
28
|
-
return pe_files
|
|
29
|
-
try:
|
|
30
|
-
pe = pefile.PE(data=pe_data)
|
|
31
|
-
size = max(section.PointerToRawData + section.SizeOfRawData for section in pe.sections)
|
|
32
|
-
if size == 0:
|
|
33
|
-
return pe_files
|
|
34
|
-
end = offset+size
|
|
35
|
-
pe_files.append((data[offset:end], offset, end))
|
|
36
|
-
offset = end
|
|
37
|
-
except Exception:
|
|
38
|
-
return pe_files
|
|
39
|
-
return pe_files
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def find_pe_files(data: bytes) -> List[bytes]:
|
|
43
|
-
"""
|
|
44
|
-
Searches for any PE files within data
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
data: The data to search
|
|
48
|
-
Returns:
|
|
49
|
-
A list of found PE files
|
|
50
|
-
"""
|
|
51
|
-
return [pe[0] for pe in _find_pe_files_with_offset(data)]
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import socket
|
|
3
|
-
|
|
4
|
-
from assemblyline.common.str_utils import safe_str
|
|
5
|
-
|
|
6
|
-
ICAP_OK = b'ICAP/1.0 200 OK'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# noinspection PyBroadException
|
|
10
|
-
class IcapClient(object):
|
|
11
|
-
"""
|
|
12
|
-
A limited Internet Content Adaptation Protocol client.
|
|
13
|
-
|
|
14
|
-
Currently only supports RESPMOD as that is all that is required to interop
|
|
15
|
-
with most ICAP based AV servers.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
RESP_CHUNK_SIZE = 65565
|
|
19
|
-
MAX_RETRY = 3
|
|
20
|
-
|
|
21
|
-
def __init__(self, host, port, respmod_service="av/respmod", action="", timeout=30, number_of_retries=MAX_RETRY):
|
|
22
|
-
self.host = host
|
|
23
|
-
self.port = port
|
|
24
|
-
self.service = respmod_service
|
|
25
|
-
self.action = action
|
|
26
|
-
self.socket = None
|
|
27
|
-
self.timeout = timeout
|
|
28
|
-
self.kill = False
|
|
29
|
-
self.number_of_retries = number_of_retries
|
|
30
|
-
self.successful_connection = False
|
|
31
|
-
|
|
32
|
-
def scan_data(self, data, name=None):
|
|
33
|
-
return self._do_respmod(name or 'filetoscan', data)
|
|
34
|
-
|
|
35
|
-
def scan_local_file(self, filepath):
|
|
36
|
-
filename = os.path.basename(filepath)
|
|
37
|
-
with open(filepath, 'r') as f:
|
|
38
|
-
data = f.read()
|
|
39
|
-
return self.scan_data(data, filename)
|
|
40
|
-
|
|
41
|
-
def options_respmod(self):
|
|
42
|
-
request = f"OPTIONS icap://{self.host}:{self.port}/{self.service} ICAP/1.0\r\n\r\n"
|
|
43
|
-
|
|
44
|
-
for i in range(self.number_of_retries):
|
|
45
|
-
if self.kill:
|
|
46
|
-
self.kill = False
|
|
47
|
-
return
|
|
48
|
-
try:
|
|
49
|
-
if not self.socket:
|
|
50
|
-
self.socket = socket.create_connection((self.host, self.port), timeout=self.timeout)
|
|
51
|
-
self.successful_connection = True
|
|
52
|
-
self.socket.sendall(request.encode())
|
|
53
|
-
response = temp_resp = self.socket.recv(self.RESP_CHUNK_SIZE)
|
|
54
|
-
while len(temp_resp) == self.RESP_CHUNK_SIZE:
|
|
55
|
-
temp_resp = self.socket.recv(self.RESP_CHUNK_SIZE)
|
|
56
|
-
response += temp_resp
|
|
57
|
-
if not response or not response.startswith(ICAP_OK):
|
|
58
|
-
raise Exception(f"Unexpected OPTIONS response: {response}")
|
|
59
|
-
return response.decode()
|
|
60
|
-
except Exception:
|
|
61
|
-
self.successful_connection = False
|
|
62
|
-
try:
|
|
63
|
-
self.socket.close()
|
|
64
|
-
except Exception:
|
|
65
|
-
pass
|
|
66
|
-
self.socket = None
|
|
67
|
-
if i == (self.number_of_retries-1):
|
|
68
|
-
raise
|
|
69
|
-
|
|
70
|
-
raise Exception("Icap server refused to respond.")
|
|
71
|
-
|
|
72
|
-
@staticmethod
|
|
73
|
-
def chunk_encode(data):
|
|
74
|
-
chunk_size = 8160
|
|
75
|
-
out = b""
|
|
76
|
-
offset = 0
|
|
77
|
-
while len(data) < offset * chunk_size:
|
|
78
|
-
out += "1FEO\r\n"
|
|
79
|
-
out += data[offset * chunk_size:(offset + 1) * chunk_size]
|
|
80
|
-
out += "\r\n"
|
|
81
|
-
offset += 1
|
|
82
|
-
|
|
83
|
-
out += b"%X\r\n" % len(data[offset * chunk_size:])
|
|
84
|
-
out += data[offset * chunk_size:]
|
|
85
|
-
out += b"\r\n0\r\n\r\n"
|
|
86
|
-
|
|
87
|
-
return out
|
|
88
|
-
|
|
89
|
-
def _do_respmod(self, filename, data):
|
|
90
|
-
encoded = self.chunk_encode(data)
|
|
91
|
-
|
|
92
|
-
# ICAP RESPMOD req-hdr is the start of the original HTTP request.
|
|
93
|
-
respmod_req_hdr = "GET /{FILENAME} HTTP/1.1\r\n\r\n".format(FILENAME=safe_str(filename))
|
|
94
|
-
|
|
95
|
-
# ICAP RESPMOD res-hdr is the start of the HTTP response for above request.
|
|
96
|
-
respmod_res_hdr = (
|
|
97
|
-
"HTTP/1.1 200 OK\r\n"
|
|
98
|
-
"Transfer-Encoding: chunked\r\n\r\n")
|
|
99
|
-
|
|
100
|
-
res_hdr_offset = len(respmod_req_hdr)
|
|
101
|
-
res_bdy_offset = len(respmod_res_hdr) + res_hdr_offset
|
|
102
|
-
|
|
103
|
-
# The ICAP RESPMOD header. Note:
|
|
104
|
-
# res-hdr offset should match the start of the GET request above.
|
|
105
|
-
# res-body offset should match the start of the response above.
|
|
106
|
-
|
|
107
|
-
respmod_icap_hdr = (
|
|
108
|
-
f"RESPMOD icap://{self.host}:{self.port}/{self.service}{self.action} ICAP/1.0\r\n"
|
|
109
|
-
f"Host: {self.host}:{self.port}\r\n"
|
|
110
|
-
"Allow: 204\r\n"
|
|
111
|
-
f"Encapsulated: req-hdr=0, res-hdr={res_hdr_offset}, res-body={res_bdy_offset}\r\n\r\n"
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
serialized_request = b"%s%s%s%s" % (respmod_icap_hdr.encode(), respmod_req_hdr.encode(),
|
|
115
|
-
respmod_res_hdr.encode(), encoded)
|
|
116
|
-
|
|
117
|
-
for i in range(self.number_of_retries):
|
|
118
|
-
if self.kill:
|
|
119
|
-
self.kill = False
|
|
120
|
-
return
|
|
121
|
-
try:
|
|
122
|
-
if not self.socket:
|
|
123
|
-
self.socket = socket.create_connection((self.host, self.port), timeout=self.timeout)
|
|
124
|
-
self.successful_connection = True
|
|
125
|
-
self.socket.sendall(serialized_request)
|
|
126
|
-
response = temp_resp = self.socket.recv(self.RESP_CHUNK_SIZE)
|
|
127
|
-
while len(temp_resp) == self.RESP_CHUNK_SIZE:
|
|
128
|
-
temp_resp = self.socket.recv(self.RESP_CHUNK_SIZE)
|
|
129
|
-
response += temp_resp
|
|
130
|
-
|
|
131
|
-
return response.decode()
|
|
132
|
-
except Exception:
|
|
133
|
-
self.successful_connection = False
|
|
134
|
-
try:
|
|
135
|
-
self.socket.close()
|
|
136
|
-
except Exception:
|
|
137
|
-
pass
|
|
138
|
-
self.socket = None
|
|
139
|
-
if i == (self.number_of_retries-1):
|
|
140
|
-
raise
|
|
141
|
-
|
|
142
|
-
raise Exception("Icap server refused to respond.")
|
|
143
|
-
|
|
144
|
-
def close(self):
|
|
145
|
-
self.kill = True
|
|
146
|
-
try:
|
|
147
|
-
self.socket.close()
|
|
148
|
-
except Exception:
|
|
149
|
-
pass
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
from re import split
|
|
2
|
-
from subprocess import Popen, PIPE
|
|
3
|
-
from assemblyline.common.str_utils import safe_str
|
|
4
|
-
from typing import List
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Certificate():
|
|
8
|
-
def __init__(self):
|
|
9
|
-
self.raw = ""
|
|
10
|
-
self.issuer = ""
|
|
11
|
-
self.owner = ""
|
|
12
|
-
self.country = ""
|
|
13
|
-
self.valid_from = ""
|
|
14
|
-
self.valid_to = ""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def keytool_printcert(cert_path: str) -> None:
|
|
18
|
-
"""
|
|
19
|
-
This function runs the 'keytool -printcert' command against a provided file
|
|
20
|
-
|
|
21
|
-
:param cert_path: A path to a certificate
|
|
22
|
-
:return: the string output of 'keytool -printcert' or None
|
|
23
|
-
"""
|
|
24
|
-
stdout, _ = Popen(["keytool", "-printcert", "-file", cert_path],
|
|
25
|
-
stderr=PIPE, stdout=PIPE).communicate()
|
|
26
|
-
stdout = safe_str(stdout)
|
|
27
|
-
|
|
28
|
-
if stdout and "keytool error" not in stdout:
|
|
29
|
-
return stdout
|
|
30
|
-
|
|
31
|
-
return None
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def certificate_chain_from_printcert(printcert: str) -> List[Certificate]:
|
|
35
|
-
"""
|
|
36
|
-
This function parses the output of 'keytool -printcert' and creates a list of Certificate objects.
|
|
37
|
-
The input to this function is the output of keytool_printcert
|
|
38
|
-
|
|
39
|
-
:param printcert: the string output of 'keytool -printcert'
|
|
40
|
-
:return: a list of the parsed out certificates. If only one certificate
|
|
41
|
-
is present and not a chain, then the list will have one element.
|
|
42
|
-
"""
|
|
43
|
-
certs: List[Certificate] = []
|
|
44
|
-
|
|
45
|
-
for cert_str in split(r'Certificate\[\d+\]:', printcert): # split printcert output in case of certificate chain
|
|
46
|
-
if cert_str == '':
|
|
47
|
-
continue
|
|
48
|
-
cert = Certificate()
|
|
49
|
-
cert.raw = cert_str.strip()
|
|
50
|
-
for line in cert_str.splitlines():
|
|
51
|
-
if "Owner:" in line:
|
|
52
|
-
cert.owner = line.split(": ", 1)[1]
|
|
53
|
-
country = cert.owner.split("C=")
|
|
54
|
-
if len(country) != 1:
|
|
55
|
-
cert.country = country[1]
|
|
56
|
-
|
|
57
|
-
elif "Issuer:" in line:
|
|
58
|
-
cert.issuer = line.split(": ", 1)[1]
|
|
59
|
-
|
|
60
|
-
elif "Valid from:" in line:
|
|
61
|
-
cert.valid_from = line.split(": ", 1)[1].split(" until:")[0]
|
|
62
|
-
cert.valid_to = line.rsplit(": ", 1)[1]
|
|
63
|
-
|
|
64
|
-
certs.append(cert)
|
|
65
|
-
|
|
66
|
-
return certs
|
|
File without changes
|
|
File without changes
|