atomicshop 3.3.8__py3-none-any.whl → 3.10.0__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 atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/basics/strings.py +1 -1
- atomicshop/certificates.py +2 -2
- atomicshop/dns.py +26 -28
- atomicshop/etws/traces/trace_tcp.py +1 -2
- atomicshop/mitm/centered_settings.py +133 -0
- atomicshop/mitm/config_static.py +22 -44
- atomicshop/mitm/connection_thread_worker.py +383 -165
- atomicshop/mitm/engines/__parent/recorder___parent.py +1 -1
- atomicshop/mitm/engines/__parent/requester___parent.py +1 -1
- atomicshop/mitm/engines/__parent/responder___parent.py +15 -2
- atomicshop/mitm/engines/create_module_template.py +1 -2
- atomicshop/mitm/import_config.py +91 -89
- atomicshop/mitm/initialize_engines.py +1 -2
- atomicshop/mitm/message.py +5 -4
- atomicshop/mitm/mitm_main.py +238 -122
- atomicshop/mitm/recs_files.py +61 -5
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +33 -12
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +104 -31
- atomicshop/networks.py +160 -92
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/ubuntu_permissions.py +47 -0
- atomicshop/print_api.py +3 -5
- atomicshop/process.py +11 -4
- atomicshop/python_functions.py +23 -108
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +140 -164
- atomicshop/web.py +63 -22
- atomicshop/web_apis/google_llm.py +22 -14
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -1
- atomicshop/wrappers/dockerw/dockerw.py +2 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +0 -190
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +5 -5
- atomicshop/wrappers/githubw.py +180 -68
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/handlers.py +1 -1
- atomicshop/wrappers/loggingw/loggingw.py +20 -4
- atomicshop/wrappers/loggingw/reading.py +18 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +0 -38
- atomicshop/wrappers/netshw.py +124 -3
- atomicshop/wrappers/playwrightw/scenarios.py +1 -1
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +9 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +12 -27
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +15 -9
- atomicshop/wrappers/socketw/certificator.py +19 -9
- atomicshop/wrappers/socketw/creator.py +101 -14
- atomicshop/wrappers/socketw/dns_server.py +17 -5
- atomicshop/wrappers/socketw/exception_wrapper.py +21 -16
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +29 -9
- atomicshop/wrappers/socketw/sender.py +10 -9
- atomicshop/wrappers/socketw/sni.py +31 -10
- atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
- atomicshop/wrappers/socketw/socket_client.py +11 -10
- atomicshop/wrappers/socketw/socket_wrapper.py +125 -32
- atomicshop/wrappers/socketw/ssl_base.py +6 -2
- atomicshop/wrappers/ubuntu_terminal.py +21 -18
- atomicshop/wrappers/win_auditw.py +189 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/RECORD +83 -109
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/ubuntu/docker_rootless.py +0 -11
- atomicshop/a_installs/ubuntu/docker_sudo.py +0 -11
- atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py +0 -10
- atomicshop/a_installs/ubuntu/mongodb.py +0 -12
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/wsl_ubuntu_lts.py +0 -10
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/archiver/__init__.py +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/search_in_archive.py +0 -284
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -73
- atomicshop/archiver/shutils.py +0 -34
- atomicshop/archiver/zips.py +0 -353
- atomicshop/file_types.py +0 -24
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/script_as_string_processor.py +0 -42
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -449
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -233
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/__init__.py +0 -0
- atomicshop/wrappers/fibratusw/install.py +0 -80
- atomicshop/wrappers/mongodbw/install_mongodb_ubuntu.py +0 -100
- atomicshop/wrappers/mongodbw/install_mongodb_win.py +0 -244
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/get_process.py +0 -123
- atomicshop/wrappers/wslw.py +0 -192
- atomicshop-3.3.8.dist-info/entry_points.txt +0 -2
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
- {atomicshop-3.3.8.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import socket
|
|
2
|
+
import time
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
LOCALHOST_IPV4: str = '127.0.0.1'
|
|
@@ -99,4 +100,35 @@ def get_host_name_from_ip_address(ip_address: str) -> str:
|
|
|
99
100
|
host_name, alias_list, ipaddr_list = socket.gethostbyaddr(ip_address)
|
|
100
101
|
_ = alias_list, ipaddr_list
|
|
101
102
|
|
|
102
|
-
return host_name
|
|
103
|
+
return host_name
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def wait_for_ip_bindable(
|
|
107
|
+
ip: str,
|
|
108
|
+
port: int = 0,
|
|
109
|
+
timeout: float = 15.0,
|
|
110
|
+
interval: float = 0.5,
|
|
111
|
+
) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Wait until a single IP is bindable (or timeout).
|
|
114
|
+
|
|
115
|
+
Raises TimeoutError if the IP cannot be bound within 'timeout' seconds.
|
|
116
|
+
"""
|
|
117
|
+
deadline = time.time() + timeout
|
|
118
|
+
last_err: OSError | None = None
|
|
119
|
+
|
|
120
|
+
while time.time() < deadline:
|
|
121
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
122
|
+
try:
|
|
123
|
+
s.bind((ip, port))
|
|
124
|
+
s.close()
|
|
125
|
+
return # success
|
|
126
|
+
except OSError as e:
|
|
127
|
+
last_err = e
|
|
128
|
+
s.close()
|
|
129
|
+
time.sleep(interval)
|
|
130
|
+
|
|
131
|
+
raise TimeoutError(
|
|
132
|
+
f"IP {ip} not bindable within {timeout} seconds; "
|
|
133
|
+
f"last error: {last_err}"
|
|
134
|
+
)
|
|
@@ -31,7 +31,8 @@ class SocketClient:
|
|
|
31
31
|
dns_servers_list: list[str] = None,
|
|
32
32
|
logger: logging.Logger = None,
|
|
33
33
|
custom_pem_client_certificate_file_path: str = None,
|
|
34
|
-
enable_sslkeylogfile_env_to_client_ssl_context: bool = False
|
|
34
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
35
|
+
sslkeylog_file_path:str = None
|
|
35
36
|
):
|
|
36
37
|
"""
|
|
37
38
|
If you have a certificate for domain, but not for the IPv4 address, the SSL Socket context can be created for
|
|
@@ -68,6 +69,7 @@ class SocketClient:
|
|
|
68
69
|
self.dns_servers_list = dns_servers_list
|
|
69
70
|
self.custom_pem_client_certificate_file_path: str = custom_pem_client_certificate_file_path
|
|
70
71
|
self.enable_sslkeylogfile_env_to_client_ssl_context: bool = enable_sslkeylogfile_env_to_client_ssl_context
|
|
72
|
+
self.sslkeylog_file_path: str = sslkeylog_file_path
|
|
71
73
|
|
|
72
74
|
if logger:
|
|
73
75
|
# Create child logger for the provided logger with the module's name.
|
|
@@ -101,7 +103,8 @@ class SocketClient:
|
|
|
101
103
|
socket_object = creator.create_socket_ipv4_tcp()
|
|
102
104
|
return creator.wrap_socket_with_ssl_context_client___default_certs___ignore_verification(
|
|
103
105
|
socket_object, self.service_name, self.custom_pem_client_certificate_file_path,
|
|
104
|
-
enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context
|
|
106
|
+
enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
107
|
+
sslkeylog_file_path=self.sslkeylog_file_path
|
|
105
108
|
)
|
|
106
109
|
|
|
107
110
|
def service_connection(
|
|
@@ -215,7 +218,6 @@ class SocketClient:
|
|
|
215
218
|
self.socket_instance = None
|
|
216
219
|
self.logger.info(f"Closed socket to service server [{self.service_name}:{self.service_port}]")
|
|
217
220
|
|
|
218
|
-
# noinspection PyUnusedLocal
|
|
219
221
|
def send_receive_to_service(
|
|
220
222
|
self,
|
|
221
223
|
request_bytes: Union[bytearray, bytes],
|
|
@@ -228,9 +230,8 @@ class SocketClient:
|
|
|
228
230
|
:param skip_send: If True, the data will not be sent to the service server. After the connection is established,
|
|
229
231
|
the function will wait for the response only.
|
|
230
232
|
"""
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
error_message = None
|
|
233
|
+
|
|
234
|
+
origin_data: bytes | None = None
|
|
234
235
|
|
|
235
236
|
service_socket, error_message = self.service_connection()
|
|
236
237
|
# If connection to service server wasn't successful
|
|
@@ -254,7 +255,7 @@ class SocketClient:
|
|
|
254
255
|
if not skip_send:
|
|
255
256
|
# Send the data received from the client to the service over socket
|
|
256
257
|
error_on_send = Sender(
|
|
257
|
-
ssl_socket=self.socket_instance,
|
|
258
|
+
ssl_socket=self.socket_instance, bytes_to_send=request_bytes, logger=self.logger).send()
|
|
258
259
|
|
|
259
260
|
# If the socket disconnected on data send
|
|
260
261
|
if error_on_send:
|
|
@@ -265,17 +266,17 @@ class SocketClient:
|
|
|
265
266
|
|
|
266
267
|
# Else if send was successful
|
|
267
268
|
if not error_on_send:
|
|
268
|
-
|
|
269
|
+
origin_data, is_socket_closed, error_message = Receiver(
|
|
269
270
|
ssl_socket=self.socket_instance, logger=self.logger).receive()
|
|
270
271
|
|
|
271
272
|
# If data received is empty meaning the socket was closed on the other side
|
|
272
|
-
if not
|
|
273
|
+
if not origin_data:
|
|
273
274
|
error_message = "Service server closed the connection on receive"
|
|
274
275
|
|
|
275
276
|
# We'll close the socket and nullify the object
|
|
276
277
|
self.close_socket()
|
|
277
278
|
|
|
278
|
-
return
|
|
279
|
+
return origin_data, error_message, self.connection_ip, self.socket_instance
|
|
279
280
|
|
|
280
281
|
def send_receive_message_list_with_interval(
|
|
281
282
|
self, requests_bytes_list: list, intervals_list: list, intervals_defaults: int, cycles: int = 1):
|
|
@@ -7,17 +7,20 @@ import socket
|
|
|
7
7
|
import shutil
|
|
8
8
|
import os
|
|
9
9
|
|
|
10
|
+
import paramiko
|
|
11
|
+
|
|
10
12
|
from ...mitm import initialize_engines
|
|
11
13
|
from ..psutilw import psutil_networks
|
|
12
14
|
from ..certauthw import certauthw
|
|
13
15
|
from ..loggingw import loggingw
|
|
14
|
-
from ...
|
|
16
|
+
from ... import package_mains_processor
|
|
15
17
|
from ...permissions import permissions
|
|
16
18
|
from ... import filesystem, certificates
|
|
17
|
-
from ...basics import booleans
|
|
19
|
+
from ...basics import booleans, tracebacks
|
|
18
20
|
from ...print_api import print_api
|
|
21
|
+
from ...ssh_remote import SSHRemote
|
|
19
22
|
|
|
20
|
-
from . import
|
|
23
|
+
from . import socket_base, creator, process_getter, accepter, statistics_csv, ssl_base, sni
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
class SocketWrapperPortInUseError(Exception):
|
|
@@ -65,10 +68,7 @@ class SocketWrapper:
|
|
|
65
68
|
ssh_user: str = None,
|
|
66
69
|
ssh_pass: str = None,
|
|
67
70
|
ssh_script_to_execute: Union[
|
|
68
|
-
Literal[
|
|
69
|
-
'process_from_port',
|
|
70
|
-
'process_from_ipv4'
|
|
71
|
-
],
|
|
71
|
+
Literal['process_from_port'],
|
|
72
72
|
None
|
|
73
73
|
] = None,
|
|
74
74
|
logs_directory: str = None,
|
|
@@ -78,6 +78,8 @@ class SocketWrapper:
|
|
|
78
78
|
statistics_logger_queue: multiprocessing.Queue = None,
|
|
79
79
|
exceptions_logger_name: str = 'SocketWrapperExceptions',
|
|
80
80
|
exceptions_logger_queue: multiprocessing.Queue = None,
|
|
81
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
82
|
+
sslkeylog_file_path: str = None,
|
|
81
83
|
print_kwargs: dict = None,
|
|
82
84
|
):
|
|
83
85
|
"""
|
|
@@ -171,6 +173,12 @@ class SocketWrapper:
|
|
|
171
173
|
:param exceptions_logger_name: string, name of the logger that will be used to log exceptions.
|
|
172
174
|
:param exceptions_logger_queue: multiprocessing.Queue, queue that will be used to log exceptions in
|
|
173
175
|
multiprocessing. You need to start the logger listener in the main process to handle the queue.
|
|
176
|
+
:param enable_sslkeylogfile_env_to_client_ssl_context: boolean, if True, each client SSL context
|
|
177
|
+
that will be created by the SocketWrapper will have save the SSL handshake keys to the file
|
|
178
|
+
defined in 'sslkeylog_file_path' parameter.
|
|
179
|
+
:param sslkeylog_file_path: string, path to file where SSL handshake keys will be saved.
|
|
180
|
+
If not provided and 'enable_sslkeylogfile_env_to_client_ssl_context' is True, then
|
|
181
|
+
the environment variable 'SSLKEYLOGFILE' will be used.
|
|
174
182
|
:param print_kwargs: dict, additional arguments to pass to the print function.
|
|
175
183
|
"""
|
|
176
184
|
|
|
@@ -206,6 +214,9 @@ class SocketWrapper:
|
|
|
206
214
|
self.ssh_script_to_execute = ssh_script_to_execute
|
|
207
215
|
self.forwarding_dns_service_ipv4_list___only_for_localhost = (
|
|
208
216
|
forwarding_dns_service_ipv4_list___only_for_localhost)
|
|
217
|
+
self.enable_sslkeylogfile_env_to_client_ssl_context: bool = (
|
|
218
|
+
enable_sslkeylogfile_env_to_client_ssl_context)
|
|
219
|
+
self.sslkeylog_file_path: str = sslkeylog_file_path
|
|
209
220
|
self.print_kwargs: dict = print_kwargs
|
|
210
221
|
|
|
211
222
|
self.socket_object = None
|
|
@@ -224,12 +235,17 @@ class SocketWrapper:
|
|
|
224
235
|
# Defining listening sockets list, which will be used with "select" library in 'loop_for_incoming_sockets'.
|
|
225
236
|
self.listening_sockets: list = list()
|
|
226
237
|
|
|
227
|
-
# Defining '
|
|
238
|
+
# Defining 'ssh_script_string' variable, which will be used to process SSH scripts.
|
|
228
239
|
self.ssh_script_processor = None
|
|
229
240
|
if self.get_process_name:
|
|
230
241
|
# noinspection PyTypeChecker
|
|
231
|
-
self.
|
|
232
|
-
|
|
242
|
+
self.package_processor: package_mains_processor.PackageMainsProcessor | None = package_mains_processor.PackageMainsProcessor(script_file_stem=self.ssh_script_to_execute)
|
|
243
|
+
|
|
244
|
+
else:
|
|
245
|
+
self.package_processor = None
|
|
246
|
+
|
|
247
|
+
# We will initialize it during the first 'get_process_name' function call.
|
|
248
|
+
self.ssh_client: SSHRemote | None = None
|
|
233
249
|
|
|
234
250
|
# If logs directory was not set, we will use the working directory.
|
|
235
251
|
if not logs_directory:
|
|
@@ -378,7 +394,8 @@ class SocketWrapper:
|
|
|
378
394
|
|
|
379
395
|
os.makedirs(self.sni_server_certificates_cache_directory, exist_ok=True)
|
|
380
396
|
print_api("Removed cached server certificates.", logger=self.logger)
|
|
381
|
-
|
|
397
|
+
else:
|
|
398
|
+
os.makedirs(self.sni_server_certificates_cache_directory, exist_ok=True)
|
|
382
399
|
|
|
383
400
|
if self.install_ca_certificate_to_root_store:
|
|
384
401
|
if not self.ca_certificate_filepath:
|
|
@@ -497,6 +514,13 @@ class SocketWrapper:
|
|
|
497
514
|
listening_sockets: list = [listening_socket_object]
|
|
498
515
|
|
|
499
516
|
while True:
|
|
517
|
+
engine_name: str = ''
|
|
518
|
+
source_ip: str = ''
|
|
519
|
+
source_hostname: str = ''
|
|
520
|
+
dest_port: int = 0
|
|
521
|
+
process_name: str = ''
|
|
522
|
+
domain_from_engine: str = ''
|
|
523
|
+
|
|
500
524
|
try:
|
|
501
525
|
# Using "select.select" which is currently the only API function that works on all
|
|
502
526
|
# operating system types: Windows / Linux / BSD.
|
|
@@ -507,7 +531,6 @@ class SocketWrapper:
|
|
|
507
531
|
|
|
508
532
|
listening_ip, listening_port = listening_socket_object.getsockname()
|
|
509
533
|
|
|
510
|
-
domain_from_engine = None
|
|
511
534
|
# Get the domain to connect on this process in case on no SNI provided.
|
|
512
535
|
for domain, ip_port_dict in self.engine.domain_target_dict.items():
|
|
513
536
|
if ip_port_dict['ip'] == listening_ip:
|
|
@@ -533,41 +556,88 @@ class SocketWrapper:
|
|
|
533
556
|
|
|
534
557
|
self.logger.info(f"Requested domain setting: {domain_from_engine}")
|
|
535
558
|
|
|
559
|
+
engine_name = get_engine_name(domain_from_engine, [self.engine])
|
|
560
|
+
|
|
536
561
|
# Wait from any connection on "accept()".
|
|
537
562
|
# 'client_socket' is socket or ssl socket, 'client_address' is a tuple (ip_address, port).
|
|
538
563
|
client_socket, client_address, accept_error_message = accepter.accept_connection_with_error(
|
|
539
564
|
listening_socket_object, domain_from_dns_server=domain_from_engine,
|
|
540
565
|
print_kwargs={'logger': self.logger})
|
|
541
566
|
|
|
567
|
+
source_ip: str = client_address[0]
|
|
568
|
+
source_port: int = client_address[1]
|
|
569
|
+
dest_port: int = listening_socket_object.getsockname()[1]
|
|
570
|
+
|
|
571
|
+
message: str = f"Accepted connection from [{source_ip}:{source_port}] to [{listening_ip}:{dest_port}] | domain: {domain_from_engine}"
|
|
572
|
+
print_api(message, logger=self.logger)
|
|
573
|
+
|
|
574
|
+
# Not always there will be a hostname resolved by the IP address, so we will leave it empty if it fails.
|
|
575
|
+
try:
|
|
576
|
+
source_hostname = socket.gethostbyaddr(source_ip)[0]
|
|
577
|
+
source_hostname = source_hostname.lower()
|
|
578
|
+
except socket.herror:
|
|
579
|
+
pass
|
|
580
|
+
|
|
542
581
|
# This is the earliest stage to ask for process name.
|
|
543
582
|
# SSH Remote / LOCALHOST script execution to identify process section.
|
|
544
583
|
# If 'get_process_name' was set to True, then this will be executed.
|
|
545
|
-
process_name = None
|
|
546
584
|
if self.get_process_name:
|
|
585
|
+
# Initializing SSHRemote class if not initialized.
|
|
586
|
+
if self.ssh_client is None:
|
|
587
|
+
self.ssh_client = SSHRemote(
|
|
588
|
+
ip_address=source_ip, username=self.ssh_user, password=self.ssh_pass, logger=self.logger)
|
|
589
|
+
|
|
547
590
|
# Get the process name from the socket.
|
|
548
|
-
get_command_instance =
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
591
|
+
get_command_instance = process_getter.GetCommandLine(
|
|
592
|
+
client_ip=source_ip,
|
|
593
|
+
client_port=source_port,
|
|
594
|
+
package_processor=self.package_processor,
|
|
595
|
+
ssh_client=self.ssh_client,
|
|
553
596
|
logger=self.logger)
|
|
554
597
|
process_name = get_command_instance.get_process_name(print_kwargs={'logger': self.logger})
|
|
555
598
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
599
|
+
# from ..pywin32w.win_event_log import fetch
|
|
600
|
+
# events = fetch.get_latest_events(
|
|
601
|
+
# server_ip=source_ip,
|
|
602
|
+
# username=self.ssh_user,
|
|
603
|
+
# password=self.ssh_pass,
|
|
604
|
+
# log_name='Security',
|
|
605
|
+
# count=50,
|
|
606
|
+
# event_id_list=[5156]
|
|
607
|
+
# )
|
|
608
|
+
#
|
|
609
|
+
# source_port = client_address[1]
|
|
610
|
+
# for event in events:
|
|
611
|
+
# if source_port == event['StringsDict']['Source Port']:
|
|
612
|
+
# process_name = event['StringsDict']['Application Name']
|
|
613
|
+
# break
|
|
614
|
+
#
|
|
615
|
+
# if process_name == '':
|
|
616
|
+
# raise RuntimeError("Failed to get process name from the remote host via Event Log.")
|
|
565
617
|
|
|
566
618
|
# If 'accept()' function worked well, SSL worked well, then 'client_socket' won't be empty.
|
|
567
619
|
if client_socket:
|
|
568
620
|
# Get the protocol type from the socket.
|
|
569
621
|
is_tls: bool = False
|
|
570
|
-
|
|
622
|
+
|
|
623
|
+
try:
|
|
624
|
+
tls_properties = ssl_base.is_tls(client_socket, timeout=1)
|
|
625
|
+
except TimeoutError:
|
|
626
|
+
error: str = "TimeoutError: TLS detection timed out. Dropping accepted socket."
|
|
627
|
+
self.logger.error(error)
|
|
628
|
+
|
|
629
|
+
self.statistics_writer.write_accept_error(
|
|
630
|
+
engine=engine_name,
|
|
631
|
+
source_host=source_hostname,
|
|
632
|
+
source_ip=source_ip,
|
|
633
|
+
error_message=error,
|
|
634
|
+
dest_port=str(dest_port),
|
|
635
|
+
host=domain_from_engine,
|
|
636
|
+
process_name=process_name)
|
|
637
|
+
|
|
638
|
+
client_socket.close()
|
|
639
|
+
continue
|
|
640
|
+
|
|
571
641
|
if tls_properties:
|
|
572
642
|
is_tls = True
|
|
573
643
|
tls_type, tls_version = tls_properties
|
|
@@ -604,7 +674,9 @@ class SocketWrapper:
|
|
|
604
674
|
forwarding_dns_service_ipv4_list___only_for_localhost=(
|
|
605
675
|
self.forwarding_dns_service_ipv4_list___only_for_localhost),
|
|
606
676
|
tls=is_tls,
|
|
607
|
-
exceptions_logger=self.exceptions_logger
|
|
677
|
+
exceptions_logger=self.exceptions_logger,
|
|
678
|
+
enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
679
|
+
sslkeylog_file_path=self.sslkeylog_file_path
|
|
608
680
|
)
|
|
609
681
|
|
|
610
682
|
ssl_client_socket, accept_error_message = \
|
|
@@ -613,6 +685,12 @@ class SocketWrapper:
|
|
|
613
685
|
print_kwargs={'logger': self.logger}
|
|
614
686
|
)
|
|
615
687
|
|
|
688
|
+
if ssl_client_socket:
|
|
689
|
+
# Handshake is done at this point, so version/cipher are available
|
|
690
|
+
self.logger.info(
|
|
691
|
+
f"TLS version={ssl_client_socket.version()} cipher={ssl_client_socket.cipher()}"
|
|
692
|
+
)
|
|
693
|
+
|
|
616
694
|
if accept_error_message:
|
|
617
695
|
# Write statistics after wrap is there was an error.
|
|
618
696
|
self.statistics_writer.write_accept_error(
|
|
@@ -660,7 +738,7 @@ class SocketWrapper:
|
|
|
660
738
|
self.threads_list.append(thread_current)
|
|
661
739
|
|
|
662
740
|
# 'thread_callable_args[1][0]' is the client socket.
|
|
663
|
-
client_address =
|
|
741
|
+
client_address = socket_base.get_source_address_from_socket(client_socket)
|
|
664
742
|
|
|
665
743
|
self.logger.info(f"Accepted connection, thread created {client_address}. Continue listening...")
|
|
666
744
|
# Else, if no client_socket was opened during, accept, then print the error.
|
|
@@ -674,8 +752,23 @@ class SocketWrapper:
|
|
|
674
752
|
dest_port=str(dest_port),
|
|
675
753
|
host=domain_from_engine,
|
|
676
754
|
process_name=process_name)
|
|
755
|
+
# Sometimes paramiko SSH connection return EOFError on connection reset, so we need to catch it separately.
|
|
756
|
+
except (ConnectionResetError, paramiko.ssh_exception.SSHException, EOFError) as e:
|
|
757
|
+
exception_string: str = tracebacks.get_as_string()
|
|
758
|
+
full_string: str = f"{str(e)} | {exception_string}"
|
|
759
|
+
self.statistics_writer.write_accept_error(
|
|
760
|
+
engine=engine_name,
|
|
761
|
+
source_host=source_hostname,
|
|
762
|
+
source_ip=source_ip,
|
|
763
|
+
error_message=full_string,
|
|
764
|
+
dest_port=str(dest_port),
|
|
765
|
+
host=domain_from_engine,
|
|
766
|
+
process_name=process_name)
|
|
677
767
|
except Exception as e:
|
|
678
|
-
|
|
768
|
+
_ = e
|
|
769
|
+
exception_string: str = tracebacks.get_as_string()
|
|
770
|
+
full_string: str = f"Engine: [{engine_name}] | {exception_string}"
|
|
771
|
+
self.exceptions_logger.write(full_string)
|
|
679
772
|
|
|
680
773
|
|
|
681
774
|
def before_socket_thread_worker(
|
|
@@ -694,7 +787,7 @@ def before_socket_thread_worker(
|
|
|
694
787
|
try:
|
|
695
788
|
callable_function(*callable_args)
|
|
696
789
|
except Exception as e:
|
|
697
|
-
exceptions_logger.write(e)
|
|
790
|
+
exceptions_logger.write(e, custom_exception_attribute='engine_name', custom_exception_attribute_placement='before')
|
|
698
791
|
|
|
699
792
|
|
|
700
793
|
def get_engine_name(domain: str, engine_list: list):
|
|
@@ -24,15 +24,19 @@ def convert_der_x509_bytes_to_pem_string(certificate) -> str:
|
|
|
24
24
|
return ssl.DER_cert_to_PEM_cert(certificate)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def is_tls(
|
|
27
|
+
def is_tls(
|
|
28
|
+
client_socket,
|
|
29
|
+
timeout: float = None
|
|
30
|
+
) -> Optional[Tuple[str, str]]:
|
|
28
31
|
"""
|
|
29
32
|
Return protocol type of the incoming socket after 'accept()'.
|
|
30
33
|
:param client_socket: incoming socket after 'accept()'.
|
|
34
|
+
:param timeout: optional timeout for receiving/peeking the first bytes.
|
|
31
35
|
:return: tuple with content type, protocol type + version.
|
|
32
36
|
If the length of the first bytes is less than 3, return None.
|
|
33
37
|
"""
|
|
34
38
|
|
|
35
|
-
first_bytes = receiver.peek_first_bytes(client_socket, bytes_amount=3)
|
|
39
|
+
first_bytes = receiver.peek_first_bytes(client_socket, bytes_amount=3, timeout=timeout)
|
|
36
40
|
|
|
37
41
|
# Sometimes only one byte is available, so we need to handle that case.
|
|
38
42
|
# convert to a tuple of ints, add three Nones, and keep only the first 3 items.
|
|
@@ -4,10 +4,15 @@ import subprocess
|
|
|
4
4
|
import shutil
|
|
5
5
|
import time
|
|
6
6
|
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
7
9
|
from ..print_api import print_api
|
|
8
10
|
from ..permissions import ubuntu_permissions
|
|
9
11
|
|
|
10
12
|
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
|
|
11
16
|
def install_packages(
|
|
12
17
|
package_list: list[str],
|
|
13
18
|
timeout_seconds: int = 0,
|
|
@@ -110,7 +115,7 @@ def update_system_packages():
|
|
|
110
115
|
Function updates the system packages.
|
|
111
116
|
:return:
|
|
112
117
|
"""
|
|
113
|
-
subprocess.check_call(['sudo', 'apt
|
|
118
|
+
subprocess.check_call(['sudo', 'apt', 'update'])
|
|
114
119
|
|
|
115
120
|
|
|
116
121
|
def upgrade_system_packages(apt_update: bool = True):
|
|
@@ -124,7 +129,7 @@ def upgrade_system_packages(apt_update: bool = True):
|
|
|
124
129
|
if apt_update:
|
|
125
130
|
update_system_packages()
|
|
126
131
|
|
|
127
|
-
subprocess.check_call(['sudo', 'apt
|
|
132
|
+
subprocess.check_call(['sudo', 'apt', 'upgrade', '-y'])
|
|
128
133
|
|
|
129
134
|
|
|
130
135
|
def is_service_running(service_name: str, user_mode: bool = False, return_false_on_error: bool = False) -> bool:
|
|
@@ -211,28 +216,25 @@ def start_service(service_name: str, sudo: bool = False, user_mode: bool = False
|
|
|
211
216
|
def start_enable_service_check_availability(
|
|
212
217
|
service_name: str,
|
|
213
218
|
wait_time_seconds: float = 30,
|
|
214
|
-
exit_on_error: bool = True,
|
|
215
219
|
start_service_bool: bool = True,
|
|
216
220
|
enable_service_bool: bool = True,
|
|
217
221
|
check_service_running: bool = True,
|
|
218
222
|
user_mode: bool = False,
|
|
219
|
-
sudo: bool = True
|
|
220
|
-
|
|
221
|
-
):
|
|
223
|
+
sudo: bool = True
|
|
224
|
+
) -> int:
|
|
222
225
|
"""
|
|
223
226
|
Function starts and enables a service and checks its availability.
|
|
224
227
|
|
|
225
228
|
:param service_name: str, the service name.
|
|
226
229
|
:param wait_time_seconds: float, the time to wait after starting the service before checking the service
|
|
227
230
|
availability.
|
|
228
|
-
:param exit_on_error: bool, if True, the function will exit the program if the service is not available.
|
|
229
231
|
:param start_service_bool: bool, if True, the service will be started.
|
|
230
232
|
:param enable_service_bool: bool, if True, the service will be enabled.
|
|
231
233
|
:param check_service_running: bool, if True, the function will check if the service is running.
|
|
232
234
|
:param user_mode: bool, if True, the service will be started and enabled in user mode.
|
|
233
235
|
:param sudo: bool, if True, the command will be executed with sudo.
|
|
234
|
-
|
|
235
|
-
:return:
|
|
236
|
+
|
|
237
|
+
:return: int, 0 if the service is running, 1 if the service is not running.
|
|
236
238
|
"""
|
|
237
239
|
|
|
238
240
|
if not start_service_bool and not enable_service_bool:
|
|
@@ -245,18 +247,19 @@ def start_enable_service_check_availability(
|
|
|
245
247
|
enable_service(service_name, user_mode=user_mode,sudo=sudo)
|
|
246
248
|
|
|
247
249
|
if check_service_running:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
250
|
+
print(f"Waiting up to {str(wait_time_seconds)} seconds for the program to start.")
|
|
251
|
+
count: int = 0
|
|
252
|
+
while not is_service_running(service_name, user_mode=user_mode) and count < wait_time_seconds:
|
|
253
|
+
count += 1
|
|
254
|
+
time.sleep(1)
|
|
252
255
|
|
|
253
256
|
if not is_service_running(service_name, user_mode=user_mode):
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if exit_on_error:
|
|
257
|
-
sys.exit(1)
|
|
257
|
+
console.print(f"[{service_name}] service failed to start.", style='red', markup=False)
|
|
258
|
+
return 1
|
|
258
259
|
else:
|
|
259
|
-
|
|
260
|
+
console.print(f"[{service_name}] service is running.", style='green', markup=False)
|
|
261
|
+
|
|
262
|
+
return 0
|
|
260
263
|
|
|
261
264
|
|
|
262
265
|
def add_path_to_bashrc(as_regular_user: bool = False):
|