atomicshop 3.3.28__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 +18 -43
- atomicshop/mitm/connection_thread_worker.py +376 -162
- 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 +79 -88
- atomicshop/mitm/initialize_engines.py +1 -2
- atomicshop/mitm/message.py +5 -4
- atomicshop/mitm/mitm_main.py +222 -121
- atomicshop/mitm/recs_files.py +61 -5
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/networks.py +108 -93
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/ubuntu_permissions.py +47 -0
- atomicshop/print_api.py +3 -5
- atomicshop/python_functions.py +23 -108
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +115 -51
- atomicshop/web.py +20 -7
- 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/factw/install/pre_install_and_install_before_restart.py +5 -5
- atomicshop/wrappers/githubw.py +175 -63
- atomicshop/wrappers/loggingw/handlers.py +1 -1
- atomicshop/wrappers/loggingw/loggingw.py +17 -1
- 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 +30 -7
- atomicshop/wrappers/socketw/dns_server.py +6 -6
- atomicshop/wrappers/socketw/exception_wrapper.py +3 -3
- 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 +23 -6
- atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
- atomicshop/wrappers/socketw/socket_client.py +6 -8
- atomicshop/wrappers/socketw/socket_wrapper.py +82 -21
- atomicshop/wrappers/socketw/ssl_base.py +6 -2
- atomicshop/wrappers/win_auditw.py +189 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/RECORD +74 -88
- 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/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/ffmpegw.py +0 -125
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/get_process.py +0 -123
- /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.28.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
- {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import win32evtlog
|
|
2
|
+
import win32security
|
|
3
|
+
import win32con
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_latest_events(
|
|
7
|
+
server_ip: str = ".",
|
|
8
|
+
username: str = None,
|
|
9
|
+
password: str = None,
|
|
10
|
+
domain: str = ".",
|
|
11
|
+
log_name: str = "Security",
|
|
12
|
+
count: int = None,
|
|
13
|
+
event_id_list: list[int] | None = None
|
|
14
|
+
):
|
|
15
|
+
"""
|
|
16
|
+
Fetch latest `count` events from a Windows Event Log (local or remote) using pywin32.
|
|
17
|
+
|
|
18
|
+
- If username/password are None => use current security context.
|
|
19
|
+
- If server_ip is ".", "localhost", "127.0.0.1", "" or None => open local log.
|
|
20
|
+
- If count is None => return *all* events in the log.
|
|
21
|
+
- If event_id_list is not None => only return events whose EventID is in that list.
|
|
22
|
+
|
|
23
|
+
:param server_ip: IPv4/hostname of remote machine, or "." / None for local
|
|
24
|
+
:param username: Username to authenticate with (optional)
|
|
25
|
+
:param password: Password for the user (optional)
|
|
26
|
+
:param domain: Domain or computer name; ignored if username is None.
|
|
27
|
+
If None and username is given, "." is used.
|
|
28
|
+
:param log_name: Log name (e.g. "Security", "System", "Application")
|
|
29
|
+
:param count: Number of most recent events to return, or None for all
|
|
30
|
+
:param event_id_list: List of Event IDs (low 16-bit) to include, or None for all
|
|
31
|
+
:return: List of dicts describing events (most recent first)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# Normalize server for OpenEventLog: None means "local machine"
|
|
35
|
+
normalized_server = server_ip
|
|
36
|
+
if server_ip in (None, "", ".", "localhost", "127.0.0.1"):
|
|
37
|
+
normalized_server = None
|
|
38
|
+
|
|
39
|
+
# Max events logic: None => infinite
|
|
40
|
+
max_events = float("inf") if count is None else count
|
|
41
|
+
|
|
42
|
+
# Precompute set of event IDs for fast membership checks
|
|
43
|
+
event_ids_set = set(event_id_list) if event_id_list is not None else None
|
|
44
|
+
|
|
45
|
+
# Decide whether we need impersonation
|
|
46
|
+
use_impersonation = username is not None and password is not None
|
|
47
|
+
|
|
48
|
+
h_user = None
|
|
49
|
+
events = []
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
if use_impersonation:
|
|
53
|
+
if domain is None:
|
|
54
|
+
domain = "."
|
|
55
|
+
|
|
56
|
+
# Log on with explicit credentials and impersonate for the remote call
|
|
57
|
+
# LOGON32_LOGON_NEW_CREDENTIALS lets us use these creds for remote access
|
|
58
|
+
# while keeping the local token mostly unchanged.
|
|
59
|
+
h_user = win32security.LogonUser(
|
|
60
|
+
username,
|
|
61
|
+
domain,
|
|
62
|
+
password,
|
|
63
|
+
win32con.LOGON32_LOGON_NEW_CREDENTIALS,
|
|
64
|
+
win32con.LOGON32_PROVIDER_WINNT50,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
win32security.ImpersonateLoggedOnUser(h_user)
|
|
68
|
+
|
|
69
|
+
# Connect to remote event log
|
|
70
|
+
# `server_ip` can be an IP or hostname; no need for leading "\\".
|
|
71
|
+
# local if normalized_server is None.
|
|
72
|
+
h_log = win32evtlog.OpenEventLog(normalized_server, log_name)
|
|
73
|
+
|
|
74
|
+
flags = (
|
|
75
|
+
win32evtlog.EVENTLOG_BACKWARDS_READ
|
|
76
|
+
| win32evtlog.EVENTLOG_SEQUENTIAL_READ
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
offset = 0 # not used with BACKWARDS_READ + SEQUENTIAL_READ, but kept for clarity
|
|
80
|
+
|
|
81
|
+
while len(events) < max_events:
|
|
82
|
+
records = win32evtlog.ReadEventLog(h_log, flags, offset)
|
|
83
|
+
if not records:
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
for ev in records:
|
|
87
|
+
# Low 16 bits are the actual Event ID
|
|
88
|
+
eid = ev.EventID & 0xFFFF
|
|
89
|
+
|
|
90
|
+
# If filtering by event IDs, skip others
|
|
91
|
+
if event_ids_set is not None and eid not in event_ids_set:
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
raw_strings = list(ev.StringInserts or [])
|
|
95
|
+
strings_dict = _parse_strings(eid, ev.SourceName, raw_strings)
|
|
96
|
+
|
|
97
|
+
evt = {
|
|
98
|
+
"RecordNumber": ev.RecordNumber,
|
|
99
|
+
"TimeGenerated": ev.TimeGenerated.Format(), # string time
|
|
100
|
+
"ComputerName": ev.ComputerName,
|
|
101
|
+
"SourceName": ev.SourceName,
|
|
102
|
+
# Low 16 bits are the actual Event ID
|
|
103
|
+
"EventID": ev.EventID & 0xFFFF,
|
|
104
|
+
"EventType": ev.EventType,
|
|
105
|
+
"EventCategory": ev.EventCategory,
|
|
106
|
+
"Strings": raw_strings,
|
|
107
|
+
"StringsDict": strings_dict,
|
|
108
|
+
}
|
|
109
|
+
events.append(evt)
|
|
110
|
+
|
|
111
|
+
if len(events) >= max_events:
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
win32evtlog.CloseEventLog(h_log)
|
|
115
|
+
|
|
116
|
+
finally:
|
|
117
|
+
# Clean up impersonation if we used it, Always revert impersonation and close handle
|
|
118
|
+
if use_impersonation and h_user is not None:
|
|
119
|
+
win32security.RevertToSelf()
|
|
120
|
+
h_user.Close()
|
|
121
|
+
|
|
122
|
+
# `events` is in newest to oldest already because of BACKWARDS_READ.
|
|
123
|
+
return events
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _parse_strings(event_id: int, source_name: str, strings: list[str]) -> dict:
|
|
127
|
+
"""
|
|
128
|
+
Convert the raw 'Strings' list into a dictionary with friendly field names
|
|
129
|
+
for specific event IDs. For unknown events, fall back to String1, String2, ...
|
|
130
|
+
|
|
131
|
+
Currently has a special mapping for:
|
|
132
|
+
- 5156 (Security log, WFP allowed connection)
|
|
133
|
+
"""
|
|
134
|
+
if not strings:
|
|
135
|
+
return {}
|
|
136
|
+
|
|
137
|
+
# Normalize source name a bit
|
|
138
|
+
src = (source_name or "").lower()
|
|
139
|
+
|
|
140
|
+
# Special-case: Security 5156 (Windows Filtering Platform has permitted a connection)
|
|
141
|
+
# Insertion strings (in order) are:
|
|
142
|
+
# 1 Process ID
|
|
143
|
+
# 2 Application Name
|
|
144
|
+
# 3 Direction
|
|
145
|
+
# 4 Source Address
|
|
146
|
+
# 5 Source Port
|
|
147
|
+
# 6 Destination Address
|
|
148
|
+
# 7 Destination Port
|
|
149
|
+
# 8 Protocol
|
|
150
|
+
# 9 Filter Run-Time ID
|
|
151
|
+
# 10 Layer Name
|
|
152
|
+
# 11 Layer Run-Time ID
|
|
153
|
+
if event_id == 5156 and "security-auditing" in src:
|
|
154
|
+
keys_5156 = [
|
|
155
|
+
"Process ID",
|
|
156
|
+
"Application Name",
|
|
157
|
+
"Direction",
|
|
158
|
+
"Source Address",
|
|
159
|
+
"Source Port",
|
|
160
|
+
"Destination Address",
|
|
161
|
+
"Destination Port",
|
|
162
|
+
"Protocol",
|
|
163
|
+
"Filter Run-Time ID",
|
|
164
|
+
"Layer Name",
|
|
165
|
+
"Layer Run-Time ID",
|
|
166
|
+
]
|
|
167
|
+
return {
|
|
168
|
+
key: strings[i]
|
|
169
|
+
for i, key in enumerate(keys_5156)
|
|
170
|
+
if i < len(strings)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# Default: generic mapping
|
|
174
|
+
return {f"String{i+1}": s for i, s in enumerate(strings)}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
import winreg
|
|
3
|
-
|
|
4
1
|
from .. import subscribe
|
|
5
|
-
from
|
|
2
|
+
from .... import win_auditw
|
|
6
3
|
|
|
7
4
|
|
|
8
|
-
AUDITING_REG_PATH: str = r"Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit"
|
|
9
|
-
PROCESS_CREATION_INCLUDE_CMDLINE_VALUE: str = "ProcessCreationIncludeCmdLine_Enabled"
|
|
10
5
|
LOG_CHANNEL: str = 'Security'
|
|
11
6
|
EVENT_ID: int = 4688
|
|
12
7
|
|
|
@@ -30,8 +25,8 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
|
|
|
30
25
|
|
|
31
26
|
def start(self):
|
|
32
27
|
"""Start the subscription process."""
|
|
33
|
-
enable_audit_process_creation()
|
|
34
|
-
enable_command_line_auditing()
|
|
28
|
+
win_auditw.enable_audit_process_creation()
|
|
29
|
+
win_auditw.enable_command_line_auditing()
|
|
35
30
|
|
|
36
31
|
super().start()
|
|
37
32
|
|
|
@@ -60,100 +55,3 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
|
|
|
60
55
|
# print(f"Error looking up account SID: {e}")
|
|
61
56
|
|
|
62
57
|
return data
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def enable_audit_process_creation(print_kwargs: dict = None):
|
|
66
|
-
"""
|
|
67
|
-
Enable the 'Audit Process Creation' policy.
|
|
68
|
-
|
|
69
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
70
|
-
"""
|
|
71
|
-
if is_audit_process_creation_enabled():
|
|
72
|
-
print_api("Audit Process Creation is already enabled.", color='blue', **(print_kwargs or {}))
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
# Enable "Audit Process Creation" policy
|
|
76
|
-
audit_policy_command = [
|
|
77
|
-
"auditpol", "/set", "/subcategory:Process Creation", "/success:enable", "/failure:enable"
|
|
78
|
-
]
|
|
79
|
-
try:
|
|
80
|
-
subprocess.run(audit_policy_command, check=True)
|
|
81
|
-
print_api("Successfully enabled 'Audit Process Creation'.", color='green', **(print_kwargs or {}))
|
|
82
|
-
except subprocess.CalledProcessError as e:
|
|
83
|
-
print_api(f"Failed to enable 'Audit Process Creation': {e}", error_type=True, color='red', **(print_kwargs or {}))
|
|
84
|
-
raise e
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def is_audit_process_creation_enabled(print_kwargs: dict = None) -> bool:
|
|
88
|
-
"""
|
|
89
|
-
Check if the 'Audit Process Creation' policy is enabled.
|
|
90
|
-
|
|
91
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
92
|
-
"""
|
|
93
|
-
# Command to check the "Audit Process Creation" policy
|
|
94
|
-
audit_policy_check_command = [
|
|
95
|
-
"auditpol", "/get", "/subcategory:Process Creation"
|
|
96
|
-
]
|
|
97
|
-
try:
|
|
98
|
-
result = subprocess.run(audit_policy_check_command, check=True, capture_output=True, text=True)
|
|
99
|
-
output = result.stdout
|
|
100
|
-
# print_api(output) # Print the output for inspection
|
|
101
|
-
|
|
102
|
-
if "Process Creation" in output and "Success and Failure" in output:
|
|
103
|
-
# print_api(
|
|
104
|
-
# "'Audit Process Creation' is enabled for both success and failure.",
|
|
105
|
-
# color='green', **(print_kwargs or {}))
|
|
106
|
-
return True
|
|
107
|
-
else:
|
|
108
|
-
# print_api(output, **(print_kwargs or {}))
|
|
109
|
-
# print_api(
|
|
110
|
-
# "'Audit Process Creation' is not fully enabled. Check the output above for details.",
|
|
111
|
-
# color='yellow', **(print_kwargs or {}))
|
|
112
|
-
return False
|
|
113
|
-
except subprocess.CalledProcessError as e:
|
|
114
|
-
print_api(f"Failed to check 'Audit Process Creation': {e}", color='red', error_type=True, **(print_kwargs or {}))
|
|
115
|
-
return False
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def enable_command_line_auditing(print_kwargs: dict = None):
|
|
119
|
-
"""
|
|
120
|
-
Enable the 'Include command line in process creation events' policy.
|
|
121
|
-
|
|
122
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
123
|
-
"""
|
|
124
|
-
|
|
125
|
-
if is_command_line_auditing_enabled():
|
|
126
|
-
print_api(
|
|
127
|
-
"[Include command line in process creation events] is already enabled.", color='blue',
|
|
128
|
-
**(print_kwargs or {}))
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
try:
|
|
132
|
-
# Open the registry key
|
|
133
|
-
with winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, AUDITING_REG_PATH) as reg_key:
|
|
134
|
-
# Set the value
|
|
135
|
-
winreg.SetValueEx(reg_key, PROCESS_CREATION_INCLUDE_CMDLINE_VALUE, 0, winreg.REG_DWORD, 1)
|
|
136
|
-
|
|
137
|
-
print_api(
|
|
138
|
-
"Successfully enabled [Include command line in process creation events].",
|
|
139
|
-
color='green', **(print_kwargs or {}))
|
|
140
|
-
except WindowsError as e:
|
|
141
|
-
print_api(
|
|
142
|
-
f"Failed to enable [Include command line in process creation events]: {e}", error_type=True,
|
|
143
|
-
color='red', **(print_kwargs or {}))
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def is_command_line_auditing_enabled():
|
|
147
|
-
try:
|
|
148
|
-
# Open the registry key
|
|
149
|
-
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, AUDITING_REG_PATH, 0, winreg.KEY_READ) as reg_key:
|
|
150
|
-
# Query the value
|
|
151
|
-
value, reg_type = winreg.QueryValueEx(reg_key, PROCESS_CREATION_INCLUDE_CMDLINE_VALUE)
|
|
152
|
-
# Check if the value is 1 (enabled)
|
|
153
|
-
return value == 1
|
|
154
|
-
except FileNotFoundError:
|
|
155
|
-
# Key or value not found, assume it's not enabled
|
|
156
|
-
return False
|
|
157
|
-
except WindowsError as e:
|
|
158
|
-
print(f"Failed to read the 'Include command line in process creation events' setting: {e}")
|
|
159
|
-
return False
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
|
|
3
1
|
from .. import subscribe
|
|
4
|
-
from
|
|
2
|
+
from .... import win_auditw
|
|
5
3
|
|
|
6
4
|
|
|
7
5
|
LOG_CHANNEL: str = 'Security'
|
|
@@ -27,7 +25,7 @@ class ProcessTerminateSubscriber(subscribe.EventLogSubscriber):
|
|
|
27
25
|
|
|
28
26
|
def start(self):
|
|
29
27
|
"""Start the subscription process."""
|
|
30
|
-
enable_audit_process_termination()
|
|
28
|
+
win_auditw.enable_audit_process_termination()
|
|
31
29
|
|
|
32
30
|
super().start()
|
|
33
31
|
|
|
@@ -48,56 +46,4 @@ class ProcessTerminateSubscriber(subscribe.EventLogSubscriber):
|
|
|
48
46
|
|
|
49
47
|
data['ProcessIdInt'] = int(data['ProcessId'], 16)
|
|
50
48
|
|
|
51
|
-
return data
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def enable_audit_process_termination(print_kwargs: dict = None):
|
|
55
|
-
"""
|
|
56
|
-
Enable the 'Audit Process Termination' policy.
|
|
57
|
-
|
|
58
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
59
|
-
"""
|
|
60
|
-
if is_audit_process_termination_enabled():
|
|
61
|
-
print_api("Audit Process Termination is already enabled.", color='blue', **(print_kwargs or {}))
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
audit_policy_command = [
|
|
65
|
-
"auditpol", "/set", "/subcategory:Process Termination", "/success:enable", "/failure:enable"
|
|
66
|
-
]
|
|
67
|
-
try:
|
|
68
|
-
subprocess.run(audit_policy_command, check=True)
|
|
69
|
-
print_api("Successfully enabled 'Audit Process Termination'.", color='green', **(print_kwargs or {}))
|
|
70
|
-
except subprocess.CalledProcessError as e:
|
|
71
|
-
print_api(f"Failed to enable 'Audit Process Termination': {e}", error_type=True, color='red', **(print_kwargs or {}))
|
|
72
|
-
raise e
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def is_audit_process_termination_enabled(print_kwargs: dict = None) -> bool:
|
|
76
|
-
"""
|
|
77
|
-
Check if the 'Audit Process Termination' policy is enabled.
|
|
78
|
-
|
|
79
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
80
|
-
"""
|
|
81
|
-
# Command to check the "Audit Process Creation" policy
|
|
82
|
-
audit_policy_check_command = [
|
|
83
|
-
"auditpol", "/get", "/subcategory:Process Termination"
|
|
84
|
-
]
|
|
85
|
-
try:
|
|
86
|
-
result = subprocess.run(audit_policy_check_command, check=True, capture_output=True, text=True)
|
|
87
|
-
output = result.stdout
|
|
88
|
-
# print_api(output) # Print the output for inspection
|
|
89
|
-
|
|
90
|
-
if "Process Termination" in output and "Success and Failure" in output:
|
|
91
|
-
# print_api(
|
|
92
|
-
# "'Audit Process Termination' is enabled for both success and failure.",
|
|
93
|
-
# color='green', **(print_kwargs or {}))
|
|
94
|
-
return True
|
|
95
|
-
else:
|
|
96
|
-
# print_api(output, **(print_kwargs or {}))
|
|
97
|
-
# print_api(
|
|
98
|
-
# "'Audit Process Termination' is not fully enabled. Check the output above for details.",
|
|
99
|
-
# color='yellow', **(print_kwargs or {}))
|
|
100
|
-
return False
|
|
101
|
-
except subprocess.CalledProcessError as e:
|
|
102
|
-
print_api(f"Failed to check 'Audit Process Termination': {e}", color='red', error_type=True, **(print_kwargs or {}))
|
|
103
|
-
return False
|
|
49
|
+
return data
|
|
@@ -6,7 +6,6 @@ from win32com.client import CDispatch
|
|
|
6
6
|
import pywintypes
|
|
7
7
|
|
|
8
8
|
from . import wmi_helpers, win32networkadapter
|
|
9
|
-
from ...psutilw import psutil_networks
|
|
10
9
|
from .... import ip_addresses
|
|
11
10
|
|
|
12
11
|
|
|
@@ -30,16 +29,13 @@ def get_network_configuration_by_adapter(
|
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
def get_adapter_network_configuration(
|
|
33
|
-
|
|
34
|
-
connection_name: str = None,
|
|
32
|
+
interface_name: str = None,
|
|
35
33
|
mac_address: str = None,
|
|
36
34
|
wmi_instance: CDispatch = None
|
|
37
35
|
) -> tuple:
|
|
38
36
|
"""
|
|
39
37
|
Get the WMI network configuration for a network adapter.
|
|
40
|
-
:param
|
|
41
|
-
This is the adapter that your internet is being used from.
|
|
42
|
-
:param connection_name: string, adapter name as shown in the network settings.
|
|
38
|
+
:param interface_name: string, adapter name as shown in the network settings.
|
|
43
39
|
:param mac_address: string, MAC address of the adapter. Format: '00:00:00:00:00:00'.
|
|
44
40
|
:param wmi_instance: WMI instance. You can get it from:
|
|
45
41
|
wrappers.pywin32s.wmis.wmi_helpers.get_wmi_instance()
|
|
@@ -47,37 +43,25 @@ def get_adapter_network_configuration(
|
|
|
47
43
|
:return: tuple(Win32_NetworkAdapterConfiguration, Win32_NetworkAdapter)
|
|
48
44
|
"""
|
|
49
45
|
|
|
50
|
-
if use_default_interface and connection_name:
|
|
51
|
-
raise ValueError("Only one of 'use_default_interface' or 'connection_name' must be provided.")
|
|
52
|
-
elif not use_default_interface and not connection_name:
|
|
53
|
-
raise ValueError("Either 'use_default_interface' or 'connection_name' must be provided.")
|
|
54
|
-
|
|
55
46
|
if not wmi_instance:
|
|
56
47
|
wmi_instance, _ = wmi_helpers.get_wmi_instance()
|
|
57
48
|
|
|
58
49
|
adapters = win32networkadapter.list_network_adapters(wmi_instance)
|
|
59
50
|
|
|
51
|
+
if interface_name is None and mac_address is None:
|
|
52
|
+
raise ValueError("Either 'interface_name' or 'mac_address' must be provided.")
|
|
53
|
+
elif interface_name and mac_address:
|
|
54
|
+
raise ValueError("Only one of 'interface_name' or 'mac_address' must be provided.")
|
|
55
|
+
|
|
60
56
|
current_adapter = None
|
|
61
|
-
if
|
|
62
|
-
default_connection_name_dict: dict = psutil_networks.get_default_connection_name()
|
|
63
|
-
if not default_connection_name_dict:
|
|
64
|
-
raise wmi_helpers.WMINetworkAdapterNotFoundError("Default network adapter not found.")
|
|
65
|
-
# Get the first key from the dictionary.
|
|
66
|
-
connection_name: str = list(default_connection_name_dict.keys())[0]
|
|
67
|
-
|
|
68
|
-
if connection_name is None and mac_address is None:
|
|
69
|
-
raise ValueError("Either 'connection_name' or 'mac_address' must be provided.")
|
|
70
|
-
elif connection_name and mac_address:
|
|
71
|
-
raise ValueError("Only one of 'connection_name' or 'mac_address' must be provided.")
|
|
72
|
-
|
|
73
|
-
if connection_name:
|
|
57
|
+
if interface_name:
|
|
74
58
|
for adapter in adapters:
|
|
75
|
-
if
|
|
59
|
+
if interface_name == adapter.NetConnectionID:
|
|
76
60
|
current_adapter = adapter
|
|
77
61
|
break
|
|
78
62
|
|
|
79
63
|
if not current_adapter:
|
|
80
|
-
raise wmi_helpers.WMINetworkAdapterNotFoundError(f"Adapter with
|
|
64
|
+
raise wmi_helpers.WMINetworkAdapterNotFoundError(f"Adapter with interface name '{interface_name}' not found.")
|
|
81
65
|
elif mac_address:
|
|
82
66
|
for adapter in adapters:
|
|
83
67
|
if mac_address == adapter.MACAddress:
|
|
@@ -93,7 +77,7 @@ def get_adapter_network_configuration(
|
|
|
93
77
|
|
|
94
78
|
# Check if the adapter exists
|
|
95
79
|
if len(adapter_configs) == 0:
|
|
96
|
-
raise wmi_helpers.WMINetworkAdapterNotFoundError(f"Adapter with
|
|
80
|
+
raise wmi_helpers.WMINetworkAdapterNotFoundError(f"Adapter with interface name '{interface_name}' not found.")
|
|
97
81
|
|
|
98
82
|
return adapter_configs[0], current_adapter
|
|
99
83
|
|
|
@@ -260,6 +244,7 @@ def get_info_from_network_config(
|
|
|
260
244
|
|
|
261
245
|
ipv4s, ipv6s, ipv4subnets, ipv6prefixes = _split_ips(network_config)
|
|
262
246
|
adapter = {
|
|
247
|
+
"caption": network_config.Caption,
|
|
263
248
|
"description": network_config.Description,
|
|
264
249
|
"interface_index": network_config.InterfaceIndex,
|
|
265
250
|
"is_dynamic": bool(network_config.DHCPEnabled),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from logging import Logger
|
|
1
2
|
from typing import Union
|
|
2
3
|
|
|
3
4
|
from win32com.client import CDispatch
|
|
@@ -80,27 +81,32 @@ def get_default_network_adapter(wmi_instance: CDispatch = None):
|
|
|
80
81
|
|
|
81
82
|
def set_dns_server(
|
|
82
83
|
dns_servers: Union[list[str], None],
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
interface_name: str = None,
|
|
85
|
+
mac_address: str = None,
|
|
86
|
+
verbose: bool = False,
|
|
87
|
+
logger: Logger = None
|
|
86
88
|
):
|
|
87
89
|
"""
|
|
88
90
|
Set the DNS servers for a network adapter.
|
|
89
91
|
:param dns_servers: list of strings, DNS server IPv4 addresses.
|
|
90
92
|
None, if you want to remove the DNS servers and make the interface to obtain them automatically from DHCP.
|
|
91
93
|
list[str], if you want to set the DNS servers manually to the list of strings.
|
|
92
|
-
:param
|
|
93
|
-
This is the adapter that your internet is being used from.
|
|
94
|
-
:param connection_name: string, adapter name as shown in the network settings.
|
|
94
|
+
:param interface_name: string, network interface name as shown in the network settings.
|
|
95
95
|
:param mac_address: string, MAC address of the adapter. Format: '00:00:00:00:00:00'.
|
|
96
|
+
|
|
97
|
+
:param verbose: bool, if True, print verbose output.
|
|
98
|
+
:param logger: Logger object, if provided, will log the output instead of printing.
|
|
96
99
|
:return:
|
|
97
100
|
"""
|
|
98
101
|
|
|
99
102
|
adapter_config, current_adapter = win32_networkadapterconfiguration.get_adapter_network_configuration(
|
|
100
|
-
|
|
103
|
+
interface_name=interface_name, mac_address=mac_address)
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
if verbose:
|
|
106
|
+
message = (
|
|
107
|
+
f"Adapter [{current_adapter.Description}], Connection name [{current_adapter.NetConnectionID}]\n"
|
|
108
|
+
f"Setting DNS servers to {dns_servers}")
|
|
109
|
+
print_api(message, color='blue', logger=logger)
|
|
104
110
|
|
|
105
111
|
# Set DNS servers
|
|
106
112
|
wmi_helpers.call_method(adapter_config, 'SetDNSServerSearchOrder', dns_servers)
|
|
@@ -3,7 +3,7 @@ import sys
|
|
|
3
3
|
|
|
4
4
|
from cryptography import x509
|
|
5
5
|
|
|
6
|
-
from . import creator,
|
|
6
|
+
from . import creator, socket_base, socket_client
|
|
7
7
|
from .. import pyopensslw, cryptographyw
|
|
8
8
|
from ..certauthw.certauthw import CertAuthWrapper
|
|
9
9
|
from ...print_api import print_api
|
|
@@ -30,7 +30,9 @@ class Certificator:
|
|
|
30
30
|
custom_private_key_path: str,
|
|
31
31
|
forwarding_dns_service_ipv4_list___only_for_localhost: list,
|
|
32
32
|
skip_extension_id_list: list,
|
|
33
|
-
tls: bool
|
|
33
|
+
tls: bool,
|
|
34
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool,
|
|
35
|
+
sslkeylog_file_path: str
|
|
34
36
|
):
|
|
35
37
|
self.ca_certificate_name = ca_certificate_name
|
|
36
38
|
self.ca_certificate_filepath = ca_certificate_filepath
|
|
@@ -49,6 +51,9 @@ class Certificator:
|
|
|
49
51
|
forwarding_dns_service_ipv4_list___only_for_localhost)
|
|
50
52
|
self.skip_extension_id_list = skip_extension_id_list
|
|
51
53
|
self.tls = tls
|
|
54
|
+
self.enable_sslkeylogfile_env_to_client_ssl_context: bool = (
|
|
55
|
+
enable_sslkeylogfile_env_to_client_ssl_context)
|
|
56
|
+
self.sslkeylog_file_path: str = sslkeylog_file_path
|
|
52
57
|
|
|
53
58
|
# noinspection PyTypeChecker
|
|
54
59
|
self.certauth_wrapper: CertAuthWrapper = None
|
|
@@ -146,13 +151,13 @@ class Certificator:
|
|
|
146
151
|
self.sni_server_certificate_from_server_socket_download_directory + \
|
|
147
152
|
os.sep + sni_received_parameters.destination_name + ".pem"
|
|
148
153
|
# Get client ip.
|
|
149
|
-
client_ip =
|
|
154
|
+
client_ip = socket_base.get_source_address_from_socket(sni_received_parameters.ssl_socket)[0]
|
|
150
155
|
|
|
151
156
|
# If we're on localhost, then use external services list in order to resolve the domain:
|
|
152
|
-
if client_ip in
|
|
157
|
+
if client_ip in socket_base.THIS_DEVICE_IP_LIST:
|
|
153
158
|
service_client = socket_client.SocketClient(
|
|
154
159
|
service_name=sni_received_parameters.destination_name,
|
|
155
|
-
service_port=
|
|
160
|
+
service_port=socket_base.get_destination_address_from_socket(sni_received_parameters.ssl_socket)[1],
|
|
156
161
|
tls=self.tls,
|
|
157
162
|
dns_servers_list=self.forwarding_dns_service_ipv4_list___only_for_localhost,
|
|
158
163
|
logger=print_kwargs.get('logger') if print_kwargs else None
|
|
@@ -161,7 +166,7 @@ class Certificator:
|
|
|
161
166
|
else:
|
|
162
167
|
service_client = socket_client.SocketClient(
|
|
163
168
|
service_name=sni_received_parameters.destination_name,
|
|
164
|
-
service_port=
|
|
169
|
+
service_port=socket_base.get_destination_address_from_socket(sni_received_parameters.ssl_socket)[1],
|
|
165
170
|
tls=self.tls,
|
|
166
171
|
logger=print_kwargs.get('logger') if print_kwargs else None
|
|
167
172
|
)
|
|
@@ -215,11 +220,16 @@ class Certificator:
|
|
|
215
220
|
sni_received_parameters.destination_name, certificate_from_socket_x509)
|
|
216
221
|
|
|
217
222
|
message = f"SNI Handler: port " \
|
|
218
|
-
f"{
|
|
223
|
+
f"{socket_base.get_destination_address_from_socket(sni_received_parameters.ssl_socket)[1]}: " \
|
|
219
224
|
f"Using certificate: {sni_server_certificate_file_path}"
|
|
220
225
|
print_api(message, **print_kwargs)
|
|
221
226
|
|
|
222
227
|
# You need to build new context and exchange the context that being inherited from the main socket,
|
|
223
228
|
# or else the context will receive previous certificate each time.
|
|
224
|
-
sni_received_parameters.ssl_socket.context =
|
|
225
|
-
creator.create_server_ssl_context___load_certificate_and_key(
|
|
229
|
+
sni_received_parameters.ssl_socket.context = (
|
|
230
|
+
creator.create_server_ssl_context___load_certificate_and_key(
|
|
231
|
+
certificate_file_path=sni_server_certificate_file_path, key_file_path=None,
|
|
232
|
+
enable_sslkeylogfile_env_to_client_ssl_context=self.enable_sslkeylogfile_env_to_client_ssl_context,
|
|
233
|
+
sslkeylog_file_path=self.sslkeylog_file_path
|
|
234
|
+
)
|
|
235
|
+
)
|
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import socket
|
|
3
3
|
import ssl
|
|
4
4
|
|
|
5
|
-
from . import
|
|
5
|
+
from . import socket_base, exception_wrapper
|
|
6
6
|
from ...print_api import print_api
|
|
7
7
|
|
|
8
8
|
|
|
@@ -25,7 +25,15 @@ def add_reusable_address_option(socket_instance):
|
|
|
25
25
|
socket_instance.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def create_ssl_context_for_server(
|
|
28
|
+
def create_ssl_context_for_server(
|
|
29
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
30
|
+
sslkeylog_file_path: str = None,
|
|
31
|
+
allow_legacy: bool = False
|
|
32
|
+
) -> ssl.SSLContext:
|
|
33
|
+
"""
|
|
34
|
+
This function creates the SSL context for the server.
|
|
35
|
+
Meaning that your script will act like a server, and the client will connect to it.
|
|
36
|
+
"""
|
|
29
37
|
# Creating context with SSL certificate and the private key before the socket
|
|
30
38
|
# https://docs.python.org/3/library/ssl.html
|
|
31
39
|
# Creating context for SSL wrapper, specifying "PROTOCOL_TLS_SERVER" will pick the best TLS version protocol for
|
|
@@ -48,6 +56,15 @@ def create_ssl_context_for_server(allow_legacy=False) -> ssl.SSLContext:
|
|
|
48
56
|
ssl_context.verify_mode = ssl.CERT_NONE
|
|
49
57
|
ssl_context.check_hostname = False
|
|
50
58
|
|
|
59
|
+
if enable_sslkeylogfile_env_to_client_ssl_context:
|
|
60
|
+
if sslkeylog_file_path is None:
|
|
61
|
+
sslkeylog_file_path = os.environ.get('SSLKEYLOGFILE')
|
|
62
|
+
|
|
63
|
+
if not os.path.exists(sslkeylog_file_path):
|
|
64
|
+
open(sslkeylog_file_path, "a").close()
|
|
65
|
+
|
|
66
|
+
ssl_context.keylog_filename = sslkeylog_file_path
|
|
67
|
+
|
|
51
68
|
# If you must support old clients that only offer TLS_RSA_* suites under OpenSSL 3:
|
|
52
69
|
if allow_legacy:
|
|
53
70
|
# This enables RSA key exchange and other legacy bits at security level 1
|
|
@@ -64,6 +81,7 @@ def create_ssl_context_for_client(
|
|
|
64
81
|
) -> ssl.SSLContext:
|
|
65
82
|
"""
|
|
66
83
|
This function creates the SSL context for the client.
|
|
84
|
+
This means that your script will act like a client, and will connect to a server.
|
|
67
85
|
The SSL context is created with the "PROTOCOL_TLS_CLIENT" protocol.
|
|
68
86
|
|
|
69
87
|
:param enable_sslkeylogfile_env_to_client_ssl_context: boolean, enables the SSLKEYLOGFILE environment variable
|
|
@@ -83,8 +101,9 @@ def create_ssl_context_for_client(
|
|
|
83
101
|
if sslkeylog_file_path is None:
|
|
84
102
|
sslkeylog_file_path = os.environ.get('SSLKEYLOGFILE')
|
|
85
103
|
|
|
86
|
-
|
|
87
|
-
|
|
104
|
+
if not os.path.exists(sslkeylog_file_path):
|
|
105
|
+
open(sslkeylog_file_path, "a").close()
|
|
106
|
+
|
|
88
107
|
ssl_context.keylog_filename = sslkeylog_file_path
|
|
89
108
|
|
|
90
109
|
current_ciphers = 'AES256-GCM-SHA384:' + ssl._DEFAULT_CIPHERS
|
|
@@ -191,10 +210,14 @@ def copy_server_ctx_settings(src: ssl.SSLContext, dst: ssl.SSLContext) -> None:
|
|
|
191
210
|
def create_server_ssl_context___load_certificate_and_key(
|
|
192
211
|
certificate_file_path: str,
|
|
193
212
|
key_file_path: str | None,
|
|
194
|
-
inherit_from: ssl.SSLContext | None = None
|
|
213
|
+
inherit_from: ssl.SSLContext | None = None,
|
|
214
|
+
enable_sslkeylogfile_env_to_client_ssl_context: bool = False,
|
|
215
|
+
sslkeylog_file_path: str = None,
|
|
195
216
|
) -> ssl.SSLContext:
|
|
196
217
|
# Create and set ssl context for server.
|
|
197
|
-
ssl_context: ssl.SSLContext = create_ssl_context_for_server(
|
|
218
|
+
ssl_context: ssl.SSLContext = create_ssl_context_for_server(
|
|
219
|
+
allow_legacy=True, enable_sslkeylogfile_env_to_client_ssl_context=enable_sslkeylogfile_env_to_client_ssl_context,
|
|
220
|
+
sslkeylog_file_path=sslkeylog_file_path)
|
|
198
221
|
|
|
199
222
|
# If you replaced contexts during SNI, copy policy from the old one
|
|
200
223
|
if inherit_from is not None:
|
|
@@ -290,7 +313,7 @@ def set_listen_on_socket(socket_object, **kwargs):
|
|
|
290
313
|
# To determine the maximum listening sockets, you may use the 'socket' library and 'SOMAXCONN' parameter
|
|
291
314
|
# from it.
|
|
292
315
|
socket_object.listen(socket.SOMAXCONN)
|
|
293
|
-
ip_address, port =
|
|
316
|
+
ip_address, port = socket_base.get_destination_address_from_socket(socket_object)
|
|
294
317
|
|
|
295
318
|
print_api(f"Listening for new connections on: {ip_address}:{port}", **kwargs)
|
|
296
319
|
|